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 |