// 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 { TemporaryRegisterAllocator::TemporaryRegisterAllocator(Zone* zone, int allocation_base) : free_temporaries_(zone), allocation_base_(allocation_base), allocation_count_(0), observer_(nullptr) {} Register TemporaryRegisterAllocator::first_temporary_register() const { DCHECK(allocation_count() > 0); return Register(allocation_base()); } Register TemporaryRegisterAllocator::last_temporary_register() const { DCHECK(allocation_count() > 0); return Register(allocation_base() + allocation_count() - 1); } void TemporaryRegisterAllocator::set_observer( TemporaryRegisterObserver* observer) { DCHECK(observer_ == nullptr); observer_ = observer; } int TemporaryRegisterAllocator::AllocateTemporaryRegister() { allocation_count_ += 1; return allocation_base() + allocation_count() - 1; } int TemporaryRegisterAllocator::BorrowTemporaryRegister() { if (free_temporaries_.empty()) { return AllocateTemporaryRegister(); } else { auto pos = free_temporaries_.begin(); int retval = *pos; free_temporaries_.erase(pos); return retval; } } int TemporaryRegisterAllocator::BorrowTemporaryRegisterNotInRange( int start_index, int end_index) { if (free_temporaries_.empty()) { int next_allocation = allocation_base() + allocation_count(); while (next_allocation >= start_index && next_allocation <= end_index) { free_temporaries_.insert(AllocateTemporaryRegister()); next_allocation += 1; } return AllocateTemporaryRegister(); } ZoneSet::iterator 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()) { return AllocateTemporaryRegister(); } } else { // If there is a free register < start_index index--; } int retval = *index; free_temporaries_.erase(index); return retval; } int TemporaryRegisterAllocator::PrepareForConsecutiveTemporaryRegisters( size_t count) { if (count == 0) { return -1; } // TODO(oth): replace use of set<> here for free_temporaries with a // more efficient structure. And/or partition into two searches - // one before the translation window and one after. // A run will require at least |count| free temporaries. while (free_temporaries_.size() < count) { free_temporaries_.insert(AllocateTemporaryRegister()); } // 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++) { int expected = *start + static_cast(run_length); if (*run_end != expected) { start = run_end; run_length = 0; } if (++run_length == count) { return *start; } } // Continue run if possible across existing last temporary. if (allocation_count_ > 0 && (start == free_temporaries_.end() || *start + static_cast(run_length) != last_temporary_register().index() + 1)) { run_length = 0; } // Pad temporaries if extended run would cross translation boundary. Register reg_first(*start); Register reg_last(*start + static_cast(count) - 1); // Ensure enough registers for run. while (run_length++ < count) { free_temporaries_.insert(AllocateTemporaryRegister()); } int run_start = last_temporary_register().index() - static_cast(count) + 1; return run_start; } bool TemporaryRegisterAllocator::RegisterIsLive(Register reg) const { if (allocation_count_ > 0) { DCHECK(reg >= first_temporary_register() && reg <= last_temporary_register()); return free_temporaries_.find(reg.index()) == free_temporaries_.end(); } else { return false; } } void TemporaryRegisterAllocator::BorrowConsecutiveTemporaryRegister( int reg_index) { DCHECK(free_temporaries_.find(reg_index) != free_temporaries_.end()); free_temporaries_.erase(reg_index); } void TemporaryRegisterAllocator::ReturnTemporaryRegister(int reg_index) { DCHECK(free_temporaries_.find(reg_index) == free_temporaries_.end()); free_temporaries_.insert(reg_index); if (observer_) { observer_->TemporaryRegisterFreeEvent(Register(reg_index)); } } BytecodeRegisterAllocator::BytecodeRegisterAllocator( Zone* zone, TemporaryRegisterAllocator* allocator) : base_allocator_(allocator), allocated_(zone), next_consecutive_register_(-1), next_consecutive_count_(-1) {} BytecodeRegisterAllocator::~BytecodeRegisterAllocator() { for (auto i = allocated_.rbegin(); i != allocated_.rend(); i++) { base_allocator()->ReturnTemporaryRegister(*i); } allocated_.clear(); } Register BytecodeRegisterAllocator::NewRegister() { int allocated = -1; if (next_consecutive_count_ <= 0) { allocated = base_allocator()->BorrowTemporaryRegister(); } else { allocated = base_allocator()->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(count) > next_consecutive_count_) { next_consecutive_register_ = base_allocator()->PrepareForConsecutiveTemporaryRegisters(count); next_consecutive_count_ = static_cast(count); } } Register BytecodeRegisterAllocator::NextConsecutiveRegister() { DCHECK_GE(next_consecutive_register_, 0); DCHECK_GT(next_consecutive_count_, 0); base_allocator()->BorrowConsecutiveTemporaryRegister( next_consecutive_register_); allocated_.push_back(next_consecutive_register_); next_consecutive_count_--; return Register(next_consecutive_register_++); } } // namespace interpreter } // namespace internal } // namespace v8