Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(71)

Unified Diff: src/interpreter/bytecode-array-builder.cc

Issue 1392933002: [Interpreter] Reduce temporary register usage in generated bytecode. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Remove dead code. Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698