Chromium Code Reviews| Index: src/interpreter/bytecode-array-builder.cc | 
| diff --git a/src/interpreter/bytecode-array-builder.cc b/src/interpreter/bytecode-array-builder.cc | 
| index b732f8b96fae8197abf145ffc3abe0af50df6e09..d6383c9eb77e11557ad6be52cae0b7beab23efb8 100644 | 
| --- a/src/interpreter/bytecode-array-builder.cc | 
| +++ b/src/interpreter/bytecode-array-builder.cc | 
| @@ -21,14 +21,13 @@ BytecodeArrayBuilder::BytecodeArrayBuilder(Isolate* isolate, Zone* zone) | 
| parameter_count_(-1), | 
| local_register_count_(-1), | 
| context_register_count_(-1), | 
| - temporary_register_count_(0), | 
| - temporary_register_next_(0) {} | 
| + free_temporaries_(zone), | 
| + temporary_register_count_(0) {} | 
| void BytecodeArrayBuilder::set_locals_count(int number_of_locals) { | 
| local_register_count_ = number_of_locals; | 
| DCHECK_LE(context_register_count_, 0); | 
| - temporary_register_next_ = local_register_count_; | 
| } | 
| @@ -40,7 +39,6 @@ void BytecodeArrayBuilder::set_parameter_count(int number_of_parameters) { | 
| void BytecodeArrayBuilder::set_context_count(int number_of_contexts) { | 
| context_register_count_ = number_of_contexts; | 
| DCHECK_GE(local_register_count_, 0); | 
| - temporary_register_next_ = local_register_count_ + context_register_count_; | 
| } | 
| @@ -56,12 +54,30 @@ Register BytecodeArrayBuilder::last_context_register() const { | 
| } | 
| +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) const { | 
| DCHECK_GE(parameter_index, 0); | 
| return Register::FromParameterIndex(parameter_index, parameter_count()); | 
| } | 
| +bool BytecodeArrayBuilder::RegisterIsParameterOrLocal(Register reg) const { | 
| + // OTH: TODO review or context? | 
| 
 
rmcilroy
2015/10/19 12:56:21
I don't think we need to worry about contexts sinc
 
oth
2015/10/20 15:28:51
Agree. Now have a RegisterIsTemporary() as an addi
 
 | 
| + return reg.is_parameter() || reg.index() < local_register_count_; | 
| 
 
rmcilroy
2015/10/19 12:56:21
use local_register_count() instead.
 
oth
2015/10/20 15:28:51
Done.
 
 | 
| +} | 
| + | 
| + | 
| Handle<BytecodeArray> BytecodeArrayBuilder::ToBytecodeArray() { | 
| DCHECK_EQ(bytecode_generated_, false); | 
| @@ -600,6 +616,7 @@ void BytecodeArrayBuilder::EnsureReturn() { | 
| } | 
| } | 
| + | 
| BytecodeArrayBuilder& BytecodeArrayBuilder::Call(Register callable, | 
| Register receiver, | 
| size_t arg_count) { | 
| @@ -654,18 +671,117 @@ size_t BytecodeArrayBuilder::GetConstantPoolEntry(Handle<Object> object) { | 
| int BytecodeArrayBuilder::BorrowTemporaryRegister() { | 
| - int temporary_reg_index = temporary_register_next_++; | 
| - int count = temporary_register_next_ - fixed_register_count(); | 
| - if (count > temporary_register_count_) { | 
| - temporary_register_count_ = count; | 
| + int retval; | 
| + if (free_temporaries_.empty()) { | 
| + temporary_register_count_ += 1; | 
| + retval = last_temporary_register().index(); | 
| + } else { | 
| + retval = free_temporaries_.back(); | 
| + free_temporaries_.pop_back(); | 
| + for (size_t i = 0; i < free_temporaries_.size(); i++) { | 
| 
 
rmcilroy
2015/10/19 12:56:21
Only do this loop if #DEBUG.
 
oth
2015/10/20 15:28:52
Re-implemented with ZoneSet.
 
 | 
| + DCHECK(free_temporaries_[i] != retval); | 
| + } | 
| } | 
| - return temporary_reg_index; | 
| + return retval; | 
| } | 
| void BytecodeArrayBuilder::ReturnTemporaryRegister(int reg_index) { | 
| - DCHECK_EQ(reg_index, temporary_register_next_ - 1); | 
| - temporary_register_next_ = reg_index; | 
| + for (size_t i = 0; i < free_temporaries_.size(); i++) { | 
| 
 
rmcilroy
2015/10/19 12:56:22
ditto
 
oth
2015/10/20 15:28:52
Re-implemented with ZoneSet.
 
 | 
| + DCHECK(free_temporaries_[i] != reg_index); | 
| + } | 
| + free_temporaries_.push_back(reg_index); | 
| +} | 
| + | 
| + | 
| +static void CheckConsecutiveRun(ZoneVector<int>* v, size_t count) { | 
| 
 
rmcilroy
2015/10/19 12:56:21
This code should only be run if #DEBUG - just make
 
oth
2015/10/20 15:28:52
Method removed.
 
 | 
| + DCHECK_GE(v->size(), count); | 
| + auto current = v->rbegin(); | 
| + while (count-- > 1) { | 
| 
 
rmcilroy
2015/10/19 12:56:21
for loop instead?
 
oth
2015/10/20 15:28:52
Ack.
 
 | 
| + auto successor = current + 1; | 
| + DCHECK_EQ(*successor, *current + 1); | 
| + current = successor; | 
| + } | 
| +} | 
| + | 
| + | 
| +void BytecodeArrayBuilder::PrepareForConsecutiveTemporaryRegisters( | 
| + size_t count) { | 
| + // TODO(oth): this is a placeholder. There should have a better | 
| + // allocator that avoids sorting, e.g. bitmap allocator with hint to | 
| + // next cell in a consecutive sequence, or something similar. | 
| + | 
| + if (count == 0) { | 
| + return; | 
| + } | 
| + | 
| + size_t run = 0; | 
| + std::sort(free_temporaries_.begin(), free_temporaries_.end()); | 
| 
 
rmcilroy
2015/10/19 12:56:21
How about we just keep free_temporaries sorted rat
 
oth
2015/10/20 15:28:52
Yes, it's become a bit overwhelming in it's curren
 
 | 
| + | 
| + // First check for a run in the existing elements | 
| 
 
rmcilroy
2015/10/19 12:56:22
How often do we end up (in the current test code)
 
oth
2015/10/20 15:28:52
IIRC it only occurs in VisitObjectLiterals which i
 
 | 
| + if (free_temporaries_.size() >= count) { | 
| + size_t run_start = 0; | 
| + int last_temporary = free_temporaries_[0] - 1; | 
| + for (size_t i = 0; i < free_temporaries_.size(); i++) { | 
| + if (free_temporaries_[i] == last_temporary + 1) { | 
| + if (i - run_start + 1 == count) { | 
| + std::rotate(free_temporaries_.begin(), | 
| + free_temporaries_.begin() + i + 1, | 
| + free_temporaries_.end()); | 
| + std::reverse(free_temporaries_.rbegin(), | 
| + free_temporaries_.rbegin() + count); | 
| + CheckConsecutiveRun(&free_temporaries_, count); | 
| + return; | 
| + } | 
| + } else { | 
| + run_start = i; | 
| + } | 
| + last_temporary = free_temporaries_[i]; | 
| + } | 
| + } | 
| + | 
| + // Next check if a run can be formed abutting the end of the | 
| + // free list. | 
| + if (!free_temporaries_.empty()) { | 
| + auto start = free_temporaries_.rbegin(); | 
| + int last_index = last_temporary_register().index() + 1; | 
| + while (run < count && start != free_temporaries_.rend() && | 
| + *start == last_index - 1) { | 
| + last_index = *start; | 
| + start++; | 
| + run++; | 
| + } | 
| + } | 
| + | 
| + // Complete run with new temporaries as necessary. | 
| + while (run < count) { | 
| + // Push back enough temporaries to ensure consecutive allocations. | 
| + temporary_register_count_ += 1; | 
| + int index = last_temporary_register().index(); | 
| + free_temporaries_.push_back(index); | 
| + run++; | 
| + } | 
| + | 
| + // Finally, reverse end of free list to give consecutive order as | 
| + // registers are popped off. | 
| + auto lhs = free_temporaries_.rbegin(); | 
| + auto rhs = lhs + count; | 
| + std::reverse(lhs, rhs); | 
| + CheckConsecutiveRun(&free_temporaries_, count); | 
| +} | 
| + | 
| + | 
| +bool BytecodeArrayBuilder::TemporaryRegisterIsLive(Register reg) const { | 
| + if (temporary_register_count_ > 0) { | 
| + for (size_t i = 0; i < free_temporaries_.size(); i++) { | 
| + if (free_temporaries_[i] == reg.index()) { | 
| + return false; | 
| + } | 
| + } | 
| + return reg <= last_temporary_register(); | 
| + } else { | 
| + return false; | 
| + } | 
| } | 
| @@ -686,10 +802,16 @@ bool BytecodeArrayBuilder::OperandIsValid(Bytecode bytecode, int operand_index, | 
| if (reg.is_function_context() || reg.is_function_closure()) { | 
| return true; | 
| } else if (reg.is_parameter()) { | 
| - int parameter_index = reg.ToParameterIndex(parameter_count()); | 
| - return parameter_index >= 0 && parameter_index < parameter_count(); | 
| + int parameter_index = reg.ToParameterIndex(parameter_count_); | 
| + return parameter_index >= 0 && parameter_index < parameter_count_; | 
| + } else if (reg.index() < local_register_count_) { | 
| 
 
rmcilroy
2015/10/19 12:56:21
I don't think this check is necessary given the ch
 
oth
2015/10/20 15:28:52
Yes, agree. Removed.
 
 | 
| + return true; | 
| + } else if (reg.index() < | 
| + local_register_count_ + context_register_count_) { | 
| 
 
rmcilroy
2015/10/19 12:56:22
reg.index() < fixed_register_count()
 
oth
2015/10/20 15:28:52
Done.
 
 | 
| + // context_register_count_ may be -1 if not initialized. | 
| 
 
rmcilroy
2015/10/19 12:56:21
This comment should no longer be true - if you spo
 
oth
2015/10/20 15:28:52
Acknowledged. Removed comment.
 
 | 
| + return true; | 
| } else { | 
| - return (reg.index() >= 0 && reg.index() < temporary_register_next_); | 
| + return TemporaryRegisterIsLive(reg); | 
| } | 
| } | 
| } | 
| @@ -873,20 +995,26 @@ bool BytecodeArrayBuilder::FitsInIdx16Operand(int value) { | 
| TemporaryRegisterScope::TemporaryRegisterScope(BytecodeArrayBuilder* builder) | 
| - : builder_(builder), count_(0), last_register_index_(-1) {} | 
| + : builder_(builder), allocated_(builder->zone()) {} | 
| TemporaryRegisterScope::~TemporaryRegisterScope() { | 
| - while (count_-- != 0) { | 
| - builder_->ReturnTemporaryRegister(last_register_index_--); | 
| + for (auto i = allocated_.rbegin(); i != allocated_.rend(); i++) { | 
| + builder_->ReturnTemporaryRegister(*i); | 
| } | 
| + allocated_.clear(); | 
| } | 
| Register TemporaryRegisterScope::NewRegister() { | 
| - count_++; | 
| - last_register_index_ = builder_->BorrowTemporaryRegister(); | 
| - return Register(last_register_index_); | 
| + int allocated = builder_->BorrowTemporaryRegister(); | 
| + allocated_.push_back(allocated); | 
| + return Register(allocated); | 
| +} | 
| + | 
| + | 
| +void TemporaryRegisterScope::PrepareForConsecutiveAllocations(size_t count) { | 
| 
 
rmcilroy
2015/10/19 12:56:21
As discussed offline - we should be moving towards
 
oth
2015/10/20 15:28:51
Added a TODO in the constructor for TemporaryRegis
 
 | 
| + builder_->PrepareForConsecutiveTemporaryRegisters(count); | 
| } | 
| } // namespace interpreter |