diff options
author | Allan Sandfeld Jensen <allan.jensen@theqtcompany.com> | 2016-01-25 11:39:07 +0100 |
---|---|---|
committer | Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com> | 2016-01-25 15:20:42 +0000 |
commit | 6c91641271e536ffaa88a1dff5127e42ee99a91e (patch) | |
tree | 703d9dd49602377ddc90cbf886aad37913f2496b /chromium/v8/src/interpreter | |
parent | b145b7fafd36f0c260d6a768c81fc14e32578099 (diff) | |
download | qtwebengine-chromium-6c91641271e536ffaa88a1dff5127e42ee99a91e.tar.gz |
BASELINE: Update Chromium to 49.0.2623.23
Also adds missing printing sources.
Change-Id: I3726b8f0c7d6751c9fc846096c571fadca7108cd
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com>
Diffstat (limited to 'chromium/v8/src/interpreter')
18 files changed, 6227 insertions, 665 deletions
diff --git a/chromium/v8/src/interpreter/OWNERS b/chromium/v8/src/interpreter/OWNERS index 906a5ce6418..5ad730c8a43 100644 --- a/chromium/v8/src/interpreter/OWNERS +++ b/chromium/v8/src/interpreter/OWNERS @@ -1 +1,6 @@ +set noparent + +bmeurer@chromium.org +mstarzinger@chromium.org +oth@chromium.org rmcilroy@chromium.org diff --git a/chromium/v8/src/interpreter/bytecode-array-builder.cc b/chromium/v8/src/interpreter/bytecode-array-builder.cc index 9c6b5905ccf..1b15fc66686 100644 --- a/chromium/v8/src/interpreter/bytecode-array-builder.cc +++ b/chromium/v8/src/interpreter/bytecode-array-builder.cc @@ -8,120 +8,262 @@ namespace v8 { namespace internal { namespace interpreter { +class BytecodeArrayBuilder::PreviousBytecodeHelper { + public: + explicit PreviousBytecodeHelper(const BytecodeArrayBuilder& array_builder) + : array_builder_(array_builder), + previous_bytecode_start_(array_builder_.last_bytecode_start_) { + // This helper is expected to be instantiated only when the last bytecode is + // in the same basic block. + DCHECK(array_builder_.LastBytecodeInSameBlock()); + } + + // Returns the previous bytecode in the same basic block. + MUST_USE_RESULT Bytecode GetBytecode() const { + DCHECK_EQ(array_builder_.last_bytecode_start_, previous_bytecode_start_); + return Bytecodes::FromByte( + array_builder_.bytecodes()->at(previous_bytecode_start_)); + } + + // Returns the operand at operand_index for the previous bytecode in the + // same basic block. + MUST_USE_RESULT uint32_t GetOperand(int operand_index) const { + DCHECK_EQ(array_builder_.last_bytecode_start_, previous_bytecode_start_); + Bytecode bytecode = GetBytecode(); + DCHECK_GE(operand_index, 0); + DCHECK_LT(operand_index, Bytecodes::NumberOfOperands(bytecode)); + size_t operand_offset = + previous_bytecode_start_ + + Bytecodes::GetOperandOffset(bytecode, operand_index); + OperandSize size = Bytecodes::GetOperandSize(bytecode, operand_index); + switch (size) { + default: + case OperandSize::kNone: + UNREACHABLE(); + case OperandSize::kByte: + return static_cast<uint32_t>( + array_builder_.bytecodes()->at(operand_offset)); + case OperandSize::kShort: + uint16_t operand = + (array_builder_.bytecodes()->at(operand_offset) << 8) + + array_builder_.bytecodes()->at(operand_offset + 1); + return static_cast<uint32_t>(operand); + } + } + + Handle<Object> GetConstantForIndexOperand(int operand_index) const { + return array_builder_.constant_array_builder()->At( + GetOperand(operand_index)); + } + + private: + const BytecodeArrayBuilder& array_builder_; + size_t previous_bytecode_start_; + + DISALLOW_COPY_AND_ASSIGN(PreviousBytecodeHelper); +}; + + BytecodeArrayBuilder::BytecodeArrayBuilder(Isolate* isolate, Zone* zone) : isolate_(isolate), + zone_(zone), bytecodes_(zone), bytecode_generated_(false), + constant_array_builder_(isolate, zone), last_block_end_(0), last_bytecode_start_(~0), - return_seen_in_block_(false), - constants_map_(isolate->heap(), zone), - constants_(zone), + exit_seen_in_block_(false), + unbound_jumps_(0), parameter_count_(-1), local_register_count_(-1), + context_register_count_(-1), temporary_register_count_(0), - temporary_register_next_(0) {} + free_temporaries_(zone) {} + + +BytecodeArrayBuilder::~BytecodeArrayBuilder() { DCHECK_EQ(0, unbound_jumps_); } void BytecodeArrayBuilder::set_locals_count(int number_of_locals) { local_register_count_ = number_of_locals; - temporary_register_next_ = local_register_count_; + DCHECK_LE(context_register_count_, 0); } -int BytecodeArrayBuilder::locals_count() const { return local_register_count_; } - - void BytecodeArrayBuilder::set_parameter_count(int number_of_parameters) { parameter_count_ = number_of_parameters; } -int BytecodeArrayBuilder::parameter_count() const { return parameter_count_; } +void BytecodeArrayBuilder::set_context_count(int number_of_contexts) { + context_register_count_ = number_of_contexts; + DCHECK_GE(local_register_count_, 0); +} + + +Register BytecodeArrayBuilder::first_context_register() const { + DCHECK_GT(context_register_count_, 0); + return Register(local_register_count_); +} + + +Register BytecodeArrayBuilder::last_context_register() const { + DCHECK_GT(context_register_count_, 0); + return Register(local_register_count_ + context_register_count_ - 1); +} + + +Register BytecodeArrayBuilder::first_temporary_register() const { + DCHECK_GT(temporary_register_count_, 0); + return Register(fixed_register_count()); +} + + +Register BytecodeArrayBuilder::last_temporary_register() const { + DCHECK_GT(temporary_register_count_, 0); + return Register(fixed_register_count() + temporary_register_count_ - 1); +} -Register BytecodeArrayBuilder::Parameter(int parameter_index) { +Register BytecodeArrayBuilder::Parameter(int parameter_index) const { DCHECK_GE(parameter_index, 0); - DCHECK_LT(parameter_index, parameter_count_); - return Register::FromParameterIndex(parameter_index, parameter_count_); + return Register::FromParameterIndex(parameter_index, parameter_count()); +} + + +bool BytecodeArrayBuilder::RegisterIsParameterOrLocal(Register reg) const { + return reg.is_parameter() || reg.index() < locals_count(); +} + + +bool BytecodeArrayBuilder::RegisterIsTemporary(Register reg) const { + return temporary_register_count_ > 0 && first_temporary_register() <= reg && + reg <= last_temporary_register(); } Handle<BytecodeArray> BytecodeArrayBuilder::ToBytecodeArray() { DCHECK_EQ(bytecode_generated_, false); - DCHECK_GE(parameter_count_, 0); - DCHECK_GE(local_register_count_, 0); - EnsureReturn(); int bytecode_size = static_cast<int>(bytecodes_.size()); - int register_count = local_register_count_ + temporary_register_count_; + int register_count = fixed_register_count() + temporary_register_count_; int frame_size = register_count * kPointerSize; - Factory* factory = isolate_->factory(); - int constants_count = static_cast<int>(constants_.size()); Handle<FixedArray> constant_pool = - factory->NewFixedArray(constants_count, TENURED); - for (int i = 0; i < constants_count; i++) { - constant_pool->set(i, *constants_[i]); - } - + constant_array_builder()->ToFixedArray(factory); Handle<BytecodeArray> output = factory->NewBytecodeArray(bytecode_size, &bytecodes_.front(), frame_size, - parameter_count_, constant_pool); + parameter_count(), constant_pool); bytecode_generated_ = true; return output; } template <size_t N> -void BytecodeArrayBuilder::Output(uint8_t(&bytes)[N]) { - DCHECK_EQ(Bytecodes::NumberOfOperands(Bytecodes::FromByte(bytes[0])), - static_cast<int>(N) - 1); +void BytecodeArrayBuilder::Output(Bytecode bytecode, uint32_t(&operands)[N]) { + // Don't output dead code. + if (exit_seen_in_block_) return; + + DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), static_cast<int>(N)); last_bytecode_start_ = bytecodes()->size(); - for (int i = 1; i < static_cast<int>(N); i++) { - DCHECK(OperandIsValid(Bytecodes::FromByte(bytes[0]), i - 1, bytes[i])); + bytecodes()->push_back(Bytecodes::ToByte(bytecode)); + for (int i = 0; i < static_cast<int>(N); i++) { + DCHECK(OperandIsValid(bytecode, i, operands[i])); + switch (Bytecodes::GetOperandSize(bytecode, i)) { + case OperandSize::kNone: + UNREACHABLE(); + case OperandSize::kByte: + bytecodes()->push_back(static_cast<uint8_t>(operands[i])); + break; + case OperandSize::kShort: { + uint8_t operand_bytes[2]; + WriteUnalignedUInt16(operand_bytes, operands[i]); + bytecodes()->insert(bytecodes()->end(), operand_bytes, + operand_bytes + 2); + break; + } + } } - bytecodes()->insert(bytecodes()->end(), bytes, bytes + N); } -void BytecodeArrayBuilder::Output(Bytecode bytecode, uint8_t operand0, - uint8_t operand1, uint8_t operand2) { - uint8_t bytes[] = {Bytecodes::ToByte(bytecode), operand0, operand1, operand2}; - Output(bytes); +void BytecodeArrayBuilder::Output(Bytecode bytecode, uint32_t operand0, + uint32_t operand1, uint32_t operand2, + uint32_t operand3) { + uint32_t operands[] = {operand0, operand1, operand2, operand3}; + Output(bytecode, operands); +} + + +void BytecodeArrayBuilder::Output(Bytecode bytecode, uint32_t operand0, + uint32_t operand1, uint32_t operand2) { + uint32_t operands[] = {operand0, operand1, operand2}; + Output(bytecode, operands); } -void BytecodeArrayBuilder::Output(Bytecode bytecode, uint8_t operand0, - uint8_t operand1) { - uint8_t bytes[] = {Bytecodes::ToByte(bytecode), operand0, operand1}; - Output(bytes); +void BytecodeArrayBuilder::Output(Bytecode bytecode, uint32_t operand0, + uint32_t operand1) { + uint32_t operands[] = {operand0, operand1}; + Output(bytecode, operands); } -void BytecodeArrayBuilder::Output(Bytecode bytecode, uint8_t operand0) { - uint8_t bytes[] = {Bytecodes::ToByte(bytecode), operand0}; - Output(bytes); +void BytecodeArrayBuilder::Output(Bytecode bytecode, uint32_t operand0) { + uint32_t operands[] = {operand0}; + Output(bytecode, operands); } void BytecodeArrayBuilder::Output(Bytecode bytecode) { - uint8_t bytes[] = {Bytecodes::ToByte(bytecode)}; - Output(bytes); + // Don't output dead code. + if (exit_seen_in_block_) return; + + DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), 0); + last_bytecode_start_ = bytecodes()->size(); + bytecodes()->push_back(Bytecodes::ToByte(bytecode)); } BytecodeArrayBuilder& BytecodeArrayBuilder::BinaryOperation(Token::Value op, - Register reg) { + Register reg, + Strength strength) { + if (is_strong(strength)) { + UNIMPLEMENTED(); + } + Output(BytecodeForBinaryOperation(op), reg.ToOperand()); return *this; } +BytecodeArrayBuilder& BytecodeArrayBuilder::CountOperation(Token::Value op, + Strength strength) { + if (is_strong(strength)) { + UNIMPLEMENTED(); + } + + Output(BytecodeForCountOperation(op)); + return *this; +} + + +BytecodeArrayBuilder& BytecodeArrayBuilder::LogicalNot() { + Output(Bytecode::kLogicalNot); + return *this; +} + + +BytecodeArrayBuilder& BytecodeArrayBuilder::TypeOf() { + Output(Bytecode::kTypeOf); + return *this; +} + + BytecodeArrayBuilder& BytecodeArrayBuilder::CompareOperation( - Token::Value op, Register reg, LanguageMode language_mode) { - if (!is_sloppy(language_mode)) { + Token::Value op, Register reg, Strength strength) { + if (is_strong(strength)) { UNIMPLEMENTED(); } @@ -146,8 +288,10 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLiteral( BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLiteral(Handle<Object> object) { size_t entry = GetConstantPoolEntry(object); - if (FitsInIdxOperand(entry)) { + if (FitsInIdx8Operand(entry)) { Output(Bytecode::kLdaConstant, static_cast<uint8_t>(entry)); + } else if (FitsInIdx16Operand(entry)) { + Output(Bytecode::kLdaConstantWide, static_cast<uint16_t>(entry)); } else { UNIMPLEMENTED(); } @@ -185,39 +329,145 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::LoadFalse() { } +BytecodeArrayBuilder& BytecodeArrayBuilder::LoadBooleanConstant(bool value) { + if (value) { + LoadTrue(); + } else { + LoadFalse(); + } + return *this; +} + + BytecodeArrayBuilder& BytecodeArrayBuilder::LoadAccumulatorWithRegister( Register reg) { - Output(Bytecode::kLdar, reg.ToOperand()); + if (!IsRegisterInAccumulator(reg)) { + Output(Bytecode::kLdar, reg.ToOperand()); + } return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::StoreAccumulatorInRegister( Register reg) { + // TODO(oth): Avoid storing the accumulator in the register if the + // previous bytecode loaded the accumulator with the same register. + // + // TODO(oth): If the previous bytecode is a MOV into this register, + // the previous instruction can be removed. The logic for determining + // these redundant MOVs appears complex. Output(Bytecode::kStar, reg.ToOperand()); + if (!IsRegisterInAccumulator(reg)) { + Output(Bytecode::kStar, reg.ToOperand()); + } return *this; } -BytecodeArrayBuilder& BytecodeArrayBuilder::LoadGlobal(int slot_index) { +BytecodeArrayBuilder& BytecodeArrayBuilder::MoveRegister(Register from, + Register to) { + DCHECK(from != to); + Output(Bytecode::kMov, from.ToOperand(), to.ToOperand()); + return *this; +} + + +BytecodeArrayBuilder& BytecodeArrayBuilder::ExchangeRegisters(Register reg0, + Register reg1) { + DCHECK(reg0 != reg1); + if (FitsInReg8Operand(reg0)) { + Output(Bytecode::kExchange, reg0.ToOperand(), reg1.ToWideOperand()); + } else if (FitsInReg8Operand(reg1)) { + Output(Bytecode::kExchange, reg1.ToOperand(), reg0.ToWideOperand()); + } else { + Output(Bytecode::kExchangeWide, reg0.ToWideOperand(), reg1.ToWideOperand()); + } + return *this; +} + + +BytecodeArrayBuilder& BytecodeArrayBuilder::LoadGlobal( + const Handle<String> name, int feedback_slot, LanguageMode language_mode, + TypeofMode typeof_mode) { + // TODO(rmcilroy): Potentially store language and typeof information in an + // operand rather than having extra bytecodes. + Bytecode bytecode = BytecodeForLoadGlobal(language_mode, typeof_mode); + size_t name_index = GetConstantPoolEntry(name); + if (FitsInIdx8Operand(name_index) && FitsInIdx8Operand(feedback_slot)) { + Output(bytecode, static_cast<uint8_t>(name_index), + static_cast<uint8_t>(feedback_slot)); + } else if (FitsInIdx16Operand(name_index) && + FitsInIdx16Operand(feedback_slot)) { + Output(BytecodeForWideOperands(bytecode), static_cast<uint16_t>(name_index), + static_cast<uint16_t>(feedback_slot)); + } else { + UNIMPLEMENTED(); + } + return *this; +} + + +BytecodeArrayBuilder& BytecodeArrayBuilder::StoreGlobal( + const Handle<String> name, int feedback_slot, LanguageMode language_mode) { + Bytecode bytecode = BytecodeForStoreGlobal(language_mode); + size_t name_index = GetConstantPoolEntry(name); + if (FitsInIdx8Operand(name_index) && FitsInIdx8Operand(feedback_slot)) { + Output(bytecode, static_cast<uint8_t>(name_index), + static_cast<uint8_t>(feedback_slot)); + } else if (FitsInIdx16Operand(name_index) && + FitsInIdx16Operand(feedback_slot)) { + Output(BytecodeForWideOperands(bytecode), static_cast<uint16_t>(name_index), + static_cast<uint16_t>(feedback_slot)); + } else { + UNIMPLEMENTED(); + } + return *this; +} + + +BytecodeArrayBuilder& BytecodeArrayBuilder::LoadContextSlot(Register context, + int slot_index) { DCHECK(slot_index >= 0); - if (FitsInIdxOperand(slot_index)) { - Output(Bytecode::kLdaGlobal, static_cast<uint8_t>(slot_index)); + if (FitsInIdx8Operand(slot_index)) { + Output(Bytecode::kLdaContextSlot, context.ToOperand(), + static_cast<uint8_t>(slot_index)); + } else if (FitsInIdx16Operand(slot_index)) { + Output(Bytecode::kLdaContextSlotWide, context.ToOperand(), + static_cast<uint16_t>(slot_index)); } else { UNIMPLEMENTED(); } return *this; } -BytecodeArrayBuilder& BytecodeArrayBuilder::LoadNamedProperty( - Register object, int feedback_slot, LanguageMode language_mode) { - if (!is_sloppy(language_mode)) { + +BytecodeArrayBuilder& BytecodeArrayBuilder::StoreContextSlot(Register context, + int slot_index) { + DCHECK(slot_index >= 0); + if (FitsInIdx8Operand(slot_index)) { + Output(Bytecode::kStaContextSlot, context.ToOperand(), + static_cast<uint8_t>(slot_index)); + } else if (FitsInIdx16Operand(slot_index)) { + Output(Bytecode::kStaContextSlotWide, context.ToOperand(), + static_cast<uint16_t>(slot_index)); + } else { UNIMPLEMENTED(); } + return *this; +} - if (FitsInIdxOperand(feedback_slot)) { - Output(Bytecode::kLoadIC, object.ToOperand(), - static_cast<uint8_t>(feedback_slot)); + +BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLookupSlot( + const Handle<String> name, TypeofMode typeof_mode) { + Bytecode bytecode = (typeof_mode == INSIDE_TYPEOF) + ? Bytecode::kLdaLookupSlotInsideTypeof + : Bytecode::kLdaLookupSlot; + size_t name_index = GetConstantPoolEntry(name); + if (FitsInIdx8Operand(name_index)) { + Output(bytecode, static_cast<uint8_t>(name_index)); + } else if (FitsInIdx16Operand(name_index)) { + Output(BytecodeForWideOperands(bytecode), + static_cast<uint16_t>(name_index)); } else { UNIMPLEMENTED(); } @@ -225,15 +475,35 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::LoadNamedProperty( } -BytecodeArrayBuilder& BytecodeArrayBuilder::LoadKeyedProperty( - Register object, int feedback_slot, LanguageMode language_mode) { - if (!is_sloppy(language_mode)) { +BytecodeArrayBuilder& BytecodeArrayBuilder::StoreLookupSlot( + const Handle<String> name, LanguageMode language_mode) { + Bytecode bytecode = BytecodeForStoreLookupSlot(language_mode); + size_t name_index = GetConstantPoolEntry(name); + if (FitsInIdx8Operand(name_index)) { + Output(bytecode, static_cast<uint8_t>(name_index)); + } else if (FitsInIdx16Operand(name_index)) { + Output(BytecodeForWideOperands(bytecode), + static_cast<uint16_t>(name_index)); + } else { UNIMPLEMENTED(); } + return *this; +} + - if (FitsInIdxOperand(feedback_slot)) { - Output(Bytecode::kKeyedLoadIC, object.ToOperand(), +BytecodeArrayBuilder& BytecodeArrayBuilder::LoadNamedProperty( + Register object, const Handle<String> name, int feedback_slot, + LanguageMode language_mode) { + Bytecode bytecode = BytecodeForLoadIC(language_mode); + size_t name_index = GetConstantPoolEntry(name); + if (FitsInIdx8Operand(name_index) && FitsInIdx8Operand(feedback_slot)) { + Output(bytecode, object.ToOperand(), static_cast<uint8_t>(name_index), static_cast<uint8_t>(feedback_slot)); + } else if (FitsInIdx16Operand(name_index) && + FitsInIdx16Operand(feedback_slot)) { + Output(BytecodeForWideOperands(bytecode), object.ToOperand(), + static_cast<uint16_t>(name_index), + static_cast<uint16_t>(feedback_slot)); } else { UNIMPLEMENTED(); } @@ -241,16 +511,34 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::LoadKeyedProperty( } -BytecodeArrayBuilder& BytecodeArrayBuilder::StoreNamedProperty( - Register object, Register name, int feedback_slot, - LanguageMode language_mode) { - if (!is_sloppy(language_mode)) { +BytecodeArrayBuilder& BytecodeArrayBuilder::LoadKeyedProperty( + Register object, int feedback_slot, LanguageMode language_mode) { + Bytecode bytecode = BytecodeForKeyedLoadIC(language_mode); + if (FitsInIdx8Operand(feedback_slot)) { + Output(bytecode, object.ToOperand(), static_cast<uint8_t>(feedback_slot)); + } else if (FitsInIdx16Operand(feedback_slot)) { + Output(BytecodeForWideOperands(bytecode), object.ToOperand(), + static_cast<uint16_t>(feedback_slot)); + } else { UNIMPLEMENTED(); } + return *this; +} - if (FitsInIdxOperand(feedback_slot)) { - Output(Bytecode::kStoreIC, object.ToOperand(), name.ToOperand(), + +BytecodeArrayBuilder& BytecodeArrayBuilder::StoreNamedProperty( + Register object, const Handle<String> name, int feedback_slot, + LanguageMode language_mode) { + Bytecode bytecode = BytecodeForStoreIC(language_mode); + size_t name_index = GetConstantPoolEntry(name); + if (FitsInIdx8Operand(name_index) && FitsInIdx8Operand(feedback_slot)) { + Output(bytecode, object.ToOperand(), static_cast<uint8_t>(name_index), static_cast<uint8_t>(feedback_slot)); + } else if (FitsInIdx16Operand(name_index) && + FitsInIdx16Operand(feedback_slot)) { + Output(BytecodeForWideOperands(bytecode), object.ToOperand(), + static_cast<uint16_t>(name_index), + static_cast<uint16_t>(feedback_slot)); } else { UNIMPLEMENTED(); } @@ -261,13 +549,30 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::StoreNamedProperty( BytecodeArrayBuilder& BytecodeArrayBuilder::StoreKeyedProperty( Register object, Register key, int feedback_slot, LanguageMode language_mode) { - if (!is_sloppy(language_mode)) { + Bytecode bytecode = BytecodeForKeyedStoreIC(language_mode); + if (FitsInIdx8Operand(feedback_slot)) { + Output(bytecode, object.ToOperand(), key.ToOperand(), + static_cast<uint8_t>(feedback_slot)); + } else if (FitsInIdx16Operand(feedback_slot)) { + Output(BytecodeForWideOperands(bytecode), object.ToOperand(), + key.ToOperand(), static_cast<uint16_t>(feedback_slot)); + } else { UNIMPLEMENTED(); } + return *this; +} - if (FitsInIdxOperand(feedback_slot)) { - Output(Bytecode::kKeyedStoreIC, object.ToOperand(), key.ToOperand(), - static_cast<uint8_t>(feedback_slot)); + +BytecodeArrayBuilder& BytecodeArrayBuilder::CreateClosure( + Handle<SharedFunctionInfo> shared_info, PretenureFlag tenured) { + size_t entry = GetConstantPoolEntry(shared_info); + DCHECK(FitsInImm8Operand(tenured)); + if (FitsInIdx8Operand(entry)) { + Output(Bytecode::kCreateClosure, static_cast<uint8_t>(entry), + static_cast<uint8_t>(tenured)); + } else if (FitsInIdx16Operand(entry)) { + Output(Bytecode::kCreateClosureWide, static_cast<uint16_t>(entry), + static_cast<uint8_t>(tenured)); } else { UNIMPLEMENTED(); } @@ -275,30 +580,150 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::StoreKeyedProperty( } -BytecodeArrayBuilder& BytecodeArrayBuilder::CastAccumulatorToBoolean() { +BytecodeArrayBuilder& BytecodeArrayBuilder::CreateArguments( + CreateArgumentsType type) { + // TODO(rmcilroy): Consider passing the type as a bytecode operand rather + // than having two different bytecodes once we have better support for + // branches in the InterpreterAssembler. + Bytecode bytecode = BytecodeForCreateArguments(type); + Output(bytecode); + return *this; +} + + +BytecodeArrayBuilder& BytecodeArrayBuilder::CreateRegExpLiteral( + Handle<String> pattern, int literal_index, int flags) { + DCHECK(FitsInImm8Operand(flags)); // Flags should fit in 8 bits. + size_t pattern_entry = GetConstantPoolEntry(pattern); + if (FitsInIdx8Operand(literal_index) && FitsInIdx8Operand(pattern_entry)) { + Output(Bytecode::kCreateRegExpLiteral, static_cast<uint8_t>(pattern_entry), + static_cast<uint8_t>(literal_index), static_cast<uint8_t>(flags)); + } else if (FitsInIdx16Operand(literal_index) && + FitsInIdx16Operand(pattern_entry)) { + Output(Bytecode::kCreateRegExpLiteralWide, + static_cast<uint16_t>(pattern_entry), + static_cast<uint16_t>(literal_index), static_cast<uint8_t>(flags)); + } else { + UNIMPLEMENTED(); + } + return *this; +} + + +BytecodeArrayBuilder& BytecodeArrayBuilder::CreateArrayLiteral( + Handle<FixedArray> constant_elements, int literal_index, int flags) { + DCHECK(FitsInImm8Operand(flags)); // Flags should fit in 8 bits. + size_t constant_elements_entry = GetConstantPoolEntry(constant_elements); + if (FitsInIdx8Operand(literal_index) && + FitsInIdx8Operand(constant_elements_entry)) { + Output(Bytecode::kCreateArrayLiteral, + static_cast<uint8_t>(constant_elements_entry), + static_cast<uint8_t>(literal_index), static_cast<uint8_t>(flags)); + } else if (FitsInIdx16Operand(literal_index) && + FitsInIdx16Operand(constant_elements_entry)) { + Output(Bytecode::kCreateArrayLiteralWide, + static_cast<uint16_t>(constant_elements_entry), + static_cast<uint16_t>(literal_index), static_cast<uint8_t>(flags)); + } else { + UNIMPLEMENTED(); + } + return *this; +} + + +BytecodeArrayBuilder& BytecodeArrayBuilder::CreateObjectLiteral( + Handle<FixedArray> constant_properties, int literal_index, int flags) { + DCHECK(FitsInImm8Operand(flags)); // Flags should fit in 8 bits. + size_t constant_properties_entry = GetConstantPoolEntry(constant_properties); + if (FitsInIdx8Operand(literal_index) && + FitsInIdx8Operand(constant_properties_entry)) { + Output(Bytecode::kCreateObjectLiteral, + static_cast<uint8_t>(constant_properties_entry), + static_cast<uint8_t>(literal_index), static_cast<uint8_t>(flags)); + } else if (FitsInIdx16Operand(literal_index) && + FitsInIdx16Operand(constant_properties_entry)) { + Output(Bytecode::kCreateObjectLiteralWide, + static_cast<uint16_t>(constant_properties_entry), + static_cast<uint16_t>(literal_index), static_cast<uint8_t>(flags)); + } else { + UNIMPLEMENTED(); + } + return *this; +} + + +BytecodeArrayBuilder& BytecodeArrayBuilder::PushContext(Register context) { + Output(Bytecode::kPushContext, context.ToOperand()); + return *this; +} + + +BytecodeArrayBuilder& BytecodeArrayBuilder::PopContext(Register context) { + Output(Bytecode::kPopContext, context.ToOperand()); + return *this; +} + + +bool BytecodeArrayBuilder::NeedToBooleanCast() { + if (!LastBytecodeInSameBlock()) { + return true; + } + PreviousBytecodeHelper previous_bytecode(*this); + switch (previous_bytecode.GetBytecode()) { + // If the previous bytecode puts a boolean in the accumulator return true. + case Bytecode::kLdaTrue: + case Bytecode::kLdaFalse: + case Bytecode::kLogicalNot: + case Bytecode::kTestEqual: + case Bytecode::kTestNotEqual: + case Bytecode::kTestEqualStrict: + case Bytecode::kTestNotEqualStrict: + case Bytecode::kTestLessThan: + case Bytecode::kTestLessThanOrEqual: + case Bytecode::kTestGreaterThan: + case Bytecode::kTestGreaterThanOrEqual: + case Bytecode::kTestInstanceOf: + case Bytecode::kTestIn: + case Bytecode::kForInDone: + return false; + default: + return true; + } +} + + +BytecodeArrayBuilder& BytecodeArrayBuilder::CastAccumulatorToJSObject() { + Output(Bytecode::kToObject); + return *this; +} + + +BytecodeArrayBuilder& BytecodeArrayBuilder::CastAccumulatorToName() { if (LastBytecodeInSameBlock()) { - // If the previous bytecode puts a boolean in the accumulator - // there is no need to emit an instruction. - switch (Bytecodes::FromByte(bytecodes()->at(last_bytecode_start_))) { - case Bytecode::kToBoolean: - UNREACHABLE(); - case Bytecode::kLdaTrue: - case Bytecode::kLdaFalse: - case Bytecode::kTestEqual: - case Bytecode::kTestNotEqual: - case Bytecode::kTestEqualStrict: - case Bytecode::kTestNotEqualStrict: - case Bytecode::kTestLessThan: - case Bytecode::kTestLessThanOrEqual: - case Bytecode::kTestGreaterThan: - case Bytecode::kTestGreaterThanOrEqual: - case Bytecode::kTestInstanceOf: - case Bytecode::kTestIn: + PreviousBytecodeHelper previous_bytecode(*this); + switch (previous_bytecode.GetBytecode()) { + case Bytecode::kToName: + case Bytecode::kTypeOf: + return *this; + case Bytecode::kLdaConstantWide: + case Bytecode::kLdaConstant: { + Handle<Object> object = previous_bytecode.GetConstantForIndexOperand(0); + if (object->IsName()) return *this; break; + } default: - Output(Bytecode::kToBoolean); + break; } } + Output(Bytecode::kToName); + return *this; +} + + +BytecodeArrayBuilder& BytecodeArrayBuilder::CastAccumulatorToNumber() { + // TODO(rmcilroy): consider omitting if the preceeding bytecode always returns + // a number. + Output(Bytecode::kToNumber); return *this; } @@ -310,15 +735,20 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::Bind(BytecodeLabel* label) { // Now treat as if the label will only be back referred to. } label->bind_to(bytecodes()->size()); + LeaveBasicBlock(); return *this; } -// static -bool BytecodeArrayBuilder::IsJumpWithImm8Operand(Bytecode jump_bytecode) { - return jump_bytecode == Bytecode::kJump || - jump_bytecode == Bytecode::kJumpIfTrue || - jump_bytecode == Bytecode::kJumpIfFalse; +BytecodeArrayBuilder& BytecodeArrayBuilder::Bind(const BytecodeLabel& target, + BytecodeLabel* label) { + DCHECK(!label->is_bound()); + DCHECK(target.is_bound()); + PatchJump(bytecodes()->begin() + target.offset(), + bytecodes()->begin() + label->offset()); + label->bind_to(target.offset()); + LeaveBasicBlock(); + return *this; } @@ -332,75 +762,180 @@ Bytecode BytecodeArrayBuilder::GetJumpWithConstantOperand( return Bytecode::kJumpIfTrueConstant; case Bytecode::kJumpIfFalse: return Bytecode::kJumpIfFalseConstant; + case Bytecode::kJumpIfToBooleanTrue: + return Bytecode::kJumpIfToBooleanTrueConstant; + case Bytecode::kJumpIfToBooleanFalse: + return Bytecode::kJumpIfToBooleanFalseConstant; + case Bytecode::kJumpIfNull: + return Bytecode::kJumpIfNullConstant; + case Bytecode::kJumpIfUndefined: + return Bytecode::kJumpIfUndefinedConstant; default: UNREACHABLE(); - return Bytecode::kJumpConstant; + return static_cast<Bytecode>(-1); } } -void BytecodeArrayBuilder::PatchJump( - const ZoneVector<uint8_t>::iterator& jump_target, - ZoneVector<uint8_t>::iterator jump_location) { - Bytecode jump_bytecode = Bytecodes::FromByte(*jump_location); - int delta = static_cast<int>(jump_target - jump_location); +// static +Bytecode BytecodeArrayBuilder::GetJumpWithConstantWideOperand( + Bytecode jump_bytecode) { + switch (jump_bytecode) { + case Bytecode::kJump: + return Bytecode::kJumpConstantWide; + case Bytecode::kJumpIfTrue: + return Bytecode::kJumpIfTrueConstantWide; + case Bytecode::kJumpIfFalse: + return Bytecode::kJumpIfFalseConstantWide; + case Bytecode::kJumpIfToBooleanTrue: + return Bytecode::kJumpIfToBooleanTrueConstantWide; + case Bytecode::kJumpIfToBooleanFalse: + return Bytecode::kJumpIfToBooleanFalseConstantWide; + case Bytecode::kJumpIfNull: + return Bytecode::kJumpIfNullConstantWide; + case Bytecode::kJumpIfUndefined: + return Bytecode::kJumpIfUndefinedConstantWide; + default: + UNREACHABLE(); + return static_cast<Bytecode>(-1); + } +} + + +// static +Bytecode BytecodeArrayBuilder::GetJumpWithToBoolean(Bytecode jump_bytecode) { + switch (jump_bytecode) { + case Bytecode::kJump: + case Bytecode::kJumpIfNull: + case Bytecode::kJumpIfUndefined: + return jump_bytecode; + case Bytecode::kJumpIfTrue: + return Bytecode::kJumpIfToBooleanTrue; + case Bytecode::kJumpIfFalse: + return Bytecode::kJumpIfToBooleanFalse; + default: + UNREACHABLE(); + } + return static_cast<Bytecode>(-1); +} - DCHECK(IsJumpWithImm8Operand(jump_bytecode)); - DCHECK_EQ(Bytecodes::Size(jump_bytecode), 2); - DCHECK_GE(delta, 0); +void BytecodeArrayBuilder::PatchIndirectJumpWith8BitOperand( + const ZoneVector<uint8_t>::iterator& jump_location, int delta) { + Bytecode jump_bytecode = Bytecodes::FromByte(*jump_location); + DCHECK(Bytecodes::IsJumpImmediate(jump_bytecode)); + ZoneVector<uint8_t>::iterator operand_location = jump_location + 1; + DCHECK_EQ(*operand_location, 0); if (FitsInImm8Operand(delta)) { - // Just update the operand - jump_location++; - *jump_location = static_cast<uint8_t>(delta); + // The jump fits within the range of an Imm8 operand, so cancel + // the reservation and jump directly. + constant_array_builder()->DiscardReservedEntry(OperandSize::kByte); + *operand_location = static_cast<uint8_t>(delta); } else { - // Update the jump type and operand - size_t entry = GetConstantPoolEntry(handle(Smi::FromInt(delta), isolate())); - if (FitsInIdxOperand(entry)) { - *jump_location++ = - Bytecodes::ToByte(GetJumpWithConstantOperand(jump_bytecode)); - *jump_location = static_cast<uint8_t>(entry); - } else { - // TODO(oth): OutputJump should reserve a constant pool entry - // when jump is written. The reservation should be used here if - // needed, or cancelled if not. This is due to the patch needing - // to match the size of the code it's replacing. In future, - // there will probably be a jump with 32-bit operand for cases - // when constant pool is full, but that needs to be emitted in - // OutputJump too. - UNIMPLEMENTED(); - } + // The jump does not fit within the range of an Imm8 operand, so + // commit reservation putting the offset into the constant pool, + // and update the jump instruction and operand. + size_t entry = constant_array_builder()->CommitReservedEntry( + OperandSize::kByte, handle(Smi::FromInt(delta), isolate())); + DCHECK(FitsInIdx8Operand(entry)); + jump_bytecode = GetJumpWithConstantOperand(jump_bytecode); + *jump_location = Bytecodes::ToByte(jump_bytecode); + *operand_location = static_cast<uint8_t>(entry); + } +} + + +void BytecodeArrayBuilder::PatchIndirectJumpWith16BitOperand( + const ZoneVector<uint8_t>::iterator& jump_location, int delta) { + DCHECK(Bytecodes::IsJumpConstantWide(Bytecodes::FromByte(*jump_location))); + ZoneVector<uint8_t>::iterator operand_location = jump_location + 1; + size_t entry = constant_array_builder()->CommitReservedEntry( + OperandSize::kShort, handle(Smi::FromInt(delta), isolate())); + DCHECK(FitsInIdx16Operand(entry)); + uint8_t operand_bytes[2]; + WriteUnalignedUInt16(operand_bytes, static_cast<uint16_t>(entry)); + DCHECK(*operand_location == 0 && *(operand_location + 1) == 0); + *operand_location++ = operand_bytes[0]; + *operand_location = operand_bytes[1]; +} + + +void BytecodeArrayBuilder::PatchJump( + const ZoneVector<uint8_t>::iterator& jump_target, + const ZoneVector<uint8_t>::iterator& jump_location) { + Bytecode jump_bytecode = Bytecodes::FromByte(*jump_location); + int delta = static_cast<int>(jump_target - jump_location); + DCHECK(Bytecodes::IsJump(jump_bytecode)); + switch (Bytecodes::GetOperandSize(jump_bytecode, 0)) { + case OperandSize::kByte: + PatchIndirectJumpWith8BitOperand(jump_location, delta); + break; + case OperandSize::kShort: + PatchIndirectJumpWith16BitOperand(jump_location, delta); + break; + case OperandSize::kNone: + UNREACHABLE(); } + unbound_jumps_--; } BytecodeArrayBuilder& BytecodeArrayBuilder::OutputJump(Bytecode jump_bytecode, BytecodeLabel* label) { - int delta; + // Don't emit dead code. + if (exit_seen_in_block_) return *this; + + // Check if the value in accumulator is boolean, if not choose an + // appropriate JumpIfToBoolean bytecode. + if (NeedToBooleanCast()) { + jump_bytecode = GetJumpWithToBoolean(jump_bytecode); + } + if (label->is_bound()) { // Label has been bound already so this is a backwards jump. CHECK_GE(bytecodes()->size(), label->offset()); CHECK_LE(bytecodes()->size(), static_cast<size_t>(kMaxInt)); size_t abs_delta = bytecodes()->size() - label->offset(); - delta = -static_cast<int>(abs_delta); - } else { - // Label has not yet been bound so this is a forward reference - // that will be patched when the label is bound. - label->set_referrer(bytecodes()->size()); - delta = 0; - } + int delta = -static_cast<int>(abs_delta); - if (FitsInImm8Operand(delta)) { - Output(jump_bytecode, static_cast<uint8_t>(delta)); - } else { - size_t entry = GetConstantPoolEntry(handle(Smi::FromInt(delta), isolate())); - if (FitsInIdxOperand(entry)) { - Output(GetJumpWithConstantOperand(jump_bytecode), - static_cast<uint8_t>(entry)); + if (FitsInImm8Operand(delta)) { + Output(jump_bytecode, static_cast<uint8_t>(delta)); } else { - UNIMPLEMENTED(); + size_t entry = + GetConstantPoolEntry(handle(Smi::FromInt(delta), isolate())); + if (FitsInIdx8Operand(entry)) { + Output(GetJumpWithConstantOperand(jump_bytecode), + static_cast<uint8_t>(entry)); + } else if (FitsInIdx16Operand(entry)) { + Output(GetJumpWithConstantWideOperand(jump_bytecode), + static_cast<uint16_t>(entry)); + } else { + UNREACHABLE(); + } + } + } else { + // The label has not yet been bound so this is a forward reference + // that will be patched when the label is bound. We create a + // reservation in the constant pool so the jump can be patched + // when the label is bound. The reservation means the maximum size + // of the operand for the constant is known and the jump can + // be emitted into the bytecode stream with space for the operand. + label->set_referrer(bytecodes()->size()); + unbound_jumps_++; + OperandSize reserved_operand_size = + constant_array_builder()->CreateReservedEntry(); + switch (reserved_operand_size) { + case OperandSize::kByte: + Output(jump_bytecode, 0); + break; + case OperandSize::kShort: + Output(GetJumpWithConstantWideOperand(jump_bytecode), 0); + break; + case OperandSize::kNone: + UNREACHABLE(); } } + LeaveBasicBlock(); return *this; } @@ -420,36 +955,89 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfFalse(BytecodeLabel* label) { } +BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfNull(BytecodeLabel* label) { + return OutputJump(Bytecode::kJumpIfNull, label); +} + + +BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfUndefined( + BytecodeLabel* label) { + return OutputJump(Bytecode::kJumpIfUndefined, label); +} + + +BytecodeArrayBuilder& BytecodeArrayBuilder::Throw() { + Output(Bytecode::kThrow); + exit_seen_in_block_ = true; + return *this; +} + + BytecodeArrayBuilder& BytecodeArrayBuilder::Return() { Output(Bytecode::kReturn); - return_seen_in_block_ = true; + exit_seen_in_block_ = true; return *this; } -BytecodeArrayBuilder& BytecodeArrayBuilder::EnterBlock() { return *this; } +BytecodeArrayBuilder& BytecodeArrayBuilder::ForInPrepare( + Register cache_type, Register cache_array, Register cache_length) { + Output(Bytecode::kForInPrepare, cache_type.ToOperand(), + cache_array.ToOperand(), cache_length.ToOperand()); + return *this; +} -BytecodeArrayBuilder& BytecodeArrayBuilder::LeaveBlock() { - last_block_end_ = bytecodes()->size(); - return_seen_in_block_ = false; +BytecodeArrayBuilder& BytecodeArrayBuilder::ForInDone(Register index, + Register cache_length) { + Output(Bytecode::kForInDone, index.ToOperand(), cache_length.ToOperand()); return *this; } +BytecodeArrayBuilder& BytecodeArrayBuilder::ForInNext(Register receiver, + Register cache_type, + Register cache_array, + Register index) { + Output(Bytecode::kForInNext, receiver.ToOperand(), cache_type.ToOperand(), + cache_array.ToOperand(), index.ToOperand()); + return *this; +} + + +BytecodeArrayBuilder& BytecodeArrayBuilder::ForInStep(Register index) { + Output(Bytecode::kForInStep, index.ToOperand()); + return *this; +} + + +void BytecodeArrayBuilder::LeaveBasicBlock() { + last_block_end_ = bytecodes()->size(); + exit_seen_in_block_ = false; +} + + void BytecodeArrayBuilder::EnsureReturn() { - if (!return_seen_in_block_) { + if (!exit_seen_in_block_) { LoadUndefined(); Return(); } } + BytecodeArrayBuilder& BytecodeArrayBuilder::Call(Register callable, Register receiver, - size_t arg_count) { - if (FitsInIdxOperand(arg_count)) { + size_t arg_count, + int feedback_slot) { + if (FitsInIdx8Operand(arg_count) && FitsInIdx8Operand(feedback_slot)) { Output(Bytecode::kCall, callable.ToOperand(), receiver.ToOperand(), - static_cast<uint8_t>(arg_count)); + static_cast<uint8_t>(arg_count), + static_cast<uint8_t>(feedback_slot)); + } else if (FitsInIdx16Operand(arg_count) && + FitsInIdx16Operand(feedback_slot)) { + Output(Bytecode::kCallWide, callable.ToOperand(), receiver.ToOperand(), + static_cast<uint16_t>(arg_count), + static_cast<uint16_t>(feedback_slot)); } else { UNIMPLEMENTED(); } @@ -457,73 +1045,249 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::Call(Register callable, } -size_t BytecodeArrayBuilder::GetConstantPoolEntry(Handle<Object> object) { - // These constants shouldn't be added to the constant pool, the should use - // specialzed bytecodes instead. - DCHECK(!object.is_identical_to(isolate_->factory()->undefined_value())); - DCHECK(!object.is_identical_to(isolate_->factory()->null_value())); - DCHECK(!object.is_identical_to(isolate_->factory()->the_hole_value())); - DCHECK(!object.is_identical_to(isolate_->factory()->true_value())); - DCHECK(!object.is_identical_to(isolate_->factory()->false_value())); +BytecodeArrayBuilder& BytecodeArrayBuilder::New(Register constructor, + Register first_arg, + size_t arg_count) { + if (!first_arg.is_valid()) { + DCHECK_EQ(0u, arg_count); + first_arg = Register(0); + } + DCHECK(FitsInIdx8Operand(arg_count)); + Output(Bytecode::kNew, constructor.ToOperand(), first_arg.ToOperand(), + static_cast<uint8_t>(arg_count)); + return *this; +} + - size_t* entry = constants_map_.Find(object); - if (!entry) { - entry = constants_map_.Get(object); - *entry = constants_.size(); - constants_.push_back(object); +BytecodeArrayBuilder& BytecodeArrayBuilder::CallRuntime( + Runtime::FunctionId function_id, Register first_arg, size_t arg_count) { + DCHECK_EQ(1, Runtime::FunctionForId(function_id)->result_size); + DCHECK(FitsInIdx16Operand(function_id)); + DCHECK(FitsInIdx8Operand(arg_count)); + if (!first_arg.is_valid()) { + DCHECK_EQ(0u, arg_count); + first_arg = Register(0); } - DCHECK(constants_[*entry].is_identical_to(object)); - return *entry; + Output(Bytecode::kCallRuntime, static_cast<uint16_t>(function_id), + first_arg.ToOperand(), static_cast<uint8_t>(arg_count)); + return *this; +} + + +BytecodeArrayBuilder& BytecodeArrayBuilder::CallRuntimeForPair( + Runtime::FunctionId function_id, Register first_arg, size_t arg_count, + Register first_return) { + DCHECK_EQ(2, Runtime::FunctionForId(function_id)->result_size); + DCHECK(FitsInIdx16Operand(function_id)); + DCHECK(FitsInIdx8Operand(arg_count)); + if (!first_arg.is_valid()) { + DCHECK_EQ(0u, arg_count); + first_arg = Register(0); + } + Output(Bytecode::kCallRuntimeForPair, static_cast<uint16_t>(function_id), + first_arg.ToOperand(), static_cast<uint8_t>(arg_count), + first_return.ToOperand()); + return *this; +} + + +BytecodeArrayBuilder& BytecodeArrayBuilder::CallJSRuntime(int context_index, + Register receiver, + size_t arg_count) { + DCHECK(FitsInIdx16Operand(context_index)); + DCHECK(FitsInIdx8Operand(arg_count)); + Output(Bytecode::kCallJSRuntime, static_cast<uint16_t>(context_index), + receiver.ToOperand(), static_cast<uint8_t>(arg_count)); + return *this; +} + + +BytecodeArrayBuilder& BytecodeArrayBuilder::Delete(Register object, + LanguageMode language_mode) { + Output(BytecodeForDelete(language_mode), object.ToOperand()); + return *this; +} + + +BytecodeArrayBuilder& BytecodeArrayBuilder::DeleteLookupSlot() { + Output(Bytecode::kDeleteLookupSlot); + return *this; +} + + +size_t BytecodeArrayBuilder::GetConstantPoolEntry(Handle<Object> object) { + return constant_array_builder()->Insert(object); } int BytecodeArrayBuilder::BorrowTemporaryRegister() { - DCHECK_GE(local_register_count_, 0); - int temporary_reg_index = temporary_register_next_++; - int count = temporary_register_next_ - local_register_count_; - if (count > temporary_register_count_) { - temporary_register_count_ = count; + if (free_temporaries_.empty()) { + temporary_register_count_ += 1; + return last_temporary_register().index(); + } else { + auto pos = free_temporaries_.begin(); + int retval = *pos; + free_temporaries_.erase(pos); + return retval; } - return temporary_reg_index; +} + + +int BytecodeArrayBuilder::BorrowTemporaryRegisterNotInRange(int start_index, + int end_index) { + auto index = free_temporaries_.lower_bound(start_index); + if (index == free_temporaries_.begin()) { + // If start_index is the first free register, check for a register + // greater than end_index. + index = free_temporaries_.upper_bound(end_index); + if (index == free_temporaries_.end()) { + temporary_register_count_ += 1; + return last_temporary_register().index(); + } + } else { + // If there is a free register < start_index + index--; + } + + int retval = *index; + free_temporaries_.erase(index); + return retval; +} + + +void BytecodeArrayBuilder::BorrowConsecutiveTemporaryRegister(int reg_index) { + DCHECK(free_temporaries_.find(reg_index) != free_temporaries_.end()); + free_temporaries_.erase(reg_index); } void BytecodeArrayBuilder::ReturnTemporaryRegister(int reg_index) { - DCHECK_EQ(reg_index, temporary_register_next_ - 1); - temporary_register_next_ = reg_index; + DCHECK(free_temporaries_.find(reg_index) == free_temporaries_.end()); + free_temporaries_.insert(reg_index); +} + + +int BytecodeArrayBuilder::PrepareForConsecutiveTemporaryRegisters( + size_t count) { + if (count == 0) { + return -1; + } + + // Search within existing temporaries for a run. + auto start = free_temporaries_.begin(); + size_t run_length = 0; + for (auto run_end = start; run_end != free_temporaries_.end(); run_end++) { + if (*run_end != *start + static_cast<int>(run_length)) { + start = run_end; + run_length = 0; + } + if (++run_length == count) { + return *start; + } + } + + // Continue run if possible across existing last temporary. + if (temporary_register_count_ > 0 && + (start == free_temporaries_.end() || + *start + static_cast<int>(run_length) != + last_temporary_register().index() + 1)) { + run_length = 0; + } + + // Ensure enough registers for run. + while (run_length++ < count) { + temporary_register_count_++; + free_temporaries_.insert(last_temporary_register().index()); + } + return last_temporary_register().index() - static_cast<int>(count) + 1; +} + + +bool BytecodeArrayBuilder::TemporaryRegisterIsLive(Register reg) const { + if (temporary_register_count_ > 0) { + DCHECK(reg.index() >= first_temporary_register().index() && + reg.index() <= last_temporary_register().index()); + return free_temporaries_.find(reg.index()) == free_temporaries_.end(); + } else { + return false; + } +} + + +bool BytecodeArrayBuilder::RegisterIsValid(Register reg) const { + if (reg.is_function_context() || reg.is_function_closure() || + reg.is_new_target()) { + return true; + } else if (reg.is_parameter()) { + int parameter_index = reg.ToParameterIndex(parameter_count_); + return parameter_index >= 0 && parameter_index < parameter_count_; + } else if (reg.index() < fixed_register_count()) { + return true; + } else { + return TemporaryRegisterIsLive(reg); + } } bool BytecodeArrayBuilder::OperandIsValid(Bytecode bytecode, int operand_index, - uint8_t operand_value) const { + uint32_t operand_value) const { OperandType operand_type = Bytecodes::GetOperandType(bytecode, operand_index); switch (operand_type) { case OperandType::kNone: return false; - case OperandType::kCount: + case OperandType::kCount16: + case OperandType::kIdx16: + return static_cast<uint16_t>(operand_value) == operand_value; + case OperandType::kCount8: case OperandType::kImm8: - case OperandType::kIdx: - return true; - case OperandType::kReg: { - Register reg = Register::FromOperand(operand_value); - if (reg.is_parameter()) { - int parameter_index = reg.ToParameterIndex(parameter_count_); - return parameter_index >= 0 && parameter_index < parameter_count_; - } else { - return (reg.index() >= 0 && reg.index() < temporary_register_next_); + case OperandType::kIdx8: + return static_cast<uint8_t>(operand_value) == operand_value; + case OperandType::kMaybeReg8: + if (operand_value == 0) { + return true; } + // Fall-through to kReg8 case. + case OperandType::kReg8: + return RegisterIsValid( + Register::FromOperand(static_cast<uint8_t>(operand_value))); + case OperandType::kRegPair8: { + Register reg0 = + Register::FromOperand(static_cast<uint8_t>(operand_value)); + Register reg1 = Register(reg0.index() + 1); + return RegisterIsValid(reg0) && RegisterIsValid(reg1); } + case OperandType::kReg16: + if (bytecode != Bytecode::kExchange && + bytecode != Bytecode::kExchangeWide) { + return false; + } + return RegisterIsValid( + Register::FromWideOperand(static_cast<uint16_t>(operand_value))); } UNREACHABLE(); return false; } + bool BytecodeArrayBuilder::LastBytecodeInSameBlock() const { return last_bytecode_start_ < bytecodes()->size() && last_bytecode_start_ >= last_block_end_; } +bool BytecodeArrayBuilder::IsRegisterInAccumulator(Register reg) { + if (LastBytecodeInSameBlock()) { + PreviousBytecodeHelper previous_bytecode(*this); + Bytecode bytecode = previous_bytecode.GetBytecode(); + if ((bytecode == Bytecode::kLdar || bytecode == Bytecode::kStar) && + (reg == Register::FromOperand(previous_bytecode.GetOperand(0)))) { + return true; + } + } + return false; +} + + // static Bytecode BytecodeArrayBuilder::BytecodeForBinaryOperation(Token::Value op) { switch (op) { @@ -537,6 +1301,32 @@ Bytecode BytecodeArrayBuilder::BytecodeForBinaryOperation(Token::Value op) { return Bytecode::kDiv; case Token::Value::MOD: return Bytecode::kMod; + case Token::Value::BIT_OR: + return Bytecode::kBitwiseOr; + case Token::Value::BIT_XOR: + return Bytecode::kBitwiseXor; + case Token::Value::BIT_AND: + return Bytecode::kBitwiseAnd; + case Token::Value::SHL: + return Bytecode::kShiftLeft; + case Token::Value::SAR: + return Bytecode::kShiftRight; + case Token::Value::SHR: + return Bytecode::kShiftRightLogical; + default: + UNREACHABLE(); + return static_cast<Bytecode>(-1); + } +} + + +// static +Bytecode BytecodeArrayBuilder::BytecodeForCountOperation(Token::Value op) { + switch (op) { + case Token::Value::ADD: + return Bytecode::kInc; + case Token::Value::SUB: + return Bytecode::kDec; default: UNREACHABLE(); return static_cast<Bytecode>(-1); @@ -575,38 +1365,242 @@ Bytecode BytecodeArrayBuilder::BytecodeForCompareOperation(Token::Value op) { // static -bool BytecodeArrayBuilder::FitsInIdxOperand(int value) { +Bytecode BytecodeArrayBuilder::BytecodeForWideOperands(Bytecode bytecode) { + switch (bytecode) { + case Bytecode::kLoadICSloppy: + return Bytecode::kLoadICSloppyWide; + case Bytecode::kLoadICStrict: + return Bytecode::kLoadICStrictWide; + case Bytecode::kKeyedLoadICSloppy: + return Bytecode::kKeyedLoadICSloppyWide; + case Bytecode::kKeyedLoadICStrict: + return Bytecode::kKeyedLoadICStrictWide; + case Bytecode::kStoreICSloppy: + return Bytecode::kStoreICSloppyWide; + case Bytecode::kStoreICStrict: + return Bytecode::kStoreICStrictWide; + case Bytecode::kKeyedStoreICSloppy: + return Bytecode::kKeyedStoreICSloppyWide; + case Bytecode::kKeyedStoreICStrict: + return Bytecode::kKeyedStoreICStrictWide; + case Bytecode::kLdaGlobalSloppy: + return Bytecode::kLdaGlobalSloppyWide; + case Bytecode::kLdaGlobalStrict: + return Bytecode::kLdaGlobalStrictWide; + case Bytecode::kLdaGlobalInsideTypeofSloppy: + return Bytecode::kLdaGlobalInsideTypeofSloppyWide; + case Bytecode::kLdaGlobalInsideTypeofStrict: + return Bytecode::kLdaGlobalInsideTypeofStrictWide; + case Bytecode::kStaGlobalSloppy: + return Bytecode::kStaGlobalSloppyWide; + case Bytecode::kStaGlobalStrict: + return Bytecode::kStaGlobalStrictWide; + case Bytecode::kLdaLookupSlot: + return Bytecode::kLdaLookupSlotWide; + case Bytecode::kLdaLookupSlotInsideTypeof: + return Bytecode::kLdaLookupSlotInsideTypeofWide; + case Bytecode::kStaLookupSlotStrict: + return Bytecode::kStaLookupSlotStrictWide; + case Bytecode::kStaLookupSlotSloppy: + return Bytecode::kStaLookupSlotSloppyWide; + default: + UNREACHABLE(); + return static_cast<Bytecode>(-1); + } +} + + +// static +Bytecode BytecodeArrayBuilder::BytecodeForLoadIC(LanguageMode language_mode) { + switch (language_mode) { + case SLOPPY: + return Bytecode::kLoadICSloppy; + case STRICT: + return Bytecode::kLoadICStrict; + case STRONG: + UNIMPLEMENTED(); + default: + UNREACHABLE(); + } + return static_cast<Bytecode>(-1); +} + + +// static +Bytecode BytecodeArrayBuilder::BytecodeForKeyedLoadIC( + LanguageMode language_mode) { + switch (language_mode) { + case SLOPPY: + return Bytecode::kKeyedLoadICSloppy; + case STRICT: + return Bytecode::kKeyedLoadICStrict; + case STRONG: + UNIMPLEMENTED(); + default: + UNREACHABLE(); + } + return static_cast<Bytecode>(-1); +} + + +// static +Bytecode BytecodeArrayBuilder::BytecodeForStoreIC(LanguageMode language_mode) { + switch (language_mode) { + case SLOPPY: + return Bytecode::kStoreICSloppy; + case STRICT: + return Bytecode::kStoreICStrict; + case STRONG: + UNIMPLEMENTED(); + default: + UNREACHABLE(); + } + return static_cast<Bytecode>(-1); +} + + +// static +Bytecode BytecodeArrayBuilder::BytecodeForKeyedStoreIC( + LanguageMode language_mode) { + switch (language_mode) { + case SLOPPY: + return Bytecode::kKeyedStoreICSloppy; + case STRICT: + return Bytecode::kKeyedStoreICStrict; + case STRONG: + UNIMPLEMENTED(); + default: + UNREACHABLE(); + } + return static_cast<Bytecode>(-1); +} + + +// static +Bytecode BytecodeArrayBuilder::BytecodeForLoadGlobal(LanguageMode language_mode, + TypeofMode typeof_mode) { + switch (language_mode) { + case SLOPPY: + return typeof_mode == INSIDE_TYPEOF + ? Bytecode::kLdaGlobalInsideTypeofSloppy + : Bytecode::kLdaGlobalSloppy; + case STRICT: + return typeof_mode == INSIDE_TYPEOF + ? Bytecode::kLdaGlobalInsideTypeofStrict + : Bytecode::kLdaGlobalStrict; + case STRONG: + UNIMPLEMENTED(); + default: + UNREACHABLE(); + } + return static_cast<Bytecode>(-1); +} + + +// static +Bytecode BytecodeArrayBuilder::BytecodeForStoreGlobal( + LanguageMode language_mode) { + switch (language_mode) { + case SLOPPY: + return Bytecode::kStaGlobalSloppy; + case STRICT: + return Bytecode::kStaGlobalStrict; + case STRONG: + UNIMPLEMENTED(); + default: + UNREACHABLE(); + } + return static_cast<Bytecode>(-1); +} + + +// static +Bytecode BytecodeArrayBuilder::BytecodeForStoreLookupSlot( + LanguageMode language_mode) { + switch (language_mode) { + case SLOPPY: + return Bytecode::kStaLookupSlotSloppy; + case STRICT: + return Bytecode::kStaLookupSlotStrict; + case STRONG: + UNIMPLEMENTED(); + default: + UNREACHABLE(); + } + return static_cast<Bytecode>(-1); +} + + +// static +Bytecode BytecodeArrayBuilder::BytecodeForCreateArguments( + CreateArgumentsType type) { + switch (type) { + case CreateArgumentsType::kMappedArguments: + return Bytecode::kCreateMappedArguments; + case CreateArgumentsType::kUnmappedArguments: + return Bytecode::kCreateUnmappedArguments; + default: + UNREACHABLE(); + } + return static_cast<Bytecode>(-1); +} + + +// static +Bytecode BytecodeArrayBuilder::BytecodeForDelete(LanguageMode language_mode) { + switch (language_mode) { + case SLOPPY: + return Bytecode::kDeletePropertySloppy; + case STRICT: + return Bytecode::kDeletePropertyStrict; + case STRONG: + UNIMPLEMENTED(); + default: + UNREACHABLE(); + } + return static_cast<Bytecode>(-1); +} + + +// static +bool BytecodeArrayBuilder::FitsInIdx8Operand(int value) { return kMinUInt8 <= value && value <= kMaxUInt8; } // static -bool BytecodeArrayBuilder::FitsInIdxOperand(size_t value) { +bool BytecodeArrayBuilder::FitsInIdx8Operand(size_t value) { return value <= static_cast<size_t>(kMaxUInt8); } // static bool BytecodeArrayBuilder::FitsInImm8Operand(int value) { - return kMinInt8 <= value && value < kMaxInt8; + return kMinInt8 <= value && value <= kMaxInt8; +} + + +// static +bool BytecodeArrayBuilder::FitsInIdx16Operand(int value) { + return kMinUInt16 <= value && value <= kMaxUInt16; } -TemporaryRegisterScope::TemporaryRegisterScope(BytecodeArrayBuilder* builder) - : builder_(builder), count_(0), last_register_index_(-1) {} +// static +bool BytecodeArrayBuilder::FitsInIdx16Operand(size_t value) { + return value <= static_cast<size_t>(kMaxUInt16); +} -TemporaryRegisterScope::~TemporaryRegisterScope() { - while (count_-- != 0) { - builder_->ReturnTemporaryRegister(last_register_index_--); - } +// static +bool BytecodeArrayBuilder::FitsInReg8Operand(Register value) { + return kMinInt8 <= value.index() && value.index() <= kMaxInt8; } -Register TemporaryRegisterScope::NewRegister() { - count_++; - last_register_index_ = builder_->BorrowTemporaryRegister(); - return Register(last_register_index_); +// static +bool BytecodeArrayBuilder::FitsInReg16Operand(Register value) { + return kMinInt16 <= value.index() && value.index() <= kMaxInt16; } } // namespace interpreter diff --git a/chromium/v8/src/interpreter/bytecode-array-builder.h b/chromium/v8/src/interpreter/bytecode-array-builder.h index d68d5e7ffbf..7c23dc3f22c 100644 --- a/chromium/v8/src/interpreter/bytecode-array-builder.h +++ b/chromium/v8/src/interpreter/bytecode-array-builder.h @@ -5,12 +5,9 @@ #ifndef V8_INTERPRETER_BYTECODE_ARRAY_BUILDER_H_ #define V8_INTERPRETER_BYTECODE_ARRAY_BUILDER_H_ -#include <vector> - -#include "src/ast.h" -#include "src/identity-map.h" +#include "src/ast/ast.h" #include "src/interpreter/bytecodes.h" -#include "src/zone.h" +#include "src/interpreter/constant-array-builder.h" #include "src/zone-containers.h" namespace v8 { @@ -21,22 +18,55 @@ class Isolate; namespace interpreter { class BytecodeLabel; +class ConstantArrayBuilder; class Register; -class BytecodeArrayBuilder { +// TODO(rmcilroy): Unify this with CreateArgumentsParameters::Type in Turbofan +// when rest parameters implementation has settled down. +enum class CreateArgumentsType { kMappedArguments, kUnmappedArguments }; + +class BytecodeArrayBuilder final { public: BytecodeArrayBuilder(Isolate* isolate, Zone* zone); + ~BytecodeArrayBuilder(); + Handle<BytecodeArray> ToBytecodeArray(); - // Set number of parameters expected by function. + // Set the number of parameters expected by function. void set_parameter_count(int number_of_params); - int parameter_count() const; + int parameter_count() const { + DCHECK_GE(parameter_count_, 0); + return parameter_count_; + } - // Set number of locals required for bytecode array. + // Set the number of locals required for bytecode array. void set_locals_count(int number_of_locals); - int locals_count() const; + int locals_count() const { + DCHECK_GE(local_register_count_, 0); + return local_register_count_; + } + + // Set number of contexts required for bytecode array. + void set_context_count(int number_of_contexts); + int context_count() const { + DCHECK_GE(context_register_count_, 0); + return context_register_count_; + } - Register Parameter(int parameter_index); + Register first_context_register() const; + Register last_context_register() const; + + // Returns the number of fixed (non-temporary) registers. + int fixed_register_count() const { return context_count() + locals_count(); } + + Register Parameter(int parameter_index) const; + + // Return true if the register |reg| represents a parameter or a + // local. + bool RegisterIsParameterOrLocal(Register reg) const; + + // Return true if the register |reg| represents a temporary register. + bool RegisterIsTemporary(Register reg) const; // Constant loads to accumulator. BytecodeArrayBuilder& LoadLiteral(v8::internal::Smi* value); @@ -46,109 +76,267 @@ class BytecodeArrayBuilder { BytecodeArrayBuilder& LoadTheHole(); BytecodeArrayBuilder& LoadTrue(); BytecodeArrayBuilder& LoadFalse(); + BytecodeArrayBuilder& LoadBooleanConstant(bool value); + + // Global loads to the accumulator and stores from the accumulator. + BytecodeArrayBuilder& LoadGlobal(const Handle<String> name, int feedback_slot, + LanguageMode language_mode, + TypeofMode typeof_mode); + BytecodeArrayBuilder& StoreGlobal(const Handle<String> name, + int feedback_slot, + LanguageMode language_mode); + + // Load the object at |slot_index| in |context| into the accumulator. + BytecodeArrayBuilder& LoadContextSlot(Register context, int slot_index); - // Global loads to accumulator. - BytecodeArrayBuilder& LoadGlobal(int slot_index); + // Stores the object in the accumulator into |slot_index| of |context|. + BytecodeArrayBuilder& StoreContextSlot(Register context, int slot_index); // Register-accumulator transfers. BytecodeArrayBuilder& LoadAccumulatorWithRegister(Register reg); BytecodeArrayBuilder& StoreAccumulatorInRegister(Register reg); - // Load properties. The property name should be in the accumulator. - BytecodeArrayBuilder& LoadNamedProperty(Register object, int feedback_slot, + // Register-register transfer. + BytecodeArrayBuilder& MoveRegister(Register from, Register to); + BytecodeArrayBuilder& ExchangeRegisters(Register reg0, Register reg1); + + // Named load property. + BytecodeArrayBuilder& LoadNamedProperty(Register object, + const Handle<String> name, + int feedback_slot, LanguageMode language_mode); + // Keyed load property. The key should be in the accumulator. BytecodeArrayBuilder& LoadKeyedProperty(Register object, int feedback_slot, LanguageMode language_mode); // Store properties. The value to be stored should be in the accumulator. - BytecodeArrayBuilder& StoreNamedProperty(Register object, Register name, + BytecodeArrayBuilder& StoreNamedProperty(Register object, + const Handle<String> name, int feedback_slot, LanguageMode language_mode); BytecodeArrayBuilder& StoreKeyedProperty(Register object, Register key, int feedback_slot, LanguageMode language_mode); + // Lookup the variable with |name|. + BytecodeArrayBuilder& LoadLookupSlot(const Handle<String> name, + TypeofMode typeof_mode); + + // Store value in the accumulator into the variable with |name|. + BytecodeArrayBuilder& StoreLookupSlot(const Handle<String> name, + LanguageMode language_mode); + + // Create a new closure for the SharedFunctionInfo. + BytecodeArrayBuilder& CreateClosure(Handle<SharedFunctionInfo> shared_info, + PretenureFlag tenured); + + // Create a new arguments object in the accumulator. + BytecodeArrayBuilder& CreateArguments(CreateArgumentsType type); + + // Literals creation. Constant elements should be in the accumulator. + BytecodeArrayBuilder& CreateRegExpLiteral(Handle<String> pattern, + int literal_index, int flags); + BytecodeArrayBuilder& CreateArrayLiteral(Handle<FixedArray> constant_elements, + int literal_index, int flags); + BytecodeArrayBuilder& CreateObjectLiteral( + Handle<FixedArray> constant_properties, int literal_index, int flags); + + // Push the context in accumulator as the new context, and store in register + // |context|. + BytecodeArrayBuilder& PushContext(Register context); + + // Pop the current context and replace with |context|. + BytecodeArrayBuilder& PopContext(Register context); + // Call a JS function. The JSFunction or Callable to be called should be in // |callable|, the receiver should be in |receiver| and all subsequent // arguments should be in registers <receiver + 1> to // <receiver + 1 + arg_count>. BytecodeArrayBuilder& Call(Register callable, Register receiver, - size_t arg_count); - - // Operators (register == lhs, accumulator = rhs). - BytecodeArrayBuilder& BinaryOperation(Token::Value binop, Register reg); + size_t arg_count, int feedback_slot); + + // Call the new operator. The |constructor| register is followed by + // |arg_count| consecutive registers containing arguments to be + // applied to the constructor. + BytecodeArrayBuilder& New(Register constructor, Register first_arg, + size_t arg_count); + + // Call the runtime function with |function_id|. The first argument should be + // in |first_arg| and all subsequent arguments should be in registers + // <first_arg + 1> to <first_arg + 1 + arg_count>. + BytecodeArrayBuilder& CallRuntime(Runtime::FunctionId function_id, + Register first_arg, size_t arg_count); + + // Call the runtime function with |function_id| that returns a pair of values. + // The first argument should be in |first_arg| and all subsequent arguments + // should be in registers <first_arg + 1> to <first_arg + 1 + arg_count>. The + // return values will be returned in <first_return> and <first_return + 1>. + BytecodeArrayBuilder& CallRuntimeForPair(Runtime::FunctionId function_id, + Register first_arg, size_t arg_count, + Register first_return); + + // Call the JS runtime function with |context_index|. The the receiver should + // be in |receiver| and all subsequent arguments should be in registers + // <receiver + 1> to <receiver + 1 + arg_count>. + BytecodeArrayBuilder& CallJSRuntime(int context_index, Register receiver, + size_t arg_count); + + // Operators (register holds the lhs value, accumulator holds the rhs value). + BytecodeArrayBuilder& BinaryOperation(Token::Value binop, Register reg, + Strength strength); + + // Count Operators (value stored in accumulator). + BytecodeArrayBuilder& CountOperation(Token::Value op, Strength strength); + + // Unary Operators. + BytecodeArrayBuilder& LogicalNot(); + BytecodeArrayBuilder& TypeOf(); + + // Deletes property from an object. This expects that accumulator contains + // the key to be deleted and the register contains a reference to the object. + BytecodeArrayBuilder& Delete(Register object, LanguageMode language_mode); + BytecodeArrayBuilder& DeleteLookupSlot(); // Tests. BytecodeArrayBuilder& CompareOperation(Token::Value op, Register reg, - LanguageMode language_mode); + Strength strength); - // Casts + // Casts. BytecodeArrayBuilder& CastAccumulatorToBoolean(); + BytecodeArrayBuilder& CastAccumulatorToJSObject(); + BytecodeArrayBuilder& CastAccumulatorToName(); + BytecodeArrayBuilder& CastAccumulatorToNumber(); // Flow Control. BytecodeArrayBuilder& Bind(BytecodeLabel* label); + BytecodeArrayBuilder& Bind(const BytecodeLabel& target, BytecodeLabel* label); + BytecodeArrayBuilder& Jump(BytecodeLabel* label); BytecodeArrayBuilder& JumpIfTrue(BytecodeLabel* label); BytecodeArrayBuilder& JumpIfFalse(BytecodeLabel* label); + BytecodeArrayBuilder& JumpIfNull(BytecodeLabel* label); + BytecodeArrayBuilder& JumpIfUndefined(BytecodeLabel* label); + + BytecodeArrayBuilder& Throw(); BytecodeArrayBuilder& Return(); - BytecodeArrayBuilder& EnterBlock(); - BytecodeArrayBuilder& LeaveBlock(); + // Complex flow control. + BytecodeArrayBuilder& ForInPrepare(Register cache_type, Register cache_array, + Register cache_length); + BytecodeArrayBuilder& ForInDone(Register index, Register cache_length); + BytecodeArrayBuilder& ForInNext(Register receiver, Register cache_type, + Register cache_array, Register index); + BytecodeArrayBuilder& ForInStep(Register index); + + // Accessors + Zone* zone() const { return zone_; } private: ZoneVector<uint8_t>* bytecodes() { return &bytecodes_; } const ZoneVector<uint8_t>* bytecodes() const { return &bytecodes_; } Isolate* isolate() const { return isolate_; } + ConstantArrayBuilder* constant_array_builder() { + return &constant_array_builder_; + } + const ConstantArrayBuilder* constant_array_builder() const { + return &constant_array_builder_; + } static Bytecode BytecodeForBinaryOperation(Token::Value op); + static Bytecode BytecodeForCountOperation(Token::Value op); static Bytecode BytecodeForCompareOperation(Token::Value op); - static bool FitsInIdxOperand(int value); - static bool FitsInIdxOperand(size_t value); + static Bytecode BytecodeForWideOperands(Bytecode bytecode); + static Bytecode BytecodeForLoadIC(LanguageMode language_mode); + static Bytecode BytecodeForKeyedLoadIC(LanguageMode language_mode); + static Bytecode BytecodeForStoreIC(LanguageMode language_mode); + static Bytecode BytecodeForKeyedStoreIC(LanguageMode language_mode); + static Bytecode BytecodeForLoadGlobal(LanguageMode language_mode, + TypeofMode typeof_mode); + static Bytecode BytecodeForStoreGlobal(LanguageMode language_mode); + static Bytecode BytecodeForStoreLookupSlot(LanguageMode language_mode); + static Bytecode BytecodeForCreateArguments(CreateArgumentsType type); + static Bytecode BytecodeForDelete(LanguageMode language_mode); + + static bool FitsInIdx8Operand(int value); + static bool FitsInIdx8Operand(size_t value); static bool FitsInImm8Operand(int value); - static bool IsJumpWithImm8Operand(Bytecode jump_bytecode); - static Bytecode GetJumpWithConstantOperand(Bytecode jump_with_smi8_operand); + static bool FitsInIdx16Operand(int value); + static bool FitsInIdx16Operand(size_t value); + static bool FitsInReg8Operand(Register value); + static bool FitsInReg16Operand(Register value); + + static Bytecode GetJumpWithConstantOperand(Bytecode jump_smi8_operand); + static Bytecode GetJumpWithConstantWideOperand(Bytecode jump_smi8_operand); + static Bytecode GetJumpWithToBoolean(Bytecode jump_smi8_operand); + + Register MapRegister(Register reg); + Register MapRegisters(Register reg, Register args_base, int args_length = 1); template <size_t N> - INLINE(void Output(uint8_t(&bytes)[N])); - void Output(Bytecode bytecode, uint8_t operand0, uint8_t operand1, - uint8_t operand2); - void Output(Bytecode bytecode, uint8_t operand0, uint8_t operand1); - void Output(Bytecode bytecode, uint8_t operand0); + INLINE(void Output(Bytecode bytecode, uint32_t(&operands)[N])); + void Output(Bytecode bytecode, uint32_t operand0, uint32_t operand1, + uint32_t operand2, uint32_t operand3); + void Output(Bytecode bytecode, uint32_t operand0, uint32_t operand1, + uint32_t operand2); + void Output(Bytecode bytecode, uint32_t operand0, uint32_t operand1); + void Output(Bytecode bytecode, uint32_t operand0); void Output(Bytecode bytecode); - void PatchJump(const ZoneVector<uint8_t>::iterator& jump_target, - ZoneVector<uint8_t>::iterator jump_location); + BytecodeArrayBuilder& OutputJump(Bytecode jump_bytecode, BytecodeLabel* label); + void PatchJump(const ZoneVector<uint8_t>::iterator& jump_target, + const ZoneVector<uint8_t>::iterator& jump_location); + void PatchIndirectJumpWith8BitOperand( + const ZoneVector<uint8_t>::iterator& jump_location, int delta); + void PatchIndirectJumpWith16BitOperand( + const ZoneVector<uint8_t>::iterator& jump_location, int delta); + void LeaveBasicBlock(); void EnsureReturn(); bool OperandIsValid(Bytecode bytecode, int operand_index, - uint8_t operand_value) const; + uint32_t operand_value) const; bool LastBytecodeInSameBlock() const; - size_t GetConstantPoolEntry(Handle<Object> object); + bool NeedToBooleanCast(); + bool IsRegisterInAccumulator(Register reg); - // Scope helpers used by TemporaryRegisterScope + bool RegisterIsValid(Register reg) const; + + // Temporary register management. int BorrowTemporaryRegister(); + int BorrowTemporaryRegisterNotInRange(int start_index, int end_index); void ReturnTemporaryRegister(int reg_index); + int PrepareForConsecutiveTemporaryRegisters(size_t count); + void BorrowConsecutiveTemporaryRegister(int reg_index); + bool TemporaryRegisterIsLive(Register reg) const; + + Register first_temporary_register() const; + Register last_temporary_register() const; + + // Gets a constant pool entry for the |object|. + size_t GetConstantPoolEntry(Handle<Object> object); Isolate* isolate_; + Zone* zone_; ZoneVector<uint8_t> bytecodes_; bool bytecode_generated_; + ConstantArrayBuilder constant_array_builder_; size_t last_block_end_; size_t last_bytecode_start_; - bool return_seen_in_block_; - - IdentityMap<size_t> constants_map_; - ZoneVector<Handle<Object>> constants_; + bool exit_seen_in_block_; + int unbound_jumps_; int parameter_count_; int local_register_count_; + int context_register_count_; int temporary_register_count_; - int temporary_register_next_; + ZoneSet<int> free_temporaries_; + + class PreviousBytecodeHelper; + friend class BytecodeRegisterAllocator; - friend class TemporaryRegisterScope; - DISALLOW_IMPLICIT_CONSTRUCTORS(BytecodeArrayBuilder); + DISALLOW_COPY_AND_ASSIGN(BytecodeArrayBuilder); }; @@ -159,23 +347,25 @@ class BytecodeArrayBuilder { class BytecodeLabel final { public: BytecodeLabel() : bound_(false), offset_(kInvalidOffset) {} - ~BytecodeLabel() { DCHECK(bound_ && offset_ != kInvalidOffset); } + + bool is_bound() const { return bound_; } + size_t offset() const { return offset_; } private: static const size_t kInvalidOffset = static_cast<size_t>(-1); - INLINE(void bind_to(size_t offset)) { + void bind_to(size_t offset) { DCHECK(!bound_ && offset != kInvalidOffset); offset_ = offset; bound_ = true; } - INLINE(void set_referrer(size_t offset)) { - DCHECK(!bound_ && offset != kInvalidOffset); + + void set_referrer(size_t offset) { + DCHECK(!bound_ && offset != kInvalidOffset && offset_ == kInvalidOffset); offset_ = offset; } - INLINE(size_t offset() const) { return offset_; } - INLINE(bool is_bound() const) { return bound_; } - INLINE(bool is_forward_target() const) { + + bool is_forward_target() const { return offset() != kInvalidOffset && !is_bound(); } @@ -188,30 +378,8 @@ class BytecodeLabel final { size_t offset_; friend class BytecodeArrayBuilder; - DISALLOW_COPY_AND_ASSIGN(BytecodeLabel); }; - -// A stack-allocated class than allows the instantiator to allocate -// temporary registers that are cleaned up when scope is closed. -class TemporaryRegisterScope { - public: - explicit TemporaryRegisterScope(BytecodeArrayBuilder* builder); - ~TemporaryRegisterScope(); - Register NewRegister(); - - private: - void* operator new(size_t size); - void operator delete(void* p); - - BytecodeArrayBuilder* builder_; - int count_; - int last_register_index_; - - DISALLOW_COPY_AND_ASSIGN(TemporaryRegisterScope); -}; - - } // namespace interpreter } // namespace internal } // namespace v8 diff --git a/chromium/v8/src/interpreter/bytecode-array-iterator.cc b/chromium/v8/src/interpreter/bytecode-array-iterator.cc index dc49308fbe8..d09d72f01a2 100644 --- a/chromium/v8/src/interpreter/bytecode-array-iterator.cc +++ b/chromium/v8/src/interpreter/bytecode-array-iterator.cc @@ -32,31 +32,66 @@ Bytecode BytecodeArrayIterator::current_bytecode() const { } -uint8_t BytecodeArrayIterator::GetRawOperand(int operand_index, - OperandType operand_type) const { +int BytecodeArrayIterator::current_bytecode_size() const { + return Bytecodes::Size(current_bytecode()); +} + + +uint32_t BytecodeArrayIterator::GetRawOperand(int operand_index, + OperandType operand_type) const { DCHECK_GE(operand_index, 0); DCHECK_LT(operand_index, Bytecodes::NumberOfOperands(current_bytecode())); DCHECK_EQ(operand_type, Bytecodes::GetOperandType(current_bytecode(), operand_index)); - int operands_start = bytecode_offset_ + 1; - return bytecode_array()->get(operands_start + operand_index); + uint8_t* operand_start = + bytecode_array()->GetFirstBytecodeAddress() + bytecode_offset_ + + Bytecodes::GetOperandOffset(current_bytecode(), operand_index); + switch (Bytecodes::SizeOfOperand(operand_type)) { + default: + case OperandSize::kNone: + UNREACHABLE(); + case OperandSize::kByte: + return static_cast<uint32_t>(*operand_start); + case OperandSize::kShort: + return ReadUnalignedUInt16(operand_start); + } } -int8_t BytecodeArrayIterator::GetSmi8Operand(int operand_index) const { - uint8_t operand = GetRawOperand(operand_index, OperandType::kImm8); +int8_t BytecodeArrayIterator::GetImmediateOperand(int operand_index) const { + uint32_t operand = GetRawOperand(operand_index, OperandType::kImm8); return static_cast<int8_t>(operand); } +int BytecodeArrayIterator::GetCountOperand(int operand_index) const { + OperandSize size = + Bytecodes::GetOperandSize(current_bytecode(), operand_index); + OperandType type = (size == OperandSize::kByte) ? OperandType::kCount8 + : OperandType::kCount16; + uint32_t operand = GetRawOperand(operand_index, type); + return static_cast<int>(operand); +} + + int BytecodeArrayIterator::GetIndexOperand(int operand_index) const { - uint8_t operand = GetRawOperand(operand_index, OperandType::kIdx); + OperandType operand_type = + Bytecodes::GetOperandType(current_bytecode(), operand_index); + DCHECK(operand_type == OperandType::kIdx8 || + operand_type == OperandType::kIdx16); + uint32_t operand = GetRawOperand(operand_index, operand_type); return static_cast<int>(operand); } Register BytecodeArrayIterator::GetRegisterOperand(int operand_index) const { - uint8_t operand = GetRawOperand(operand_index, OperandType::kReg); + OperandType operand_type = + Bytecodes::GetOperandType(current_bytecode(), operand_index); + DCHECK(operand_type == OperandType::kReg8 || + operand_type == OperandType::kRegPair8 || + operand_type == OperandType::kMaybeReg8 || + operand_type == OperandType::kReg16); + uint32_t operand = GetRawOperand(operand_index, operand_type); return Register::FromOperand(operand); } @@ -67,6 +102,22 @@ Handle<Object> BytecodeArrayIterator::GetConstantForIndexOperand( return FixedArray::get(constants, GetIndexOperand(operand_index)); } + +int BytecodeArrayIterator::GetJumpTargetOffset() const { + Bytecode bytecode = current_bytecode(); + if (interpreter::Bytecodes::IsJumpImmediate(bytecode)) { + int relative_offset = GetImmediateOperand(0); + return current_offset() + relative_offset; + } else if (interpreter::Bytecodes::IsJumpConstant(bytecode) || + interpreter::Bytecodes::IsJumpConstantWide(bytecode)) { + Smi* smi = Smi::cast(*GetConstantForIndexOperand(0)); + return current_offset() + smi->value(); + } else { + UNREACHABLE(); + return kMinInt; + } +} + } // namespace interpreter } // namespace internal } // namespace v8 diff --git a/chromium/v8/src/interpreter/bytecode-array-iterator.h b/chromium/v8/src/interpreter/bytecode-array-iterator.h index 0d9011f242e..e67fa974bd0 100644 --- a/chromium/v8/src/interpreter/bytecode-array-iterator.h +++ b/chromium/v8/src/interpreter/bytecode-array-iterator.h @@ -20,19 +20,26 @@ class BytecodeArrayIterator { void Advance(); bool done() const; Bytecode current_bytecode() const; + int current_bytecode_size() const; int current_offset() const { return bytecode_offset_; } const Handle<BytecodeArray>& bytecode_array() const { return bytecode_array_; } - int8_t GetSmi8Operand(int operand_index) const; + int8_t GetImmediateOperand(int operand_index) const; int GetIndexOperand(int operand_index) const; + int GetCountOperand(int operand_index) const; Register GetRegisterOperand(int operand_index) const; Handle<Object> GetConstantForIndexOperand(int operand_index) const; // Get the raw byte for the given operand. Note: you should prefer using the // typed versions above which cast the return to an appropriate type. - uint8_t GetRawOperand(int operand_index, OperandType operand_type) const; + uint32_t GetRawOperand(int operand_index, OperandType operand_type) const; + + // Returns the absolute offset of the branch target at the current + // bytecode. It is an error to call this method if the bytecode is + // not for a jump or conditional jump. + int GetJumpTargetOffset() const; private: Handle<BytecodeArray> bytecode_array_; diff --git a/chromium/v8/src/interpreter/bytecode-generator.cc b/chromium/v8/src/interpreter/bytecode-generator.cc index 7257fd41342..959e1551497 100644 --- a/chromium/v8/src/interpreter/bytecode-generator.cc +++ b/chromium/v8/src/interpreter/bytecode-generator.cc @@ -4,83 +4,482 @@ #include "src/interpreter/bytecode-generator.h" -#include <stack> - +#include "src/ast/scopes.h" #include "src/compiler.h" +#include "src/interpreter/bytecode-register-allocator.h" +#include "src/interpreter/control-flow-builders.h" #include "src/objects.h" -#include "src/scopes.h" -#include "src/token.h" +#include "src/parsing/parser.h" +#include "src/parsing/token.h" namespace v8 { namespace internal { namespace interpreter { -BytecodeGenerator::BytecodeGenerator(Isolate* isolate, Zone* zone) - : builder_(isolate, zone) { - InitializeAstVisitor(isolate, zone); + +// Scoped class tracking context objects created by the visitor. Represents +// mutations of the context chain within the function body, allowing pushing and +// popping of the current {context_register} during visitation. +class BytecodeGenerator::ContextScope BASE_EMBEDDED { + public: + ContextScope(BytecodeGenerator* generator, Scope* scope, + bool should_pop_context = true) + : generator_(generator), + scope_(scope), + outer_(generator_->execution_context()), + register_(generator_->NextContextRegister()), + depth_(0), + should_pop_context_(should_pop_context) { + if (outer_) { + depth_ = outer_->depth_ + 1; + generator_->builder()->PushContext(register_); + } + generator_->set_execution_context(this); + } + + ~ContextScope() { + if (outer_ && should_pop_context_) { + generator_->builder()->PopContext(outer_->reg()); + } + generator_->set_execution_context(outer_); + } + + // Returns the depth of the given |scope| for the current execution context. + int ContextChainDepth(Scope* scope) { + return scope_->ContextChainLength(scope); + } + + // Returns the execution context at |depth| in the current context chain if it + // is a function local execution context, otherwise returns nullptr. + ContextScope* Previous(int depth) { + if (depth > depth_) { + return nullptr; + } + + ContextScope* previous = this; + for (int i = depth; i > 0; --i) { + previous = previous->outer_; + } + return previous; + } + + Scope* scope() const { return scope_; } + Register reg() const { return register_; } + + private: + BytecodeGenerator* generator_; + Scope* scope_; + ContextScope* outer_; + Register register_; + int depth_; + bool should_pop_context_; +}; + + +// Scoped class for tracking control statements entered by the +// visitor. The pattern derives AstGraphBuilder::ControlScope. +class BytecodeGenerator::ControlScope BASE_EMBEDDED { + public: + explicit ControlScope(BytecodeGenerator* generator) + : generator_(generator), outer_(generator->execution_control()) { + generator_->set_execution_control(this); + } + virtual ~ControlScope() { generator_->set_execution_control(outer()); } + + void Break(Statement* stmt) { PerformCommand(CMD_BREAK, stmt); } + void Continue(Statement* stmt) { PerformCommand(CMD_CONTINUE, stmt); } + + protected: + enum Command { CMD_BREAK, CMD_CONTINUE }; + void PerformCommand(Command command, Statement* statement); + virtual bool Execute(Command command, Statement* statement) = 0; + + BytecodeGenerator* generator() const { return generator_; } + ControlScope* outer() const { return outer_; } + + private: + BytecodeGenerator* generator_; + ControlScope* outer_; + + DISALLOW_COPY_AND_ASSIGN(ControlScope); +}; + + +// Scoped class for enabling break inside blocks and switch blocks. +class BytecodeGenerator::ControlScopeForBreakable final + : public BytecodeGenerator::ControlScope { + public: + ControlScopeForBreakable(BytecodeGenerator* generator, + BreakableStatement* statement, + BreakableControlFlowBuilder* control_builder) + : ControlScope(generator), + statement_(statement), + control_builder_(control_builder) {} + + protected: + virtual bool Execute(Command command, Statement* statement) { + if (statement != statement_) return false; + switch (command) { + case CMD_BREAK: + control_builder_->Break(); + return true; + case CMD_CONTINUE: + break; + } + return false; + } + + private: + Statement* statement_; + BreakableControlFlowBuilder* control_builder_; +}; + + +// Scoped class for enabling 'break' and 'continue' in iteration +// constructs, e.g. do...while, while..., for... +class BytecodeGenerator::ControlScopeForIteration final + : public BytecodeGenerator::ControlScope { + public: + ControlScopeForIteration(BytecodeGenerator* generator, + IterationStatement* statement, + LoopBuilder* loop_builder) + : ControlScope(generator), + statement_(statement), + loop_builder_(loop_builder) {} + + protected: + virtual bool Execute(Command command, Statement* statement) { + if (statement != statement_) return false; + switch (command) { + case CMD_BREAK: + loop_builder_->Break(); + return true; + case CMD_CONTINUE: + loop_builder_->Continue(); + return true; + } + return false; + } + + private: + Statement* statement_; + LoopBuilder* loop_builder_; +}; + + +void BytecodeGenerator::ControlScope::PerformCommand(Command command, + Statement* statement) { + ControlScope* current = this; + do { + if (current->Execute(command, statement)) return; + current = current->outer(); + } while (current != nullptr); + UNREACHABLE(); } -BytecodeGenerator::~BytecodeGenerator() {} +class BytecodeGenerator::RegisterAllocationScope { + public: + explicit RegisterAllocationScope(BytecodeGenerator* generator) + : generator_(generator), + outer_(generator->register_allocator()), + allocator_(builder()) { + generator_->set_register_allocator(this); + } + + virtual ~RegisterAllocationScope() { + generator_->set_register_allocator(outer_); + } + + Register NewRegister() { + RegisterAllocationScope* current_scope = generator()->register_allocator(); + if ((current_scope == this) || + (current_scope->outer() == this && + !current_scope->allocator_.HasConsecutiveAllocations())) { + // Regular case - Allocating registers in current or outer context. + // VisitForRegisterValue allocates register in outer context. + return allocator_.NewRegister(); + } else { + // If it is required to allocate a register other than current or outer + // scopes, allocate a new temporary register. It might be expensive to + // walk the full context chain and compute the list of consecutive + // reservations in the innerscopes. + UNIMPLEMENTED(); + return Register(-1); + } + } + + void PrepareForConsecutiveAllocations(size_t count) { + allocator_.PrepareForConsecutiveAllocations(count); + } + + Register NextConsecutiveRegister() { + return allocator_.NextConsecutiveRegister(); + } + + bool RegisterIsAllocatedInThisScope(Register reg) const { + return allocator_.RegisterIsAllocatedInThisScope(reg); + } + + RegisterAllocationScope* outer() const { return outer_; } + + private: + BytecodeGenerator* generator() const { return generator_; } + BytecodeArrayBuilder* builder() const { return generator_->builder(); } + + BytecodeGenerator* generator_; + RegisterAllocationScope* outer_; + BytecodeRegisterAllocator allocator_; + + DISALLOW_COPY_AND_ASSIGN(RegisterAllocationScope); +}; + + +// Scoped base class for determining where the result of an expression +// is stored. +class BytecodeGenerator::ExpressionResultScope { + public: + ExpressionResultScope(BytecodeGenerator* generator, Expression::Context kind) + : generator_(generator), + kind_(kind), + outer_(generator->execution_result()), + allocator_(generator), + result_identified_(false) { + generator_->set_execution_result(this); + } + + virtual ~ExpressionResultScope() { + generator_->set_execution_result(outer_); + DCHECK(result_identified()); + } + + bool IsEffect() const { return kind_ == Expression::kEffect; } + bool IsValue() const { return kind_ == Expression::kValue; } + + virtual void SetResultInAccumulator() = 0; + virtual void SetResultInRegister(Register reg) = 0; + + protected: + ExpressionResultScope* outer() const { return outer_; } + BytecodeArrayBuilder* builder() const { return generator_->builder(); } + const RegisterAllocationScope* allocator() const { return &allocator_; } + + void set_result_identified() { + DCHECK(!result_identified()); + result_identified_ = true; + } + + bool result_identified() const { return result_identified_; } + + private: + BytecodeGenerator* generator_; + Expression::Context kind_; + ExpressionResultScope* outer_; + RegisterAllocationScope allocator_; + bool result_identified_; + + DISALLOW_COPY_AND_ASSIGN(ExpressionResultScope); +}; + + +// Scoped class used when the result of the current expression is not +// expected to produce a result. +class BytecodeGenerator::EffectResultScope final + : public ExpressionResultScope { + public: + explicit EffectResultScope(BytecodeGenerator* generator) + : ExpressionResultScope(generator, Expression::kEffect) { + set_result_identified(); + } + + virtual void SetResultInAccumulator() {} + virtual void SetResultInRegister(Register reg) {} +}; + + +// Scoped class used when the result of the current expression to be +// evaluated should go into the interpreter's accumulator register. +class BytecodeGenerator::AccumulatorResultScope final + : public ExpressionResultScope { + public: + explicit AccumulatorResultScope(BytecodeGenerator* generator) + : ExpressionResultScope(generator, Expression::kValue) {} + + virtual void SetResultInAccumulator() { set_result_identified(); } + + virtual void SetResultInRegister(Register reg) { + builder()->LoadAccumulatorWithRegister(reg); + set_result_identified(); + } +}; + + +// Scoped class used when the result of the current expression to be +// evaluated should go into an interpreter register. +class BytecodeGenerator::RegisterResultScope final + : public ExpressionResultScope { + public: + explicit RegisterResultScope(BytecodeGenerator* generator) + : ExpressionResultScope(generator, Expression::kValue) {} + + virtual void SetResultInAccumulator() { + result_register_ = allocator()->outer()->NewRegister(); + builder()->StoreAccumulatorInRegister(result_register_); + set_result_identified(); + } + + virtual void SetResultInRegister(Register reg) { + DCHECK(builder()->RegisterIsParameterOrLocal(reg) || + (builder()->RegisterIsTemporary(reg) && + !allocator()->RegisterIsAllocatedInThisScope(reg))); + result_register_ = reg; + set_result_identified(); + } + + Register ResultRegister() const { return result_register_; } + + private: + Register result_register_; +}; + + +BytecodeGenerator::BytecodeGenerator(Isolate* isolate, Zone* zone) + : isolate_(isolate), + zone_(zone), + builder_(isolate, zone), + info_(nullptr), + scope_(nullptr), + globals_(0, zone), + execution_control_(nullptr), + execution_context_(nullptr), + execution_result_(nullptr), + register_allocator_(nullptr) { + InitializeAstVisitor(isolate); +} Handle<BytecodeArray> BytecodeGenerator::MakeBytecode(CompilationInfo* info) { set_info(info); set_scope(info->scope()); - // This a temporary guard (oth). - DCHECK(scope()->is_function_scope()); + // Initialize the incoming context. + ContextScope incoming_context(this, scope(), false); + + builder()->set_parameter_count(info->num_parameters_including_this()); + builder()->set_locals_count(scope()->num_stack_slots()); + builder()->set_context_count(scope()->MaxNestedContextChainLength()); + + // Build function context only if there are context allocated variables. + if (scope()->NeedsContext()) { + // Push a new inner context scope for the function. + VisitNewLocalFunctionContext(); + ContextScope local_function_context(this, scope(), false); + VisitBuildLocalActivationContext(); + MakeBytecodeBody(); + } else { + MakeBytecodeBody(); + } + + set_scope(nullptr); + set_info(nullptr); + return builder_.ToBytecodeArray(); +} + + +void BytecodeGenerator::MakeBytecodeBody() { + // Build the arguments object if it is used. + VisitArgumentsObject(scope()->arguments()); + + // TODO(mythria): Build rest arguments array if it is used. + int rest_index; + if (scope()->rest_parameter(&rest_index)) { + UNIMPLEMENTED(); + } + + // Build assignment to {.this_function} variable if it is used. + VisitThisFunctionVariable(scope()->this_function_var()); - builder().set_parameter_count(info->num_parameters_including_this()); - builder().set_locals_count(scope()->num_stack_slots()); + // Build assignment to {new.target} variable if it is used. + VisitNewTargetVariable(scope()->new_target_var()); + + // TODO(rmcilroy): Emit tracing call if requested to do so. + if (FLAG_trace) { + UNIMPLEMENTED(); + } - // Visit implicit declaration of the function name. - if (scope()->is_function_scope() && scope()->function() != NULL) { - VisitVariableDeclaration(scope()->function()); + // Visit illegal re-declaration and bail out if it exists. + if (scope()->HasIllegalRedeclaration()) { + Visit(scope()->GetIllegalRedeclaration()); + return; } // Visit declarations within the function scope. VisitDeclarations(scope()->declarations()); // Visit statements in the function body. - VisitStatements(info->literal()->body()); - - set_scope(nullptr); - set_info(nullptr); - return builder_.ToBytecodeArray(); + VisitStatements(info()->literal()->body()); } -void BytecodeGenerator::VisitBlock(Block* node) { - builder().EnterBlock(); - if (node->scope() == NULL) { +void BytecodeGenerator::VisitBlock(Block* stmt) { + BlockBuilder block_builder(this->builder()); + ControlScopeForBreakable execution_control(this, stmt, &block_builder); + + if (stmt->scope() == NULL) { // Visit statements in the same scope, no declarations. - VisitStatements(node->statements()); + VisitStatements(stmt->statements()); } else { // Visit declarations and statements in a block scope. - if (node->scope()->ContextLocalCount() > 0) { - UNIMPLEMENTED(); + if (stmt->scope()->NeedsContext()) { + VisitNewLocalBlockContext(stmt->scope()); + ContextScope scope(this, stmt->scope()); + VisitDeclarations(stmt->scope()->declarations()); + VisitStatements(stmt->statements()); } else { - VisitDeclarations(node->scope()->declarations()); - VisitStatements(node->statements()); + VisitDeclarations(stmt->scope()->declarations()); + VisitStatements(stmt->statements()); } } - builder().LeaveBlock(); + if (stmt->labels() != nullptr) block_builder.EndBlock(); } void BytecodeGenerator::VisitVariableDeclaration(VariableDeclaration* decl) { Variable* variable = decl->proxy()->var(); + VariableMode mode = decl->mode(); + // Const and let variables are initialized with the hole so that we can + // check that they are only assigned once. + bool hole_init = mode == CONST || mode == CONST_LEGACY || mode == LET; switch (variable->location()) { case VariableLocation::GLOBAL: - case VariableLocation::UNALLOCATED: - UNIMPLEMENTED(); + case VariableLocation::UNALLOCATED: { + Handle<Oddball> value = variable->binding_needs_init() + ? isolate()->factory()->the_hole_value() + : isolate()->factory()->undefined_value(); + globals()->push_back(variable->name()); + globals()->push_back(value); break; - case VariableLocation::PARAMETER: + } case VariableLocation::LOCAL: - // Details stored in scope, i.e. variable index. + if (hole_init) { + Register destination(variable->index()); + builder()->LoadTheHole().StoreAccumulatorInRegister(destination); + } + break; + case VariableLocation::PARAMETER: + if (hole_init) { + // The parameter indices are shifted by 1 (receiver is variable + // index -1 but is parameter index 0 in BytecodeArrayBuilder). + Register destination(builder()->Parameter(variable->index() + 1)); + builder()->LoadTheHole().StoreAccumulatorInRegister(destination); + } break; case VariableLocation::CONTEXT: + if (hole_init) { + builder()->LoadTheHole().StoreContextSlot(execution_context()->reg(), + variable->index()); + } + break; case VariableLocation::LOOKUP: UNIMPLEMENTED(); break; @@ -89,7 +488,34 @@ void BytecodeGenerator::VisitVariableDeclaration(VariableDeclaration* decl) { void BytecodeGenerator::VisitFunctionDeclaration(FunctionDeclaration* decl) { - UNIMPLEMENTED(); + Variable* variable = decl->proxy()->var(); + switch (variable->location()) { + case VariableLocation::GLOBAL: + case VariableLocation::UNALLOCATED: { + Handle<SharedFunctionInfo> function = Compiler::GetSharedFunctionInfo( + decl->fun(), info()->script(), info()); + // Check for stack-overflow exception. + if (function.is_null()) return SetStackOverflow(); + globals()->push_back(variable->name()); + globals()->push_back(function); + break; + } + case VariableLocation::PARAMETER: + case VariableLocation::LOCAL: { + VisitForAccumulatorValue(decl->fun()); + VisitVariableAssignment(variable, FeedbackVectorSlot::Invalid()); + break; + } + case VariableLocation::CONTEXT: { + DCHECK_EQ(0, execution_context()->ContextChainDepth(variable->scope())); + VisitForAccumulatorValue(decl->fun()); + builder()->StoreContextSlot(execution_context()->reg(), + variable->index()); + break; + } + case VariableLocation::LOOKUP: + UNIMPLEMENTED(); + } } @@ -103,31 +529,80 @@ void BytecodeGenerator::VisitExportDeclaration(ExportDeclaration* decl) { } +void BytecodeGenerator::VisitDeclarations( + ZoneList<Declaration*>* declarations) { + RegisterAllocationScope register_scope(this); + DCHECK(globals()->empty()); + AstVisitor::VisitDeclarations(declarations); + if (globals()->empty()) return; + int array_index = 0; + Handle<FixedArray> data = isolate()->factory()->NewFixedArray( + static_cast<int>(globals()->size()), TENURED); + for (Handle<Object> obj : *globals()) data->set(array_index++, *obj); + int encoded_flags = DeclareGlobalsEvalFlag::encode(info()->is_eval()) | + DeclareGlobalsNativeFlag::encode(info()->is_native()) | + DeclareGlobalsLanguageMode::encode(language_mode()); + + Register pairs = register_allocator()->NewRegister(); + builder()->LoadLiteral(data); + builder()->StoreAccumulatorInRegister(pairs); + + Register flags = register_allocator()->NewRegister(); + builder()->LoadLiteral(Smi::FromInt(encoded_flags)); + builder()->StoreAccumulatorInRegister(flags); + DCHECK(flags.index() == pairs.index() + 1); + + builder()->CallRuntime(Runtime::kDeclareGlobals, pairs, 2); + globals()->clear(); +} + + +void BytecodeGenerator::VisitStatements(ZoneList<Statement*>* statements) { + for (int i = 0; i < statements->length(); i++) { + // Allocate an outer register allocations scope for the statement. + RegisterAllocationScope allocation_scope(this); + Statement* stmt = statements->at(i); + Visit(stmt); + if (stmt->IsJump()) break; + } +} + + void BytecodeGenerator::VisitExpressionStatement(ExpressionStatement* stmt) { - Visit(stmt->expression()); + VisitForEffect(stmt->expression()); } void BytecodeGenerator::VisitEmptyStatement(EmptyStatement* stmt) { - // TODO(oth): For control-flow it could be useful to signal empty paths here. } void BytecodeGenerator::VisitIfStatement(IfStatement* stmt) { - BytecodeLabel else_start, else_end; - // TODO(oth): Spot easy cases where there code would not need to - // emit the then block or the else block, e.g. condition is - // obviously true/1/false/0. - Visit(stmt->condition()); - builder().CastAccumulatorToBoolean(); - builder().JumpIfFalse(&else_start); - - Visit(stmt->then_statement()); - builder().Jump(&else_end); - builder().Bind(&else_start); - - Visit(stmt->else_statement()); - builder().Bind(&else_end); + BytecodeLabel else_label, end_label; + if (stmt->condition()->ToBooleanIsTrue()) { + // Generate then block unconditionally as always true. + Visit(stmt->then_statement()); + } else if (stmt->condition()->ToBooleanIsFalse()) { + // Generate else block unconditionally if it exists. + if (stmt->HasElseStatement()) { + Visit(stmt->else_statement()); + } + } else { + // TODO(oth): If then statement is BreakStatement or + // ContinueStatement we can reduce number of generated + // jump/jump_ifs here. See BasicLoops test. + VisitForAccumulatorValue(stmt->condition()); + builder()->JumpIfFalse(&else_label); + Visit(stmt->then_statement()); + if (stmt->HasElseStatement()) { + builder()->Jump(&end_label); + builder()->Bind(&else_label); + Visit(stmt->else_statement()); + } else { + builder()->Bind(&else_label); + } + builder()->Bind(&end_label); + } } @@ -138,18 +613,18 @@ void BytecodeGenerator::VisitSloppyBlockFunctionStatement( void BytecodeGenerator::VisitContinueStatement(ContinueStatement* stmt) { - UNIMPLEMENTED(); + execution_control()->Continue(stmt->target()); } void BytecodeGenerator::VisitBreakStatement(BreakStatement* stmt) { - UNIMPLEMENTED(); + execution_control()->Break(stmt->target()); } void BytecodeGenerator::VisitReturnStatement(ReturnStatement* stmt) { - Visit(stmt->expression()); - builder().Return(); + VisitForAccumulatorValue(stmt->expression()); + builder()->Return(); } @@ -159,30 +634,220 @@ void BytecodeGenerator::VisitWithStatement(WithStatement* stmt) { void BytecodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { - UNIMPLEMENTED(); + // We need this scope because we visit for register values. We have to + // maintain a execution result scope where registers can be allocated. + ZoneList<CaseClause*>* clauses = stmt->cases(); + SwitchBuilder switch_builder(builder(), clauses->length()); + ControlScopeForBreakable scope(this, stmt, &switch_builder); + int default_index = -1; + + // Keep the switch value in a register until a case matches. + Register tag = VisitForRegisterValue(stmt->tag()); + + // Iterate over all cases and create nodes for label comparison. + BytecodeLabel done_label; + for (int i = 0; i < clauses->length(); i++) { + CaseClause* clause = clauses->at(i); + + // The default is not a test, remember index. + if (clause->is_default()) { + default_index = i; + continue; + } + + // Perform label comparison as if via '===' with tag. + VisitForAccumulatorValue(clause->label()); + builder()->CompareOperation(Token::Value::EQ_STRICT, tag, + language_mode_strength()); + switch_builder.Case(i); + } + + if (default_index >= 0) { + // Emit default jump if there is a default case. + switch_builder.DefaultAt(default_index); + } else { + // Otherwise if we have reached here none of the cases matched, so jump to + // done. + builder()->Jump(&done_label); + } + + // Iterate over all cases and create the case bodies. + for (int i = 0; i < clauses->length(); i++) { + CaseClause* clause = clauses->at(i); + switch_builder.SetCaseTarget(i); + VisitStatements(clause->statements()); + } + builder()->Bind(&done_label); + + switch_builder.SetBreakTarget(done_label); } -void BytecodeGenerator::VisitCaseClause(CaseClause* clause) { UNIMPLEMENTED(); } +void BytecodeGenerator::VisitCaseClause(CaseClause* clause) { + // Handled entirely in VisitSwitchStatement. + UNREACHABLE(); +} void BytecodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) { - UNIMPLEMENTED(); + LoopBuilder loop_builder(builder()); + ControlScopeForIteration execution_control(this, stmt, &loop_builder); + loop_builder.LoopHeader(); + if (stmt->cond()->ToBooleanIsFalse()) { + Visit(stmt->body()); + loop_builder.Condition(); + } else if (stmt->cond()->ToBooleanIsTrue()) { + loop_builder.Condition(); + Visit(stmt->body()); + loop_builder.JumpToHeader(); + } else { + Visit(stmt->body()); + loop_builder.Condition(); + VisitForAccumulatorValue(stmt->cond()); + loop_builder.JumpToHeaderIfTrue(); + } + loop_builder.EndLoop(); } void BytecodeGenerator::VisitWhileStatement(WhileStatement* stmt) { - UNIMPLEMENTED(); + if (stmt->cond()->ToBooleanIsFalse()) { + // If the condition is false there is no need to generate the loop. + return; + } + + LoopBuilder loop_builder(builder()); + ControlScopeForIteration execution_control(this, stmt, &loop_builder); + loop_builder.LoopHeader(); + loop_builder.Condition(); + if (!stmt->cond()->ToBooleanIsTrue()) { + VisitForAccumulatorValue(stmt->cond()); + loop_builder.BreakIfFalse(); + } + Visit(stmt->body()); + loop_builder.JumpToHeader(); + loop_builder.EndLoop(); } void BytecodeGenerator::VisitForStatement(ForStatement* stmt) { - UNIMPLEMENTED(); + if (stmt->init() != nullptr) { + Visit(stmt->init()); + } + if (stmt->cond() && stmt->cond()->ToBooleanIsFalse()) { + // If the condition is known to be false there is no need to generate + // body, next or condition blocks. Init block should be generated. + return; + } + + LoopBuilder loop_builder(builder()); + ControlScopeForIteration execution_control(this, stmt, &loop_builder); + + loop_builder.LoopHeader(); + loop_builder.Condition(); + if (stmt->cond() && !stmt->cond()->ToBooleanIsTrue()) { + VisitForAccumulatorValue(stmt->cond()); + loop_builder.BreakIfFalse(); + } + Visit(stmt->body()); + if (stmt->next() != nullptr) { + loop_builder.Next(); + Visit(stmt->next()); + } + loop_builder.JumpToHeader(); + loop_builder.EndLoop(); +} + + +void BytecodeGenerator::VisitForInAssignment(Expression* expr, + FeedbackVectorSlot slot) { + DCHECK(expr->IsValidReferenceExpression()); + + // Evaluate assignment starting with the value to be stored in the + // accumulator. + Property* property = expr->AsProperty(); + LhsKind assign_type = Property::GetAssignType(property); + switch (assign_type) { + case VARIABLE: { + Variable* variable = expr->AsVariableProxy()->var(); + VisitVariableAssignment(variable, slot); + break; + } + case NAMED_PROPERTY: { + RegisterAllocationScope register_scope(this); + Register value = register_allocator()->NewRegister(); + builder()->StoreAccumulatorInRegister(value); + Register object = VisitForRegisterValue(property->obj()); + Handle<String> name = property->key()->AsLiteral()->AsPropertyName(); + builder()->LoadAccumulatorWithRegister(value); + builder()->StoreNamedProperty(object, name, feedback_index(slot), + language_mode()); + break; + } + case KEYED_PROPERTY: { + RegisterAllocationScope register_scope(this); + Register value = register_allocator()->NewRegister(); + builder()->StoreAccumulatorInRegister(value); + Register object = VisitForRegisterValue(property->obj()); + Register key = VisitForRegisterValue(property->key()); + builder()->LoadAccumulatorWithRegister(value); + builder()->StoreKeyedProperty(object, key, feedback_index(slot), + language_mode()); + break; + } + case NAMED_SUPER_PROPERTY: + case KEYED_SUPER_PROPERTY: + UNIMPLEMENTED(); + } } void BytecodeGenerator::VisitForInStatement(ForInStatement* stmt) { - UNIMPLEMENTED(); + if (stmt->subject()->IsNullLiteral() || + stmt->subject()->IsUndefinedLiteral(isolate())) { + // ForIn generates lots of code, skip if it wouldn't produce any effects. + return; + } + + LoopBuilder loop_builder(builder()); + ControlScopeForIteration control_scope(this, stmt, &loop_builder); + BytecodeLabel subject_null_label, subject_undefined_label, not_object_label; + + // Prepare the state for executing ForIn. + VisitForAccumulatorValue(stmt->subject()); + builder()->JumpIfUndefined(&subject_undefined_label); + builder()->JumpIfNull(&subject_null_label); + Register receiver = register_allocator()->NewRegister(); + builder()->CastAccumulatorToJSObject(); + builder()->JumpIfNull(¬_object_label); + builder()->StoreAccumulatorInRegister(receiver); + Register cache_type = register_allocator()->NewRegister(); + Register cache_array = register_allocator()->NewRegister(); + Register cache_length = register_allocator()->NewRegister(); + builder()->ForInPrepare(cache_type, cache_array, cache_length); + + // Set up loop counter + Register index = register_allocator()->NewRegister(); + builder()->LoadLiteral(Smi::FromInt(0)); + builder()->StoreAccumulatorInRegister(index); + + // The loop + loop_builder.LoopHeader(); + loop_builder.Condition(); + builder()->ForInDone(index, cache_length); + loop_builder.BreakIfTrue(); + builder()->ForInNext(receiver, cache_type, cache_array, index); + loop_builder.ContinueIfUndefined(); + VisitForInAssignment(stmt->each(), stmt->EachFeedbackSlot()); + Visit(stmt->body()); + loop_builder.Next(); + builder()->ForInStep(index); + builder()->StoreAccumulatorInRegister(index); + loop_builder.JumpToHeader(); + loop_builder.EndLoop(); + builder()->Bind(¬_object_label); + builder()->Bind(&subject_null_label); + builder()->Bind(&subject_undefined_label); } @@ -192,11 +857,20 @@ void BytecodeGenerator::VisitForOfStatement(ForOfStatement* stmt) { void BytecodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) { + if (FLAG_ignition_fake_try_catch) { + Visit(stmt->try_block()); + return; + } UNIMPLEMENTED(); } void BytecodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) { + if (FLAG_ignition_fake_try_catch) { + Visit(stmt->try_block()); + Visit(stmt->finally_block()); + return; + } UNIMPLEMENTED(); } @@ -207,7 +881,13 @@ void BytecodeGenerator::VisitDebuggerStatement(DebuggerStatement* stmt) { void BytecodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) { - UNIMPLEMENTED(); + // Find or build a shared function info. + Handle<SharedFunctionInfo> shared_info = + Compiler::GetSharedFunctionInfo(expr, info()->script(), info()); + CHECK(!shared_info.is_null()); // TODO(rmcilroy): Set stack overflow? + builder()->CreateClosure(shared_info, + expr->pretenure() ? TENURED : NOT_TENURED); + execution_result()->SetResultInAccumulator(); } @@ -222,84 +902,434 @@ void BytecodeGenerator::VisitNativeFunctionLiteral( } -void BytecodeGenerator::VisitConditional(Conditional* expr) { UNIMPLEMENTED(); } +void BytecodeGenerator::VisitDoExpression(DoExpression* expr) { + UNIMPLEMENTED(); +} + + +void BytecodeGenerator::VisitConditional(Conditional* expr) { + // TODO(rmcilroy): Spot easy cases where there code would not need to + // emit the then block or the else block, e.g. condition is + // obviously true/1/false/0. + + BytecodeLabel else_label, end_label; + + VisitForAccumulatorValue(expr->condition()); + builder()->JumpIfFalse(&else_label); + + VisitForAccumulatorValue(expr->then_expression()); + builder()->Jump(&end_label); + + builder()->Bind(&else_label); + VisitForAccumulatorValue(expr->else_expression()); + builder()->Bind(&end_label); + + execution_result()->SetResultInAccumulator(); +} void BytecodeGenerator::VisitLiteral(Literal* expr) { - Handle<Object> value = expr->value(); - if (value->IsSmi()) { - builder().LoadLiteral(Smi::cast(*value)); - } else if (value->IsUndefined()) { - builder().LoadUndefined(); - } else if (value->IsTrue()) { - builder().LoadTrue(); - } else if (value->IsFalse()) { - builder().LoadFalse(); - } else if (value->IsNull()) { - builder().LoadNull(); - } else if (value->IsTheHole()) { - builder().LoadTheHole(); - } else { - builder().LoadLiteral(value); + if (!execution_result()->IsEffect()) { + Handle<Object> value = expr->value(); + if (value->IsSmi()) { + builder()->LoadLiteral(Smi::cast(*value)); + } else if (value->IsUndefined()) { + builder()->LoadUndefined(); + } else if (value->IsTrue()) { + builder()->LoadTrue(); + } else if (value->IsFalse()) { + builder()->LoadFalse(); + } else if (value->IsNull()) { + builder()->LoadNull(); + } else if (value->IsTheHole()) { + builder()->LoadTheHole(); + } else { + builder()->LoadLiteral(value); + } + execution_result()->SetResultInAccumulator(); } } void BytecodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) { - UNIMPLEMENTED(); + // Materialize a regular expression literal. + builder()->CreateRegExpLiteral(expr->pattern(), expr->literal_index(), + expr->flags()); + execution_result()->SetResultInAccumulator(); } void BytecodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { - UNIMPLEMENTED(); + // Deep-copy the literal boilerplate. + builder()->CreateObjectLiteral(expr->constant_properties(), + expr->literal_index(), + expr->ComputeFlags(true)); + Register literal; + + // Store computed values into the literal. + bool literal_in_accumulator = true; + int property_index = 0; + AccessorTable accessor_table(zone()); + for (; property_index < expr->properties()->length(); property_index++) { + ObjectLiteral::Property* property = expr->properties()->at(property_index); + if (property->is_computed_name()) break; + if (property->IsCompileTimeValue()) continue; + + if (literal_in_accumulator) { + literal = register_allocator()->NewRegister(); + builder()->StoreAccumulatorInRegister(literal); + literal_in_accumulator = false; + } + + RegisterAllocationScope inner_register_scope(this); + Literal* literal_key = property->key()->AsLiteral(); + switch (property->kind()) { + case ObjectLiteral::Property::CONSTANT: + UNREACHABLE(); + case ObjectLiteral::Property::MATERIALIZED_LITERAL: + DCHECK(!CompileTimeValue::IsCompileTimeValue(property->value())); + // Fall through. + case ObjectLiteral::Property::COMPUTED: { + // It is safe to use [[Put]] here because the boilerplate already + // contains computed properties with an uninitialized value. + if (literal_key->value()->IsInternalizedString()) { + if (property->emit_store()) { + VisitForAccumulatorValue(property->value()); + builder()->StoreNamedProperty( + literal, literal_key->AsPropertyName(), + feedback_index(property->GetSlot(0)), language_mode()); + } else { + VisitForEffect(property->value()); + } + } else { + register_allocator()->PrepareForConsecutiveAllocations(3); + Register key = register_allocator()->NextConsecutiveRegister(); + Register value = register_allocator()->NextConsecutiveRegister(); + Register language = register_allocator()->NextConsecutiveRegister(); + // TODO(oth): This is problematic - can't assume contiguous here. + // literal is allocated in outer register scope, whereas key, value, + // language are in another. + DCHECK(Register::AreContiguous(literal, key, value, language)); + VisitForAccumulatorValue(property->key()); + builder()->StoreAccumulatorInRegister(key); + VisitForAccumulatorValue(property->value()); + builder()->StoreAccumulatorInRegister(value); + if (property->emit_store()) { + builder() + ->LoadLiteral(Smi::FromInt(SLOPPY)) + .StoreAccumulatorInRegister(language) + .CallRuntime(Runtime::kSetProperty, literal, 4); + VisitSetHomeObject(value, literal, property); + } + } + break; + } + case ObjectLiteral::Property::PROTOTYPE: { + register_allocator()->PrepareForConsecutiveAllocations(1); + DCHECK(property->emit_store()); + Register value = register_allocator()->NextConsecutiveRegister(); + DCHECK(Register::AreContiguous(literal, value)); + VisitForAccumulatorValue(property->value()); + builder()->StoreAccumulatorInRegister(value).CallRuntime( + Runtime::kInternalSetPrototype, literal, 2); + break; + } + case ObjectLiteral::Property::GETTER: + if (property->emit_store()) { + accessor_table.lookup(literal_key)->second->getter = property; + } + break; + case ObjectLiteral::Property::SETTER: + if (property->emit_store()) { + accessor_table.lookup(literal_key)->second->setter = property; + } + break; + } + } + + // Define accessors, using only a single call to the runtime for each pair of + // corresponding getters and setters. + for (AccessorTable::Iterator it = accessor_table.begin(); + it != accessor_table.end(); ++it) { + RegisterAllocationScope inner_register_scope(this); + register_allocator()->PrepareForConsecutiveAllocations(4); + Register name = register_allocator()->NextConsecutiveRegister(); + Register getter = register_allocator()->NextConsecutiveRegister(); + Register setter = register_allocator()->NextConsecutiveRegister(); + Register attr = register_allocator()->NextConsecutiveRegister(); + DCHECK(Register::AreContiguous(literal, name, getter, setter, attr)); + VisitForAccumulatorValue(it->first); + builder()->StoreAccumulatorInRegister(name); + VisitObjectLiteralAccessor(literal, it->second->getter, getter); + VisitObjectLiteralAccessor(literal, it->second->setter, setter); + builder() + ->LoadLiteral(Smi::FromInt(NONE)) + .StoreAccumulatorInRegister(attr) + .CallRuntime(Runtime::kDefineAccessorPropertyUnchecked, literal, 5); + } + + // Object literals have two parts. The "static" part on the left contains no + // computed property names, and so we can compute its map ahead of time; see + // Runtime_CreateObjectLiteralBoilerplate. The second "dynamic" part starts + // with the first computed property name and continues with all properties to + // its right. All the code from above initializes the static component of the + // object literal, and arranges for the map of the result to reflect the + // static order in which the keys appear. For the dynamic properties, we + // compile them into a series of "SetOwnProperty" runtime calls. This will + // preserve insertion order. + for (; property_index < expr->properties()->length(); property_index++) { + if (literal_in_accumulator) { + literal = register_allocator()->NewRegister(); + builder()->StoreAccumulatorInRegister(literal); + literal_in_accumulator = false; + } + + ObjectLiteral::Property* property = expr->properties()->at(property_index); + RegisterAllocationScope inner_register_scope(this); + if (property->kind() == ObjectLiteral::Property::PROTOTYPE) { + DCHECK(property->emit_store()); + Register value = register_allocator()->NewRegister(); + DCHECK(Register::AreContiguous(literal, value)); + VisitForAccumulatorValue(property->value()); + builder()->StoreAccumulatorInRegister(value).CallRuntime( + Runtime::kInternalSetPrototype, literal, 2); + continue; + } + + register_allocator()->PrepareForConsecutiveAllocations(3); + Register key = register_allocator()->NextConsecutiveRegister(); + Register value = register_allocator()->NextConsecutiveRegister(); + Register attr = register_allocator()->NextConsecutiveRegister(); + DCHECK(Register::AreContiguous(literal, key, value, attr)); + + VisitForAccumulatorValue(property->key()); + builder()->CastAccumulatorToName().StoreAccumulatorInRegister(key); + VisitForAccumulatorValue(property->value()); + builder()->StoreAccumulatorInRegister(value); + VisitSetHomeObject(value, literal, property); + builder()->LoadLiteral(Smi::FromInt(NONE)).StoreAccumulatorInRegister(attr); + Runtime::FunctionId function_id = static_cast<Runtime::FunctionId>(-1); + switch (property->kind()) { + case ObjectLiteral::Property::CONSTANT: + case ObjectLiteral::Property::COMPUTED: + case ObjectLiteral::Property::MATERIALIZED_LITERAL: + function_id = Runtime::kDefineDataPropertyUnchecked; + break; + case ObjectLiteral::Property::PROTOTYPE: + UNREACHABLE(); // Handled specially above. + break; + case ObjectLiteral::Property::GETTER: + function_id = Runtime::kDefineGetterPropertyUnchecked; + break; + case ObjectLiteral::Property::SETTER: + function_id = Runtime::kDefineSetterPropertyUnchecked; + break; + } + builder()->CallRuntime(function_id, literal, 4); + } + + // Transform literals that contain functions to fast properties. + if (expr->has_function()) { + DCHECK(!literal_in_accumulator); + builder()->CallRuntime(Runtime::kToFastProperties, literal, 1); + } + + if (!literal_in_accumulator) { + // Restore literal array into accumulator. + builder()->LoadAccumulatorWithRegister(literal); + } + execution_result()->SetResultInAccumulator(); } void BytecodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { - UNIMPLEMENTED(); + // Deep-copy the literal boilerplate. + builder()->CreateArrayLiteral(expr->constant_elements(), + expr->literal_index(), + expr->ComputeFlags(true)); + Register index, literal; + + // Evaluate all the non-constant subexpressions and store them into the + // newly cloned array. + bool literal_in_accumulator = true; + for (int array_index = 0; array_index < expr->values()->length(); + array_index++) { + Expression* subexpr = expr->values()->at(array_index); + if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue; + if (subexpr->IsSpread()) { + // TODO(rmcilroy): Deal with spread expressions. + UNIMPLEMENTED(); + } + + if (literal_in_accumulator) { + index = register_allocator()->NewRegister(); + literal = register_allocator()->NewRegister(); + builder()->StoreAccumulatorInRegister(literal); + literal_in_accumulator = false; + } + + FeedbackVectorSlot slot = expr->LiteralFeedbackSlot(); + builder() + ->LoadLiteral(Smi::FromInt(array_index)) + .StoreAccumulatorInRegister(index); + VisitForAccumulatorValue(subexpr); + builder()->StoreKeyedProperty(literal, index, feedback_index(slot), + language_mode()); + } + + if (!literal_in_accumulator) { + // Restore literal array into accumulator. + builder()->LoadAccumulatorWithRegister(literal); + } + execution_result()->SetResultInAccumulator(); } void BytecodeGenerator::VisitVariableProxy(VariableProxy* proxy) { - VisitVariableLoad(proxy->var()); + VisitVariableLoad(proxy->var(), proxy->VariableFeedbackSlot()); } -void BytecodeGenerator::VisitVariableLoad(Variable* variable) { +void BytecodeGenerator::VisitVariableLoad(Variable* variable, + FeedbackVectorSlot slot, + TypeofMode typeof_mode) { switch (variable->location()) { case VariableLocation::LOCAL: { - Register source(variable->index()); - builder().LoadAccumulatorWithRegister(source); + Register source(Register(variable->index())); + builder()->LoadAccumulatorWithRegister(source); + execution_result()->SetResultInAccumulator(); break; } case VariableLocation::PARAMETER: { // The parameter indices are shifted by 1 (receiver is variable // index -1 but is parameter index 0 in BytecodeArrayBuilder). - Register source(builder().Parameter(variable->index() + 1)); - builder().LoadAccumulatorWithRegister(source); + Register source = builder()->Parameter(variable->index() + 1); + builder()->LoadAccumulatorWithRegister(source); + execution_result()->SetResultInAccumulator(); break; } - case VariableLocation::GLOBAL: { - // Global var, const, or let variable. - // TODO(rmcilroy): If context chain depth is short enough, do this using - // a generic version of LoadGlobalViaContextStub rather than calling the - // runtime. - DCHECK(variable->IsStaticGlobalObjectProperty()); - builder().LoadGlobal(variable->index()); + case VariableLocation::GLOBAL: + case VariableLocation::UNALLOCATED: { + builder()->LoadGlobal(variable->name(), feedback_index(slot), + language_mode(), typeof_mode); + execution_result()->SetResultInAccumulator(); + break; + } + case VariableLocation::CONTEXT: { + int depth = execution_context()->ContextChainDepth(variable->scope()); + ContextScope* context = execution_context()->Previous(depth); + Register context_reg; + if (context) { + context_reg = context->reg(); + } else { + context_reg = register_allocator()->NewRegister(); + // Walk the context chain to find the context at the given depth. + // TODO(rmcilroy): Perform this work in a bytecode handler once we have + // a generic mechanism for performing jumps in interpreter.cc. + // TODO(mythria): Also update bytecode graph builder with correct depth + // when this changes. + builder() + ->LoadAccumulatorWithRegister(execution_context()->reg()) + .StoreAccumulatorInRegister(context_reg); + for (int i = 0; i < depth; ++i) { + builder() + ->LoadContextSlot(context_reg, Context::PREVIOUS_INDEX) + .StoreAccumulatorInRegister(context_reg); + } + } + builder()->LoadContextSlot(context_reg, variable->index()); + execution_result()->SetResultInAccumulator(); + // TODO(rmcilroy): Perform check for uninitialized legacy const, const and + // let variables. + break; + } + case VariableLocation::LOOKUP: { + builder()->LoadLookupSlot(variable->name(), typeof_mode); + execution_result()->SetResultInAccumulator(); + break; + } + } +} + + +void BytecodeGenerator::VisitVariableLoadForAccumulatorValue( + Variable* variable, FeedbackVectorSlot slot, TypeofMode typeof_mode) { + AccumulatorResultScope accumulator_result(this); + VisitVariableLoad(variable, slot, typeof_mode); +} + + +Register BytecodeGenerator::VisitVariableLoadForRegisterValue( + Variable* variable, FeedbackVectorSlot slot, TypeofMode typeof_mode) { + RegisterResultScope register_scope(this); + VisitVariableLoad(variable, slot, typeof_mode); + return register_scope.ResultRegister(); +} + + +void BytecodeGenerator::VisitVariableAssignment(Variable* variable, + FeedbackVectorSlot slot) { + switch (variable->location()) { + case VariableLocation::LOCAL: { + // TODO(rmcilroy): support const mode initialization. + Register destination(variable->index()); + builder()->StoreAccumulatorInRegister(destination); + break; + } + case VariableLocation::PARAMETER: { + // The parameter indices are shifted by 1 (receiver is variable + // index -1 but is parameter index 0 in BytecodeArrayBuilder). + Register destination(builder()->Parameter(variable->index() + 1)); + builder()->StoreAccumulatorInRegister(destination); + break; + } + case VariableLocation::GLOBAL: + case VariableLocation::UNALLOCATED: { + builder()->StoreGlobal(variable->name(), feedback_index(slot), + language_mode()); + break; + } + case VariableLocation::CONTEXT: { + // TODO(rmcilroy): support const mode initialization. + int depth = execution_context()->ContextChainDepth(variable->scope()); + ContextScope* context = execution_context()->Previous(depth); + Register context_reg; + if (context) { + context_reg = context->reg(); + } else { + Register value_temp = register_allocator()->NewRegister(); + context_reg = register_allocator()->NewRegister(); + // Walk the context chain to find the context at the given depth. + // TODO(rmcilroy): Perform this work in a bytecode handler once we have + // a generic mechanism for performing jumps in interpreter.cc. + // TODO(mythria): Also update bytecode graph builder with correct depth + // when this changes. + builder() + ->StoreAccumulatorInRegister(value_temp) + .LoadAccumulatorWithRegister(execution_context()->reg()) + .StoreAccumulatorInRegister(context_reg); + for (int i = 0; i < depth; ++i) { + builder() + ->LoadContextSlot(context_reg, Context::PREVIOUS_INDEX) + .StoreAccumulatorInRegister(context_reg); + } + builder()->LoadAccumulatorWithRegister(value_temp); + } + builder()->StoreContextSlot(context_reg, variable->index()); + break; + } + case VariableLocation::LOOKUP: { + builder()->StoreLookupSlot(variable->name(), language_mode()); break; } - case VariableLocation::UNALLOCATED: - case VariableLocation::CONTEXT: - case VariableLocation::LOOKUP: - UNIMPLEMENTED(); } } void BytecodeGenerator::VisitAssignment(Assignment* expr) { DCHECK(expr->target()->IsValidReferenceExpression()); - TemporaryRegisterScope temporary_register_scope(&builder_); Register object, key; + Handle<String> name; // Left-hand side can only be a property, a global or a variable slot. Property* property = expr->target()->AsProperty(); @@ -310,22 +1340,24 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) { case VARIABLE: // Nothing to do to evaluate variable assignment LHS. break; - case NAMED_PROPERTY: - object = temporary_register_scope.NewRegister(); - key = temporary_register_scope.NewRegister(); - Visit(property->obj()); - builder().StoreAccumulatorInRegister(object); - builder().LoadLiteral(property->key()->AsLiteral()->AsPropertyName()); - builder().StoreAccumulatorInRegister(key); + case NAMED_PROPERTY: { + object = VisitForRegisterValue(property->obj()); + name = property->key()->AsLiteral()->AsPropertyName(); break; - case KEYED_PROPERTY: - object = temporary_register_scope.NewRegister(); - key = temporary_register_scope.NewRegister(); - Visit(property->obj()); - builder().StoreAccumulatorInRegister(object); - Visit(property->key()); - builder().StoreAccumulatorInRegister(key); + } + case KEYED_PROPERTY: { + object = VisitForRegisterValue(property->obj()); + if (expr->is_compound()) { + // Use VisitForAccumulator and store to register so that the key is + // still in the accumulator for loading the old value below. + key = register_allocator()->NewRegister(); + VisitForAccumulatorValue(property->key()); + builder()->StoreAccumulatorInRegister(key); + } else { + key = VisitForRegisterValue(property->key()); + } break; + } case NAMED_SUPER_PROPERTY: case KEYED_SUPER_PROPERTY: UNIMPLEMENTED(); @@ -334,151 +1366,510 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) { // Evaluate the value and potentially handle compound assignments by loading // the left-hand side value and performing a binary operation. if (expr->is_compound()) { - UNIMPLEMENTED(); + Register old_value; + switch (assign_type) { + case VARIABLE: { + VariableProxy* proxy = expr->target()->AsVariableProxy(); + old_value = VisitVariableLoadForRegisterValue( + proxy->var(), proxy->VariableFeedbackSlot()); + break; + } + case NAMED_PROPERTY: { + FeedbackVectorSlot slot = property->PropertyFeedbackSlot(); + old_value = register_allocator()->NewRegister(); + builder() + ->LoadNamedProperty(object, name, feedback_index(slot), + language_mode()) + .StoreAccumulatorInRegister(old_value); + break; + } + case KEYED_PROPERTY: { + // Key is already in accumulator at this point due to evaluating the + // LHS above. + FeedbackVectorSlot slot = property->PropertyFeedbackSlot(); + old_value = register_allocator()->NewRegister(); + builder() + ->LoadKeyedProperty(object, feedback_index(slot), language_mode()) + .StoreAccumulatorInRegister(old_value); + break; + } + case NAMED_SUPER_PROPERTY: + case KEYED_SUPER_PROPERTY: + UNIMPLEMENTED(); + break; + } + VisitForAccumulatorValue(expr->value()); + builder()->BinaryOperation(expr->binary_op(), old_value, + language_mode_strength()); } else { - Visit(expr->value()); + VisitForAccumulatorValue(expr->value()); } // Store the value. - FeedbackVectorICSlot slot = expr->AssignmentSlot(); + FeedbackVectorSlot slot = expr->AssignmentSlot(); switch (assign_type) { case VARIABLE: { + // TODO(oth): The VisitVariableAssignment() call is hard to reason about. + // Is the value in the accumulator safe? Yes, but scary. Variable* variable = expr->target()->AsVariableProxy()->var(); - DCHECK(variable->location() == VariableLocation::LOCAL); - Register destination(variable->index()); - builder().StoreAccumulatorInRegister(destination); + VisitVariableAssignment(variable, slot); break; } case NAMED_PROPERTY: - builder().StoreNamedProperty(object, key, feedback_index(slot), - language_mode()); + builder()->StoreNamedProperty(object, name, feedback_index(slot), + language_mode()); break; case KEYED_PROPERTY: - builder().StoreKeyedProperty(object, key, feedback_index(slot), - language_mode()); + builder()->StoreKeyedProperty(object, key, feedback_index(slot), + language_mode()); break; case NAMED_SUPER_PROPERTY: case KEYED_SUPER_PROPERTY: UNIMPLEMENTED(); } + execution_result()->SetResultInAccumulator(); } void BytecodeGenerator::VisitYield(Yield* expr) { UNIMPLEMENTED(); } -void BytecodeGenerator::VisitThrow(Throw* expr) { UNIMPLEMENTED(); } +void BytecodeGenerator::VisitThrow(Throw* expr) { + VisitForAccumulatorValue(expr->exception()); + builder()->Throw(); +} void BytecodeGenerator::VisitPropertyLoad(Register obj, Property* expr) { LhsKind property_kind = Property::GetAssignType(expr); - FeedbackVectorICSlot slot = expr->PropertyFeedbackSlot(); + FeedbackVectorSlot slot = expr->PropertyFeedbackSlot(); switch (property_kind) { case VARIABLE: UNREACHABLE(); case NAMED_PROPERTY: { - builder().LoadLiteral(expr->key()->AsLiteral()->AsPropertyName()); - builder().LoadNamedProperty(obj, feedback_index(slot), language_mode()); + builder()->LoadNamedProperty(obj, + expr->key()->AsLiteral()->AsPropertyName(), + feedback_index(slot), language_mode()); break; } case KEYED_PROPERTY: { - Visit(expr->key()); - builder().LoadKeyedProperty(obj, feedback_index(slot), language_mode()); + VisitForAccumulatorValue(expr->key()); + builder()->LoadKeyedProperty(obj, feedback_index(slot), language_mode()); break; } case NAMED_SUPER_PROPERTY: case KEYED_SUPER_PROPERTY: UNIMPLEMENTED(); } + execution_result()->SetResultInAccumulator(); +} + + +void BytecodeGenerator::VisitPropertyLoadForAccumulator(Register obj, + Property* expr) { + AccumulatorResultScope result_scope(this); + VisitPropertyLoad(obj, expr); } void BytecodeGenerator::VisitProperty(Property* expr) { - TemporaryRegisterScope temporary_register_scope(&builder_); - Register obj = temporary_register_scope.NewRegister(); - Visit(expr->obj()); - builder().StoreAccumulatorInRegister(obj); + Register obj = VisitForRegisterValue(expr->obj()); VisitPropertyLoad(obj, expr); } +Register BytecodeGenerator::VisitArguments(ZoneList<Expression*>* args) { + if (args->length() == 0) { + return Register(); + } + + // Visit arguments and place in a contiguous block of temporary + // registers. Return the first temporary register corresponding to + // the first argument. + // + // NB the caller may have already called + // PrepareForConsecutiveAllocations() with args->length() + N. The + // second call here will be a no-op provided there have been N or + // less calls to NextConsecutiveRegister(). Otherwise, the arguments + // here will be consecutive, but they will not be consecutive with + // earlier consecutive allocations made by the caller. + register_allocator()->PrepareForConsecutiveAllocations(args->length()); + + // Visit for first argument that goes into returned register + Register first_arg = register_allocator()->NextConsecutiveRegister(); + VisitForAccumulatorValue(args->at(0)); + builder()->StoreAccumulatorInRegister(first_arg); + + // Visit remaining arguments + for (int i = 1; i < static_cast<int>(args->length()); i++) { + Register ith_arg = register_allocator()->NextConsecutiveRegister(); + VisitForAccumulatorValue(args->at(i)); + builder()->StoreAccumulatorInRegister(ith_arg); + DCHECK(ith_arg.index() - i == first_arg.index()); + } + return first_arg; +} + + void BytecodeGenerator::VisitCall(Call* expr) { Expression* callee_expr = expr->expression(); Call::CallType call_type = expr->GetCallType(isolate()); // Prepare the callee and the receiver to the function call. This depends on // the semantics of the underlying call type. - TemporaryRegisterScope temporary_register_scope(&builder_); - Register callee = temporary_register_scope.NewRegister(); - Register receiver = temporary_register_scope.NewRegister(); + + // The receiver and arguments need to be allocated consecutively for + // Call(). We allocate the callee and receiver consecutively for calls to + // kLoadLookupSlot. Future optimizations could avoid this there are no + // arguments or the receiver and arguments are already consecutive. + ZoneList<Expression*>* args = expr->arguments(); + register_allocator()->PrepareForConsecutiveAllocations(args->length() + 2); + Register callee = register_allocator()->NextConsecutiveRegister(); + Register receiver = register_allocator()->NextConsecutiveRegister(); switch (call_type) { - case Call::PROPERTY_CALL: { + case Call::NAMED_PROPERTY_CALL: + case Call::KEYED_PROPERTY_CALL: { Property* property = callee_expr->AsProperty(); - if (property->IsSuperAccess()) { - UNIMPLEMENTED(); - } - Visit(property->obj()); - builder().StoreAccumulatorInRegister(receiver); - // Perform a property load of the callee. - VisitPropertyLoad(receiver, property); - builder().StoreAccumulatorInRegister(callee); + VisitForAccumulatorValue(property->obj()); + builder()->StoreAccumulatorInRegister(receiver); + VisitPropertyLoadForAccumulator(receiver, property); + builder()->StoreAccumulatorInRegister(callee); break; } case Call::GLOBAL_CALL: { // Receiver is undefined for global calls. - builder().LoadUndefined().StoreAccumulatorInRegister(receiver); + builder()->LoadUndefined().StoreAccumulatorInRegister(receiver); // Load callee as a global variable. VariableProxy* proxy = callee_expr->AsVariableProxy(); - VisitVariableLoad(proxy->var()); - builder().StoreAccumulatorInRegister(callee); + VisitVariableLoadForAccumulatorValue(proxy->var(), + proxy->VariableFeedbackSlot()); + builder()->StoreAccumulatorInRegister(callee); break; } case Call::LOOKUP_SLOT_CALL: + case Call::POSSIBLY_EVAL_CALL: { + if (callee_expr->AsVariableProxy()->var()->IsLookupSlot()) { + RegisterAllocationScope inner_register_scope(this); + register_allocator()->PrepareForConsecutiveAllocations(2); + Register context = register_allocator()->NextConsecutiveRegister(); + Register name = register_allocator()->NextConsecutiveRegister(); + + // Call LoadLookupSlot to get the callee and receiver. + DCHECK(Register::AreContiguous(callee, receiver)); + Variable* variable = callee_expr->AsVariableProxy()->var(); + builder() + ->MoveRegister(Register::function_context(), context) + .LoadLiteral(variable->name()) + .StoreAccumulatorInRegister(name) + .CallRuntimeForPair(Runtime::kLoadLookupSlot, context, 2, callee); + break; + } + // Fall through. + DCHECK_EQ(call_type, Call::POSSIBLY_EVAL_CALL); + } + case Call::OTHER_CALL: { + builder()->LoadUndefined().StoreAccumulatorInRegister(receiver); + VisitForAccumulatorValue(callee_expr); + builder()->StoreAccumulatorInRegister(callee); + break; + } + case Call::NAMED_SUPER_PROPERTY_CALL: + case Call::KEYED_SUPER_PROPERTY_CALL: case Call::SUPER_CALL: - case Call::POSSIBLY_EVAL_CALL: - case Call::OTHER_CALL: UNIMPLEMENTED(); } // Evaluate all arguments to the function call and store in sequential // registers. - ZoneList<Expression*>* args = expr->arguments(); - for (int i = 0; i < args->length(); ++i) { - Visit(args->at(i)); - Register arg = temporary_register_scope.NewRegister(); - DCHECK(arg.index() - i == receiver.index() + 1); - builder().StoreAccumulatorInRegister(arg); + Register arg = VisitArguments(args); + CHECK(args->length() == 0 || arg.index() == receiver.index() + 1); + + // Resolve callee for a potential direct eval call. This block will mutate the + // callee value. + if (call_type == Call::POSSIBLY_EVAL_CALL && args->length() > 0) { + RegisterAllocationScope inner_register_scope(this); + register_allocator()->PrepareForConsecutiveAllocations(5); + Register callee_for_eval = register_allocator()->NextConsecutiveRegister(); + Register source = register_allocator()->NextConsecutiveRegister(); + Register function = register_allocator()->NextConsecutiveRegister(); + Register language = register_allocator()->NextConsecutiveRegister(); + Register position = register_allocator()->NextConsecutiveRegister(); + + // Set up arguments for ResolvePossiblyDirectEval by copying callee, source + // strings and function closure, and loading language and + // position. + builder() + ->MoveRegister(callee, callee_for_eval) + .MoveRegister(arg, source) + .MoveRegister(Register::function_closure(), function) + .LoadLiteral(Smi::FromInt(language_mode())) + .StoreAccumulatorInRegister(language) + .LoadLiteral( + Smi::FromInt(execution_context()->scope()->start_position())) + .StoreAccumulatorInRegister(position); + + // Call ResolvePossiblyDirectEval and modify the callee. + builder() + ->CallRuntime(Runtime::kResolvePossiblyDirectEval, callee_for_eval, 5) + .StoreAccumulatorInRegister(callee); } - // TODO(rmcilroy): Deal with possible direct eval here? // TODO(rmcilroy): Use CallIC to allow call type feedback. - builder().Call(callee, receiver, args->length()); + builder()->Call(callee, receiver, args->length(), + feedback_index(expr->CallFeedbackICSlot())); + execution_result()->SetResultInAccumulator(); +} + + +void BytecodeGenerator::VisitCallNew(CallNew* expr) { + Register constructor = register_allocator()->NewRegister(); + VisitForAccumulatorValue(expr->expression()); + builder()->StoreAccumulatorInRegister(constructor); + + ZoneList<Expression*>* args = expr->arguments(); + Register first_arg = VisitArguments(args); + builder()->New(constructor, first_arg, args->length()); + execution_result()->SetResultInAccumulator(); } -void BytecodeGenerator::VisitCallNew(CallNew* expr) { UNIMPLEMENTED(); } +void BytecodeGenerator::VisitCallRuntime(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); + Register receiver; + if (expr->is_jsruntime()) { + // Allocate a register for the receiver and load it with undefined. + register_allocator()->PrepareForConsecutiveAllocations(args->length() + 1); + receiver = register_allocator()->NextConsecutiveRegister(); + builder()->LoadUndefined().StoreAccumulatorInRegister(receiver); + } + // Evaluate all arguments to the runtime call. + Register first_arg = VisitArguments(args); + + if (expr->is_jsruntime()) { + DCHECK(args->length() == 0 || first_arg.index() == receiver.index() + 1); + builder()->CallJSRuntime(expr->context_index(), receiver, args->length()); + } else { + Runtime::FunctionId function_id = expr->function()->function_id; + builder()->CallRuntime(function_id, first_arg, args->length()); + } + execution_result()->SetResultInAccumulator(); +} + + +void BytecodeGenerator::VisitVoid(UnaryOperation* expr) { + VisitForEffect(expr->expression()); + builder()->LoadUndefined(); + execution_result()->SetResultInAccumulator(); +} + + +void BytecodeGenerator::VisitTypeOf(UnaryOperation* expr) { + if (expr->expression()->IsVariableProxy()) { + // Typeof does not throw a reference error on global variables, hence we + // perform a non-contextual load in case the operand is a variable proxy. + VariableProxy* proxy = expr->expression()->AsVariableProxy(); + VisitVariableLoadForAccumulatorValue( + proxy->var(), proxy->VariableFeedbackSlot(), INSIDE_TYPEOF); + } else { + VisitForAccumulatorValue(expr->expression()); + } + builder()->TypeOf(); + execution_result()->SetResultInAccumulator(); +} -void BytecodeGenerator::VisitCallRuntime(CallRuntime* expr) { UNIMPLEMENTED(); } +void BytecodeGenerator::VisitNot(UnaryOperation* expr) { + VisitForAccumulatorValue(expr->expression()); + builder()->LogicalNot(); + execution_result()->SetResultInAccumulator(); +} void BytecodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { - UNIMPLEMENTED(); + switch (expr->op()) { + case Token::Value::NOT: + VisitNot(expr); + break; + case Token::Value::TYPEOF: + VisitTypeOf(expr); + break; + case Token::Value::VOID: + VisitVoid(expr); + break; + case Token::Value::DELETE: + VisitDelete(expr); + break; + case Token::Value::BIT_NOT: + case Token::Value::ADD: + case Token::Value::SUB: + // These operators are converted to an equivalent binary operators in + // the parser. These operators are not expected to be visited here. + UNREACHABLE(); + default: + UNREACHABLE(); + } +} + + +void BytecodeGenerator::VisitDelete(UnaryOperation* expr) { + if (expr->expression()->IsProperty()) { + // Delete of an object property is allowed both in sloppy + // and strict modes. + Property* property = expr->expression()->AsProperty(); + Register object = VisitForRegisterValue(property->obj()); + VisitForAccumulatorValue(property->key()); + builder()->Delete(object, language_mode()); + } else if (expr->expression()->IsVariableProxy()) { + // Delete of an unqualified identifier is allowed in sloppy mode but is + // not allowed in strict mode. Deleting 'this' is allowed in both modes. + VariableProxy* proxy = expr->expression()->AsVariableProxy(); + Variable* variable = proxy->var(); + DCHECK(is_sloppy(language_mode()) || variable->HasThisName(isolate())); + switch (variable->location()) { + case VariableLocation::GLOBAL: + case VariableLocation::UNALLOCATED: { + // Global var, let, const or variables not explicitly declared. + Register native_context = register_allocator()->NewRegister(); + Register global_object = register_allocator()->NewRegister(); + builder() + ->LoadContextSlot(execution_context()->reg(), + Context::NATIVE_CONTEXT_INDEX) + .StoreAccumulatorInRegister(native_context) + .LoadContextSlot(native_context, Context::EXTENSION_INDEX) + .StoreAccumulatorInRegister(global_object) + .LoadLiteral(variable->name()) + .Delete(global_object, language_mode()); + break; + } + case VariableLocation::PARAMETER: + case VariableLocation::LOCAL: + case VariableLocation::CONTEXT: { + // Deleting local var/let/const, context variables, and arguments + // does not have any effect. + if (variable->HasThisName(isolate())) { + builder()->LoadTrue(); + } else { + builder()->LoadFalse(); + } + break; + } + case VariableLocation::LOOKUP: { + builder()->LoadLiteral(variable->name()).DeleteLookupSlot(); + break; + } + default: + UNREACHABLE(); + } + } else { + // Delete of an unresolvable reference returns true. + VisitForEffect(expr->expression()); + builder()->LoadTrue(); + } + execution_result()->SetResultInAccumulator(); } void BytecodeGenerator::VisitCountOperation(CountOperation* expr) { - UNIMPLEMENTED(); + DCHECK(expr->expression()->IsValidReferenceExpressionOrThis()); + + // Left-hand side can only be a property, a global or a variable slot. + Property* property = expr->expression()->AsProperty(); + LhsKind assign_type = Property::GetAssignType(property); + + // TODO(rmcilroy): Set is_postfix to false if visiting for effect. + bool is_postfix = expr->is_postfix(); + + // Evaluate LHS expression and get old value. + Register obj, key, old_value; + Handle<String> name; + switch (assign_type) { + case VARIABLE: { + VariableProxy* proxy = expr->expression()->AsVariableProxy(); + VisitVariableLoadForAccumulatorValue(proxy->var(), + proxy->VariableFeedbackSlot()); + break; + } + case NAMED_PROPERTY: { + FeedbackVectorSlot slot = property->PropertyFeedbackSlot(); + obj = VisitForRegisterValue(property->obj()); + name = property->key()->AsLiteral()->AsPropertyName(); + builder()->LoadNamedProperty(obj, name, feedback_index(slot), + language_mode()); + break; + } + case KEYED_PROPERTY: { + FeedbackVectorSlot slot = property->PropertyFeedbackSlot(); + obj = VisitForRegisterValue(property->obj()); + // Use visit for accumulator here since we need the key in the accumulator + // for the LoadKeyedProperty. + key = register_allocator()->NewRegister(); + VisitForAccumulatorValue(property->key()); + builder()->StoreAccumulatorInRegister(key).LoadKeyedProperty( + obj, feedback_index(slot), language_mode()); + break; + } + case NAMED_SUPER_PROPERTY: + case KEYED_SUPER_PROPERTY: + UNIMPLEMENTED(); + } + + // Convert old value into a number. + if (!is_strong(language_mode())) { + builder()->CastAccumulatorToNumber(); + } + + // Save result for postfix expressions. + if (is_postfix) { + old_value = register_allocator()->outer()->NewRegister(); + builder()->StoreAccumulatorInRegister(old_value); + } + + // Perform +1/-1 operation. + builder()->CountOperation(expr->binary_op(), language_mode_strength()); + + // Store the value. + FeedbackVectorSlot feedback_slot = expr->CountSlot(); + switch (assign_type) { + case VARIABLE: { + Variable* variable = expr->expression()->AsVariableProxy()->var(); + VisitVariableAssignment(variable, feedback_slot); + break; + } + case NAMED_PROPERTY: { + builder()->StoreNamedProperty(obj, name, feedback_index(feedback_slot), + language_mode()); + break; + } + case KEYED_PROPERTY: { + builder()->StoreKeyedProperty(obj, key, feedback_index(feedback_slot), + language_mode()); + break; + } + case NAMED_SUPER_PROPERTY: + case KEYED_SUPER_PROPERTY: + UNIMPLEMENTED(); + } + + // Restore old value for postfix expressions. + if (is_postfix) { + execution_result()->SetResultInRegister(old_value); + } else { + execution_result()->SetResultInAccumulator(); + } } void BytecodeGenerator::VisitBinaryOperation(BinaryOperation* binop) { switch (binop->op()) { case Token::COMMA: + VisitCommaExpression(binop); + break; case Token::OR: + VisitLogicalOrExpression(binop); + break; case Token::AND: - UNIMPLEMENTED(); + VisitLogicalAndExpression(binop); break; default: VisitArithmeticExpression(binop); @@ -488,17 +1879,18 @@ void BytecodeGenerator::VisitBinaryOperation(BinaryOperation* binop) { void BytecodeGenerator::VisitCompareOperation(CompareOperation* expr) { - Token::Value op = expr->op(); - Expression* left = expr->left(); - Expression* right = expr->right(); + Register lhs = VisitForRegisterValue(expr->left()); + VisitForAccumulatorValue(expr->right()); + builder()->CompareOperation(expr->op(), lhs, language_mode_strength()); + execution_result()->SetResultInAccumulator(); +} - TemporaryRegisterScope temporary_register_scope(&builder_); - Register temporary = temporary_register_scope.NewRegister(); - Visit(left); - builder().StoreAccumulatorInRegister(temporary); - Visit(right); - builder().CompareOperation(op, temporary, language_mode()); +void BytecodeGenerator::VisitArithmeticExpression(BinaryOperation* expr) { + Register lhs = VisitForRegisterValue(expr->left()); + VisitForAccumulatorValue(expr->right()); + builder()->BinaryOperation(expr->op(), lhs, language_mode_strength()); + execution_result()->SetResultInAccumulator(); } @@ -511,7 +1903,7 @@ void BytecodeGenerator::VisitEmptyParentheses(EmptyParentheses* expr) { void BytecodeGenerator::VisitThisFunction(ThisFunction* expr) { - UNIMPLEMENTED(); + execution_result()->SetResultInRegister(Register::function_closure()); } @@ -526,18 +1918,248 @@ void BytecodeGenerator::VisitSuperPropertyReference( } -void BytecodeGenerator::VisitArithmeticExpression(BinaryOperation* binop) { - Token::Value op = binop->op(); +void BytecodeGenerator::VisitCommaExpression(BinaryOperation* binop) { + VisitForEffect(binop->left()); + Visit(binop->right()); +} + + +void BytecodeGenerator::VisitLogicalOrExpression(BinaryOperation* binop) { + Expression* left = binop->left(); + Expression* right = binop->right(); + + // Short-circuit evaluation- If it is known that left is always true, + // no need to visit right + if (left->ToBooleanIsTrue()) { + VisitForAccumulatorValue(left); + } else { + BytecodeLabel end_label; + VisitForAccumulatorValue(left); + builder()->JumpIfTrue(&end_label); + VisitForAccumulatorValue(right); + builder()->Bind(&end_label); + } + execution_result()->SetResultInAccumulator(); +} + + +void BytecodeGenerator::VisitLogicalAndExpression(BinaryOperation* binop) { Expression* left = binop->left(); Expression* right = binop->right(); - TemporaryRegisterScope temporary_register_scope(&builder_); - Register temporary = temporary_register_scope.NewRegister(); + // Short-circuit evaluation- If it is known that left is always false, + // no need to visit right + if (left->ToBooleanIsFalse()) { + VisitForAccumulatorValue(left); + } else { + BytecodeLabel end_label; + VisitForAccumulatorValue(left); + builder()->JumpIfFalse(&end_label); + VisitForAccumulatorValue(right); + builder()->Bind(&end_label); + } + execution_result()->SetResultInAccumulator(); +} + + +void BytecodeGenerator::VisitRewritableAssignmentExpression( + RewritableAssignmentExpression* expr) { + Visit(expr->expression()); +} + + +void BytecodeGenerator::VisitNewLocalFunctionContext() { + AccumulatorResultScope accumulator_execution_result(this); + Scope* scope = this->scope(); + + // Allocate a new local context. + if (scope->is_script_scope()) { + RegisterAllocationScope register_scope(this); + Register closure = register_allocator()->NewRegister(); + Register scope_info = register_allocator()->NewRegister(); + DCHECK(Register::AreContiguous(closure, scope_info)); + builder() + ->LoadAccumulatorWithRegister(Register::function_closure()) + .StoreAccumulatorInRegister(closure) + .LoadLiteral(scope->GetScopeInfo(isolate())) + .StoreAccumulatorInRegister(scope_info) + .CallRuntime(Runtime::kNewScriptContext, closure, 2); + } else { + builder()->CallRuntime(Runtime::kNewFunctionContext, + Register::function_closure(), 1); + } + execution_result()->SetResultInAccumulator(); +} + + +void BytecodeGenerator::VisitBuildLocalActivationContext() { + Scope* scope = this->scope(); + + if (scope->has_this_declaration() && scope->receiver()->IsContextSlot()) { + Variable* variable = scope->receiver(); + Register receiver(builder()->Parameter(0)); + // Context variable (at bottom of the context chain). + DCHECK_EQ(0, scope->ContextChainLength(variable->scope())); + builder()->LoadAccumulatorWithRegister(receiver).StoreContextSlot( + execution_context()->reg(), variable->index()); + } + + // Copy parameters into context if necessary. + int num_parameters = scope->num_parameters(); + for (int i = 0; i < num_parameters; i++) { + Variable* variable = scope->parameter(i); + if (!variable->IsContextSlot()) continue; + + // The parameter indices are shifted by 1 (receiver is variable + // index -1 but is parameter index 0 in BytecodeArrayBuilder). + Register parameter(builder()->Parameter(i + 1)); + // Context variable (at bottom of the context chain). + DCHECK_EQ(0, scope->ContextChainLength(variable->scope())); + builder()->LoadAccumulatorWithRegister(parameter) + .StoreContextSlot(execution_context()->reg(), variable->index()); + } +} + + +void BytecodeGenerator::VisitNewLocalBlockContext(Scope* scope) { + AccumulatorResultScope accumulator_execution_result(this); + DCHECK(scope->is_block_scope()); + + // Allocate a new local block context. + register_allocator()->PrepareForConsecutiveAllocations(2); + Register scope_info = register_allocator()->NextConsecutiveRegister(); + Register closure = register_allocator()->NextConsecutiveRegister(); + + builder() + ->LoadLiteral(scope->GetScopeInfo(isolate())) + .StoreAccumulatorInRegister(scope_info); + VisitFunctionClosureForContext(); + builder() + ->StoreAccumulatorInRegister(closure) + .CallRuntime(Runtime::kPushBlockContext, scope_info, 2); + execution_result()->SetResultInAccumulator(); +} + + +void BytecodeGenerator::VisitObjectLiteralAccessor( + Register home_object, ObjectLiteralProperty* property, Register value_out) { + // TODO(rmcilroy): Replace value_out with VisitForRegister(); + if (property == nullptr) { + builder()->LoadNull().StoreAccumulatorInRegister(value_out); + } else { + VisitForAccumulatorValue(property->value()); + builder()->StoreAccumulatorInRegister(value_out); + VisitSetHomeObject(value_out, home_object, property); + } +} + + +void BytecodeGenerator::VisitSetHomeObject(Register value, Register home_object, + ObjectLiteralProperty* property, + int slot_number) { + Expression* expr = property->value(); + if (!FunctionLiteral::NeedsHomeObject(expr)) return; + + UNIMPLEMENTED(); +} + + +void BytecodeGenerator::VisitArgumentsObject(Variable* variable) { + if (variable == nullptr) return; + + DCHECK(variable->IsContextSlot() || variable->IsStackAllocated()); + + // Allocate and initialize a new arguments object and assign to the + // {arguments} variable. + CreateArgumentsType type = + is_strict(language_mode()) || !info()->has_simple_parameters() + ? CreateArgumentsType::kUnmappedArguments + : CreateArgumentsType::kMappedArguments; + builder()->CreateArguments(type); + VisitVariableAssignment(variable, FeedbackVectorSlot::Invalid()); +} + - Visit(left); - builder().StoreAccumulatorInRegister(temporary); - Visit(right); - builder().BinaryOperation(op, temporary); +void BytecodeGenerator::VisitThisFunctionVariable(Variable* variable) { + if (variable == nullptr) return; + + // TODO(rmcilroy): Remove once we have tests which exercise this code path. + UNIMPLEMENTED(); + + // Store the closure we were called with in the given variable. + builder()->LoadAccumulatorWithRegister(Register::function_closure()); + VisitVariableAssignment(variable, FeedbackVectorSlot::Invalid()); +} + + +void BytecodeGenerator::VisitNewTargetVariable(Variable* variable) { + if (variable == nullptr) return; + + // Store the new target we were called with in the given variable. + builder()->LoadAccumulatorWithRegister(Register::new_target()); + VisitVariableAssignment(variable, FeedbackVectorSlot::Invalid()); +} + + +void BytecodeGenerator::VisitFunctionClosureForContext() { + AccumulatorResultScope accumulator_execution_result(this); + Scope* closure_scope = execution_context()->scope()->ClosureScope(); + if (closure_scope->is_script_scope() || + closure_scope->is_module_scope()) { + // Contexts nested in the native context have a canonical empty function as + // their closure, not the anonymous closure containing the global code. + Register native_context = register_allocator()->NewRegister(); + builder() + ->LoadContextSlot(execution_context()->reg(), + Context::NATIVE_CONTEXT_INDEX) + .StoreAccumulatorInRegister(native_context) + .LoadContextSlot(native_context, Context::CLOSURE_INDEX); + } else { + DCHECK(closure_scope->is_function_scope()); + builder()->LoadAccumulatorWithRegister(Register::function_closure()); + } + execution_result()->SetResultInAccumulator(); +} + + +// Visits the expression |expr| and places the result in the accumulator. +void BytecodeGenerator::VisitForAccumulatorValue(Expression* expr) { + AccumulatorResultScope accumulator_scope(this); + Visit(expr); +} + + +// Visits the expression |expr| and discards the result. +void BytecodeGenerator::VisitForEffect(Expression* expr) { + EffectResultScope effect_scope(this); + Visit(expr); +} + + +// Visits the expression |expr| and returns the register containing +// the expression result. +Register BytecodeGenerator::VisitForRegisterValue(Expression* expr) { + RegisterResultScope register_scope(this); + Visit(expr); + return register_scope.ResultRegister(); +} + + +Register BytecodeGenerator::NextContextRegister() const { + if (execution_context() == nullptr) { + // Return the incoming function context for the outermost execution context. + return Register::function_context(); + } + Register previous = execution_context()->reg(); + if (previous == Register::function_context()) { + // If the previous context was the incoming function context, then the next + // context register is the first local context register. + return builder_.first_context_register(); + } else { + // Otherwise use the next local context register. + DCHECK_LT(previous.index(), builder_.last_context_register().index()); + return Register(previous.index() + 1); + } } @@ -546,7 +2168,12 @@ LanguageMode BytecodeGenerator::language_mode() const { } -int BytecodeGenerator::feedback_index(FeedbackVectorICSlot slot) const { +Strength BytecodeGenerator::language_mode_strength() const { + return strength(language_mode()); +} + + +int BytecodeGenerator::feedback_index(FeedbackVectorSlot slot) const { return info()->feedback_vector()->GetIndex(slot); } diff --git a/chromium/v8/src/interpreter/bytecode-generator.h b/chromium/v8/src/interpreter/bytecode-generator.h index 99536c33fb5..8bda7be3016 100644 --- a/chromium/v8/src/interpreter/bytecode-generator.h +++ b/chromium/v8/src/interpreter/bytecode-generator.h @@ -5,7 +5,7 @@ #ifndef V8_INTERPRETER_BYTECODE_GENERATOR_H_ #define V8_INTERPRETER_BYTECODE_GENERATOR_H_ -#include "src/ast.h" +#include "src/ast/ast.h" #include "src/interpreter/bytecode-array-builder.h" #include "src/interpreter/bytecodes.h" @@ -13,10 +13,9 @@ namespace v8 { namespace internal { namespace interpreter { -class BytecodeGenerator : public AstVisitor { +class BytecodeGenerator final : public AstVisitor { public: BytecodeGenerator(Isolate* isolate, Zone* zone); - virtual ~BytecodeGenerator(); Handle<BytecodeArray> MakeBytecode(CompilationInfo* info); @@ -24,25 +23,126 @@ class BytecodeGenerator : public AstVisitor { AST_NODE_LIST(DECLARE_VISIT) #undef DECLARE_VISIT + // Visiting function for declarations list and statements are overridden. + void VisitDeclarations(ZoneList<Declaration*>* declarations) override; + void VisitStatements(ZoneList<Statement*>* statments) override; + private: + class ContextScope; + class ControlScope; + class ControlScopeForBreakable; + class ControlScopeForIteration; + class ExpressionResultScope; + class EffectResultScope; + class AccumulatorResultScope; + class RegisterResultScope; + class RegisterAllocationScope; + + void MakeBytecodeBody(); + Register NextContextRegister() const; + DEFINE_AST_VISITOR_SUBCLASS_MEMBERS(); + // Dispatched from VisitBinaryOperation. void VisitArithmeticExpression(BinaryOperation* binop); + void VisitCommaExpression(BinaryOperation* binop); + void VisitLogicalOrExpression(BinaryOperation* binop); + void VisitLogicalAndExpression(BinaryOperation* binop); + + // Dispatched from VisitUnaryOperation. + void VisitVoid(UnaryOperation* expr); + void VisitTypeOf(UnaryOperation* expr); + void VisitNot(UnaryOperation* expr); + void VisitDelete(UnaryOperation* expr); + + // Used by flow control routines to evaluate loop condition. + void VisitCondition(Expression* expr); + + // Helper visitors which perform common operations. + Register VisitArguments(ZoneList<Expression*>* arguments); + void VisitPropertyLoad(Register obj, Property* expr); - void VisitVariableLoad(Variable* variable); + void VisitPropertyLoadForAccumulator(Register obj, Property* expr); + + void VisitVariableLoad(Variable* variable, FeedbackVectorSlot slot, + TypeofMode typeof_mode = NOT_INSIDE_TYPEOF); + void VisitVariableLoadForAccumulatorValue( + Variable* variable, FeedbackVectorSlot slot, + TypeofMode typeof_mode = NOT_INSIDE_TYPEOF); + MUST_USE_RESULT Register + VisitVariableLoadForRegisterValue(Variable* variable, FeedbackVectorSlot slot, + TypeofMode typeof_mode = NOT_INSIDE_TYPEOF); + void VisitVariableAssignment(Variable* variable, FeedbackVectorSlot slot); + + void VisitArgumentsObject(Variable* variable); + void VisitThisFunctionVariable(Variable* variable); + void VisitNewTargetVariable(Variable* variable); + void VisitNewLocalFunctionContext(); + void VisitBuildLocalActivationContext(); + void VisitNewLocalBlockContext(Scope* scope); + void VisitFunctionClosureForContext(); + void VisitSetHomeObject(Register value, Register home_object, + ObjectLiteralProperty* property, int slot_number = 0); + void VisitObjectLiteralAccessor(Register home_object, + ObjectLiteralProperty* property, + Register value_out); + void VisitForInAssignment(Expression* expr, FeedbackVectorSlot slot); + + // Visitors for obtaining expression result in the accumulator, in a + // register, or just getting the effect. + void VisitForAccumulatorValue(Expression* expression); + MUST_USE_RESULT Register VisitForRegisterValue(Expression* expression); + void VisitForEffect(Expression* node); + + // Methods for tracking and remapping register. + void RecordStoreToRegister(Register reg); + Register LoadFromAliasedRegister(Register reg); + + inline BytecodeArrayBuilder* builder() { return &builder_; } + + inline Isolate* isolate() const { return isolate_; } + inline Zone* zone() const { return zone_; } - inline BytecodeArrayBuilder& builder() { return builder_; } inline Scope* scope() const { return scope_; } inline void set_scope(Scope* scope) { scope_ = scope; } inline CompilationInfo* info() const { return info_; } inline void set_info(CompilationInfo* info) { info_ = info; } - LanguageMode language_mode() const; - int feedback_index(FeedbackVectorICSlot slot) const; + inline ControlScope* execution_control() const { return execution_control_; } + inline void set_execution_control(ControlScope* scope) { + execution_control_ = scope; + } + inline ContextScope* execution_context() const { return execution_context_; } + inline void set_execution_context(ContextScope* context) { + execution_context_ = context; + } + inline void set_execution_result(ExpressionResultScope* execution_result) { + execution_result_ = execution_result; + } + ExpressionResultScope* execution_result() const { return execution_result_; } + inline void set_register_allocator( + RegisterAllocationScope* register_allocator) { + register_allocator_ = register_allocator; + } + RegisterAllocationScope* register_allocator() const { + return register_allocator_; + } + + ZoneVector<Handle<Object>>* globals() { return &globals_; } + inline LanguageMode language_mode() const; + Strength language_mode_strength() const; + int feedback_index(FeedbackVectorSlot slot) const; + Isolate* isolate_; + Zone* zone_; BytecodeArrayBuilder builder_; CompilationInfo* info_; Scope* scope_; + ZoneVector<Handle<Object>> globals_; + ControlScope* execution_control_; + ContextScope* execution_context_; + ExpressionResultScope* execution_result_; + RegisterAllocationScope* register_allocator_; }; } // namespace interpreter diff --git a/chromium/v8/src/interpreter/bytecode-register-allocator.cc b/chromium/v8/src/interpreter/bytecode-register-allocator.cc new file mode 100644 index 00000000000..4efb612db52 --- /dev/null +++ b/chromium/v8/src/interpreter/bytecode-register-allocator.cc @@ -0,0 +1,72 @@ +// 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-register-allocator.h" + +#include "src/interpreter/bytecode-array-builder.h" + +namespace v8 { +namespace internal { +namespace interpreter { + +BytecodeRegisterAllocator::BytecodeRegisterAllocator( + BytecodeArrayBuilder* builder) + : builder_(builder), + allocated_(builder->zone()), + next_consecutive_register_(-1), + next_consecutive_count_(-1) {} + + +BytecodeRegisterAllocator::~BytecodeRegisterAllocator() { + for (auto i = allocated_.rbegin(); i != allocated_.rend(); i++) { + builder_->ReturnTemporaryRegister(*i); + } + allocated_.clear(); +} + + +Register BytecodeRegisterAllocator::NewRegister() { + int allocated = -1; + if (next_consecutive_count_ <= 0) { + allocated = builder_->BorrowTemporaryRegister(); + } else { + allocated = builder_->BorrowTemporaryRegisterNotInRange( + next_consecutive_register_, + next_consecutive_register_ + next_consecutive_count_ - 1); + } + allocated_.push_back(allocated); + return Register(allocated); +} + + +bool BytecodeRegisterAllocator::RegisterIsAllocatedInThisScope( + Register reg) const { + for (auto i = allocated_.begin(); i != allocated_.end(); i++) { + if (*i == reg.index()) return true; + } + return false; +} + + +void BytecodeRegisterAllocator::PrepareForConsecutiveAllocations(size_t count) { + if (static_cast<int>(count) > next_consecutive_count_) { + next_consecutive_register_ = + builder_->PrepareForConsecutiveTemporaryRegisters(count); + next_consecutive_count_ = static_cast<int>(count); + } +} + + +Register BytecodeRegisterAllocator::NextConsecutiveRegister() { + DCHECK_GE(next_consecutive_register_, 0); + DCHECK_GT(next_consecutive_count_, 0); + builder_->BorrowConsecutiveTemporaryRegister(next_consecutive_register_); + allocated_.push_back(next_consecutive_register_); + next_consecutive_count_--; + return Register(next_consecutive_register_++); +} + +} // namespace interpreter +} // namespace internal +} // namespace v8 diff --git a/chromium/v8/src/interpreter/bytecode-register-allocator.h b/chromium/v8/src/interpreter/bytecode-register-allocator.h new file mode 100644 index 00000000000..74ab3a42727 --- /dev/null +++ b/chromium/v8/src/interpreter/bytecode-register-allocator.h @@ -0,0 +1,49 @@ +// 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. + +#ifndef V8_INTERPRETER_BYTECODE_REGISTER_ALLOCATOR_H_ +#define V8_INTERPRETER_BYTECODE_REGISTER_ALLOCATOR_H_ + +#include "src/zone-containers.h" + +namespace v8 { +namespace internal { +namespace interpreter { + +class BytecodeArrayBuilder; +class Register; + +// A class than allows the instantiator to allocate temporary registers that are +// cleaned up when scope is closed. +class BytecodeRegisterAllocator { + public: + explicit BytecodeRegisterAllocator(BytecodeArrayBuilder* builder); + ~BytecodeRegisterAllocator(); + Register NewRegister(); + + void PrepareForConsecutiveAllocations(size_t count); + Register NextConsecutiveRegister(); + + bool RegisterIsAllocatedInThisScope(Register reg) const; + + bool HasConsecutiveAllocations() const { return next_consecutive_count_ > 0; } + + private: + void* operator new(size_t size); + void operator delete(void* p); + + BytecodeArrayBuilder* builder_; + ZoneVector<int> allocated_; + int next_consecutive_register_; + int next_consecutive_count_; + + DISALLOW_COPY_AND_ASSIGN(BytecodeRegisterAllocator); +}; + +} // namespace interpreter +} // namespace internal +} // namespace v8 + + +#endif // V8_INTERPRETER_BYTECODE_REGISTER_ALLOCATOR_H_ diff --git a/chromium/v8/src/interpreter/bytecode-traits.h b/chromium/v8/src/interpreter/bytecode-traits.h new file mode 100644 index 00000000000..fd778d7c927 --- /dev/null +++ b/chromium/v8/src/interpreter/bytecode-traits.h @@ -0,0 +1,180 @@ +// 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. + +#ifndef V8_INTERPRETER_BYTECODE_TRAITS_H_ +#define V8_INTERPRETER_BYTECODE_TRAITS_H_ + +#include "src/interpreter/bytecodes.h" + +namespace v8 { +namespace internal { +namespace interpreter { + +// TODO(rmcilroy): consider simplifying this to avoid the template magic. + +// Template helpers to deduce the number of operands each bytecode has. +#define OPERAND_TERM OperandType::kNone, OperandType::kNone, OperandType::kNone + +template <OperandType> +struct OperandTraits {}; + +#define DECLARE_OPERAND_SIZE(Name, Size) \ + template <> \ + struct OperandTraits<OperandType::k##Name> { \ + static const OperandSize kSizeType = Size; \ + static const int kSize = static_cast<int>(Size); \ + }; +OPERAND_TYPE_LIST(DECLARE_OPERAND_SIZE) +#undef DECLARE_OPERAND_SIZE + + +template <OperandType... Args> +struct BytecodeTraits {}; + +template <OperandType operand_0, OperandType operand_1, OperandType operand_2, + OperandType operand_3> +struct BytecodeTraits<operand_0, operand_1, operand_2, operand_3, + OPERAND_TERM> { + static OperandType GetOperandType(int i) { + DCHECK(0 <= i && i < kOperandCount); + const OperandType kOperands[] = {operand_0, operand_1, operand_2, + operand_3}; + return kOperands[i]; + } + + static inline OperandSize GetOperandSize(int i) { + DCHECK(0 <= i && i < kOperandCount); + const OperandSize kOperandSizes[] = + {OperandTraits<operand_0>::kSizeType, + OperandTraits<operand_1>::kSizeType, + OperandTraits<operand_2>::kSizeType, + OperandTraits<operand_3>::kSizeType}; + return kOperandSizes[i]; + } + + static inline int GetOperandOffset(int i) { + DCHECK(0 <= i && i < kOperandCount); + const int kOffset0 = 1; + const int kOffset1 = kOffset0 + OperandTraits<operand_0>::kSize; + const int kOffset2 = kOffset1 + OperandTraits<operand_1>::kSize; + const int kOffset3 = kOffset2 + OperandTraits<operand_2>::kSize; + const int kOperandOffsets[] = {kOffset0, kOffset1, kOffset2, kOffset3}; + return kOperandOffsets[i]; + } + + static const int kOperandCount = 4; + static const int kSize = + 1 + OperandTraits<operand_0>::kSize + OperandTraits<operand_1>::kSize + + OperandTraits<operand_2>::kSize + OperandTraits<operand_3>::kSize; +}; + + +template <OperandType operand_0, OperandType operand_1, OperandType operand_2> +struct BytecodeTraits<operand_0, operand_1, operand_2, OPERAND_TERM> { + static inline OperandType GetOperandType(int i) { + DCHECK(0 <= i && i <= 2); + const OperandType kOperands[] = {operand_0, operand_1, operand_2}; + return kOperands[i]; + } + + static inline OperandSize GetOperandSize(int i) { + DCHECK(0 <= i && i < kOperandCount); + const OperandSize kOperandSizes[] = + {OperandTraits<operand_0>::kSizeType, + OperandTraits<operand_1>::kSizeType, + OperandTraits<operand_2>::kSizeType}; + return kOperandSizes[i]; + } + + static inline int GetOperandOffset(int i) { + DCHECK(0 <= i && i < kOperandCount); + const int kOffset0 = 1; + const int kOffset1 = kOffset0 + OperandTraits<operand_0>::kSize; + const int kOffset2 = kOffset1 + OperandTraits<operand_1>::kSize; + const int kOperandOffsets[] = {kOffset0, kOffset1, kOffset2}; + return kOperandOffsets[i]; + } + + static const int kOperandCount = 3; + static const int kSize = + 1 + OperandTraits<operand_0>::kSize + OperandTraits<operand_1>::kSize + + OperandTraits<operand_2>::kSize; +}; + +template <OperandType operand_0, OperandType operand_1> +struct BytecodeTraits<operand_0, operand_1, OPERAND_TERM> { + static inline OperandType GetOperandType(int i) { + DCHECK(0 <= i && i < kOperandCount); + const OperandType kOperands[] = {operand_0, operand_1}; + return kOperands[i]; + } + + static inline OperandSize GetOperandSize(int i) { + DCHECK(0 <= i && i < kOperandCount); + const OperandSize kOperandSizes[] = + {OperandTraits<operand_0>::kSizeType, + OperandTraits<operand_1>::kSizeType}; + return kOperandSizes[i]; + } + + static inline int GetOperandOffset(int i) { + DCHECK(0 <= i && i < kOperandCount); + const int kOffset0 = 1; + const int kOffset1 = kOffset0 + OperandTraits<operand_0>::kSize; + const int kOperandOffsets[] = {kOffset0, kOffset1}; + return kOperandOffsets[i]; + } + + static const int kOperandCount = 2; + static const int kSize = + 1 + OperandTraits<operand_0>::kSize + OperandTraits<operand_1>::kSize; +}; + +template <OperandType operand_0> +struct BytecodeTraits<operand_0, OPERAND_TERM> { + static inline OperandType GetOperandType(int i) { + DCHECK(i == 0); + return operand_0; + } + + static inline OperandSize GetOperandSize(int i) { + DCHECK(i == 0); + return OperandTraits<operand_0>::kSizeType; + } + + static inline int GetOperandOffset(int i) { + DCHECK(i == 0); + return 1; + } + + static const int kOperandCount = 1; + static const int kSize = 1 + OperandTraits<operand_0>::kSize; +}; + +template <> +struct BytecodeTraits<OperandType::kNone, OPERAND_TERM> { + static inline OperandType GetOperandType(int i) { + UNREACHABLE(); + return OperandType::kNone; + } + + static inline OperandSize GetOperandSize(int i) { + UNREACHABLE(); + return OperandSize::kNone; + } + + static inline int GetOperandOffset(int i) { + UNREACHABLE(); + return 1; + } + + static const int kOperandCount = 0; + static const int kSize = 1 + OperandTraits<OperandType::kNone>::kSize; +}; + +} // namespace interpreter +} // namespace internal +} // namespace v8 + +#endif // V8_INTERPRETER_BYTECODE_TRAITS_H_ diff --git a/chromium/v8/src/interpreter/bytecodes.cc b/chromium/v8/src/interpreter/bytecodes.cc index e5b9ab73a9c..2d4406cc1bc 100644 --- a/chromium/v8/src/interpreter/bytecodes.cc +++ b/chromium/v8/src/interpreter/bytecodes.cc @@ -5,25 +5,12 @@ #include "src/interpreter/bytecodes.h" #include "src/frames.h" +#include "src/interpreter/bytecode-traits.h" namespace v8 { namespace internal { namespace interpreter { -// Maximum number of operands a bytecode may have. -static const int kMaxOperands = 3; - -// kBytecodeTable relies on kNone being the same as zero to detect length. -STATIC_ASSERT(static_cast<int>(OperandType::kNone) == 0); - -static const OperandType kBytecodeTable[][kMaxOperands] = { -#define DECLARE_OPERAND(_, ...) \ - { __VA_ARGS__ } \ - , - BYTECODE_LIST(DECLARE_OPERAND) -#undef DECLARE_OPERAND -}; - // static const char* Bytecodes::ToString(Bytecode bytecode) { @@ -42,7 +29,7 @@ const char* Bytecodes::ToString(Bytecode bytecode) { // static const char* Bytecodes::OperandTypeToString(OperandType operand_type) { switch (operand_type) { -#define CASE(Name) \ +#define CASE(Name, _) \ case OperandType::k##Name: \ return #Name; OPERAND_TYPE_LIST(CASE) @@ -54,6 +41,21 @@ const char* Bytecodes::OperandTypeToString(OperandType operand_type) { // static +const char* Bytecodes::OperandSizeToString(OperandSize operand_size) { + switch (operand_size) { + case OperandSize::kNone: + return "None"; + case OperandSize::kByte: + return "Byte"; + case OperandSize::kShort: + return "Short"; + } + UNREACHABLE(); + return ""; +} + + +// static uint8_t Bytecodes::ToByte(Bytecode bytecode) { return static_cast<uint8_t>(bytecode); } @@ -68,38 +70,166 @@ Bytecode Bytecodes::FromByte(uint8_t value) { // static +int Bytecodes::Size(Bytecode bytecode) { + DCHECK(bytecode <= Bytecode::kLast); + switch (bytecode) { +#define CASE(Name, ...) \ + case Bytecode::k##Name: \ + return BytecodeTraits<__VA_ARGS__, OPERAND_TERM>::kSize; + BYTECODE_LIST(CASE) +#undef CASE + } + UNREACHABLE(); + return 0; +} + + +// static int Bytecodes::NumberOfOperands(Bytecode bytecode) { DCHECK(bytecode <= Bytecode::kLast); - int count; - uint8_t row = ToByte(bytecode); - for (count = 0; count < kMaxOperands; count++) { - if (kBytecodeTable[row][count] == OperandType::kNone) { - break; - } + switch (bytecode) { +#define CASE(Name, ...) \ + case Bytecode::k##Name: \ + return BytecodeTraits<__VA_ARGS__, OPERAND_TERM>::kOperandCount; + BYTECODE_LIST(CASE) +#undef CASE } - return count; + UNREACHABLE(); + return 0; } // static OperandType Bytecodes::GetOperandType(Bytecode bytecode, int i) { - DCHECK(bytecode <= Bytecode::kLast && i < NumberOfOperands(bytecode)); - return kBytecodeTable[ToByte(bytecode)][i]; + DCHECK(bytecode <= Bytecode::kLast); + switch (bytecode) { +#define CASE(Name, ...) \ + case Bytecode::k##Name: \ + return BytecodeTraits<__VA_ARGS__, OPERAND_TERM>::GetOperandType(i); + BYTECODE_LIST(CASE) +#undef CASE + } + UNREACHABLE(); + return OperandType::kNone; } // static -int Bytecodes::Size(Bytecode bytecode) { - return 1 + NumberOfOperands(bytecode); +OperandSize Bytecodes::GetOperandSize(Bytecode bytecode, int i) { + DCHECK(bytecode <= Bytecode::kLast); + switch (bytecode) { +#define CASE(Name, ...) \ + case Bytecode::k##Name: \ + return BytecodeTraits<__VA_ARGS__, OPERAND_TERM>::GetOperandSize(i); + BYTECODE_LIST(CASE) +#undef CASE + } + UNREACHABLE(); + return OperandSize::kNone; } // static -int Bytecodes::MaximumNumberOfOperands() { return kMaxOperands; } +int Bytecodes::GetOperandOffset(Bytecode bytecode, int i) { + DCHECK(bytecode <= Bytecode::kLast); + switch (bytecode) { +#define CASE(Name, ...) \ + case Bytecode::k##Name: \ + return BytecodeTraits<__VA_ARGS__, OPERAND_TERM>::GetOperandOffset(i); + BYTECODE_LIST(CASE) +#undef CASE + } + UNREACHABLE(); + return 0; +} // static -int Bytecodes::MaximumSize() { return 1 + kMaxOperands; } +OperandSize Bytecodes::SizeOfOperand(OperandType operand_type) { + switch (operand_type) { +#define CASE(Name, Size) \ + case OperandType::k##Name: \ + return Size; + OPERAND_TYPE_LIST(CASE) +#undef CASE + } + UNREACHABLE(); + return OperandSize::kNone; +} + + +// static +bool Bytecodes::IsConditionalJumpImmediate(Bytecode bytecode) { + return bytecode == Bytecode::kJumpIfTrue || + bytecode == Bytecode::kJumpIfFalse || + bytecode == Bytecode::kJumpIfToBooleanTrue || + bytecode == Bytecode::kJumpIfToBooleanFalse || + bytecode == Bytecode::kJumpIfNull || + bytecode == Bytecode::kJumpIfUndefined; +} + + +// static +bool Bytecodes::IsConditionalJumpConstant(Bytecode bytecode) { + return bytecode == Bytecode::kJumpIfTrueConstant || + bytecode == Bytecode::kJumpIfFalseConstant || + bytecode == Bytecode::kJumpIfToBooleanTrueConstant || + bytecode == Bytecode::kJumpIfToBooleanFalseConstant || + bytecode == Bytecode::kJumpIfNullConstant || + bytecode == Bytecode::kJumpIfUndefinedConstant; +} + + +// static +bool Bytecodes::IsConditionalJumpConstantWide(Bytecode bytecode) { + return bytecode == Bytecode::kJumpIfTrueConstantWide || + bytecode == Bytecode::kJumpIfFalseConstantWide || + bytecode == Bytecode::kJumpIfToBooleanTrueConstantWide || + bytecode == Bytecode::kJumpIfToBooleanFalseConstantWide || + bytecode == Bytecode::kJumpIfNullConstantWide || + bytecode == Bytecode::kJumpIfUndefinedConstantWide; +} + + +// static +bool Bytecodes::IsConditionalJump(Bytecode bytecode) { + return IsConditionalJumpImmediate(bytecode) || + IsConditionalJumpConstant(bytecode) || + IsConditionalJumpConstantWide(bytecode); +} + + +// static +bool Bytecodes::IsJumpImmediate(Bytecode bytecode) { + return bytecode == Bytecode::kJump || IsConditionalJumpImmediate(bytecode); +} + + +// static +bool Bytecodes::IsJumpConstant(Bytecode bytecode) { + return bytecode == Bytecode::kJumpConstant || + IsConditionalJumpConstant(bytecode); +} + + +// static +bool Bytecodes::IsJumpConstantWide(Bytecode bytecode) { + return bytecode == Bytecode::kJumpConstantWide || + IsConditionalJumpConstantWide(bytecode); +} + + +// static +bool Bytecodes::IsJump(Bytecode bytecode) { + return IsJumpImmediate(bytecode) || IsJumpConstant(bytecode) || + IsJumpConstantWide(bytecode); +} + + +// static +bool Bytecodes::IsJumpOrReturn(Bytecode bytecode) { + return bytecode == Bytecode::kReturn || IsJump(bytecode); +} // static @@ -114,30 +244,44 @@ std::ostream& Bytecodes::Decode(std::ostream& os, const uint8_t* bytecode_start, SNPrintF(buf, "%02x ", bytecode_start[i]); os << buf.start(); } - for (int i = bytecode_size; i < Bytecodes::MaximumSize(); i++) { + const int kBytecodeColumnSize = 6; + for (int i = bytecode_size; i < kBytecodeColumnSize; i++) { os << " "; } os << bytecode << " "; - const uint8_t* operands_start = bytecode_start + 1; - int operands_size = bytecode_size - 1; - for (int i = 0; i < operands_size; i++) { + int number_of_operands = NumberOfOperands(bytecode); + for (int i = 0; i < number_of_operands; i++) { OperandType op_type = GetOperandType(bytecode, i); - uint8_t operand = operands_start[i]; + const uint8_t* operand_start = + &bytecode_start[GetOperandOffset(bytecode, i)]; switch (op_type) { - case interpreter::OperandType::kCount: - os << "#" << static_cast<unsigned int>(operand); + case interpreter::OperandType::kCount8: + os << "#" << static_cast<unsigned int>(*operand_start); break; - case interpreter::OperandType::kIdx: - os << "[" << static_cast<unsigned int>(operand) << "]"; + case interpreter::OperandType::kCount16: + os << '#' << ReadUnalignedUInt16(operand_start); + break; + case interpreter::OperandType::kIdx8: + os << "[" << static_cast<unsigned int>(*operand_start) << "]"; + break; + case interpreter::OperandType::kIdx16: + os << "[" << ReadUnalignedUInt16(operand_start) << "]"; break; case interpreter::OperandType::kImm8: - os << "#" << static_cast<int>(static_cast<int8_t>(operand)); + os << "#" << static_cast<int>(static_cast<int8_t>(*operand_start)); break; - case interpreter::OperandType::kReg: { - Register reg = Register::FromOperand(operand); - if (reg.is_parameter()) { + case interpreter::OperandType::kReg8: + case interpreter::OperandType::kMaybeReg8: { + Register reg = Register::FromOperand(*operand_start); + if (reg.is_function_context()) { + os << "<context>"; + } else if (reg.is_function_closure()) { + os << "<closure>"; + } else if (reg.is_new_target()) { + os << "<new.target>"; + } else if (reg.is_parameter()) { int parameter_index = reg.ToParameterIndex(parameter_count); if (parameter_index == 0) { os << "<this>"; @@ -149,11 +293,34 @@ std::ostream& Bytecodes::Decode(std::ostream& os, const uint8_t* bytecode_start, } break; } + case interpreter::OperandType::kRegPair8: { + Register reg = Register::FromOperand(*operand_start); + if (reg.is_parameter()) { + int parameter_index = reg.ToParameterIndex(parameter_count); + DCHECK_NE(parameter_index, 0); + os << "a" << parameter_index - 1 << "-" << parameter_index; + } else { + os << "r" << reg.index() << "-" << reg.index() + 1; + } + break; + } + case interpreter::OperandType::kReg16: { + Register reg = + Register::FromWideOperand(ReadUnalignedUInt16(operand_start)); + if (reg.is_parameter()) { + int parameter_index = reg.ToParameterIndex(parameter_count); + DCHECK_NE(parameter_index, 0); + os << "a" << parameter_index - 1; + } else { + os << "r" << reg.index(); + } + break; + } case interpreter::OperandType::kNone: UNREACHABLE(); break; } - if (i != operands_size - 1) { + if (i != number_of_operands - 1) { os << ", "; } } @@ -171,8 +338,19 @@ std::ostream& operator<<(std::ostream& os, const OperandType& operand_type) { } +std::ostream& operator<<(std::ostream& os, const OperandSize& operand_size) { + return os << Bytecodes::OperandSizeToString(operand_size); +} + + static const int kLastParamRegisterIndex = -InterpreterFrameConstants::kLastParamFromRegisterPointer / kPointerSize; +static const int kFunctionClosureRegisterIndex = + -InterpreterFrameConstants::kFunctionFromRegisterPointer / kPointerSize; +static const int kFunctionContextRegisterIndex = + -InterpreterFrameConstants::kContextFromRegisterPointer / kPointerSize; +static const int kNewTargetRegisterIndex = + -InterpreterFrameConstants::kNewTargetFromRegisterPointer / kPointerSize; // Registers occupy range 0-127 in 8-bit value leaving 128 unused values. @@ -187,7 +365,7 @@ Register Register::FromParameterIndex(int index, int parameter_count) { DCHECK_LE(parameter_count, kMaxParameterIndex + 1); int register_index = kLastParamRegisterIndex - parameter_count + index + 1; DCHECK_LT(register_index, 0); - DCHECK_GE(register_index, Register::kMinRegisterIndex); + DCHECK_GE(register_index, kMinInt8); return Register(register_index); } @@ -198,16 +376,78 @@ int Register::ToParameterIndex(int parameter_count) const { } +Register Register::function_closure() { + return Register(kFunctionClosureRegisterIndex); +} + + +bool Register::is_function_closure() const { + return index() == kFunctionClosureRegisterIndex; +} + + +Register Register::function_context() { + return Register(kFunctionContextRegisterIndex); +} + + +bool Register::is_function_context() const { + return index() == kFunctionContextRegisterIndex; +} + + +Register Register::new_target() { return Register(kNewTargetRegisterIndex); } + + +bool Register::is_new_target() const { + return index() == kNewTargetRegisterIndex; +} + + int Register::MaxParameterIndex() { return kMaxParameterIndex; } -uint8_t Register::ToOperand() const { return static_cast<uint8_t>(-index_); } +uint8_t Register::ToOperand() const { + DCHECK_GE(index_, kMinInt8); + DCHECK_LE(index_, kMaxInt8); + return static_cast<uint8_t>(-index_); +} Register Register::FromOperand(uint8_t operand) { return Register(-static_cast<int8_t>(operand)); } + +uint16_t Register::ToWideOperand() const { + DCHECK_GE(index_, kMinInt16); + DCHECK_LE(index_, kMaxInt16); + return static_cast<uint16_t>(-index_); +} + + +Register Register::FromWideOperand(uint16_t operand) { + return Register(-static_cast<int16_t>(operand)); +} + + +bool Register::AreContiguous(Register reg1, Register reg2, Register reg3, + Register reg4, Register reg5) { + if (reg1.index() + 1 != reg2.index()) { + return false; + } + if (reg3.is_valid() && reg2.index() + 1 != reg3.index()) { + return false; + } + if (reg4.is_valid() && reg3.index() + 1 != reg4.index()) { + return false; + } + if (reg5.is_valid() && reg4.index() + 1 != reg5.index()) { + return false; + } + return true; +} + } // namespace interpreter } // namespace internal } // namespace v8 diff --git a/chromium/v8/src/interpreter/bytecodes.h b/chromium/v8/src/interpreter/bytecodes.h index 38628422772..a9beb6c918e 100644 --- a/chromium/v8/src/interpreter/bytecodes.h +++ b/chromium/v8/src/interpreter/bytecodes.h @@ -16,82 +16,234 @@ namespace internal { namespace interpreter { // The list of operand types used by bytecodes. -#define OPERAND_TYPE_LIST(V) \ - V(None) \ - V(Count) \ - V(Imm8) \ - V(Idx) \ - V(Reg) +#define OPERAND_TYPE_LIST(V) \ + \ + /* None operand. */ \ + V(None, OperandSize::kNone) \ + \ + /* Byte operands. */ \ + V(Count8, OperandSize::kByte) \ + V(Imm8, OperandSize::kByte) \ + V(Idx8, OperandSize::kByte) \ + V(MaybeReg8, OperandSize::kByte) \ + V(Reg8, OperandSize::kByte) \ + V(RegPair8, OperandSize::kByte) \ + \ + /* Short operands. */ \ + V(Count16, OperandSize::kShort) \ + V(Idx16, OperandSize::kShort) \ + V(Reg16, OperandSize::kShort) // The list of bytecodes which are interpreted by the interpreter. -#define BYTECODE_LIST(V) \ - \ - /* Loading the accumulator */ \ - V(LdaZero, OperandType::kNone) \ - V(LdaSmi8, OperandType::kImm8) \ - V(LdaConstant, OperandType::kIdx) \ - V(LdaUndefined, OperandType::kNone) \ - V(LdaNull, OperandType::kNone) \ - V(LdaTheHole, OperandType::kNone) \ - V(LdaTrue, OperandType::kNone) \ - V(LdaFalse, OperandType::kNone) \ - \ - /* Load globals */ \ - V(LdaGlobal, OperandType::kIdx) \ - \ - /* Register-accumulator transfers */ \ - V(Ldar, OperandType::kReg) \ - V(Star, OperandType::kReg) \ - \ - /* LoadIC operations */ \ - V(LoadIC, OperandType::kReg, OperandType::kIdx) \ - V(KeyedLoadIC, OperandType::kReg, OperandType::kIdx) \ - \ - /* StoreIC operations */ \ - V(StoreIC, OperandType::kReg, OperandType::kReg, OperandType::kIdx) \ - V(KeyedStoreIC, OperandType::kReg, OperandType::kReg, OperandType::kIdx) \ - \ - /* Binary Operators */ \ - V(Add, OperandType::kReg) \ - V(Sub, OperandType::kReg) \ - V(Mul, OperandType::kReg) \ - V(Div, OperandType::kReg) \ - V(Mod, OperandType::kReg) \ - \ - /* Call operations. */ \ - V(Call, OperandType::kReg, OperandType::kReg, OperandType::kCount) \ - \ - /* Test Operators */ \ - V(TestEqual, OperandType::kReg) \ - V(TestNotEqual, OperandType::kReg) \ - V(TestEqualStrict, OperandType::kReg) \ - V(TestNotEqualStrict, OperandType::kReg) \ - V(TestLessThan, OperandType::kReg) \ - V(TestGreaterThan, OperandType::kReg) \ - V(TestLessThanOrEqual, OperandType::kReg) \ - V(TestGreaterThanOrEqual, OperandType::kReg) \ - V(TestInstanceOf, OperandType::kReg) \ - V(TestIn, OperandType::kReg) \ - \ - /* Cast operators */ \ - V(ToBoolean, OperandType::kNone) \ - \ - /* Control Flow */ \ - V(Jump, OperandType::kImm8) \ - V(JumpConstant, OperandType::kIdx) \ - V(JumpIfTrue, OperandType::kImm8) \ - V(JumpIfTrueConstant, OperandType::kIdx) \ - V(JumpIfFalse, OperandType::kImm8) \ - V(JumpIfFalseConstant, OperandType::kIdx) \ +#define BYTECODE_LIST(V) \ + \ + /* Loading the accumulator */ \ + V(LdaZero, OperandType::kNone) \ + V(LdaSmi8, OperandType::kImm8) \ + V(LdaUndefined, OperandType::kNone) \ + V(LdaNull, OperandType::kNone) \ + V(LdaTheHole, OperandType::kNone) \ + V(LdaTrue, OperandType::kNone) \ + V(LdaFalse, OperandType::kNone) \ + V(LdaConstant, OperandType::kIdx8) \ + V(LdaConstantWide, OperandType::kIdx16) \ + \ + /* Globals */ \ + V(LdaGlobalSloppy, OperandType::kIdx8, OperandType::kIdx8) \ + V(LdaGlobalStrict, OperandType::kIdx8, OperandType::kIdx8) \ + V(LdaGlobalInsideTypeofSloppy, OperandType::kIdx8, OperandType::kIdx8) \ + V(LdaGlobalInsideTypeofStrict, OperandType::kIdx8, OperandType::kIdx8) \ + V(LdaGlobalSloppyWide, OperandType::kIdx16, OperandType::kIdx16) \ + V(LdaGlobalStrictWide, OperandType::kIdx16, OperandType::kIdx16) \ + V(LdaGlobalInsideTypeofSloppyWide, OperandType::kIdx16, OperandType::kIdx16) \ + V(LdaGlobalInsideTypeofStrictWide, OperandType::kIdx16, OperandType::kIdx16) \ + V(StaGlobalSloppy, OperandType::kIdx8, OperandType::kIdx8) \ + V(StaGlobalStrict, OperandType::kIdx8, OperandType::kIdx8) \ + V(StaGlobalSloppyWide, OperandType::kIdx16, OperandType::kIdx16) \ + V(StaGlobalStrictWide, OperandType::kIdx16, OperandType::kIdx16) \ + \ + /* Context operations */ \ + V(PushContext, OperandType::kReg8) \ + V(PopContext, OperandType::kReg8) \ + V(LdaContextSlot, OperandType::kReg8, OperandType::kIdx8) \ + V(StaContextSlot, OperandType::kReg8, OperandType::kIdx8) \ + V(LdaContextSlotWide, OperandType::kReg8, OperandType::kIdx16) \ + V(StaContextSlotWide, OperandType::kReg8, OperandType::kIdx16) \ + \ + /* Load-Store lookup slots */ \ + V(LdaLookupSlot, OperandType::kIdx8) \ + V(LdaLookupSlotInsideTypeof, OperandType::kIdx8) \ + V(LdaLookupSlotWide, OperandType::kIdx16) \ + V(LdaLookupSlotInsideTypeofWide, OperandType::kIdx16) \ + V(StaLookupSlotSloppy, OperandType::kIdx8) \ + V(StaLookupSlotStrict, OperandType::kIdx8) \ + V(StaLookupSlotSloppyWide, OperandType::kIdx16) \ + V(StaLookupSlotStrictWide, OperandType::kIdx16) \ + \ + /* Register-accumulator transfers */ \ + V(Ldar, OperandType::kReg8) \ + V(Star, OperandType::kReg8) \ + \ + /* Register-register transfers */ \ + V(Mov, OperandType::kReg8, OperandType::kReg8) \ + V(Exchange, OperandType::kReg8, OperandType::kReg16) \ + V(ExchangeWide, OperandType::kReg16, OperandType::kReg16) \ + \ + /* LoadIC operations */ \ + V(LoadICSloppy, OperandType::kReg8, OperandType::kIdx8, OperandType::kIdx8) \ + V(LoadICStrict, OperandType::kReg8, OperandType::kIdx8, OperandType::kIdx8) \ + V(KeyedLoadICSloppy, OperandType::kReg8, OperandType::kIdx8) \ + V(KeyedLoadICStrict, OperandType::kReg8, OperandType::kIdx8) \ + /* TODO(rmcilroy): Wide register operands too? */ \ + V(LoadICSloppyWide, OperandType::kReg8, OperandType::kIdx16, \ + OperandType::kIdx16) \ + V(LoadICStrictWide, OperandType::kReg8, OperandType::kIdx16, \ + OperandType::kIdx16) \ + V(KeyedLoadICSloppyWide, OperandType::kReg8, OperandType::kIdx16) \ + V(KeyedLoadICStrictWide, OperandType::kReg8, OperandType::kIdx16) \ + \ + /* StoreIC operations */ \ + V(StoreICSloppy, OperandType::kReg8, OperandType::kIdx8, OperandType::kIdx8) \ + V(StoreICStrict, OperandType::kReg8, OperandType::kIdx8, OperandType::kIdx8) \ + V(KeyedStoreICSloppy, OperandType::kReg8, OperandType::kReg8, \ + OperandType::kIdx8) \ + V(KeyedStoreICStrict, OperandType::kReg8, OperandType::kReg8, \ + OperandType::kIdx8) \ + /* TODO(rmcilroy): Wide register operands too? */ \ + V(StoreICSloppyWide, OperandType::kReg8, OperandType::kIdx16, \ + OperandType::kIdx16) \ + V(StoreICStrictWide, OperandType::kReg8, OperandType::kIdx16, \ + OperandType::kIdx16) \ + V(KeyedStoreICSloppyWide, OperandType::kReg8, OperandType::kReg8, \ + OperandType::kIdx16) \ + V(KeyedStoreICStrictWide, OperandType::kReg8, OperandType::kReg8, \ + OperandType::kIdx16) \ + \ + /* Binary Operators */ \ + V(Add, OperandType::kReg8) \ + V(Sub, OperandType::kReg8) \ + V(Mul, OperandType::kReg8) \ + V(Div, OperandType::kReg8) \ + V(Mod, OperandType::kReg8) \ + V(BitwiseOr, OperandType::kReg8) \ + V(BitwiseXor, OperandType::kReg8) \ + V(BitwiseAnd, OperandType::kReg8) \ + V(ShiftLeft, OperandType::kReg8) \ + V(ShiftRight, OperandType::kReg8) \ + V(ShiftRightLogical, OperandType::kReg8) \ + \ + /* Unary Operators */ \ + V(Inc, OperandType::kNone) \ + V(Dec, OperandType::kNone) \ + V(LogicalNot, OperandType::kNone) \ + V(TypeOf, OperandType::kNone) \ + V(DeletePropertyStrict, OperandType::kReg8) \ + V(DeletePropertySloppy, OperandType::kReg8) \ + V(DeleteLookupSlot, OperandType::kNone) \ + \ + /* Call operations */ \ + V(Call, OperandType::kReg8, OperandType::kReg8, OperandType::kCount8, \ + OperandType::kIdx8) \ + V(CallWide, OperandType::kReg8, OperandType::kReg8, OperandType::kCount16, \ + OperandType::kIdx16) \ + V(CallRuntime, OperandType::kIdx16, OperandType::kMaybeReg8, \ + OperandType::kCount8) \ + V(CallRuntimeForPair, OperandType::kIdx16, OperandType::kMaybeReg8, \ + OperandType::kCount8, OperandType::kRegPair8) \ + V(CallJSRuntime, OperandType::kIdx16, OperandType::kReg8, \ + OperandType::kCount8) \ + \ + /* New operator */ \ + V(New, OperandType::kReg8, OperandType::kMaybeReg8, OperandType::kCount8) \ + \ + /* Test Operators */ \ + V(TestEqual, OperandType::kReg8) \ + V(TestNotEqual, OperandType::kReg8) \ + V(TestEqualStrict, OperandType::kReg8) \ + V(TestNotEqualStrict, OperandType::kReg8) \ + V(TestLessThan, OperandType::kReg8) \ + V(TestGreaterThan, OperandType::kReg8) \ + V(TestLessThanOrEqual, OperandType::kReg8) \ + V(TestGreaterThanOrEqual, OperandType::kReg8) \ + V(TestInstanceOf, OperandType::kReg8) \ + V(TestIn, OperandType::kReg8) \ + \ + /* Cast operators */ \ + V(ToName, OperandType::kNone) \ + V(ToNumber, OperandType::kNone) \ + V(ToObject, OperandType::kNone) \ + \ + /* Literals */ \ + V(CreateRegExpLiteral, OperandType::kIdx8, OperandType::kIdx8, \ + OperandType::kImm8) \ + V(CreateArrayLiteral, OperandType::kIdx8, OperandType::kIdx8, \ + OperandType::kImm8) \ + V(CreateObjectLiteral, OperandType::kIdx8, OperandType::kIdx8, \ + OperandType::kImm8) \ + V(CreateRegExpLiteralWide, OperandType::kIdx16, OperandType::kIdx16, \ + OperandType::kImm8) \ + V(CreateArrayLiteralWide, OperandType::kIdx16, OperandType::kIdx16, \ + OperandType::kImm8) \ + V(CreateObjectLiteralWide, OperandType::kIdx16, OperandType::kIdx16, \ + OperandType::kImm8) \ + \ + /* Closure allocation */ \ + V(CreateClosure, OperandType::kIdx8, OperandType::kImm8) \ + V(CreateClosureWide, OperandType::kIdx16, OperandType::kImm8) \ + \ + /* Arguments allocation */ \ + V(CreateMappedArguments, OperandType::kNone) \ + V(CreateUnmappedArguments, OperandType::kNone) \ + \ + /* Control Flow */ \ + V(Jump, OperandType::kImm8) \ + V(JumpConstant, OperandType::kIdx8) \ + V(JumpConstantWide, OperandType::kIdx16) \ + V(JumpIfTrue, OperandType::kImm8) \ + V(JumpIfTrueConstant, OperandType::kIdx8) \ + V(JumpIfTrueConstantWide, OperandType::kIdx16) \ + V(JumpIfFalse, OperandType::kImm8) \ + V(JumpIfFalseConstant, OperandType::kIdx8) \ + V(JumpIfFalseConstantWide, OperandType::kIdx16) \ + V(JumpIfToBooleanTrue, OperandType::kImm8) \ + V(JumpIfToBooleanTrueConstant, OperandType::kIdx8) \ + V(JumpIfToBooleanTrueConstantWide, OperandType::kIdx16) \ + V(JumpIfToBooleanFalse, OperandType::kImm8) \ + V(JumpIfToBooleanFalseConstant, OperandType::kIdx8) \ + V(JumpIfToBooleanFalseConstantWide, OperandType::kIdx16) \ + V(JumpIfNull, OperandType::kImm8) \ + V(JumpIfNullConstant, OperandType::kIdx8) \ + V(JumpIfNullConstantWide, OperandType::kIdx16) \ + V(JumpIfUndefined, OperandType::kImm8) \ + V(JumpIfUndefinedConstant, OperandType::kIdx8) \ + V(JumpIfUndefinedConstantWide, OperandType::kIdx16) \ + \ + /* Complex flow control For..in */ \ + V(ForInPrepare, OperandType::kReg8, OperandType::kReg8, OperandType::kReg8) \ + V(ForInDone, OperandType::kReg8, OperandType::kReg8) \ + V(ForInNext, OperandType::kReg8, OperandType::kReg8, OperandType::kReg8, \ + OperandType::kReg8) \ + V(ForInStep, OperandType::kReg8) \ + \ + /* Non-local flow control */ \ + V(Throw, OperandType::kNone) \ V(Return, OperandType::kNone) +// Enumeration of the size classes of operand types used by bytecodes. +enum class OperandSize : uint8_t { + kNone = 0, + kByte = 1, + kShort = 2, +}; + + // Enumeration of operand types used by bytecodes. enum class OperandType : uint8_t { -#define DECLARE_OPERAND_TYPE(Name) k##Name, +#define DECLARE_OPERAND_TYPE(Name, _) k##Name, OPERAND_TYPE_LIST(DECLARE_OPERAND_TYPE) #undef DECLARE_OPERAND_TYPE -#define COUNT_OPERAND_TYPES(x) +1 +#define COUNT_OPERAND_TYPES(x, _) +1 // The COUNT_OPERAND macro will turn this into kLast = -1 +1 +1... which will // evaluate to the same value as the last operand. kLast = -1 OPERAND_TYPE_LIST(COUNT_OPERAND_TYPES) @@ -112,33 +264,61 @@ enum class Bytecode : uint8_t { }; -// An interpreter register which is located in the function's register file +// An interpreter Register which is located in the function's Register file // in its stack-frame. Register hold parameters, this, and expression values. class Register { public: - static const int kMaxRegisterIndex = 127; - static const int kMinRegisterIndex = -128; - Register() : index_(kIllegalIndex) {} - explicit Register(int index) : index_(index) { - DCHECK_LE(index_, kMaxRegisterIndex); - DCHECK_GE(index_, kMinRegisterIndex); - } + explicit Register(int index) : index_(index) {} int index() const { DCHECK(index_ != kIllegalIndex); return index_; } bool is_parameter() const { return index() < 0; } + bool is_valid() const { return index_ != kIllegalIndex; } static Register FromParameterIndex(int index, int parameter_count); int ToParameterIndex(int parameter_count) const; static int MaxParameterIndex(); + // Returns the register for the function's closure object. + static Register function_closure(); + bool is_function_closure() const; + + // Returns the register for the function's outer context. + static Register function_context(); + bool is_function_context() const; + + // Returns the register for the incoming new target value. + static Register new_target(); + bool is_new_target() const; + static Register FromOperand(uint8_t operand); uint8_t ToOperand() const; + static Register FromWideOperand(uint16_t operand); + uint16_t ToWideOperand() const; + + static bool AreContiguous(Register reg1, Register reg2, + Register reg3 = Register(), + Register reg4 = Register(), + Register reg5 = Register()); + + bool operator==(const Register& other) const { + return index() == other.index(); + } + bool operator!=(const Register& other) const { + return index() != other.index(); + } + bool operator<(const Register& other) const { + return index() < other.index(); + } + bool operator<=(const Register& other) const { + return index() <= other.index(); + } + private: static const int kIllegalIndex = kMaxInt; @@ -157,6 +337,9 @@ class Bytecodes { // Returns string representation of |operand_type|. static const char* OperandTypeToString(OperandType operand_type); + // Returns string representation of |operand_size|. + static const char* OperandSizeToString(OperandSize operand_size); + // Returns byte value of bytecode. static uint8_t ToByte(Bytecode bytecode); @@ -169,14 +352,53 @@ class Bytecodes { // Return the i-th operand of |bytecode|. static OperandType GetOperandType(Bytecode bytecode, int i); + // Return the size of the i-th operand of |bytecode|. + static OperandSize GetOperandSize(Bytecode bytecode, int i); + + // Returns the offset of the i-th operand of |bytecode| relative to the start + // of the bytecode. + static int GetOperandOffset(Bytecode bytecode, int i); + // Returns the size of the bytecode including its operands. static int Size(Bytecode bytecode); - // The maximum number of operands across all bytecodes. - static int MaximumNumberOfOperands(); + // Returns the size of |operand|. + static OperandSize SizeOfOperand(OperandType operand); + + // Return true if the bytecode is a conditional jump taking + // an immediate byte operand (OperandType::kImm8). + static bool IsConditionalJumpImmediate(Bytecode bytecode); + + // Return true if the bytecode is a conditional jump taking + // a constant pool entry (OperandType::kIdx8). + static bool IsConditionalJumpConstant(Bytecode bytecode); + + // Return true if the bytecode is a conditional jump taking + // a constant pool entry (OperandType::kIdx16). + static bool IsConditionalJumpConstantWide(Bytecode bytecode); + + // Return true if the bytecode is a conditional jump taking + // any kind of operand. + static bool IsConditionalJump(Bytecode bytecode); + + // Return true if the bytecode is a jump or a conditional jump taking + // an immediate byte operand (OperandType::kImm8). + static bool IsJumpImmediate(Bytecode bytecode); + + // Return true if the bytecode is a jump or conditional jump taking a + // constant pool entry (OperandType::kIdx8). + static bool IsJumpConstant(Bytecode bytecode); + + // Return true if the bytecode is a jump or conditional jump taking a + // constant pool entry (OperandType::kIdx16). + static bool IsJumpConstantWide(Bytecode bytecode); + + // Return true if the bytecode is a jump or conditional jump taking + // any kind of operand. + static bool IsJump(Bytecode bytecode); - // Maximum size of a bytecode and its operands. - static int MaximumSize(); + // Return true if the bytecode is a conditional jump, a jump, or a return. + static bool IsJumpOrReturn(Bytecode bytecode); // Decode a single bytecode and operands to |os|. static std::ostream& Decode(std::ostream& os, const uint8_t* bytecode_start, @@ -188,6 +410,7 @@ class Bytecodes { std::ostream& operator<<(std::ostream& os, const Bytecode& bytecode); std::ostream& operator<<(std::ostream& os, const OperandType& operand_type); +std::ostream& operator<<(std::ostream& os, const OperandSize& operand_type); } // namespace interpreter } // namespace internal diff --git a/chromium/v8/src/interpreter/constant-array-builder.cc b/chromium/v8/src/interpreter/constant-array-builder.cc new file mode 100644 index 00000000000..2586e1ff4d5 --- /dev/null +++ b/chromium/v8/src/interpreter/constant-array-builder.cc @@ -0,0 +1,174 @@ +// 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/constant-array-builder.h" + +#include "src/isolate.h" +#include "src/objects-inl.h" + +namespace v8 { +namespace internal { +namespace interpreter { + +ConstantArrayBuilder::ConstantArraySlice::ConstantArraySlice(Zone* zone, + size_t start_index, + size_t capacity) + : start_index_(start_index), + capacity_(capacity), + reserved_(0), + constants_(zone) {} + + +void ConstantArrayBuilder::ConstantArraySlice::Reserve() { + DCHECK_GT(available(), 0u); + reserved_++; + DCHECK_LE(reserved_, capacity() - constants_.size()); +} + + +void ConstantArrayBuilder::ConstantArraySlice::Unreserve() { + DCHECK_GT(reserved_, 0u); + reserved_--; +} + + +size_t ConstantArrayBuilder::ConstantArraySlice::Allocate( + Handle<Object> object) { + DCHECK_GT(available(), 0u); + size_t index = constants_.size(); + DCHECK_LT(index, capacity()); + constants_.push_back(object); + return index + start_index(); +} + + +Handle<Object> ConstantArrayBuilder::ConstantArraySlice::At( + size_t index) const { + return constants_[index - start_index()]; +} + + +STATIC_CONST_MEMBER_DEFINITION const size_t ConstantArrayBuilder::kMaxCapacity; +STATIC_CONST_MEMBER_DEFINITION const size_t ConstantArrayBuilder::kLowCapacity; + + +ConstantArrayBuilder::ConstantArrayBuilder(Isolate* isolate, Zone* zone) + : isolate_(isolate), + idx8_slice_(zone, 0, kLowCapacity), + idx16_slice_(zone, kLowCapacity, kHighCapacity), + constants_map_(isolate->heap(), zone) { + STATIC_ASSERT(kMaxCapacity == static_cast<size_t>(kMaxUInt16 + 1)); + DCHECK_EQ(idx8_slice_.start_index(), 0u); + DCHECK_EQ(idx8_slice_.capacity(), kLowCapacity); + DCHECK_EQ(idx16_slice_.start_index(), kLowCapacity); + DCHECK_EQ(idx16_slice_.capacity(), kMaxCapacity - kLowCapacity); +} + + +size_t ConstantArrayBuilder::size() const { + if (idx16_slice_.size() > 0) { + return idx16_slice_.start_index() + idx16_slice_.size(); + } else { + return idx8_slice_.size(); + } +} + + +Handle<Object> ConstantArrayBuilder::At(size_t index) const { + if (index >= idx16_slice_.start_index()) { + return idx16_slice_.At(index); + } else if (index < idx8_slice_.size()) { + return idx8_slice_.At(index); + } else { + return isolate_->factory()->the_hole_value(); + } +} + + +Handle<FixedArray> ConstantArrayBuilder::ToFixedArray(Factory* factory) const { + Handle<FixedArray> fixed_array = + factory->NewFixedArray(static_cast<int>(size()), PretenureFlag::TENURED); + for (int i = 0; i < fixed_array->length(); i++) { + fixed_array->set(i, *At(static_cast<size_t>(i))); + } + return fixed_array; +} + + +size_t ConstantArrayBuilder::Insert(Handle<Object> object) { + index_t* entry = constants_map_.Find(object); + return (entry == nullptr) ? AllocateEntry(object) : *entry; +} + + +ConstantArrayBuilder::index_t ConstantArrayBuilder::AllocateEntry( + Handle<Object> object) { + DCHECK(!object->IsOddball()); + size_t index; + index_t* entry = constants_map_.Get(object); + if (idx8_slice_.available() > 0) { + index = idx8_slice_.Allocate(object); + } else { + index = idx16_slice_.Allocate(object); + } + CHECK_LT(index, kMaxCapacity); + *entry = static_cast<index_t>(index); + return *entry; +} + + +OperandSize ConstantArrayBuilder::CreateReservedEntry() { + if (idx8_slice_.available() > 0) { + idx8_slice_.Reserve(); + return OperandSize::kByte; + } else if (idx16_slice_.available() > 0) { + idx16_slice_.Reserve(); + return OperandSize::kShort; + } else { + UNREACHABLE(); + return OperandSize::kNone; + } +} + + +size_t ConstantArrayBuilder::CommitReservedEntry(OperandSize operand_size, + Handle<Object> object) { + DiscardReservedEntry(operand_size); + size_t index; + index_t* entry = constants_map_.Find(object); + if (nullptr == entry) { + index = AllocateEntry(object); + } else { + if (operand_size == OperandSize::kByte && + *entry >= idx8_slice_.capacity()) { + // The object is already in the constant array, but has an index + // outside the range of an idx8 operand so we need to create a + // duplicate entry in the idx8 operand range to satisfy the + // commitment. + *entry = static_cast<index_t>(idx8_slice_.Allocate(object)); + } + index = *entry; + } + DCHECK(operand_size == OperandSize::kShort || index < idx8_slice_.capacity()); + DCHECK_LT(index, kMaxCapacity); + return index; +} + + +void ConstantArrayBuilder::DiscardReservedEntry(OperandSize operand_size) { + switch (operand_size) { + case OperandSize::kByte: + idx8_slice_.Unreserve(); + return; + case OperandSize::kShort: + idx16_slice_.Unreserve(); + return; + default: + UNREACHABLE(); + } +} + +} // namespace interpreter +} // namespace internal +} // namespace v8 diff --git a/chromium/v8/src/interpreter/constant-array-builder.h b/chromium/v8/src/interpreter/constant-array-builder.h new file mode 100644 index 00000000000..c882b1d540a --- /dev/null +++ b/chromium/v8/src/interpreter/constant-array-builder.h @@ -0,0 +1,97 @@ +// 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. + +#ifndef V8_INTERPRETER_CONSTANT_ARRAY_BUILDER_H_ +#define V8_INTERPRETER_CONSTANT_ARRAY_BUILDER_H_ + +#include "src/identity-map.h" +#include "src/interpreter/bytecodes.h" +#include "src/zone-containers.h" + +namespace v8 { +namespace internal { + +class Factory; +class Isolate; + +namespace interpreter { + +// A helper class for constructing constant arrays for the interpreter. +class ConstantArrayBuilder final : public ZoneObject { + public: + // Capacity of the 8-bit operand slice. + static const size_t kLowCapacity = 1u << kBitsPerByte; + + // Capacity of the combined 8-bit and 16-bit operand slices. + static const size_t kMaxCapacity = 1u << (2 * kBitsPerByte); + + // Capacity of the 16-bit operand slice. + static const size_t kHighCapacity = kMaxCapacity - kLowCapacity; + + ConstantArrayBuilder(Isolate* isolate, Zone* zone); + + // Generate a fixed array of constants based on inserted objects. + Handle<FixedArray> ToFixedArray(Factory* factory) const; + + // Returns the object in the constant pool array that at index + // |index|. + Handle<Object> At(size_t index) const; + + // Returns the number of elements in the array. + size_t size() const; + + // Insert an object into the constants array if it is not already + // present. Returns the array index associated with the object. + size_t Insert(Handle<Object> object); + + // Creates a reserved entry in the constant pool and returns + // the size of the operand that'll be required to hold the entry + // when committed. + OperandSize CreateReservedEntry(); + + // Commit reserved entry and returns the constant pool index for the + // object. + size_t CommitReservedEntry(OperandSize operand_size, Handle<Object> object); + + // Discards constant pool reservation. + void DiscardReservedEntry(OperandSize operand_size); + + private: + typedef uint16_t index_t; + + index_t AllocateEntry(Handle<Object> object); + + struct ConstantArraySlice final { + ConstantArraySlice(Zone* zone, size_t start_index, size_t capacity); + void Reserve(); + void Unreserve(); + size_t Allocate(Handle<Object> object); + Handle<Object> At(size_t index) const; + + inline size_t available() const { return capacity() - reserved() - size(); } + inline size_t reserved() const { return reserved_; } + inline size_t capacity() const { return capacity_; } + inline size_t size() const { return constants_.size(); } + inline size_t start_index() const { return start_index_; } + + private: + const size_t start_index_; + const size_t capacity_; + size_t reserved_; + ZoneVector<Handle<Object>> constants_; + + DISALLOW_COPY_AND_ASSIGN(ConstantArraySlice); + }; + + Isolate* isolate_; + ConstantArraySlice idx8_slice_; + ConstantArraySlice idx16_slice_; + IdentityMap<index_t> constants_map_; +}; + +} // namespace interpreter +} // namespace internal +} // namespace v8 + +#endif // V8_INTERPRETER_CONSTANT_ARRAY_BUILDER_H_ diff --git a/chromium/v8/src/interpreter/control-flow-builders.cc b/chromium/v8/src/interpreter/control-flow-builders.cc new file mode 100644 index 00000000000..99066e8c7e9 --- /dev/null +++ b/chromium/v8/src/interpreter/control-flow-builders.cc @@ -0,0 +1,142 @@ +// 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/control-flow-builders.h" + +namespace v8 { +namespace internal { +namespace interpreter { + + +BreakableControlFlowBuilder::~BreakableControlFlowBuilder() { + DCHECK(break_sites_.empty()); +} + + +void BreakableControlFlowBuilder::SetBreakTarget(const BytecodeLabel& target) { + BindLabels(target, &break_sites_); +} + + +void BreakableControlFlowBuilder::EmitJump(ZoneVector<BytecodeLabel>* sites) { + sites->push_back(BytecodeLabel()); + builder()->Jump(&sites->back()); +} + + +void BreakableControlFlowBuilder::EmitJumpIfTrue( + ZoneVector<BytecodeLabel>* sites) { + sites->push_back(BytecodeLabel()); + builder()->JumpIfTrue(&sites->back()); +} + + +void BreakableControlFlowBuilder::EmitJumpIfFalse( + ZoneVector<BytecodeLabel>* sites) { + sites->push_back(BytecodeLabel()); + builder()->JumpIfFalse(&sites->back()); +} + + +void BreakableControlFlowBuilder::EmitJumpIfUndefined( + ZoneVector<BytecodeLabel>* sites) { + sites->push_back(BytecodeLabel()); + builder()->JumpIfUndefined(&sites->back()); +} + + +void BreakableControlFlowBuilder::EmitJumpIfNull( + ZoneVector<BytecodeLabel>* sites) { + sites->push_back(BytecodeLabel()); + builder()->JumpIfNull(&sites->back()); +} + + +void BreakableControlFlowBuilder::EmitJump(ZoneVector<BytecodeLabel>* sites, + int index) { + builder()->Jump(&sites->at(index)); +} + + +void BreakableControlFlowBuilder::EmitJumpIfTrue( + ZoneVector<BytecodeLabel>* sites, int index) { + builder()->JumpIfTrue(&sites->at(index)); +} + + +void BreakableControlFlowBuilder::EmitJumpIfFalse( + ZoneVector<BytecodeLabel>* sites, int index) { + builder()->JumpIfFalse(&sites->at(index)); +} + + +void BreakableControlFlowBuilder::BindLabels(const BytecodeLabel& target, + ZoneVector<BytecodeLabel>* sites) { + for (size_t i = 0; i < sites->size(); i++) { + BytecodeLabel& site = sites->at(i); + builder()->Bind(target, &site); + } + sites->clear(); +} + + +void BlockBuilder::EndBlock() { + builder()->Bind(&block_end_); + SetBreakTarget(block_end_); +} + + +LoopBuilder::~LoopBuilder() { DCHECK(continue_sites_.empty()); } + + +void LoopBuilder::LoopHeader() { + // Jumps from before the loop header into the loop violate ordering + // requirements of bytecode basic blocks. The only entry into a loop + // must be the loop header. Surely breaks is okay? Not if nested + // and misplaced between the headers. + DCHECK(break_sites_.empty() && continue_sites_.empty()); + builder()->Bind(&loop_header_); +} + + +void LoopBuilder::EndLoop() { + // Loop must have closed form, i.e. all loop elements are within the loop, + // the loop header precedes the body and next elements in the loop. + DCHECK(loop_header_.is_bound()); + builder()->Bind(&loop_end_); + SetBreakTarget(loop_end_); + if (next_.is_bound()) { + DCHECK(!condition_.is_bound() || next_.offset() >= condition_.offset()); + SetContinueTarget(next_); + } else { + DCHECK(condition_.is_bound()); + DCHECK_GE(condition_.offset(), loop_header_.offset()); + DCHECK_LE(condition_.offset(), loop_end_.offset()); + SetContinueTarget(condition_); + } +} + + +void LoopBuilder::SetContinueTarget(const BytecodeLabel& target) { + BindLabels(target, &continue_sites_); +} + + +SwitchBuilder::~SwitchBuilder() { +#ifdef DEBUG + for (auto site : case_sites_) { + DCHECK(site.is_bound()); + } +#endif +} + + +void SwitchBuilder::SetCaseTarget(int index) { + BytecodeLabel& site = case_sites_.at(index); + builder()->Bind(&site); +} + +} // namespace interpreter +} // namespace internal +} // namespace v8 diff --git a/chromium/v8/src/interpreter/control-flow-builders.h b/chromium/v8/src/interpreter/control-flow-builders.h new file mode 100644 index 00000000000..24a7dfe3e5b --- /dev/null +++ b/chromium/v8/src/interpreter/control-flow-builders.h @@ -0,0 +1,151 @@ +// 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. + +#ifndef V8_INTERPRETER_CONTROL_FLOW_BUILDERS_H_ +#define V8_INTERPRETER_CONTROL_FLOW_BUILDERS_H_ + +#include "src/interpreter/bytecode-array-builder.h" + +#include "src/zone-containers.h" + +namespace v8 { +namespace internal { +namespace interpreter { + +class ControlFlowBuilder BASE_EMBEDDED { + public: + explicit ControlFlowBuilder(BytecodeArrayBuilder* builder) + : builder_(builder) {} + virtual ~ControlFlowBuilder() {} + + protected: + BytecodeArrayBuilder* builder() const { return builder_; } + + private: + BytecodeArrayBuilder* builder_; + + DISALLOW_COPY_AND_ASSIGN(ControlFlowBuilder); +}; + +class BreakableControlFlowBuilder : public ControlFlowBuilder { + public: + explicit BreakableControlFlowBuilder(BytecodeArrayBuilder* builder) + : ControlFlowBuilder(builder), + break_sites_(builder->zone()) {} + virtual ~BreakableControlFlowBuilder(); + + // This method should be called by the control flow owner before + // destruction to update sites that emit jumps for break. + void SetBreakTarget(const BytecodeLabel& break_target); + + // This method is called when visiting break statements in the AST. + // Inserts a jump to a unbound label that is patched when the corresponding + // SetBreakTarget is called. + void Break() { EmitJump(&break_sites_); } + void BreakIfTrue() { EmitJumpIfTrue(&break_sites_); } + void BreakIfFalse() { EmitJumpIfFalse(&break_sites_); } + void BreakIfUndefined() { EmitJumpIfUndefined(&break_sites_); } + void BreakIfNull() { EmitJumpIfNull(&break_sites_); } + + protected: + void EmitJump(ZoneVector<BytecodeLabel>* labels); + void EmitJump(ZoneVector<BytecodeLabel>* labels, int index); + void EmitJumpIfTrue(ZoneVector<BytecodeLabel>* labels); + void EmitJumpIfTrue(ZoneVector<BytecodeLabel>* labels, int index); + void EmitJumpIfFalse(ZoneVector<BytecodeLabel>* labels); + void EmitJumpIfFalse(ZoneVector<BytecodeLabel>* labels, int index); + void EmitJumpIfUndefined(ZoneVector<BytecodeLabel>* labels); + void EmitJumpIfNull(ZoneVector<BytecodeLabel>* labels); + + void BindLabels(const BytecodeLabel& target, ZoneVector<BytecodeLabel>* site); + + // Unbound labels that identify jumps for break statements in the code. + ZoneVector<BytecodeLabel> break_sites_; +}; + + +// Class to track control flow for block statements (which can break in JS). +class BlockBuilder final : public BreakableControlFlowBuilder { + public: + explicit BlockBuilder(BytecodeArrayBuilder* builder) + : BreakableControlFlowBuilder(builder) {} + + void EndBlock(); + + private: + BytecodeLabel block_end_; +}; + + +// A class to help with co-ordinating break and continue statements with +// their loop. +class LoopBuilder final : public BreakableControlFlowBuilder { + public: + explicit LoopBuilder(BytecodeArrayBuilder* builder) + : BreakableControlFlowBuilder(builder), + continue_sites_(builder->zone()) {} + ~LoopBuilder(); + + void LoopHeader(); + void Condition() { builder()->Bind(&condition_); } + void Next() { builder()->Bind(&next_); } + void JumpToHeader() { builder()->Jump(&loop_header_); } + void JumpToHeaderIfTrue() { builder()->JumpIfTrue(&loop_header_); } + void EndLoop(); + + // This method is called when visiting continue statements in the AST. + // Inserts a jump to a unbound label that is patched when the corresponding + // SetContinueTarget is called. + void Continue() { EmitJump(&continue_sites_); } + void ContinueIfTrue() { EmitJumpIfTrue(&continue_sites_); } + void ContinueIfUndefined() { EmitJumpIfUndefined(&continue_sites_); } + void ContinueIfNull() { EmitJumpIfNull(&continue_sites_); } + + private: + void SetContinueTarget(const BytecodeLabel& continue_target); + + BytecodeLabel loop_header_; + BytecodeLabel condition_; + BytecodeLabel next_; + BytecodeLabel loop_end_; + + // Unbound labels that identify jumps for continue statements in the code. + ZoneVector<BytecodeLabel> continue_sites_; +}; + + +// A class to help with co-ordinating break statements with their switch. +class SwitchBuilder final : public BreakableControlFlowBuilder { + public: + explicit SwitchBuilder(BytecodeArrayBuilder* builder, int number_of_cases) + : BreakableControlFlowBuilder(builder), + case_sites_(builder->zone()) { + case_sites_.resize(number_of_cases); + } + ~SwitchBuilder(); + + // This method should be called by the SwitchBuilder owner when the case + // statement with |index| is emitted to update the case jump site. + void SetCaseTarget(int index); + + // This method is called when visiting case comparison operation for |index|. + // Inserts a JumpIfTrue to a unbound label that is patched when the + // corresponding SetCaseTarget is called. + void Case(int index) { EmitJumpIfTrue(&case_sites_, index); } + + // This method is called when all cases comparisons have been emitted if there + // is a default case statement. Inserts a Jump to a unbound label that is + // patched when the corresponding SetCaseTarget is called. + void DefaultAt(int index) { EmitJump(&case_sites_, index); } + + private: + // Unbound labels that identify jumps for case statements in the code. + ZoneVector<BytecodeLabel> case_sites_; +}; + +} // namespace interpreter +} // namespace internal +} // namespace v8 + +#endif // V8_INTERPRETER_CONTROL_FLOW_BUILDERS_H_ diff --git a/chromium/v8/src/interpreter/interpreter.cc b/chromium/v8/src/interpreter/interpreter.cc index 2d97fc8ef28..574602b0ed8 100644 --- a/chromium/v8/src/interpreter/interpreter.cc +++ b/chromium/v8/src/interpreter/interpreter.cc @@ -17,6 +17,7 @@ namespace internal { namespace interpreter { using compiler::Node; + #define __ assembler-> @@ -59,21 +60,17 @@ void Interpreter::Initialize() { bool Interpreter::MakeBytecode(CompilationInfo* info) { - Handle<SharedFunctionInfo> shared_info = info->shared_info(); - BytecodeGenerator generator(info->isolate(), info->zone()); info->EnsureFeedbackVector(); Handle<BytecodeArray> bytecodes = generator.MakeBytecode(info); if (FLAG_print_bytecode) { - bytecodes->Print(); - } - - DCHECK(shared_info->function_data()->IsUndefined()); - if (!shared_info->function_data()->IsUndefined()) { - return false; + OFStream os(stdout); + os << "Function: " << info->GetDebugName().get() << std::endl; + bytecodes->Print(os); + os << std::flush; } - shared_info->set_function_data(*bytecodes); + info->SetBytecodeArray(bytecodes); info->SetCode(info->isolate()->builtins()->InterpreterEntryTrampoline()); return true; } @@ -100,17 +97,14 @@ void Interpreter::DoLdaZero(compiler::InterpreterAssembler* assembler) { // // Load an 8-bit integer literal into the accumulator as a Smi. void Interpreter::DoLdaSmi8(compiler::InterpreterAssembler* assembler) { - Node* raw_int = __ BytecodeOperandImm8(0); + Node* raw_int = __ BytecodeOperandImm(0); Node* smi_int = __ SmiTag(raw_int); __ SetAccumulator(smi_int); __ Dispatch(); } -// LdaConstant <idx> -// -// Load constant literal at |idx| in the constant pool into the accumulator. -void Interpreter::DoLdaConstant(compiler::InterpreterAssembler* assembler) { +void Interpreter::DoLoadConstant(compiler::InterpreterAssembler* assembler) { Node* index = __ BytecodeOperandIdx(0); Node* constant = __ LoadConstantPoolEntry(index); __ SetAccumulator(constant); @@ -118,6 +112,22 @@ void Interpreter::DoLdaConstant(compiler::InterpreterAssembler* assembler) { } +// LdaConstant <idx> +// +// Load constant literal at |idx| in the constant pool into the accumulator. +void Interpreter::DoLdaConstant(compiler::InterpreterAssembler* assembler) { + DoLoadConstant(assembler); +} + + +// LdaConstantWide <idx> +// +// Load constant literal at |idx| in the constant pool into the accumulator. +void Interpreter::DoLdaConstantWide(compiler::InterpreterAssembler* assembler) { + DoLoadConstant(assembler); +} + + // LdaUndefined // // Load Undefined into the accumulator. @@ -191,20 +201,439 @@ void Interpreter::DoStar(compiler::InterpreterAssembler* assembler) { } -// LdaGlobal <slot_index> +// Exchange <reg8> <reg16> +// +// Exchange two registers. +void Interpreter::DoExchange(compiler::InterpreterAssembler* assembler) { + Node* reg0_index = __ BytecodeOperandReg(0); + Node* reg1_index = __ BytecodeOperandReg(1); + Node* reg0_value = __ LoadRegister(reg0_index); + Node* reg1_value = __ LoadRegister(reg1_index); + __ StoreRegister(reg1_value, reg0_index); + __ StoreRegister(reg0_value, reg1_index); + __ Dispatch(); +} + + +// ExchangeWide <reg16> <reg16> +// +// Exchange two registers. +void Interpreter::DoExchangeWide(compiler::InterpreterAssembler* assembler) { + return DoExchange(assembler); +} + + +// Mov <src> <dst> +// +// Stores the value of register <src> to register <dst>. +void Interpreter::DoMov(compiler::InterpreterAssembler* assembler) { + Node* src_index = __ BytecodeOperandReg(0); + Node* src_value = __ LoadRegister(src_index); + Node* dst_index = __ BytecodeOperandReg(1); + __ StoreRegister(src_value, dst_index); + __ Dispatch(); +} + + +void Interpreter::DoLoadGlobal(Callable ic, + compiler::InterpreterAssembler* assembler) { + // Get the global object. + Node* context = __ GetContext(); + Node* native_context = + __ LoadContextSlot(context, Context::NATIVE_CONTEXT_INDEX); + Node* global = __ LoadContextSlot(native_context, Context::EXTENSION_INDEX); + + // Load the global via the LoadIC. + Node* code_target = __ HeapConstant(ic.code()); + Node* constant_index = __ BytecodeOperandIdx(0); + Node* name = __ LoadConstantPoolEntry(constant_index); + Node* raw_slot = __ BytecodeOperandIdx(1); + Node* smi_slot = __ SmiTag(raw_slot); + Node* type_feedback_vector = __ LoadTypeFeedbackVector(); + Node* result = __ CallIC(ic.descriptor(), code_target, global, name, smi_slot, + type_feedback_vector); + __ SetAccumulator(result); + __ Dispatch(); +} + + +// LdaGlobalSloppy <name_index> <slot> +// +// Load the global with name in constant pool entry <name_index> into the +// accumulator using FeedBackVector slot <slot> in sloppy mode. +void Interpreter::DoLdaGlobalSloppy(compiler::InterpreterAssembler* assembler) { + Callable ic = CodeFactory::LoadICInOptimizedCode(isolate_, NOT_INSIDE_TYPEOF, + SLOPPY, UNINITIALIZED); + DoLoadGlobal(ic, assembler); +} + + +// LdaGlobalSloppy <name_index> <slot> +// +// Load the global with name in constant pool entry <name_index> into the +// accumulator using FeedBackVector slot <slot> in strict mode. +void Interpreter::DoLdaGlobalStrict(compiler::InterpreterAssembler* assembler) { + Callable ic = CodeFactory::LoadICInOptimizedCode(isolate_, NOT_INSIDE_TYPEOF, + STRICT, UNINITIALIZED); + DoLoadGlobal(ic, assembler); +} + + +// LdaGlobalInsideTypeofSloppy <name_index> <slot> +// +// Load the global with name in constant pool entry <name_index> into the +// accumulator using FeedBackVector slot <slot> in sloppy mode. +void Interpreter::DoLdaGlobalInsideTypeofSloppy( + compiler::InterpreterAssembler* assembler) { + Callable ic = CodeFactory::LoadICInOptimizedCode(isolate_, INSIDE_TYPEOF, + SLOPPY, UNINITIALIZED); + DoLoadGlobal(ic, assembler); +} + + +// LdaGlobalInsideTypeofStrict <name_index> <slot> +// +// Load the global with name in constant pool entry <name_index> into the +// accumulator using FeedBackVector slot <slot> in strict mode. +void Interpreter::DoLdaGlobalInsideTypeofStrict( + compiler::InterpreterAssembler* assembler) { + Callable ic = CodeFactory::LoadICInOptimizedCode(isolate_, INSIDE_TYPEOF, + STRICT, UNINITIALIZED); + DoLoadGlobal(ic, assembler); +} + + +// LdaGlobalSloppyWide <name_index> <slot> +// +// Load the global with name in constant pool entry <name_index> into the +// accumulator using FeedBackVector slot <slot> in sloppy mode. +void Interpreter::DoLdaGlobalSloppyWide( + compiler::InterpreterAssembler* assembler) { + Callable ic = CodeFactory::LoadICInOptimizedCode(isolate_, NOT_INSIDE_TYPEOF, + SLOPPY, UNINITIALIZED); + DoLoadGlobal(ic, assembler); +} + + +// LdaGlobalSloppyWide <name_index> <slot> +// +// Load the global with name in constant pool entry <name_index> into the +// accumulator using FeedBackVector slot <slot> in strict mode. +void Interpreter::DoLdaGlobalStrictWide( + compiler::InterpreterAssembler* assembler) { + Callable ic = CodeFactory::LoadICInOptimizedCode(isolate_, NOT_INSIDE_TYPEOF, + STRICT, UNINITIALIZED); + DoLoadGlobal(ic, assembler); +} + + +// LdaGlobalInsideTypeofSloppyWide <name_index> <slot> +// +// Load the global with name in constant pool entry <name_index> into the +// accumulator using FeedBackVector slot <slot> in sloppy mode. +void Interpreter::DoLdaGlobalInsideTypeofSloppyWide( + compiler::InterpreterAssembler* assembler) { + Callable ic = CodeFactory::LoadICInOptimizedCode(isolate_, INSIDE_TYPEOF, + SLOPPY, UNINITIALIZED); + DoLoadGlobal(ic, assembler); +} + + +// LdaGlobalInsideTypeofSloppyWide <name_index> <slot> +// +// Load the global with name in constant pool entry <name_index> into the +// accumulator using FeedBackVector slot <slot> in strict mode. +void Interpreter::DoLdaGlobalInsideTypeofStrictWide( + compiler::InterpreterAssembler* assembler) { + Callable ic = CodeFactory::LoadICInOptimizedCode(isolate_, INSIDE_TYPEOF, + STRICT, UNINITIALIZED); + DoLoadGlobal(ic, assembler); +} + + +void Interpreter::DoStoreGlobal(Callable ic, + compiler::InterpreterAssembler* assembler) { + // Get the global object. + Node* context = __ GetContext(); + Node* native_context = + __ LoadContextSlot(context, Context::NATIVE_CONTEXT_INDEX); + Node* global = __ LoadContextSlot(native_context, Context::EXTENSION_INDEX); + + // Store the global via the StoreIC. + Node* code_target = __ HeapConstant(ic.code()); + Node* constant_index = __ BytecodeOperandIdx(0); + Node* name = __ LoadConstantPoolEntry(constant_index); + Node* value = __ GetAccumulator(); + Node* raw_slot = __ BytecodeOperandIdx(1); + Node* smi_slot = __ SmiTag(raw_slot); + Node* type_feedback_vector = __ LoadTypeFeedbackVector(); + __ CallIC(ic.descriptor(), code_target, global, name, value, smi_slot, + type_feedback_vector); + + __ Dispatch(); +} + + +// StaGlobalSloppy <name_index> <slot> +// +// Store the value in the accumulator into the global with name in constant pool +// entry <name_index> using FeedBackVector slot <slot> in sloppy mode. +void Interpreter::DoStaGlobalSloppy(compiler::InterpreterAssembler* assembler) { + Callable ic = + CodeFactory::StoreICInOptimizedCode(isolate_, SLOPPY, UNINITIALIZED); + DoStoreGlobal(ic, assembler); +} + + +// StaGlobalStrict <name_index> <slot> +// +// Store the value in the accumulator into the global with name in constant pool +// entry <name_index> using FeedBackVector slot <slot> in strict mode. +void Interpreter::DoStaGlobalStrict(compiler::InterpreterAssembler* assembler) { + Callable ic = + CodeFactory::StoreICInOptimizedCode(isolate_, STRICT, UNINITIALIZED); + DoStoreGlobal(ic, assembler); +} + + +// StaGlobalSloppyWide <name_index> <slot> +// +// Store the value in the accumulator into the global with name in constant pool +// entry <name_index> using FeedBackVector slot <slot> in sloppy mode. +void Interpreter::DoStaGlobalSloppyWide( + compiler::InterpreterAssembler* assembler) { + Callable ic = + CodeFactory::StoreICInOptimizedCode(isolate_, SLOPPY, UNINITIALIZED); + DoStoreGlobal(ic, assembler); +} + + +// StaGlobalStrictWide <name_index> <slot> +// +// Store the value in the accumulator into the global with name in constant pool +// entry <name_index> using FeedBackVector slot <slot> in strict mode. +void Interpreter::DoStaGlobalStrictWide( + compiler::InterpreterAssembler* assembler) { + Callable ic = + CodeFactory::StoreICInOptimizedCode(isolate_, STRICT, UNINITIALIZED); + DoStoreGlobal(ic, assembler); +} + + +// LdaContextSlot <context> <slot_index> // -// Load the global at |slot_index| into the accumulator. -void Interpreter::DoLdaGlobal(compiler::InterpreterAssembler* assembler) { - Node* slot_index = __ BytecodeOperandIdx(0); - Node* smi_slot_index = __ SmiTag(slot_index); - Node* result = __ CallRuntime(Runtime::kLoadGlobalViaContext, smi_slot_index); +// Load the object in |slot_index| of |context| into the accumulator. +void Interpreter::DoLdaContextSlot(compiler::InterpreterAssembler* assembler) { + Node* reg_index = __ BytecodeOperandReg(0); + Node* context = __ LoadRegister(reg_index); + Node* slot_index = __ BytecodeOperandIdx(1); + Node* result = __ LoadContextSlot(context, slot_index); __ SetAccumulator(result); __ Dispatch(); } -void Interpreter::DoPropertyLoadIC(Callable ic, +// LdaContextSlotWide <context> <slot_index> +// +// Load the object in |slot_index| of |context| into the accumulator. +void Interpreter::DoLdaContextSlotWide( + compiler::InterpreterAssembler* assembler) { + DoLdaContextSlot(assembler); +} + + +// StaContextSlot <context> <slot_index> +// +// Stores the object in the accumulator into |slot_index| of |context|. +void Interpreter::DoStaContextSlot(compiler::InterpreterAssembler* assembler) { + Node* value = __ GetAccumulator(); + Node* reg_index = __ BytecodeOperandReg(0); + Node* context = __ LoadRegister(reg_index); + Node* slot_index = __ BytecodeOperandIdx(1); + __ StoreContextSlot(context, slot_index, value); + __ Dispatch(); +} + + +// StaContextSlot <context> <slot_index> +// +// Stores the object in the accumulator into |slot_index| of |context|. +void Interpreter::DoStaContextSlotWide( + compiler::InterpreterAssembler* assembler) { + DoStaContextSlot(assembler); +} + + +void Interpreter::DoLoadLookupSlot(Runtime::FunctionId function_id, compiler::InterpreterAssembler* assembler) { + Node* index = __ BytecodeOperandIdx(0); + Node* name = __ LoadConstantPoolEntry(index); + Node* context = __ GetContext(); + Node* result_pair = __ CallRuntime(function_id, context, name); + Node* result = __ Projection(0, result_pair); + __ SetAccumulator(result); + __ Dispatch(); +} + + +// LdaLookupSlot <name_index> +// +// Lookup the object with the name in constant pool entry |name_index| +// dynamically. +void Interpreter::DoLdaLookupSlot(compiler::InterpreterAssembler* assembler) { + DoLoadLookupSlot(Runtime::kLoadLookupSlot, assembler); +} + + +// LdaLookupSlotInsideTypeof <name_index> +// +// Lookup the object with the name in constant pool entry |name_index| +// dynamically without causing a NoReferenceError. +void Interpreter::DoLdaLookupSlotInsideTypeof( + compiler::InterpreterAssembler* assembler) { + DoLoadLookupSlot(Runtime::kLoadLookupSlotNoReferenceError, assembler); +} + + +// LdaLookupSlotWide <name_index> +// +// Lookup the object with the name in constant pool entry |name_index| +// dynamically. +void Interpreter::DoLdaLookupSlotWide( + compiler::InterpreterAssembler* assembler) { + DoLdaLookupSlot(assembler); +} + + +// LdaLookupSlotInsideTypeofWide <name_index> +// +// Lookup the object with the name in constant pool entry |name_index| +// dynamically without causing a NoReferenceError. +void Interpreter::DoLdaLookupSlotInsideTypeofWide( + compiler::InterpreterAssembler* assembler) { + DoLdaLookupSlotInsideTypeof(assembler); +} + + +void Interpreter::DoStoreLookupSlot(LanguageMode language_mode, + compiler::InterpreterAssembler* assembler) { + Node* value = __ GetAccumulator(); + Node* index = __ BytecodeOperandIdx(0); + Node* name = __ LoadConstantPoolEntry(index); + Node* context = __ GetContext(); + Node* language_mode_node = __ NumberConstant(language_mode); + Node* result = __ CallRuntime(Runtime::kStoreLookupSlot, value, context, name, + language_mode_node); + __ SetAccumulator(result); + __ Dispatch(); +} + + +// StaLookupSlotSloppy <name_index> +// +// Store the object in accumulator to the object with the name in constant +// pool entry |name_index| in sloppy mode. +void Interpreter::DoStaLookupSlotSloppy( + compiler::InterpreterAssembler* assembler) { + DoStoreLookupSlot(LanguageMode::SLOPPY, assembler); +} + + +// StaLookupSlotStrict <name_index> +// +// Store the object in accumulator to the object with the name in constant +// pool entry |name_index| in strict mode. +void Interpreter::DoStaLookupSlotStrict( + compiler::InterpreterAssembler* assembler) { + DoStoreLookupSlot(LanguageMode::STRICT, assembler); +} + + +// StaLookupSlotSloppyWide <name_index> +// +// Store the object in accumulator to the object with the name in constant +// pool entry |name_index| in sloppy mode. +void Interpreter::DoStaLookupSlotSloppyWide( + compiler::InterpreterAssembler* assembler) { + DoStaLookupSlotSloppy(assembler); +} + + +// StaLookupSlotStrictWide <name_index> +// +// Store the object in accumulator to the object with the name in constant +// pool entry |name_index| in strict mode. +void Interpreter::DoStaLookupSlotStrictWide( + compiler::InterpreterAssembler* assembler) { + DoStaLookupSlotStrict(assembler); +} + + +void Interpreter::DoLoadIC(Callable ic, + compiler::InterpreterAssembler* assembler) { + Node* code_target = __ HeapConstant(ic.code()); + Node* register_index = __ BytecodeOperandReg(0); + Node* object = __ LoadRegister(register_index); + Node* constant_index = __ BytecodeOperandIdx(1); + Node* name = __ LoadConstantPoolEntry(constant_index); + Node* raw_slot = __ BytecodeOperandIdx(2); + Node* smi_slot = __ SmiTag(raw_slot); + Node* type_feedback_vector = __ LoadTypeFeedbackVector(); + Node* result = __ CallIC(ic.descriptor(), code_target, object, name, smi_slot, + type_feedback_vector); + __ SetAccumulator(result); + __ Dispatch(); +} + + +// LoadICSloppy <object> <name_index> <slot> +// +// Calls the sloppy mode LoadIC at FeedBackVector slot <slot> for <object> and +// the name at constant pool entry <name_index>. +void Interpreter::DoLoadICSloppy(compiler::InterpreterAssembler* assembler) { + Callable ic = CodeFactory::LoadICInOptimizedCode(isolate_, NOT_INSIDE_TYPEOF, + SLOPPY, UNINITIALIZED); + DoLoadIC(ic, assembler); +} + + +// LoadICStrict <object> <name_index> <slot> +// +// Calls the sloppy mode LoadIC at FeedBackVector slot <slot> for <object> and +// the name at constant pool entry <name_index>. +void Interpreter::DoLoadICStrict(compiler::InterpreterAssembler* assembler) { + Callable ic = CodeFactory::LoadICInOptimizedCode(isolate_, NOT_INSIDE_TYPEOF, + STRICT, UNINITIALIZED); + DoLoadIC(ic, assembler); +} + + +// LoadICSloppyWide <object> <name_index> <slot> +// +// Calls the sloppy mode LoadIC at FeedBackVector slot <slot> for <object> and +// the name at constant pool entry <name_index>. +void Interpreter::DoLoadICSloppyWide( + compiler::InterpreterAssembler* assembler) { + Callable ic = CodeFactory::LoadICInOptimizedCode(isolate_, NOT_INSIDE_TYPEOF, + SLOPPY, UNINITIALIZED); + DoLoadIC(ic, assembler); +} + + +// LoadICStrictWide <object> <name_index> <slot> +// +// Calls the sloppy mode LoadIC at FeedBackVector slot <slot> for <object> and +// the name at constant pool entry <name_index>. +void Interpreter::DoLoadICStrictWide( + compiler::InterpreterAssembler* assembler) { + Callable ic = CodeFactory::LoadICInOptimizedCode(isolate_, NOT_INSIDE_TYPEOF, + STRICT, UNINITIALIZED); + DoLoadIC(ic, assembler); +} + + +void Interpreter::DoKeyedLoadIC(Callable ic, + compiler::InterpreterAssembler* assembler) { Node* code_target = __ HeapConstant(ic.code()); Node* reg_index = __ BytecodeOperandReg(0); Node* object = __ LoadRegister(reg_index); @@ -219,30 +648,123 @@ void Interpreter::DoPropertyLoadIC(Callable ic, } -// LoadIC <object> <slot> +// KeyedLoadICSloppy <object> <slot> // -// Calls the LoadIC at FeedBackVector slot <slot> for <object> and the name -// in the accumulator. -void Interpreter::DoLoadIC(compiler::InterpreterAssembler* assembler) { - Callable ic = CodeFactory::LoadICInOptimizedCode(isolate_, NOT_INSIDE_TYPEOF, - SLOPPY, UNINITIALIZED); - DoPropertyLoadIC(ic, assembler); +// Calls the sloppy mode KeyedLoadIC at FeedBackVector slot <slot> for <object> +// and the key in the accumulator. +void Interpreter::DoKeyedLoadICSloppy( + compiler::InterpreterAssembler* assembler) { + Callable ic = + CodeFactory::KeyedLoadICInOptimizedCode(isolate_, SLOPPY, UNINITIALIZED); + DoKeyedLoadIC(ic, assembler); } -// KeyedLoadIC <object> <slot> +// KeyedLoadICStrict <object> <slot> // -// Calls the KeyedLoadIC at FeedBackVector slot <slot> for <object> and the key -// in the accumulator. -void Interpreter::DoKeyedLoadIC(compiler::InterpreterAssembler* assembler) { +// Calls the strict mode KeyedLoadIC at FeedBackVector slot <slot> for <object> +// and the key in the accumulator. +void Interpreter::DoKeyedLoadICStrict( + compiler::InterpreterAssembler* assembler) { + Callable ic = + CodeFactory::KeyedLoadICInOptimizedCode(isolate_, STRICT, UNINITIALIZED); + DoKeyedLoadIC(ic, assembler); +} + + +// KeyedLoadICSloppyWide <object> <slot> +// +// Calls the sloppy mode KeyedLoadIC at FeedBackVector slot <slot> for <object> +// and the key in the accumulator. +void Interpreter::DoKeyedLoadICSloppyWide( + compiler::InterpreterAssembler* assembler) { Callable ic = CodeFactory::KeyedLoadICInOptimizedCode(isolate_, SLOPPY, UNINITIALIZED); - DoPropertyLoadIC(ic, assembler); + DoKeyedLoadIC(ic, assembler); } -void Interpreter::DoPropertyStoreIC(Callable ic, - compiler::InterpreterAssembler* assembler) { +// KeyedLoadICStrictWide <object> <slot> +// +// Calls the strict mode KeyedLoadIC at FeedBackVector slot <slot> for <object> +// and the key in the accumulator. +void Interpreter::DoKeyedLoadICStrictWide( + compiler::InterpreterAssembler* assembler) { + Callable ic = + CodeFactory::KeyedLoadICInOptimizedCode(isolate_, STRICT, UNINITIALIZED); + DoKeyedLoadIC(ic, assembler); +} + + +void Interpreter::DoStoreIC(Callable ic, + compiler::InterpreterAssembler* assembler) { + Node* code_target = __ HeapConstant(ic.code()); + Node* object_reg_index = __ BytecodeOperandReg(0); + Node* object = __ LoadRegister(object_reg_index); + Node* constant_index = __ BytecodeOperandIdx(1); + Node* name = __ LoadConstantPoolEntry(constant_index); + Node* value = __ GetAccumulator(); + Node* raw_slot = __ BytecodeOperandIdx(2); + Node* smi_slot = __ SmiTag(raw_slot); + Node* type_feedback_vector = __ LoadTypeFeedbackVector(); + __ CallIC(ic.descriptor(), code_target, object, name, value, smi_slot, + type_feedback_vector); + __ Dispatch(); +} + + +// StoreICSloppy <object> <name_index> <slot> +// +// Calls the sloppy mode StoreIC at FeedBackVector slot <slot> for <object> and +// the name in constant pool entry <name_index> with the value in the +// accumulator. +void Interpreter::DoStoreICSloppy(compiler::InterpreterAssembler* assembler) { + Callable ic = + CodeFactory::StoreICInOptimizedCode(isolate_, SLOPPY, UNINITIALIZED); + DoStoreIC(ic, assembler); +} + + +// StoreICStrict <object> <name_index> <slot> +// +// Calls the strict mode StoreIC at FeedBackVector slot <slot> for <object> and +// the name in constant pool entry <name_index> with the value in the +// accumulator. +void Interpreter::DoStoreICStrict(compiler::InterpreterAssembler* assembler) { + Callable ic = + CodeFactory::StoreICInOptimizedCode(isolate_, STRICT, UNINITIALIZED); + DoStoreIC(ic, assembler); +} + + +// StoreICSloppyWide <object> <name_index> <slot> +// +// Calls the sloppy mode StoreIC at FeedBackVector slot <slot> for <object> and +// the name in constant pool entry <name_index> with the value in the +// accumulator. +void Interpreter::DoStoreICSloppyWide( + compiler::InterpreterAssembler* assembler) { + Callable ic = + CodeFactory::StoreICInOptimizedCode(isolate_, SLOPPY, UNINITIALIZED); + DoStoreIC(ic, assembler); +} + + +// StoreICStrictWide <object> <name_index> <slot> +// +// Calls the strict mode StoreIC at FeedBackVector slot <slot> for <object> and +// the name in constant pool entry <name_index> with the value in the +// accumulator. +void Interpreter::DoStoreICStrictWide( + compiler::InterpreterAssembler* assembler) { + Callable ic = + CodeFactory::StoreICInOptimizedCode(isolate_, STRICT, UNINITIALIZED); + DoStoreIC(ic, assembler); +} + + +void Interpreter::DoKeyedStoreIC(Callable ic, + compiler::InterpreterAssembler* assembler) { Node* code_target = __ HeapConstant(ic.code()); Node* object_reg_index = __ BytecodeOperandReg(0); Node* object = __ LoadRegister(object_reg_index); @@ -252,32 +774,80 @@ void Interpreter::DoPropertyStoreIC(Callable ic, Node* raw_slot = __ BytecodeOperandIdx(2); Node* smi_slot = __ SmiTag(raw_slot); Node* type_feedback_vector = __ LoadTypeFeedbackVector(); - Node* result = __ CallIC(ic.descriptor(), code_target, object, name, value, - smi_slot, type_feedback_vector); - __ SetAccumulator(result); + __ CallIC(ic.descriptor(), code_target, object, name, value, smi_slot, + type_feedback_vector); __ Dispatch(); } -// StoreIC <object> <name> <slot> +// KeyedStoreICSloppy <object> <key> <slot> // -// Calls the StoreIC at FeedBackVector slot <slot> for <object> and the name -// <name> with the value in the accumulator. -void Interpreter::DoStoreIC(compiler::InterpreterAssembler* assembler) { +// Calls the sloppy mode KeyStoreIC at FeedBackVector slot <slot> for <object> +// and the key <key> with the value in the accumulator. +void Interpreter::DoKeyedStoreICSloppy( + compiler::InterpreterAssembler* assembler) { Callable ic = - CodeFactory::StoreICInOptimizedCode(isolate_, SLOPPY, UNINITIALIZED); - DoPropertyStoreIC(ic, assembler); + CodeFactory::KeyedStoreICInOptimizedCode(isolate_, SLOPPY, UNINITIALIZED); + DoKeyedStoreIC(ic, assembler); } -// KeyedStoreIC <object> <key> <slot> +// KeyedStoreICStore <object> <key> <slot> // -// Calls the KeyStoreIC at FeedBackVector slot <slot> for <object> and the key -// <key> with the value in the accumulator. -void Interpreter::DoKeyedStoreIC(compiler::InterpreterAssembler* assembler) { +// Calls the strict mode KeyStoreIC at FeedBackVector slot <slot> for <object> +// and the key <key> with the value in the accumulator. +void Interpreter::DoKeyedStoreICStrict( + compiler::InterpreterAssembler* assembler) { + Callable ic = + CodeFactory::KeyedStoreICInOptimizedCode(isolate_, STRICT, UNINITIALIZED); + DoKeyedStoreIC(ic, assembler); +} + + +// KeyedStoreICSloppyWide <object> <key> <slot> +// +// Calls the sloppy mode KeyStoreIC at FeedBackVector slot <slot> for <object> +// and the key <key> with the value in the accumulator. +void Interpreter::DoKeyedStoreICSloppyWide( + compiler::InterpreterAssembler* assembler) { Callable ic = CodeFactory::KeyedStoreICInOptimizedCode(isolate_, SLOPPY, UNINITIALIZED); - DoPropertyStoreIC(ic, assembler); + DoKeyedStoreIC(ic, assembler); +} + + +// KeyedStoreICStoreWide <object> <key> <slot> +// +// Calls the strict mode KeyStoreIC at FeedBackVector slot <slot> for <object> +// and the key <key> with the value in the accumulator. +void Interpreter::DoKeyedStoreICStrictWide( + compiler::InterpreterAssembler* assembler) { + Callable ic = + CodeFactory::KeyedStoreICInOptimizedCode(isolate_, STRICT, UNINITIALIZED); + DoKeyedStoreIC(ic, assembler); +} + + +// PushContext <context> +// +// Pushes the accumulator as the current context, and saves it in <context> +void Interpreter::DoPushContext(compiler::InterpreterAssembler* assembler) { + Node* reg_index = __ BytecodeOperandReg(0); + Node* context = __ GetAccumulator(); + __ SetContext(context); + __ StoreRegister(context, reg_index); + __ Dispatch(); +} + + +// PopContext <context> +// +// Pops the current context and sets <context> as the new context. +void Interpreter::DoPopContext(compiler::InterpreterAssembler* assembler) { + Node* reg_index = __ BytecodeOperandReg(0); + Node* context = __ LoadRegister(reg_index); + __ SetContext(context); + __ Dispatch(); } @@ -334,22 +904,275 @@ void Interpreter::DoMod(compiler::InterpreterAssembler* assembler) { } -// Call <receiver> <arg_count> +// BitwiseOr <src> // -// Call a JS function with receiver and |arg_count| arguments in subsequent -// registers. The JSfunction or Callable to call is in the accumulator. -void Interpreter::DoCall(compiler::InterpreterAssembler* assembler) { +// BitwiseOr register <src> to accumulator. +void Interpreter::DoBitwiseOr(compiler::InterpreterAssembler* assembler) { + DoBinaryOp(Runtime::kBitwiseOr, assembler); +} + + +// BitwiseXor <src> +// +// BitwiseXor register <src> to accumulator. +void Interpreter::DoBitwiseXor(compiler::InterpreterAssembler* assembler) { + DoBinaryOp(Runtime::kBitwiseXor, assembler); +} + + +// BitwiseAnd <src> +// +// BitwiseAnd register <src> to accumulator. +void Interpreter::DoBitwiseAnd(compiler::InterpreterAssembler* assembler) { + DoBinaryOp(Runtime::kBitwiseAnd, assembler); +} + + +// ShiftLeft <src> +// +// Left shifts register <src> by the count specified in the accumulator. +// Register <src> is converted to an int32 and the accumulator to uint32 +// before the operation. 5 lsb bits from the accumulator are used as count +// i.e. <src> << (accumulator & 0x1F). +void Interpreter::DoShiftLeft(compiler::InterpreterAssembler* assembler) { + DoBinaryOp(Runtime::kShiftLeft, assembler); +} + + +// ShiftRight <src> +// +// Right shifts register <src> by the count specified in the accumulator. +// Result is sign extended. Register <src> is converted to an int32 and the +// accumulator to uint32 before the operation. 5 lsb bits from the accumulator +// are used as count i.e. <src> >> (accumulator & 0x1F). +void Interpreter::DoShiftRight(compiler::InterpreterAssembler* assembler) { + DoBinaryOp(Runtime::kShiftRight, assembler); +} + + +// ShiftRightLogical <src> +// +// Right Shifts register <src> by the count specified in the accumulator. +// Result is zero-filled. The accumulator and register <src> are converted to +// uint32 before the operation 5 lsb bits from the accumulator are used as +// count i.e. <src> << (accumulator & 0x1F). +void Interpreter::DoShiftRightLogical( + compiler::InterpreterAssembler* assembler) { + DoBinaryOp(Runtime::kShiftRightLogical, assembler); +} + + +void Interpreter::DoCountOp(Runtime::FunctionId function_id, + compiler::InterpreterAssembler* assembler) { + Node* value = __ GetAccumulator(); + Node* one = __ NumberConstant(1); + Node* result = __ CallRuntime(function_id, value, one); + __ SetAccumulator(result); + __ Dispatch(); +} + + +// Inc +// +// Increments value in the accumulator by one. +void Interpreter::DoInc(compiler::InterpreterAssembler* assembler) { + DoCountOp(Runtime::kAdd, assembler); +} + + +// Dec +// +// Decrements value in the accumulator by one. +void Interpreter::DoDec(compiler::InterpreterAssembler* assembler) { + DoCountOp(Runtime::kSubtract, assembler); +} + + +// LogicalNot +// +// Perform logical-not on the accumulator, first casting the +// accumulator to a boolean value if required. +void Interpreter::DoLogicalNot(compiler::InterpreterAssembler* assembler) { + Node* accumulator = __ GetAccumulator(); + Node* result = __ CallRuntime(Runtime::kInterpreterLogicalNot, accumulator); + __ SetAccumulator(result); + __ Dispatch(); +} + + +// TypeOf +// +// Load the accumulator with the string representating type of the +// object in the accumulator. +void Interpreter::DoTypeOf(compiler::InterpreterAssembler* assembler) { + Node* accumulator = __ GetAccumulator(); + Node* result = __ CallRuntime(Runtime::kInterpreterTypeOf, accumulator); + __ SetAccumulator(result); + __ Dispatch(); +} + + +void Interpreter::DoDelete(Runtime::FunctionId function_id, + compiler::InterpreterAssembler* assembler) { + Node* reg_index = __ BytecodeOperandReg(0); + Node* object = __ LoadRegister(reg_index); + Node* key = __ GetAccumulator(); + Node* result = __ CallRuntime(function_id, object, key); + __ SetAccumulator(result); + __ Dispatch(); +} + + +// DeletePropertyStrict +// +// Delete the property specified in the accumulator from the object +// referenced by the register operand following strict mode semantics. +void Interpreter::DoDeletePropertyStrict( + compiler::InterpreterAssembler* assembler) { + DoDelete(Runtime::kDeleteProperty_Strict, assembler); +} + + +// DeletePropertySloppy +// +// Delete the property specified in the accumulator from the object +// referenced by the register operand following sloppy mode semantics. +void Interpreter::DoDeletePropertySloppy( + compiler::InterpreterAssembler* assembler) { + DoDelete(Runtime::kDeleteProperty_Sloppy, assembler); +} + + +// DeleteLookupSlot +// +// Delete the variable with the name specified in the accumulator by dynamically +// looking it up. +void Interpreter::DoDeleteLookupSlot( + compiler::InterpreterAssembler* assembler) { + Node* name = __ GetAccumulator(); + Node* context = __ GetContext(); + Node* result = __ CallRuntime(Runtime::kDeleteLookupSlot, context, name); + __ SetAccumulator(result); + __ Dispatch(); +} + + +void Interpreter::DoJSCall(compiler::InterpreterAssembler* assembler) { Node* function_reg = __ BytecodeOperandReg(0); Node* function = __ LoadRegister(function_reg); Node* receiver_reg = __ BytecodeOperandReg(1); Node* first_arg = __ RegisterLocation(receiver_reg); Node* args_count = __ BytecodeOperandCount(2); + // TODO(rmcilroy): Use the call type feedback slot to call via CallIC. + Node* result = __ CallJS(function, first_arg, args_count); + __ SetAccumulator(result); + __ Dispatch(); +} + + +// Call <callable> <receiver> <arg_count> +// +// Call a JSfunction or Callable in |callable| with the |receiver| and +// |arg_count| arguments in subsequent registers. +void Interpreter::DoCall(compiler::InterpreterAssembler* assembler) { + DoJSCall(assembler); +} + + +// CallWide <callable> <receiver> <arg_count> +// +// Call a JSfunction or Callable in |callable| with the |receiver| and +// |arg_count| arguments in subsequent registers. +void Interpreter::DoCallWide(compiler::InterpreterAssembler* assembler) { + DoJSCall(assembler); +} + + +// CallRuntime <function_id> <first_arg> <arg_count> +// +// Call the runtime function |function_id| with the first argument in +// register |first_arg| and |arg_count| arguments in subsequent +// registers. +void Interpreter::DoCallRuntime(compiler::InterpreterAssembler* assembler) { + Node* function_id = __ BytecodeOperandIdx(0); + Node* first_arg_reg = __ BytecodeOperandReg(1); + Node* first_arg = __ RegisterLocation(first_arg_reg); + Node* args_count = __ BytecodeOperandCount(2); + Node* result = __ CallRuntime(function_id, first_arg, args_count); + __ SetAccumulator(result); + __ Dispatch(); +} + + +// CallRuntimeForPair <function_id> <first_arg> <arg_count> <first_return> +// +// Call the runtime function |function_id| which returns a pair, with the +// first argument in register |first_arg| and |arg_count| arguments in +// subsequent registers. Returns the result in <first_return> and +// <first_return + 1> +void Interpreter::DoCallRuntimeForPair( + compiler::InterpreterAssembler* assembler) { + // Call the runtime function. + Node* function_id = __ BytecodeOperandIdx(0); + Node* first_arg_reg = __ BytecodeOperandReg(1); + Node* first_arg = __ RegisterLocation(first_arg_reg); + Node* args_count = __ BytecodeOperandCount(2); + Node* result_pair = __ CallRuntime(function_id, first_arg, args_count, 2); + + // Store the results in <first_return> and <first_return + 1> + Node* first_return_reg = __ BytecodeOperandReg(3); + Node* second_return_reg = __ NextRegister(first_return_reg); + Node* result0 = __ Projection(0, result_pair); + Node* result1 = __ Projection(1, result_pair); + __ StoreRegister(result0, first_return_reg); + __ StoreRegister(result1, second_return_reg); + + __ Dispatch(); +} + + +// CallJSRuntime <context_index> <receiver> <arg_count> +// +// Call the JS runtime function that has the |context_index| with the receiver +// in register |receiver| and |arg_count| arguments in subsequent registers. +void Interpreter::DoCallJSRuntime(compiler::InterpreterAssembler* assembler) { + Node* context_index = __ BytecodeOperandIdx(0); + Node* receiver_reg = __ BytecodeOperandReg(1); + Node* first_arg = __ RegisterLocation(receiver_reg); + Node* args_count = __ BytecodeOperandCount(2); + + // Get the function to call from the native context. + Node* context = __ GetContext(); + Node* native_context = + __ LoadContextSlot(context, Context::NATIVE_CONTEXT_INDEX); + Node* function = __ LoadContextSlot(native_context, context_index); + + // Call the function. Node* result = __ CallJS(function, first_arg, args_count); __ SetAccumulator(result); __ Dispatch(); } +// New <constructor> <first_arg> <arg_count> +// +// Call operator new with |constructor| and the first argument in +// register |first_arg| and |arg_count| arguments in subsequent +// +void Interpreter::DoNew(compiler::InterpreterAssembler* assembler) { + Callable ic = CodeFactory::InterpreterPushArgsAndConstruct(isolate_); + Node* constructor_reg = __ BytecodeOperandReg(0); + Node* constructor = __ LoadRegister(constructor_reg); + Node* first_arg_reg = __ BytecodeOperandReg(1); + Node* first_arg = __ RegisterLocation(first_arg_reg); + Node* args_count = __ BytecodeOperandCount(2); + Node* result = + __ CallConstruct(constructor, constructor, first_arg, args_count); + __ SetAccumulator(result); + __ Dispatch(); +} + + // TestEqual <src> // // Test if the value in the <src> register equals the accumulator. @@ -438,28 +1261,51 @@ void Interpreter::DoTestInstanceOf(compiler::InterpreterAssembler* assembler) { } -// ToBoolean +// ToName +// +// Cast the object referenced by the accumulator to a name. +void Interpreter::DoToName(compiler::InterpreterAssembler* assembler) { + Node* accumulator = __ GetAccumulator(); + Node* result = __ CallRuntime(Runtime::kToName, accumulator); + __ SetAccumulator(result); + __ Dispatch(); +} + + +// ToNumber // -// Cast the object referenced by the accumulator to a boolean. -void Interpreter::DoToBoolean(compiler::InterpreterAssembler* assembler) { - // TODO(oth): The next CL for test operations has interpreter specific - // runtime calls. This looks like another candidate. +// Cast the object referenced by the accumulator to a number. +void Interpreter::DoToNumber(compiler::InterpreterAssembler* assembler) { + Node* accumulator = __ GetAccumulator(); + Node* result = __ CallRuntime(Runtime::kToNumber, accumulator); + __ SetAccumulator(result); + __ Dispatch(); +} + + +// ToObject +// +// Cast the object referenced by the accumulator to a JSObject. +void Interpreter::DoToObject(compiler::InterpreterAssembler* assembler) { + Node* accumulator = __ GetAccumulator(); + Node* result = __ CallRuntime(Runtime::kToObject, accumulator); + __ SetAccumulator(result); __ Dispatch(); } // Jump <imm8> // -// Jump by number of bytes represented by an immediate operand. +// Jump by number of bytes represented by the immediate operand |imm8|. void Interpreter::DoJump(compiler::InterpreterAssembler* assembler) { - Node* relative_jump = __ BytecodeOperandImm8(0); + Node* relative_jump = __ BytecodeOperandImm(0); __ Jump(relative_jump); } -// JumpConstant <idx> +// JumpConstant <idx8> // -// Jump by number of bytes in the Smi in the |idx| entry in the constant pool. +// Jump by number of bytes in the Smi in the |idx8| entry in the constant pool. void Interpreter::DoJumpConstant(compiler::InterpreterAssembler* assembler) { Node* index = __ BytecodeOperandIdx(0); Node* constant = __ LoadConstantPoolEntry(index); @@ -468,21 +1314,31 @@ void Interpreter::DoJumpConstant(compiler::InterpreterAssembler* assembler) { } +// JumpConstantWide <idx16> +// +// Jump by number of bytes in the Smi in the |idx16| entry in the +// constant pool. +void Interpreter::DoJumpConstantWide( + compiler::InterpreterAssembler* assembler) { + DoJumpConstant(assembler); +} + + // JumpIfTrue <imm8> // // Jump by number of bytes represented by an immediate operand if the // accumulator contains true. void Interpreter::DoJumpIfTrue(compiler::InterpreterAssembler* assembler) { Node* accumulator = __ GetAccumulator(); - Node* relative_jump = __ BytecodeOperandImm8(0); + Node* relative_jump = __ BytecodeOperandImm(0); Node* true_value = __ BooleanConstant(true); __ JumpIfWordEqual(accumulator, true_value, relative_jump); } -// JumpIfTrueConstant <idx> +// JumpIfTrueConstant <idx8> // -// Jump by number of bytes in the Smi in the |idx| entry in the constant pool +// Jump by number of bytes in the Smi in the |idx8| entry in the constant pool // if the accumulator contains true. void Interpreter::DoJumpIfTrueConstant( compiler::InterpreterAssembler* assembler) { @@ -495,21 +1351,31 @@ void Interpreter::DoJumpIfTrueConstant( } +// JumpIfTrueConstantWide <idx16> +// +// Jump by number of bytes in the Smi in the |idx16| entry in the constant pool +// if the accumulator contains true. +void Interpreter::DoJumpIfTrueConstantWide( + compiler::InterpreterAssembler* assembler) { + DoJumpIfTrueConstant(assembler); +} + + // JumpIfFalse <imm8> // // Jump by number of bytes represented by an immediate operand if the // accumulator contains false. void Interpreter::DoJumpIfFalse(compiler::InterpreterAssembler* assembler) { Node* accumulator = __ GetAccumulator(); - Node* relative_jump = __ BytecodeOperandImm8(0); + Node* relative_jump = __ BytecodeOperandImm(0); Node* false_value = __ BooleanConstant(false); __ JumpIfWordEqual(accumulator, false_value, relative_jump); } -// JumpIfFalseConstant <idx> +// JumpIfFalseConstant <idx8> // -// Jump by number of bytes in the Smi in the |idx| entry in the constant pool +// Jump by number of bytes in the Smi in the |idx8| entry in the constant pool // if the accumulator contains false. void Interpreter::DoJumpIfFalseConstant( compiler::InterpreterAssembler* assembler) { @@ -522,14 +1388,393 @@ void Interpreter::DoJumpIfFalseConstant( } +// JumpIfFalseConstant <idx16> +// +// Jump by number of bytes in the Smi in the |idx16| entry in the constant pool +// if the accumulator contains false. +void Interpreter::DoJumpIfFalseConstantWide( + compiler::InterpreterAssembler* assembler) { + DoJumpIfFalseConstant(assembler); +} + + +// JumpIfToBooleanTrue <imm8> +// +// Jump by number of bytes represented by an immediate operand if the object +// referenced by the accumulator is true when the object is cast to boolean. +void Interpreter::DoJumpIfToBooleanTrue( + compiler::InterpreterAssembler* assembler) { + Node* accumulator = __ GetAccumulator(); + Node* to_boolean_value = + __ CallRuntime(Runtime::kInterpreterToBoolean, accumulator); + Node* relative_jump = __ BytecodeOperandImm(0); + Node* true_value = __ BooleanConstant(true); + __ JumpIfWordEqual(to_boolean_value, true_value, relative_jump); +} + + +// JumpIfToBooleanTrueConstant <idx8> +// +// Jump by number of bytes in the Smi in the |idx8| entry in the constant pool +// if the object referenced by the accumulator is true when the object is cast +// to boolean. +void Interpreter::DoJumpIfToBooleanTrueConstant( + compiler::InterpreterAssembler* assembler) { + Node* accumulator = __ GetAccumulator(); + Node* to_boolean_value = + __ CallRuntime(Runtime::kInterpreterToBoolean, accumulator); + Node* index = __ BytecodeOperandIdx(0); + Node* constant = __ LoadConstantPoolEntry(index); + Node* relative_jump = __ SmiUntag(constant); + Node* true_value = __ BooleanConstant(true); + __ JumpIfWordEqual(to_boolean_value, true_value, relative_jump); +} + + +// JumpIfToBooleanTrueConstantWide <idx16> +// +// Jump by number of bytes in the Smi in the |idx16| entry in the constant pool +// if the object referenced by the accumulator is true when the object is cast +// to boolean. +void Interpreter::DoJumpIfToBooleanTrueConstantWide( + compiler::InterpreterAssembler* assembler) { + DoJumpIfToBooleanTrueConstant(assembler); +} + + +// JumpIfToBooleanFalse <imm8> +// +// Jump by number of bytes represented by an immediate operand if the object +// referenced by the accumulator is false when the object is cast to boolean. +void Interpreter::DoJumpIfToBooleanFalse( + compiler::InterpreterAssembler* assembler) { + Node* accumulator = __ GetAccumulator(); + Node* to_boolean_value = + __ CallRuntime(Runtime::kInterpreterToBoolean, accumulator); + Node* relative_jump = __ BytecodeOperandImm(0); + Node* false_value = __ BooleanConstant(false); + __ JumpIfWordEqual(to_boolean_value, false_value, relative_jump); +} + + +// JumpIfToBooleanFalseConstant <idx8> +// +// Jump by number of bytes in the Smi in the |idx8| entry in the constant pool +// if the object referenced by the accumulator is false when the object is cast +// to boolean. +void Interpreter::DoJumpIfToBooleanFalseConstant( + compiler::InterpreterAssembler* assembler) { + Node* accumulator = __ GetAccumulator(); + Node* to_boolean_value = + __ CallRuntime(Runtime::kInterpreterToBoolean, accumulator); + Node* index = __ BytecodeOperandIdx(0); + Node* constant = __ LoadConstantPoolEntry(index); + Node* relative_jump = __ SmiUntag(constant); + Node* false_value = __ BooleanConstant(false); + __ JumpIfWordEqual(to_boolean_value, false_value, relative_jump); +} + + +// JumpIfToBooleanFalseConstantWide <idx16> +// +// Jump by number of bytes in the Smi in the |idx16| entry in the constant pool +// if the object referenced by the accumulator is false when the object is cast +// to boolean. +void Interpreter::DoJumpIfToBooleanFalseConstantWide( + compiler::InterpreterAssembler* assembler) { + DoJumpIfToBooleanFalseConstant(assembler); +} + + +// JumpIfNull <imm8> +// +// Jump by number of bytes represented by an immediate operand if the object +// referenced by the accumulator is the null constant. +void Interpreter::DoJumpIfNull(compiler::InterpreterAssembler* assembler) { + Node* accumulator = __ GetAccumulator(); + Node* null_value = __ HeapConstant(isolate_->factory()->null_value()); + Node* relative_jump = __ BytecodeOperandImm(0); + __ JumpIfWordEqual(accumulator, null_value, relative_jump); +} + + +// JumpIfNullConstant <idx8> +// +// Jump by number of bytes in the Smi in the |idx8| entry in the constant pool +// if the object referenced by the accumulator is the null constant. +void Interpreter::DoJumpIfNullConstant( + compiler::InterpreterAssembler* assembler) { + Node* accumulator = __ GetAccumulator(); + Node* null_value = __ HeapConstant(isolate_->factory()->null_value()); + Node* index = __ BytecodeOperandIdx(0); + Node* constant = __ LoadConstantPoolEntry(index); + Node* relative_jump = __ SmiUntag(constant); + __ JumpIfWordEqual(accumulator, null_value, relative_jump); +} + + +// JumpIfNullConstantWide <idx16> +// +// Jump by number of bytes in the Smi in the |idx16| entry in the constant pool +// if the object referenced by the accumulator is the null constant. +void Interpreter::DoJumpIfNullConstantWide( + compiler::InterpreterAssembler* assembler) { + DoJumpIfNullConstant(assembler); +} + + +// jumpifundefined <imm8> +// +// Jump by number of bytes represented by an immediate operand if the object +// referenced by the accumulator is the undefined constant. +void Interpreter::DoJumpIfUndefined(compiler::InterpreterAssembler* assembler) { + Node* accumulator = __ GetAccumulator(); + Node* undefined_value = + __ HeapConstant(isolate_->factory()->undefined_value()); + Node* relative_jump = __ BytecodeOperandImm(0); + __ JumpIfWordEqual(accumulator, undefined_value, relative_jump); +} + + +// JumpIfUndefinedConstant <idx8> +// +// Jump by number of bytes in the Smi in the |idx8| entry in the constant pool +// if the object referenced by the accumulator is the undefined constant. +void Interpreter::DoJumpIfUndefinedConstant( + compiler::InterpreterAssembler* assembler) { + Node* accumulator = __ GetAccumulator(); + Node* undefined_value = + __ HeapConstant(isolate_->factory()->undefined_value()); + Node* index = __ BytecodeOperandIdx(0); + Node* constant = __ LoadConstantPoolEntry(index); + Node* relative_jump = __ SmiUntag(constant); + __ JumpIfWordEqual(accumulator, undefined_value, relative_jump); +} + + +// JumpIfUndefinedConstantWide <idx16> +// +// Jump by number of bytes in the Smi in the |idx16| entry in the constant pool +// if the object referenced by the accumulator is the undefined constant. +void Interpreter::DoJumpIfUndefinedConstantWide( + compiler::InterpreterAssembler* assembler) { + DoJumpIfUndefinedConstant(assembler); +} + + +void Interpreter::DoCreateLiteral(Runtime::FunctionId function_id, + compiler::InterpreterAssembler* assembler) { + Node* index = __ BytecodeOperandIdx(0); + Node* constant_elements = __ LoadConstantPoolEntry(index); + Node* literal_index_raw = __ BytecodeOperandIdx(1); + Node* literal_index = __ SmiTag(literal_index_raw); + Node* flags_raw = __ BytecodeOperandImm(2); + Node* flags = __ SmiTag(flags_raw); + Node* closure = __ LoadRegister(Register::function_closure()); + Node* result = __ CallRuntime(function_id, closure, literal_index, + constant_elements, flags); + __ SetAccumulator(result); + __ Dispatch(); +} + + +// CreateRegExpLiteral <pattern_idx> <literal_idx> <flags> +// +// Creates a regular expression literal for literal index <literal_idx> with +// <flags> and the pattern in <pattern_idx>. +void Interpreter::DoCreateRegExpLiteral( + compiler::InterpreterAssembler* assembler) { + DoCreateLiteral(Runtime::kCreateRegExpLiteral, assembler); +} + + +// CreateRegExpLiteralWide <pattern_idx> <literal_idx> <flags> +// +// Creates a regular expression literal for literal index <literal_idx> with +// <flags> and the pattern in <pattern_idx>. +void Interpreter::DoCreateRegExpLiteralWide( + compiler::InterpreterAssembler* assembler) { + DoCreateLiteral(Runtime::kCreateRegExpLiteral, assembler); +} + + +// CreateArrayLiteral <element_idx> <literal_idx> <flags> +// +// Creates an array literal for literal index <literal_idx> with flags <flags> +// and constant elements in <element_idx>. +void Interpreter::DoCreateArrayLiteral( + compiler::InterpreterAssembler* assembler) { + DoCreateLiteral(Runtime::kCreateArrayLiteral, assembler); +} + + +// CreateArrayLiteralWide <element_idx> <literal_idx> <flags> +// +// Creates an array literal for literal index <literal_idx> with flags <flags> +// and constant elements in <element_idx>. +void Interpreter::DoCreateArrayLiteralWide( + compiler::InterpreterAssembler* assembler) { + DoCreateLiteral(Runtime::kCreateArrayLiteral, assembler); +} + + +// CreateObjectLiteral <element_idx> <literal_idx> <flags> +// +// Creates an object literal for literal index <literal_idx> with flags <flags> +// and constant elements in <element_idx>. +void Interpreter::DoCreateObjectLiteral( + compiler::InterpreterAssembler* assembler) { + DoCreateLiteral(Runtime::kCreateObjectLiteral, assembler); +} + + +// CreateObjectLiteralWide <element_idx> <literal_idx> <flags> +// +// Creates an object literal for literal index <literal_idx> with flags <flags> +// and constant elements in <element_idx>. +void Interpreter::DoCreateObjectLiteralWide( + compiler::InterpreterAssembler* assembler) { + DoCreateLiteral(Runtime::kCreateObjectLiteral, assembler); +} + + +// CreateClosure <index> <tenured> +// +// Creates a new closure for SharedFunctionInfo at position |index| in the +// constant pool and with the PretenureFlag <tenured>. +void Interpreter::DoCreateClosure(compiler::InterpreterAssembler* assembler) { + // TODO(rmcilroy): Possibly call FastNewClosureStub when possible instead of + // calling into the runtime. + Node* index = __ BytecodeOperandIdx(0); + Node* shared = __ LoadConstantPoolEntry(index); + Node* tenured_raw = __ BytecodeOperandImm(1); + Node* tenured = __ SmiTag(tenured_raw); + Node* result = + __ CallRuntime(Runtime::kInterpreterNewClosure, shared, tenured); + __ SetAccumulator(result); + __ Dispatch(); +} + + +// CreateClosureWide <index> <tenured> +// +// Creates a new closure for SharedFunctionInfo at position |index| in the +// constant pool and with the PretenureFlag <tenured>. +void Interpreter::DoCreateClosureWide( + compiler::InterpreterAssembler* assembler) { + return DoCreateClosure(assembler); +} + + +// CreateMappedArguments +// +// Creates a new mapped arguments object. +void Interpreter::DoCreateMappedArguments( + compiler::InterpreterAssembler* assembler) { + Node* closure = __ LoadRegister(Register::function_closure()); + Node* result = __ CallRuntime(Runtime::kNewSloppyArguments_Generic, closure); + __ SetAccumulator(result); + __ Dispatch(); +} + + +// CreateUnmappedArguments +// +// Creates a new unmapped arguments object. +void Interpreter::DoCreateUnmappedArguments( + compiler::InterpreterAssembler* assembler) { + Node* closure = __ LoadRegister(Register::function_closure()); + Node* result = __ CallRuntime(Runtime::kNewStrictArguments_Generic, closure); + __ SetAccumulator(result); + __ Dispatch(); +} + + +// Throw +// +// Throws the exception in the accumulator. +void Interpreter::DoThrow(compiler::InterpreterAssembler* assembler) { + Node* exception = __ GetAccumulator(); + __ CallRuntime(Runtime::kThrow, exception); + // We shouldn't ever return from a throw. + __ Abort(kUnexpectedReturnFromThrow); +} + + // Return // -// Return the value in register 0. +// Return the value in the accumulator. void Interpreter::DoReturn(compiler::InterpreterAssembler* assembler) { __ Return(); } +// ForInPrepare <cache_type> <cache_array> <cache_length> +// +// Returns state for for..in loop execution based on the object in the +// accumulator. The registers |cache_type|, |cache_array|, and +// |cache_length| represent output parameters. +void Interpreter::DoForInPrepare(compiler::InterpreterAssembler* assembler) { + Node* object = __ GetAccumulator(); + Node* result = __ CallRuntime(Runtime::kInterpreterForInPrepare, object); + for (int i = 0; i < 3; i++) { + // 0 == cache_type, 1 == cache_array, 2 == cache_length + Node* cache_info = __ LoadFixedArrayElement(result, i); + Node* cache_info_reg = __ BytecodeOperandReg(i); + __ StoreRegister(cache_info, cache_info_reg); + } + __ SetAccumulator(result); + __ Dispatch(); +} + + +// ForInNext <receiver> <cache_type> <cache_array> <index> +// +// Returns the next enumerable property in the the accumulator. +void Interpreter::DoForInNext(compiler::InterpreterAssembler* assembler) { + Node* receiver_reg = __ BytecodeOperandReg(0); + Node* receiver = __ LoadRegister(receiver_reg); + Node* cache_type_reg = __ BytecodeOperandReg(1); + Node* cache_type = __ LoadRegister(cache_type_reg); + Node* cache_array_reg = __ BytecodeOperandReg(2); + Node* cache_array = __ LoadRegister(cache_array_reg); + Node* index_reg = __ BytecodeOperandReg(3); + Node* index = __ LoadRegister(index_reg); + Node* result = __ CallRuntime(Runtime::kForInNext, receiver, cache_array, + cache_type, index); + __ SetAccumulator(result); + __ Dispatch(); +} + + +// ForInDone <index> <cache_length> +// +// Returns true if the end of the enumerable properties has been reached. +void Interpreter::DoForInDone(compiler::InterpreterAssembler* assembler) { + // TODO(oth): Implement directly rather than making a runtime call. + Node* index_reg = __ BytecodeOperandReg(0); + Node* index = __ LoadRegister(index_reg); + Node* cache_length_reg = __ BytecodeOperandReg(1); + Node* cache_length = __ LoadRegister(cache_length_reg); + Node* result = __ CallRuntime(Runtime::kForInDone, index, cache_length); + __ SetAccumulator(result); + __ Dispatch(); +} + + +// ForInStep <index> +// +// Increments the loop counter in register |index| and stores the result +// in the accumulator. +void Interpreter::DoForInStep(compiler::InterpreterAssembler* assembler) { + // TODO(oth): Implement directly rather than making a runtime call. + Node* index_reg = __ BytecodeOperandReg(0); + Node* index = __ LoadRegister(index_reg); + Node* result = __ CallRuntime(Runtime::kForInStep, index); + __ SetAccumulator(result); + __ Dispatch(); +} + } // namespace interpreter } // namespace internal } // namespace v8 diff --git a/chromium/v8/src/interpreter/interpreter.h b/chromium/v8/src/interpreter/interpreter.h index c32b6831d0a..ef9b5d1fe3e 100644 --- a/chromium/v8/src/interpreter/interpreter.h +++ b/chromium/v8/src/interpreter/interpreter.h @@ -11,8 +11,8 @@ #include "src/base/macros.h" #include "src/builtins.h" #include "src/interpreter/bytecodes.h" +#include "src/parsing/token.h" #include "src/runtime/runtime.h" -#include "src/token.h" namespace v8 { namespace internal { @@ -54,16 +54,53 @@ class Interpreter { void DoBinaryOp(Runtime::FunctionId function_id, compiler::InterpreterAssembler* assembler); + // Generates code to perform the count operations via |function_id|. + void DoCountOp(Runtime::FunctionId function_id, + compiler::InterpreterAssembler* assembler); + // Generates code to perform the comparison operation associated with // |compare_op|. void DoCompareOp(Token::Value compare_op, compiler::InterpreterAssembler* assembler); - // Generates code to perform a property load via |ic|. - void DoPropertyLoadIC(Callable ic, compiler::InterpreterAssembler* assembler); + // Generates code to load a constant from the constant pool. + void DoLoadConstant(compiler::InterpreterAssembler* assembler); + + // Generates code to perform a global load via |ic|. + void DoLoadGlobal(Callable ic, compiler::InterpreterAssembler* assembler); + + // Generates code to perform a global store via |ic|. + void DoStoreGlobal(Callable ic, compiler::InterpreterAssembler* assembler); + + // Generates code to perform a named property load via |ic|. + void DoLoadIC(Callable ic, compiler::InterpreterAssembler* assembler); + + // Generates code to perform a keyed property load via |ic|. + void DoKeyedLoadIC(Callable ic, compiler::InterpreterAssembler* assembler); + + // Generates code to perform a namedproperty store via |ic|. + void DoStoreIC(Callable ic, compiler::InterpreterAssembler* assembler); + + // Generates code to perform a keyed property store via |ic|. + void DoKeyedStoreIC(Callable ic, compiler::InterpreterAssembler* assembler); + + // Generates code to perform a JS call. + void DoJSCall(compiler::InterpreterAssembler* assembler); + + // Generates code ro create a literal via |function_id|. + void DoCreateLiteral(Runtime::FunctionId function_id, + compiler::InterpreterAssembler* assembler); + + // Generates code to perform delete via function_id. + void DoDelete(Runtime::FunctionId function_id, + compiler::InterpreterAssembler* assembler); + + // Generates code to perform a lookup slot load via |function_id|. + void DoLoadLookupSlot(Runtime::FunctionId function_id, + compiler::InterpreterAssembler* assembler); - // Generates code to perform a property store via |ic|. - void DoPropertyStoreIC(Callable ic, + // Generates code to perform a lookup slot store depending on |language_mode|. + void DoStoreLookupSlot(LanguageMode language_mode, compiler::InterpreterAssembler* assembler); bool IsInterpreterTableInitialized(Handle<FixedArray> handler_table); |