Commit ef93854a authored by oth's avatar oth Committed by Commit bot

[interpreter] Move temporary register allocator into own file.

Moves the temporary register allocator out of the bytecode array
builder into TemporaryRegisterAllocator class and adds unittests.
Particular must be taken around the translation window boundary
motivating the addition of tests.

Also adds a Clear() method to IdentityMap() which is called by
the destructor. This allows classes to hold an IdentityMap if
they are zone allocated. Classes must call Clear() before the zone
is re-cycled or face v8 heap corruption.

BUG=v8:4280,v8:4675
LOG=N

Review URL: https://codereview.chromium.org/1651133002

Cr-Commit-Position: refs/heads/master@{#33686}
parent d8fd30b5
......@@ -14,11 +14,18 @@ namespace internal {
static const int kInitialIdentityMapSize = 4;
static const int kResizeFactor = 4;
IdentityMapBase::~IdentityMapBase() {
if (keys_) heap_->UnregisterStrongRoots(keys_);
IdentityMapBase::~IdentityMapBase() { Clear(); }
void IdentityMapBase::Clear() {
if (keys_) {
heap_->UnregisterStrongRoots(keys_);
keys_ = nullptr;
values_ = nullptr;
size_ = 0;
mask_ = 0;
}
}
IdentityMapBase::RawEntry IdentityMapBase::Lookup(Object* key) {
int index = LookupIndex(key);
return index >= 0 ? &values_[index] : nullptr;
......
......@@ -36,6 +36,7 @@ class IdentityMapBase {
RawEntry GetEntry(Object* key);
RawEntry FindEntry(Object* key);
void Clear();
private:
// Internal implementation should not be called directly by subclasses.
......@@ -85,6 +86,9 @@ class IdentityMap : public IdentityMapBase {
// Set the value for the given key.
void Set(Handle<Object> key, V v) { Set(*key, v); }
void Set(Object* key, V v) { *(reinterpret_cast<V*>(GetEntry(key))) = v; }
// Removes all elements from the map.
void Clear() { IdentityMapBase::Clear(); }
};
} // namespace internal
} // namespace v8
......
......@@ -64,7 +64,9 @@ class BytecodeArrayBuilder::PreviousBytecodeHelper BASE_EMBEDDED {
DISALLOW_COPY_AND_ASSIGN(PreviousBytecodeHelper);
};
BytecodeArrayBuilder::BytecodeArrayBuilder(Isolate* isolate, Zone* zone)
BytecodeArrayBuilder::BytecodeArrayBuilder(Isolate* isolate, Zone* zone,
int parameter_count,
int context_count, int locals_count)
: isolate_(isolate),
zone_(zone),
bytecodes_(zone),
......@@ -75,32 +77,17 @@ BytecodeArrayBuilder::BytecodeArrayBuilder(Isolate* isolate, Zone* zone)
last_bytecode_start_(~0),
exit_seen_in_block_(false),
unbound_jumps_(0),
parameter_count_(-1),
local_register_count_(-1),
context_register_count_(-1),
temporary_register_count_(0),
free_temporaries_(zone),
register_translator_(this) {}
BytecodeArrayBuilder::~BytecodeArrayBuilder() { DCHECK_EQ(0, unbound_jumps_); }
void BytecodeArrayBuilder::set_locals_count(int number_of_locals) {
local_register_count_ = number_of_locals;
DCHECK_LE(context_register_count_, 0);
}
void BytecodeArrayBuilder::set_parameter_count(int number_of_parameters) {
parameter_count_ = number_of_parameters;
}
void BytecodeArrayBuilder::set_context_count(int number_of_contexts) {
context_register_count_ = number_of_contexts;
parameter_count_(parameter_count),
local_register_count_(locals_count),
context_register_count_(context_count),
temporary_allocator_(zone, fixed_register_count()),
register_translator_(this) {
DCHECK_GE(parameter_count_, 0);
DCHECK_GE(context_register_count_, 0);
DCHECK_GE(local_register_count_, 0);
}
BytecodeArrayBuilder::~BytecodeArrayBuilder() { DCHECK_EQ(0, unbound_jumps_); }
Register BytecodeArrayBuilder::first_context_register() const {
DCHECK_GT(context_register_count_, 0);
......@@ -114,18 +101,6 @@ 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());
......@@ -137,12 +112,6 @@ bool BytecodeArrayBuilder::RegisterIsParameterOrLocal(Register reg) const {
}
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);
EnsureReturn();
......@@ -1208,148 +1177,10 @@ size_t BytecodeArrayBuilder::GetConstantPoolEntry(Handle<Object> object) {
return constant_array_builder()->Insert(object);
}
void BytecodeArrayBuilder::ForgeTemporaryRegister() {
temporary_register_count_++;
}
int BytecodeArrayBuilder::BorrowTemporaryRegister() {
if (free_temporaries_.empty()) {
ForgeTemporaryRegister();
return last_temporary_register().index();
} else {
auto pos = free_temporaries_.begin();
int retval = *pos;
free_temporaries_.erase(pos);
return retval;
}
}
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()) {
ForgeTemporaryRegister();
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(free_temporaries_.find(reg_index) == free_temporaries_.end());
free_temporaries_.insert(reg_index);
}
int BytecodeArrayBuilder::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) {
ForgeTemporaryRegister();
free_temporaries_.insert(last_temporary_register().index());
}
// 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<int>(run_length);
if (*run_end != expected) {
start = run_end;
run_length = 0;
}
Register reg_start(*start);
Register reg_expected(expected);
if (RegisterTranslator::DistanceToTranslationWindow(reg_start) > 0 &&
RegisterTranslator::DistanceToTranslationWindow(reg_expected) <= 0) {
// Run straddles the lower edge of the translation window. Registers
// after the start of this boundary are displaced by the register
// translator to provide a hole for translation. Runs either side
// of the boundary are fine.
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;
}
// Pad temporaries if extended run would cross translation boundary.
Register reg_first(*start);
Register reg_last(*start + static_cast<int>(count) - 1);
DCHECK_GT(RegisterTranslator::DistanceToTranslationWindow(reg_first),
RegisterTranslator::DistanceToTranslationWindow(reg_last));
while (RegisterTranslator::DistanceToTranslationWindow(reg_first) > 0 &&
RegisterTranslator::DistanceToTranslationWindow(reg_last) <= 0) {
ForgeTemporaryRegister();
free_temporaries_.insert(last_temporary_register().index());
start = --free_temporaries_.end();
reg_first = Register(*start);
reg_last = Register(*start + static_cast<int>(count) - 1);
run_length = 0;
}
// Ensure enough registers for run.
while (run_length++ < count) {
ForgeTemporaryRegister();
free_temporaries_.insert(last_temporary_register().index());
}
int run_start =
last_temporary_register().index() - static_cast<int>(count) + 1;
DCHECK(RegisterTranslator::DistanceToTranslationWindow(Register(run_start)) <=
0 ||
RegisterTranslator::DistanceToTranslationWindow(
Register(run_start + static_cast<int>(count) - 1)) > 0);
return run_start;
}
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;
}
return temporary_register_allocator()->RegisterIsLive(reg);
}
bool BytecodeArrayBuilder::OperandIsValid(Bytecode bytecode, int operand_index,
uint32_t operand_value) const {
OperandType operand_type = Bytecodes::GetOperandType(bytecode, operand_index);
......
......@@ -6,6 +6,7 @@
#define V8_INTERPRETER_BYTECODE_ARRAY_BUILDER_H_
#include "src/ast/ast.h"
#include "src/interpreter/bytecode-register-allocator.h"
#include "src/interpreter/bytecodes.h"
#include "src/interpreter/constant-array-builder.h"
#include "src/interpreter/handler-table-builder.h"
......@@ -26,29 +27,27 @@ class Register;
// when rest parameters implementation has settled down.
enum class CreateArgumentsType { kMappedArguments, kUnmappedArguments };
class BytecodeArrayBuilder final : private RegisterMover {
class BytecodeArrayBuilder final : public ZoneObject, private RegisterMover {
public:
BytecodeArrayBuilder(Isolate* isolate, Zone* zone);
BytecodeArrayBuilder(Isolate* isolate, Zone* zone, int parameter_count,
int context_count, int locals_count);
~BytecodeArrayBuilder();
Handle<BytecodeArray> ToBytecodeArray();
// Set the number of parameters expected by function.
void set_parameter_count(int number_of_params);
// Get the number of parameters expected by function.
int parameter_count() const {
DCHECK_GE(parameter_count_, 0);
return parameter_count_;
}
// Set the number of locals required for bytecode array.
void set_locals_count(int number_of_locals);
// Get the number of locals required for bytecode array.
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);
// Get number of contexts required for bytecode array.
int context_count() const {
DCHECK_GE(context_register_count_, 0);
return context_register_count_;
......@@ -62,7 +61,11 @@ class BytecodeArrayBuilder final : private RegisterMover {
// Returns the number of fixed and temporary registers.
int fixed_and_temporary_register_count() const {
return fixed_register_count() + temporary_register_count_;
return fixed_register_count() + temporary_register_count();
}
int temporary_register_count() const {
return temporary_register_allocator()->allocation_count();
}
// Returns the number of registers used for translating wide
......@@ -78,8 +81,8 @@ class BytecodeArrayBuilder final : private RegisterMover {
// local.
bool RegisterIsParameterOrLocal(Register reg) const;
// Return true if the register |reg| represents a temporary register.
bool RegisterIsTemporary(Register reg) const;
// Returns true if the register |reg| is a live temporary register.
bool TemporaryRegisterIsLive(Register reg) const;
// Constant loads to accumulator.
BytecodeArrayBuilder& LoadLiteral(v8::internal::Smi* value);
......@@ -251,6 +254,12 @@ class BytecodeArrayBuilder final : private RegisterMover {
// Accessors
Zone* zone() const { return zone_; }
TemporaryRegisterAllocator* temporary_register_allocator() {
return &temporary_allocator_;
}
const TemporaryRegisterAllocator* temporary_register_allocator() const {
return &temporary_allocator_;
}
private:
class PreviousBytecodeHelper;
......@@ -318,18 +327,6 @@ class BytecodeArrayBuilder final : private RegisterMover {
bool NeedToBooleanCast();
bool IsRegisterInAccumulator(Register reg);
// Temporary register management.
void ForgeTemporaryRegister();
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);
......@@ -360,8 +357,7 @@ class BytecodeArrayBuilder final : private RegisterMover {
int parameter_count_;
int local_register_count_;
int context_register_count_;
int temporary_register_count_;
ZoneSet<int> free_temporaries_;
TemporaryRegisterAllocator temporary_allocator_;
RegisterTranslator register_translator_;
DISALLOW_COPY_AND_ASSIGN(BytecodeArrayBuilder);
......
......@@ -89,15 +89,27 @@ Register BytecodeArrayIterator::GetRegisterOperand(int operand_index) const {
Bytecodes::GetOperandType(current_bytecode(), operand_index);
DCHECK(Bytecodes::IsRegisterOperandType(operand_type));
uint32_t operand = GetRawOperand(operand_index, operand_type);
Register reg;
switch (Bytecodes::GetOperandSize(current_bytecode(), operand_index)) {
case OperandSize::kByte:
return Register::FromOperand(static_cast<uint8_t>(operand));
reg = Register::FromOperand(static_cast<uint8_t>(operand));
break;
case OperandSize::kShort:
return Register::FromWideOperand(static_cast<uint16_t>(operand));
reg = Register::FromWideOperand(static_cast<uint16_t>(operand));
break;
case OperandSize::kNone:
UNREACHABLE();
reg = Register::invalid_value();
break;
}
return Register();
DCHECK_GE(reg.index(),
Register::FromParameterIndex(0, bytecode_array()->parameter_count())
.index());
DCHECK(reg.index() < bytecode_array()->register_count() ||
(reg.index() == 0 &&
Bytecodes::IsMaybeRegisterOperandType(
Bytecodes::GetOperandType(current_bytecode(), operand_index))));
return reg;
}
int BytecodeArrayIterator::GetRegisterOperandRange(int operand_index) const {
......@@ -121,8 +133,7 @@ int BytecodeArrayIterator::GetRegisterOperandRange(int operand_index) const {
// reciever.
OperandType next_operand_type =
Bytecodes::GetOperandType(current_bytecode(), operand_index + 1);
if (next_operand_type == OperandType::kRegCount8 ||
next_operand_type == OperandType::kRegCount16) {
if (Bytecodes::IsRegisterCountOperandType(next_operand_type)) {
return GetCountOperand(operand_index + 1);
}
}
......
......@@ -373,7 +373,8 @@ class BytecodeGenerator::RegisterAllocationScope {
explicit RegisterAllocationScope(BytecodeGenerator* generator)
: generator_(generator),
outer_(generator->register_allocator()),
allocator_(builder()) {
allocator_(builder()->zone(),
builder()->temporary_register_allocator()) {
generator_->set_register_allocator(this);
}
......@@ -395,11 +396,11 @@ class BytecodeGenerator::RegisterAllocationScope {
// walk the full context chain and compute the list of consecutive
// reservations in the innerscopes.
UNIMPLEMENTED();
return Register(-1);
return Register::invalid_value();
}
}
void PrepareForConsecutiveAllocations(size_t count) {
void PrepareForConsecutiveAllocations(int count) {
allocator_.PrepareForConsecutiveAllocations(count);
}
......@@ -520,7 +521,7 @@ class BytecodeGenerator::RegisterResultScope final
virtual void SetResultInRegister(Register reg) {
DCHECK(builder()->RegisterIsParameterOrLocal(reg) ||
(builder()->RegisterIsTemporary(reg) &&
(builder()->TemporaryRegisterIsLive(reg) &&
!allocator()->RegisterIsAllocatedInThisScope(reg)));
result_register_ = reg;
set_result_identified();
......@@ -532,11 +533,10 @@ class BytecodeGenerator::RegisterResultScope final
Register result_register_;
};
BytecodeGenerator::BytecodeGenerator(Isolate* isolate, Zone* zone)
: isolate_(isolate),
zone_(zone),
builder_(isolate, zone),
builder_(nullptr),
info_(nullptr),
scope_(nullptr),
globals_(0, zone),
......@@ -552,16 +552,17 @@ Handle<BytecodeArray> BytecodeGenerator::MakeBytecode(CompilationInfo* info) {
set_info(info);
set_scope(info->scope());
// Initialize bytecode array builder.
set_builder(new (zone()) BytecodeArrayBuilder(
isolate(), zone(), info->num_parameters_including_this(),
scope()->MaxNestedContextChainLength(), scope()->num_stack_slots()));
// Initialize the incoming context.
ContextScope incoming_context(this, scope(), false);
// Initialize control scope.
ControlScopeForTopLevel control(this);
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.
......@@ -575,7 +576,7 @@ Handle<BytecodeArray> BytecodeGenerator::MakeBytecode(CompilationInfo* info) {
set_scope(nullptr);
set_info(nullptr);
return builder_.ToBytecodeArray();
return builder()->ToBytecodeArray();
}
......
......@@ -105,7 +105,8 @@ class BytecodeGenerator final : public AstVisitor {
void RecordStoreToRegister(Register reg);
Register LoadFromAliasedRegister(Register reg);
inline BytecodeArrayBuilder* builder() { return &builder_; }
inline void set_builder(BytecodeArrayBuilder* builder) { builder_ = builder; }
inline BytecodeArrayBuilder* builder() const { return builder_; }
inline Isolate* isolate() const { return isolate_; }
inline Zone* zone() const { return zone_; }
......@@ -142,7 +143,7 @@ class BytecodeGenerator final : public AstVisitor {
Isolate* isolate_;
Zone* zone_;
BytecodeArrayBuilder builder_;
BytecodeArrayBuilder* builder_;
CompilationInfo* info_;
Scope* scope_;
ZoneVector<Handle<Object>> globals_;
......
......@@ -10,17 +10,173 @@ namespace v8 {
namespace internal {
namespace interpreter {
TemporaryRegisterAllocator::TemporaryRegisterAllocator(Zone* zone,
int allocation_base)
: free_temporaries_(zone),
allocation_base_(allocation_base),
allocation_count_(0) {}
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);
}
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<int>::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<int>(run_length);
if (*run_end != expected) {
start = run_end;
run_length = 0;
}
Register reg_start(*start);
Register reg_expected(expected);
if (RegisterTranslator::DistanceToTranslationWindow(reg_start) > 0 &&
RegisterTranslator::DistanceToTranslationWindow(reg_expected) <= 0) {
// Run straddles the lower edge of the translation window. Registers
// after the start of this boundary are displaced by the register
// translator to provide a hole for translation. Runs either side
// of the boundary are fine.
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<int>(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<int>(count) - 1);
DCHECK_GT(RegisterTranslator::DistanceToTranslationWindow(reg_first),
RegisterTranslator::DistanceToTranslationWindow(reg_last));
while (RegisterTranslator::DistanceToTranslationWindow(reg_first) > 0 &&
RegisterTranslator::DistanceToTranslationWindow(reg_last) <= 0) {
auto pos_insert_pair =
free_temporaries_.insert(AllocateTemporaryRegister());
reg_first = Register(*pos_insert_pair.first);
reg_last = Register(reg_first.index() + static_cast<int>(count) - 1);
run_length = 0;
}
// Ensure enough registers for run.
while (run_length++ < count) {
free_temporaries_.insert(AllocateTemporaryRegister());
}
int run_start =
last_temporary_register().index() - static_cast<int>(count) + 1;
DCHECK(RegisterTranslator::DistanceToTranslationWindow(Register(run_start)) <=
0 ||
RegisterTranslator::DistanceToTranslationWindow(
Register(run_start + static_cast<int>(count) - 1)) > 0);
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);
}
BytecodeRegisterAllocator::BytecodeRegisterAllocator(
BytecodeArrayBuilder* builder)
: builder_(builder),
allocated_(builder->zone()),
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++) {
builder_->ReturnTemporaryRegister(*i);
base_allocator()->ReturnTemporaryRegister(*i);
}
allocated_.clear();
}
......@@ -29,9 +185,9 @@ BytecodeRegisterAllocator::~BytecodeRegisterAllocator() {
Register BytecodeRegisterAllocator::NewRegister() {
int allocated = -1;
if (next_consecutive_count_ <= 0) {
allocated = builder_->BorrowTemporaryRegister();
allocated = base_allocator()->BorrowTemporaryRegister();
} else {
allocated = builder_->BorrowTemporaryRegisterNotInRange(
allocated = base_allocator()->BorrowTemporaryRegisterNotInRange(
next_consecutive_register_,
next_consecutive_register_ + next_consecutive_count_ - 1);
}
......@@ -52,7 +208,7 @@ bool BytecodeRegisterAllocator::RegisterIsAllocatedInThisScope(
void BytecodeRegisterAllocator::PrepareForConsecutiveAllocations(size_t count) {
if (static_cast<int>(count) > next_consecutive_count_) {
next_consecutive_register_ =
builder_->PrepareForConsecutiveTemporaryRegisters(count);
base_allocator()->PrepareForConsecutiveTemporaryRegisters(count);
next_consecutive_count_ = static_cast<int>(count);
}
}
......@@ -61,7 +217,8 @@ void BytecodeRegisterAllocator::PrepareForConsecutiveAllocations(size_t count) {
Register BytecodeRegisterAllocator::NextConsecutiveRegister() {
DCHECK_GE(next_consecutive_register_, 0);
DCHECK_GT(next_consecutive_count_, 0);
builder_->BorrowConsecutiveTemporaryRegister(next_consecutive_register_);
base_allocator()->BorrowConsecutiveTemporaryRegister(
next_consecutive_register_);
allocated_.push_back(next_consecutive_register_);
next_consecutive_count_--;
return Register(next_consecutive_register_++);
......
......@@ -5,6 +5,7 @@
#ifndef V8_INTERPRETER_BYTECODE_REGISTER_ALLOCATOR_H_
#define V8_INTERPRETER_BYTECODE_REGISTER_ALLOCATOR_H_
#include "src/interpreter/bytecodes.h"
#include "src/zone-containers.h"
namespace v8 {
......@@ -14,11 +15,62 @@ namespace interpreter {
class BytecodeArrayBuilder;
class Register;
class TemporaryRegisterAllocator final {
public:
TemporaryRegisterAllocator(Zone* zone, int start_index);
// Borrow a temporary register.
int BorrowTemporaryRegister();
// Borrow a temporary register from the register range outside of
// |start_index| to |end_index|.
int BorrowTemporaryRegisterNotInRange(int start_index, int end_index);
// Return a temporary register when no longer used.
void ReturnTemporaryRegister(int reg_index);
// Ensure a run of consecutive registers is available. Each register in
// the range should be borrowed with BorrowConsecutiveTemporaryRegister().
// Returns the start index of the run.
int PrepareForConsecutiveTemporaryRegisters(size_t count);
// Borrow a register from a range prepared with
// PrepareForConsecutiveTemporaryRegisters().
void BorrowConsecutiveTemporaryRegister(int reg_index);
// Returns true if |reg| is a temporary register and is currently
// borrowed.
bool RegisterIsLive(Register reg) const;
// Returns the first register in the range of temporary registers.
Register first_temporary_register() const;
// Returns the last register in the range of temporary registers.
Register last_temporary_register() const;
// Returns the start index of temporary register allocations.
int allocation_base() const { return allocation_base_; }
// Returns the number of temporary register allocations made.
int allocation_count() const { return allocation_count_; }
private:
// Allocate a temporary register.
int AllocateTemporaryRegister();
ZoneSet<int> free_temporaries_;
int allocation_base_;
int allocation_count_;
DISALLOW_COPY_AND_ASSIGN(TemporaryRegisterAllocator);
};
// A class than allows the instantiator to allocate temporary registers that are
// cleaned up when scope is closed.
class BytecodeRegisterAllocator {
class BytecodeRegisterAllocator final {
public:
explicit BytecodeRegisterAllocator(BytecodeArrayBuilder* builder);
explicit BytecodeRegisterAllocator(Zone* zone,
TemporaryRegisterAllocator* allocator);
~BytecodeRegisterAllocator();
Register NewRegister();
......@@ -30,10 +82,9 @@ class BytecodeRegisterAllocator {
bool HasConsecutiveAllocations() const { return next_consecutive_count_ > 0; }
private:
void* operator new(size_t size);
void operator delete(void* p);
TemporaryRegisterAllocator* base_allocator() const { return base_allocator_; }
BytecodeArrayBuilder* builder_;
TemporaryRegisterAllocator* base_allocator_;
ZoneVector<int> allocated_;
int next_consecutive_register_;
int next_consecutive_count_;
......
......@@ -262,6 +262,16 @@ bool Bytecodes::IsJumpOrReturn(Bytecode bytecode) {
return bytecode == Bytecode::kReturn || IsJump(bytecode);
}
bool Bytecodes::IsMaybeRegisterOperandType(OperandType operand_type) {
return (operand_type == OperandType::kMaybeReg8 ||
operand_type == OperandType::kMaybeReg16);
}
bool Bytecodes::IsRegisterCountOperandType(OperandType operand_type) {
return (operand_type == OperandType::kRegCount8 ||
operand_type == OperandType::kRegCount16);
}
// static
bool Bytecodes::IsRegisterOperandType(OperandType operand_type) {
switch (operand_type) {
......
......@@ -318,6 +318,9 @@ class Register {
static int MaxRegisterIndex();
static int MaxRegisterIndexForByteOperand();
// Returns an invalid register.
static Register invalid_value() { return Register(); }
// Returns the register for the function's closure object.
static Register function_closure();
bool is_function_closure() const;
......@@ -453,6 +456,14 @@ class Bytecodes {
// Returns true if the bytecode is a conditional jump, a jump, or a return.
static bool IsJumpOrReturn(Bytecode bytecode);
// Returns true if |operand_type| is a maybe register operand
// (kMaybeReg8/kMaybeReg16).
static bool IsMaybeRegisterOperandType(OperandType operand_type);
// Returns true if |operand_type| is a register count operand
// (kRegCount8/kRegCount16).
static bool IsRegisterCountOperandType(OperandType operand_type);
// Returns true if |operand_type| is any type of register operand.
static bool IsRegisterOperandType(OperandType operand_type);
......
......@@ -85,19 +85,19 @@ Handle<Object> ConstantArrayBuilder::At(size_t index) const {
}
}
Handle<FixedArray> ConstantArrayBuilder::ToFixedArray() const {
Handle<FixedArray> ConstantArrayBuilder::ToFixedArray() {
Handle<FixedArray> fixed_array = isolate_->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)));
}
constants_map()->Clear();
return fixed_array;
}
size_t ConstantArrayBuilder::Insert(Handle<Object> object) {
index_t* entry = constants_map_.Find(object);
index_t* entry = constants_map()->Find(object);
return (entry == nullptr) ? AllocateEntry(object) : *entry;
}
......@@ -106,7 +106,7 @@ ConstantArrayBuilder::index_t ConstantArrayBuilder::AllocateEntry(
Handle<Object> object) {
DCHECK(!object->IsOddball());
size_t index;
index_t* entry = constants_map_.Get(object);
index_t* entry = constants_map()->Get(object);
if (idx8_slice_.available() > 0) {
index = idx8_slice_.Allocate(object);
} else {
......@@ -136,7 +136,7 @@ size_t ConstantArrayBuilder::CommitReservedEntry(OperandSize operand_size,
Handle<Object> object) {
DiscardReservedEntry(operand_size);
size_t index;
index_t* entry = constants_map_.Find(object);
index_t* entry = constants_map()->Find(object);
if (nullptr == entry) {
index = AllocateEntry(object);
} else {
......
......@@ -16,8 +16,11 @@ class Isolate;
namespace interpreter {
// A helper class for constructing constant arrays for the interpreter.
class ConstantArrayBuilder final : public ZoneObject {
// A helper class for constructing constant arrays for the
// interpreter. Each instance of this class is intended to be used to
// generate exactly one FixedArray of constants via the ToFixedArray
// method.
class ConstantArrayBuilder final BASE_EMBEDDED {
public:
// Capacity of the 8-bit operand slice.
static const size_t kLowCapacity = 1u << kBitsPerByte;
......@@ -31,7 +34,7 @@ class ConstantArrayBuilder final : public ZoneObject {
ConstantArrayBuilder(Isolate* isolate, Zone* zone);
// Generate a fixed array of constants based on inserted objects.
Handle<FixedArray> ToFixedArray() const;
Handle<FixedArray> ToFixedArray();
// Returns the object in the constant pool array that at index
// |index|.
......@@ -83,6 +86,8 @@ class ConstantArrayBuilder final : public ZoneObject {
DISALLOW_COPY_AND_ASSIGN(ConstantArraySlice);
};
IdentityMap<index_t>* constants_map() { return &constants_map_; }
Isolate* isolate_;
ConstantArraySlice idx8_slice_;
ConstantArraySlice idx16_slice_;
......
......@@ -2559,8 +2559,6 @@ TEST(BytecodeGraphBuilderForOf) {
TEST(JumpWithConstantsAndWideConstants) {
HandleAndZoneScope scope;
auto isolate = scope.main_isolate();
const int kStep = 19;
int start = 7;
for (int constants = start; constants < 256 + 3 * kStep; constants += kStep) {
......@@ -2585,6 +2583,9 @@ TEST(JumpWithConstantsAndWideConstants) {
script_os << "}\n";
script_os << kFunctionName << "(0);\n";
std::string script(script_os.str());
HandleAndZoneScope scope;
auto isolate = scope.main_isolate();
auto factory = isolate->factory();
auto zone = scope.main_zone();
for (int a = 0; a < 3; a++) {
......
......@@ -7276,9 +7276,6 @@ TEST(DeleteLookupSlotInEval) {
}
TEST(WideRegisters) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
// Prepare prologue that creates frame for lots of registers.
std::ostringstream os;
for (size_t i = 0; i < 157; ++i) {
......@@ -7493,6 +7490,9 @@ TEST(WideRegisters) {
B(Return), //
}}};
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
for (size_t i = 0; i < arraysize(snippets); ++i) {
std::string body = prologue + snippets[i].code_snippet;
Handle<BytecodeArray> bytecode_array =
......
This diff is collapsed.
......@@ -21,11 +21,8 @@ class BytecodeArrayBuilderTest : public TestWithIsolateAndZone {
TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
BytecodeArrayBuilder builder(isolate(), zone());
BytecodeArrayBuilder builder(isolate(), zone(), 0, 1, 131);
builder.set_locals_count(131);
builder.set_context_count(1);
builder.set_parameter_count(0);
CHECK_EQ(builder.locals_count(), 131);
CHECK_EQ(builder.context_count(), 1);
CHECK_EQ(builder.fixed_register_count(), 132);
......@@ -312,12 +309,9 @@ TEST_F(BytecodeArrayBuilderTest, FrameSizesLookGood) {
for (int locals = 0; locals < 5; locals++) {
for (int contexts = 0; contexts < 4; contexts++) {
for (int temps = 0; temps < 3; temps++) {
BytecodeArrayBuilder builder(isolate(), zone());
builder.set_parameter_count(0);
builder.set_locals_count(locals);
builder.set_context_count(contexts);
BytecodeRegisterAllocator temporaries(&builder);
BytecodeArrayBuilder builder(isolate(), zone(), 0, contexts, locals);
BytecodeRegisterAllocator temporaries(
zone(), builder.temporary_register_allocator());
for (int i = 0; i < temps; i++) {
builder.StoreAccumulatorInRegister(temporaries.NewRegister());
}
......@@ -348,11 +342,7 @@ TEST_F(BytecodeArrayBuilderTest, RegisterValues) {
TEST_F(BytecodeArrayBuilderTest, Parameters) {
BytecodeArrayBuilder builder(isolate(), zone());
builder.set_parameter_count(10);
builder.set_locals_count(0);
builder.set_context_count(0);
BytecodeArrayBuilder builder(isolate(), zone(), 10, 0, 0);
Register param0(builder.Parameter(0));
Register param9(builder.Parameter(9));
CHECK_EQ(param9.index() - param0.index(), 9);
......@@ -360,12 +350,9 @@ TEST_F(BytecodeArrayBuilderTest, Parameters) {
TEST_F(BytecodeArrayBuilderTest, RegisterType) {
BytecodeArrayBuilder builder(isolate(), zone());
builder.set_parameter_count(10);
builder.set_locals_count(3);
builder.set_context_count(0);
BytecodeRegisterAllocator register_allocator(&builder);
BytecodeArrayBuilder builder(isolate(), zone(), 10, 0, 3);
BytecodeRegisterAllocator register_allocator(
zone(), builder.temporary_register_allocator());
Register temp0 = register_allocator.NewRegister();
Register param0(builder.Parameter(0));
Register param9(builder.Parameter(9));
......@@ -386,11 +373,7 @@ TEST_F(BytecodeArrayBuilderTest, RegisterType) {
TEST_F(BytecodeArrayBuilderTest, Constants) {
BytecodeArrayBuilder builder(isolate(), zone());
builder.set_parameter_count(0);
builder.set_locals_count(0);
builder.set_context_count(0);
BytecodeArrayBuilder builder(isolate(), zone(), 0, 0, 0);
Factory* factory = isolate()->factory();
Handle<HeapObject> heap_num_1 = factory->NewHeapNumber(3.14);
Handle<HeapObject> heap_num_2 = factory->NewHeapNumber(5.2);
......@@ -412,11 +395,7 @@ TEST_F(BytecodeArrayBuilderTest, Constants) {
TEST_F(BytecodeArrayBuilderTest, ForwardJumps) {
static const int kFarJumpDistance = 256;
BytecodeArrayBuilder builder(isolate(), zone());
builder.set_parameter_count(0);
builder.set_locals_count(1);
builder.set_context_count(0);
BytecodeArrayBuilder builder(isolate(), zone(), 0, 0, 1);
Register reg(0);
BytecodeLabel far0, far1, far2, far3, far4;
BytecodeLabel near0, near1, near2, near3, near4;
......@@ -528,10 +507,7 @@ TEST_F(BytecodeArrayBuilderTest, ForwardJumps) {
TEST_F(BytecodeArrayBuilderTest, BackwardJumps) {
BytecodeArrayBuilder builder(isolate(), zone());
builder.set_parameter_count(0);
builder.set_locals_count(1);
builder.set_context_count(0);
BytecodeArrayBuilder builder(isolate(), zone(), 0, 0, 1);
Register reg(0);
BytecodeLabel label0, label1, label2, label3, label4;
......@@ -624,10 +600,7 @@ TEST_F(BytecodeArrayBuilderTest, BackwardJumps) {
TEST_F(BytecodeArrayBuilderTest, LabelReuse) {
BytecodeArrayBuilder builder(isolate(), zone());
builder.set_parameter_count(0);
builder.set_locals_count(0);
builder.set_context_count(0);
BytecodeArrayBuilder builder(isolate(), zone(), 0, 0, 0);
// Labels can only have 1 forward reference, but
// can be referred to mulitple times once bound.
......@@ -655,16 +628,11 @@ TEST_F(BytecodeArrayBuilderTest, LabelReuse) {
TEST_F(BytecodeArrayBuilderTest, LabelAddressReuse) {
static const int kRepeats = 3;
BytecodeArrayBuilder builder(isolate(), zone());
builder.set_parameter_count(0);
builder.set_locals_count(0);
builder.set_context_count(0);
BytecodeArrayBuilder builder(isolate(), zone(), 0, 0, 0);
for (int i = 0; i < kRepeats; i++) {
BytecodeLabel label;
builder.Jump(&label).Bind(&label).Jump(&label).Jump(&label);
}
builder.Return();
Handle<BytecodeArray> array = builder.ToBytecodeArray();
......
......@@ -22,11 +22,7 @@ class BytecodeArrayIteratorTest : public TestWithIsolateAndZone {
TEST_F(BytecodeArrayIteratorTest, IteratesBytecodeArray) {
// Use a builder to create an array with containing multiple bytecodes
// with 0, 1 and 2 operands.
BytecodeArrayBuilder builder(isolate(), zone());
builder.set_parameter_count(3);
builder.set_locals_count(2);
builder.set_context_count(0);
BytecodeArrayBuilder builder(isolate(), zone(), 3, 2, 0);
Factory* factory = isolate()->factory();
Handle<HeapObject> heap_num_0 = factory->NewHeapNumber(2.718);
Handle<HeapObject> heap_num_1 = factory->NewHeapNumber(2147483647);
......
......@@ -12,51 +12,219 @@ namespace v8 {
namespace internal {
namespace interpreter {
class TemporaryRegisterAllocatorTest : public TestWithIsolateAndZone {
public:
TemporaryRegisterAllocatorTest() : allocator_(zone(), 0) {}
~TemporaryRegisterAllocatorTest() override {}
TemporaryRegisterAllocator* allocator() { return &allocator_; }
private:
TemporaryRegisterAllocator allocator_;
};
TEST_F(TemporaryRegisterAllocatorTest, FirstAllocation) {
CHECK_EQ(allocator()->allocation_count(), 0);
int reg0_index = allocator()->BorrowTemporaryRegister();
CHECK_EQ(reg0_index, 0);
CHECK_EQ(allocator()->allocation_count(), 1);
CHECK(allocator()->RegisterIsLive(Register(reg0_index)));
allocator()->ReturnTemporaryRegister(reg0_index);
CHECK(!allocator()->RegisterIsLive(Register(reg0_index)));
CHECK_EQ(allocator()->allocation_count(), 1);
CHECK(allocator()->first_temporary_register() == Register(0));
CHECK(allocator()->last_temporary_register() == Register(0));
}
TEST_F(TemporaryRegisterAllocatorTest, SimpleAllocations) {
for (int i = 0; i < 13; i++) {
int reg_index = allocator()->BorrowTemporaryRegister();
CHECK_EQ(reg_index, i);
CHECK_EQ(allocator()->allocation_count(), i + 1);
}
for (int i = 0; i < 13; i++) {
CHECK(allocator()->RegisterIsLive(Register(i)));
allocator()->ReturnTemporaryRegister(i);
CHECK(!allocator()->RegisterIsLive(Register(i)));
int reg_index = allocator()->BorrowTemporaryRegister();
CHECK_EQ(reg_index, i);
CHECK_EQ(allocator()->allocation_count(), 13);
}
for (int i = 0; i < 13; i++) {
CHECK(allocator()->RegisterIsLive(Register(i)));
allocator()->ReturnTemporaryRegister(i);
CHECK(!allocator()->RegisterIsLive(Register(i)));
}
}
TEST_F(TemporaryRegisterAllocatorTest, SimpleRangeAllocation) {
static const int kRunLength = 7;
int start = allocator()->PrepareForConsecutiveTemporaryRegisters(kRunLength);
CHECK(!allocator()->RegisterIsLive(Register(start)));
for (int i = 0; i < kRunLength; i++) {
CHECK(!allocator()->RegisterIsLive(Register(start + i)));
allocator()->BorrowConsecutiveTemporaryRegister(start + i);
CHECK(allocator()->RegisterIsLive(Register(start + i)));
}
}
TEST_F(TemporaryRegisterAllocatorTest, RangeAllocationAbuttingFree) {
static const int kFreeCount = 3;
static const int kRunLength = 6;
for (int i = 0; i < kFreeCount; i++) {
int to_free = allocator()->BorrowTemporaryRegister();
CHECK_EQ(to_free, i);
}
for (int i = 0; i < kFreeCount; i++) {
allocator()->ReturnTemporaryRegister(i);
}
int start = allocator()->PrepareForConsecutiveTemporaryRegisters(kRunLength);
CHECK(!allocator()->RegisterIsLive(Register(start)));
for (int i = 0; i < kRunLength; i++) {
CHECK(!allocator()->RegisterIsLive(Register(start + i)));
allocator()->BorrowConsecutiveTemporaryRegister(start + i);
CHECK(allocator()->RegisterIsLive(Register(start + i)));
}
}
TEST_F(TemporaryRegisterAllocatorTest, RangeAllocationAbuttingHole) {
static const int kPreAllocatedCount = 7;
static const int kPreAllocatedFreeCount = 6;
static const int kRunLength = 8;
for (int i = 0; i < kPreAllocatedCount; i++) {
int to_free = allocator()->BorrowTemporaryRegister();
CHECK_EQ(to_free, i);
}
for (int i = 0; i < kPreAllocatedFreeCount; i++) {
allocator()->ReturnTemporaryRegister(i);
}
int start = allocator()->PrepareForConsecutiveTemporaryRegisters(kRunLength);
CHECK(!allocator()->RegisterIsLive(Register(start)));
CHECK_EQ(start, kPreAllocatedCount);
for (int i = 0; i < kRunLength; i++) {
CHECK(!allocator()->RegisterIsLive(Register(start + i)));
allocator()->BorrowConsecutiveTemporaryRegister(start + i);
CHECK(allocator()->RegisterIsLive(Register(start + i)));
}
}
TEST_F(TemporaryRegisterAllocatorTest, RangeAllocationAvailableInTemporaries) {
static const int kNotRunLength = 13;
static const int kRunLength = 8;
// Allocate big batch
for (int i = 0; i < kNotRunLength * 2 + kRunLength; i++) {
int allocated = allocator()->BorrowTemporaryRegister();
CHECK_EQ(allocated, i);
}
// Free every other register either side of target.
for (int i = 0; i < kNotRunLength; i++) {
if ((i & 2) == 1) {
allocator()->ReturnTemporaryRegister(i);
allocator()->ReturnTemporaryRegister(kNotRunLength + kRunLength + i);
}
}
// Free all registers for target.
for (int i = kNotRunLength; i < kNotRunLength + kRunLength; i++) {
allocator()->ReturnTemporaryRegister(i);
}
int start = allocator()->PrepareForConsecutiveTemporaryRegisters(kRunLength);
CHECK_EQ(start, kNotRunLength);
for (int i = 0; i < kRunLength; i++) {
CHECK(!allocator()->RegisterIsLive(Register(start + i)));
allocator()->BorrowConsecutiveTemporaryRegister(start + i);
CHECK(allocator()->RegisterIsLive(Register(start + i)));
}
}
TEST_F(TemporaryRegisterAllocatorTest, RangeAvoidsTranslationBoundary) {
int boundary = RegisterTranslator::DistanceToTranslationWindow(Register(0));
int limit = boundary + 64;
for (int run_length = 2; run_length < 32; run_length += 7) {
ZoneVector<int> run_starts(zone());
for (int start = 0; start < limit; start += run_length) {
int run_start =
allocator()->PrepareForConsecutiveTemporaryRegisters(run_length);
run_starts.push_back(run_start);
for (int i = 0; i < run_length; i++) {
allocator()->BorrowConsecutiveTemporaryRegister(run_start + i);
}
CHECK(run_start >= boundary || run_start + run_length <= boundary);
}
for (size_t batch = 0; batch < run_starts.size(); batch++) {
for (int i = run_starts[batch]; i < run_starts[batch] + run_length; i++) {
allocator()->ReturnTemporaryRegister(i);
}
}
}
}
TEST_F(TemporaryRegisterAllocatorTest, NotInRange) {
for (int i = 0; i < 10; i++) {
int reg = allocator()->BorrowTemporaryRegisterNotInRange(2, 5);
CHECK(reg == i || (reg > 2 && reg == i + 4));
}
for (int i = 0; i < 10; i++) {
if (i < 2) {
allocator()->ReturnTemporaryRegister(i);
} else {
allocator()->ReturnTemporaryRegister(i + 4);
}
}
int reg0 = allocator()->BorrowTemporaryRegisterNotInRange(0, 3);
CHECK_EQ(reg0, 4);
int reg1 = allocator()->BorrowTemporaryRegisterNotInRange(3, 10);
CHECK_EQ(reg1, 2);
int reg2 = allocator()->BorrowTemporaryRegisterNotInRange(2, 6);
CHECK_EQ(reg2, 1);
allocator()->ReturnTemporaryRegister(reg0);
allocator()->ReturnTemporaryRegister(reg1);
allocator()->ReturnTemporaryRegister(reg2);
}
class BytecodeRegisterAllocatorTest : public TestWithIsolateAndZone {
public:
BytecodeRegisterAllocatorTest() {}
~BytecodeRegisterAllocatorTest() override {}
};
TEST_F(BytecodeRegisterAllocatorTest, TemporariesRecycled) {
BytecodeArrayBuilder builder(isolate(), zone());
builder.set_parameter_count(0);
builder.set_locals_count(0);
builder.set_context_count(0);
BytecodeArrayBuilder builder(isolate(), zone(), 0, 0, 0);
int first;
{
BytecodeRegisterAllocator temporaries(&builder);
first = temporaries.NewRegister().index();
temporaries.NewRegister();
temporaries.NewRegister();
temporaries.NewRegister();
BytecodeRegisterAllocator allocator(zone(),
builder.temporary_register_allocator());
first = allocator.NewRegister().index();
allocator.NewRegister();
allocator.NewRegister();
allocator.NewRegister();
}
int second;
{
BytecodeRegisterAllocator temporaries(&builder);
second = temporaries.NewRegister().index();
BytecodeRegisterAllocator allocator(zone(),
builder.temporary_register_allocator());
second = allocator.NewRegister().index();
}
CHECK_EQ(first, second);
}
TEST_F(BytecodeRegisterAllocatorTest, ConsecutiveRegisters) {
BytecodeArrayBuilder builder(isolate(), zone());
builder.set_parameter_count(0);
builder.set_locals_count(0);
builder.set_context_count(0);
BytecodeRegisterAllocator temporaries(&builder);
temporaries.PrepareForConsecutiveAllocations(4);
Register reg0 = temporaries.NextConsecutiveRegister();
Register other = temporaries.NewRegister();
Register reg1 = temporaries.NextConsecutiveRegister();
Register reg2 = temporaries.NextConsecutiveRegister();
Register reg3 = temporaries.NextConsecutiveRegister();
BytecodeArrayBuilder builder(isolate(), zone(), 0, 0, 0);
BytecodeRegisterAllocator allocator(zone(),
builder.temporary_register_allocator());
allocator.PrepareForConsecutiveAllocations(4);
Register reg0 = allocator.NextConsecutiveRegister();
Register other = allocator.NewRegister();
Register reg1 = allocator.NextConsecutiveRegister();
Register reg2 = allocator.NextConsecutiveRegister();
Register reg3 = allocator.NextConsecutiveRegister();
USE(other);
CHECK(Register::AreContiguous(reg0, reg1, reg2, reg3));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment