| Index: src/lithium-allocator.h
|
| ===================================================================
|
| --- src/lithium-allocator.h (revision 6800)
|
| +++ src/lithium-allocator.h (working copy)
|
| @@ -31,6 +31,7 @@
|
| #include "v8.h"
|
|
|
| #include "data-flow.h"
|
| +#include "lithium.h"
|
| #include "zone.h"
|
|
|
| namespace v8 {
|
| @@ -48,9 +49,10 @@
|
|
|
| class LArgument;
|
| class LChunk;
|
| +class LOperand;
|
| +class LUnallocated;
|
| class LConstantOperand;
|
| class LGap;
|
| -class LInstruction;
|
| class LParallelMove;
|
| class LPointerMap;
|
| class LStackSlot;
|
| @@ -150,373 +152,57 @@
|
| };
|
|
|
|
|
| -class LOperand: public ZoneObject {
|
| - public:
|
| - enum Kind {
|
| - INVALID,
|
| - UNALLOCATED,
|
| - CONSTANT_OPERAND,
|
| - STACK_SLOT,
|
| - DOUBLE_STACK_SLOT,
|
| - REGISTER,
|
| - DOUBLE_REGISTER,
|
| - ARGUMENT
|
| - };
|
| +// A register-allocator view of a Lithium instruction. It contains the id of
|
| +// the output operand and a list of input operand uses.
|
|
|
| - LOperand() : value_(KindField::encode(INVALID)) { }
|
| +class LInstruction;
|
| +class LEnvironment;
|
|
|
| - Kind kind() const { return KindField::decode(value_); }
|
| - int index() const { return static_cast<int>(value_) >> kKindFieldWidth; }
|
| - bool IsConstantOperand() const { return kind() == CONSTANT_OPERAND; }
|
| - bool IsStackSlot() const { return kind() == STACK_SLOT; }
|
| - bool IsDoubleStackSlot() const { return kind() == DOUBLE_STACK_SLOT; }
|
| - bool IsRegister() const { return kind() == REGISTER; }
|
| - bool IsDoubleRegister() const { return kind() == DOUBLE_REGISTER; }
|
| - bool IsArgument() const { return kind() == ARGUMENT; }
|
| - bool IsUnallocated() const { return kind() == UNALLOCATED; }
|
| - bool Equals(LOperand* other) const { return value_ == other->value_; }
|
| - int VirtualRegister();
|
| -
|
| - void PrintTo(StringStream* stream);
|
| - void ConvertTo(Kind kind, int index) {
|
| - value_ = KindField::encode(kind);
|
| - value_ |= index << kKindFieldWidth;
|
| - ASSERT(this->index() == index);
|
| - }
|
| -
|
| - protected:
|
| - static const int kKindFieldWidth = 3;
|
| - class KindField : public BitField<Kind, 0, kKindFieldWidth> { };
|
| -
|
| - LOperand(Kind kind, int index) { ConvertTo(kind, index); }
|
| -
|
| - unsigned value_;
|
| -};
|
| -
|
| -
|
| -class LUnallocated: public LOperand {
|
| +// Iterator for non-null temp operands.
|
| +class TempIterator BASE_EMBEDDED {
|
| public:
|
| - enum Policy {
|
| - NONE,
|
| - ANY,
|
| - FIXED_REGISTER,
|
| - FIXED_DOUBLE_REGISTER,
|
| - FIXED_SLOT,
|
| - MUST_HAVE_REGISTER,
|
| - WRITABLE_REGISTER,
|
| - SAME_AS_FIRST_INPUT,
|
| - IGNORE
|
| - };
|
| + inline explicit TempIterator(LInstruction* instr);
|
| + inline bool HasNext();
|
| + inline LOperand* Next();
|
| + inline void Advance();
|
|
|
| - // Lifetime of operand inside the instruction.
|
| - enum Lifetime {
|
| - // USED_AT_START operand is guaranteed to be live only at
|
| - // instruction start. Register allocator is free to assign the same register
|
| - // to some other operand used inside instruction (i.e. temporary or
|
| - // output).
|
| - USED_AT_START,
|
| -
|
| - // USED_AT_END operand is treated as live until the end of
|
| - // instruction. This means that register allocator will not reuse it's
|
| - // register for any other operand inside instruction.
|
| - USED_AT_END
|
| - };
|
| -
|
| - explicit LUnallocated(Policy policy) : LOperand(UNALLOCATED, 0) {
|
| - Initialize(policy, 0, USED_AT_END);
|
| - }
|
| -
|
| - LUnallocated(Policy policy, int fixed_index) : LOperand(UNALLOCATED, 0) {
|
| - Initialize(policy, fixed_index, USED_AT_END);
|
| - }
|
| -
|
| - LUnallocated(Policy policy, Lifetime lifetime) : LOperand(UNALLOCATED, 0) {
|
| - Initialize(policy, 0, lifetime);
|
| - }
|
| -
|
| - // The superclass has a KindField. Some policies have a signed fixed
|
| - // index in the upper bits.
|
| - static const int kPolicyWidth = 4;
|
| - static const int kLifetimeWidth = 1;
|
| - static const int kVirtualRegisterWidth = 17;
|
| -
|
| - static const int kPolicyShift = kKindFieldWidth;
|
| - static const int kLifetimeShift = kPolicyShift + kPolicyWidth;
|
| - static const int kVirtualRegisterShift = kLifetimeShift + kLifetimeWidth;
|
| - static const int kFixedIndexShift =
|
| - kVirtualRegisterShift + kVirtualRegisterWidth;
|
| -
|
| - class PolicyField : public BitField<Policy, kPolicyShift, kPolicyWidth> { };
|
| -
|
| - class LifetimeField
|
| - : public BitField<Lifetime, kLifetimeShift, kLifetimeWidth> {
|
| - };
|
| -
|
| - class VirtualRegisterField
|
| - : public BitField<unsigned,
|
| - kVirtualRegisterShift,
|
| - kVirtualRegisterWidth> {
|
| - };
|
| -
|
| - static const int kMaxVirtualRegisters = 1 << (kVirtualRegisterWidth + 1);
|
| - static const int kMaxFixedIndices = 128;
|
| -
|
| - bool HasIgnorePolicy() const { return policy() == IGNORE; }
|
| - bool HasNoPolicy() const { return policy() == NONE; }
|
| - bool HasAnyPolicy() const {
|
| - return policy() == ANY;
|
| - }
|
| - bool HasFixedPolicy() const {
|
| - return policy() == FIXED_REGISTER ||
|
| - policy() == FIXED_DOUBLE_REGISTER ||
|
| - policy() == FIXED_SLOT;
|
| - }
|
| - bool HasRegisterPolicy() const {
|
| - return policy() == WRITABLE_REGISTER || policy() == MUST_HAVE_REGISTER;
|
| - }
|
| - bool HasSameAsInputPolicy() const {
|
| - return policy() == SAME_AS_FIRST_INPUT;
|
| - }
|
| - Policy policy() const { return PolicyField::decode(value_); }
|
| - void set_policy(Policy policy) {
|
| - value_ &= ~PolicyField::mask();
|
| - value_ |= PolicyField::encode(policy);
|
| - }
|
| - int fixed_index() const {
|
| - return static_cast<int>(value_) >> kFixedIndexShift;
|
| - }
|
| -
|
| - unsigned virtual_register() const {
|
| - return VirtualRegisterField::decode(value_);
|
| - }
|
| -
|
| - void set_virtual_register(unsigned id) {
|
| - value_ &= ~VirtualRegisterField::mask();
|
| - value_ |= VirtualRegisterField::encode(id);
|
| - }
|
| -
|
| - LUnallocated* CopyUnconstrained() {
|
| - LUnallocated* result = new LUnallocated(ANY);
|
| - result->set_virtual_register(virtual_register());
|
| - return result;
|
| - }
|
| -
|
| - static LUnallocated* cast(LOperand* op) {
|
| - ASSERT(op->IsUnallocated());
|
| - return reinterpret_cast<LUnallocated*>(op);
|
| - }
|
| -
|
| - bool IsUsedAtStart() {
|
| - return LifetimeField::decode(value_) == USED_AT_START;
|
| - }
|
| -
|
| private:
|
| - void Initialize(Policy policy, int fixed_index, Lifetime lifetime) {
|
| - value_ |= PolicyField::encode(policy);
|
| - value_ |= LifetimeField::encode(lifetime);
|
| - value_ |= fixed_index << kFixedIndexShift;
|
| - ASSERT(this->fixed_index() == fixed_index);
|
| - }
|
| + inline int AdvanceToNext(int start);
|
| + LInstruction* instr_;
|
| + int limit_;
|
| + int current_;
|
| };
|
|
|
|
|
| -class LMoveOperands BASE_EMBEDDED {
|
| +// Iterator for non-constant input operands.
|
| +class InputIterator BASE_EMBEDDED {
|
| public:
|
| - LMoveOperands(LOperand* from, LOperand* to) : from_(from), to_(to) { }
|
| + inline explicit InputIterator(LInstruction* instr);
|
| + inline bool HasNext();
|
| + inline LOperand* Next();
|
| + inline void Advance();
|
|
|
| - LOperand* from() const { return from_; }
|
| - LOperand* to() const { return to_; }
|
| - bool IsRedundant() const {
|
| - return IsEliminated() || from_->Equals(to_) || IsIgnored();
|
| - }
|
| - bool IsEliminated() const { return from_ == NULL; }
|
| - bool IsIgnored() const {
|
| - if (to_ != NULL && to_->IsUnallocated() &&
|
| - LUnallocated::cast(to_)->HasIgnorePolicy()) {
|
| - return true;
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - void Eliminate() { from_ = to_ = NULL; }
|
| -
|
| private:
|
| - LOperand* from_;
|
| - LOperand* to_;
|
| + inline int AdvanceToNext(int start);
|
| + LInstruction* instr_;
|
| + int limit_;
|
| + int current_;
|
| };
|
|
|
|
|
| -class LConstantOperand: public LOperand {
|
| +class UseIterator BASE_EMBEDDED {
|
| public:
|
| - static LConstantOperand* Create(int index) {
|
| - ASSERT(index >= 0);
|
| - if (index < kNumCachedOperands) return &cache[index];
|
| - return new LConstantOperand(index);
|
| - }
|
| + inline explicit UseIterator(LInstruction* instr);
|
| + inline bool HasNext();
|
| + inline LOperand* Next();
|
| + inline void Advance();
|
|
|
| - static LConstantOperand* cast(LOperand* op) {
|
| - ASSERT(op->IsConstantOperand());
|
| - return reinterpret_cast<LConstantOperand*>(op);
|
| - }
|
| -
|
| - static void SetupCache();
|
| -
|
| private:
|
| - static const int kNumCachedOperands = 128;
|
| - static LConstantOperand cache[];
|
| -
|
| - LConstantOperand() : LOperand() { }
|
| - explicit LConstantOperand(int index) : LOperand(CONSTANT_OPERAND, index) { }
|
| + InputIterator input_iterator_;
|
| + DeepIterator env_iterator_;
|
| };
|
|
|
|
|
| -class LArgument: public LOperand {
|
| - public:
|
| - explicit LArgument(int index) : LOperand(ARGUMENT, index) { }
|
| -
|
| - static LArgument* cast(LOperand* op) {
|
| - ASSERT(op->IsArgument());
|
| - return reinterpret_cast<LArgument*>(op);
|
| - }
|
| -};
|
| -
|
| -
|
| -class LStackSlot: public LOperand {
|
| - public:
|
| - static LStackSlot* Create(int index) {
|
| - ASSERT(index >= 0);
|
| - if (index < kNumCachedOperands) return &cache[index];
|
| - return new LStackSlot(index);
|
| - }
|
| -
|
| - static LStackSlot* cast(LOperand* op) {
|
| - ASSERT(op->IsStackSlot());
|
| - return reinterpret_cast<LStackSlot*>(op);
|
| - }
|
| -
|
| - static void SetupCache();
|
| -
|
| - private:
|
| - static const int kNumCachedOperands = 128;
|
| - static LStackSlot cache[];
|
| -
|
| - LStackSlot() : LOperand() { }
|
| - explicit LStackSlot(int index) : LOperand(STACK_SLOT, index) { }
|
| -};
|
| -
|
| -
|
| -class LDoubleStackSlot: public LOperand {
|
| - public:
|
| - static LDoubleStackSlot* Create(int index) {
|
| - ASSERT(index >= 0);
|
| - if (index < kNumCachedOperands) return &cache[index];
|
| - return new LDoubleStackSlot(index);
|
| - }
|
| -
|
| - static LDoubleStackSlot* cast(LOperand* op) {
|
| - ASSERT(op->IsStackSlot());
|
| - return reinterpret_cast<LDoubleStackSlot*>(op);
|
| - }
|
| -
|
| - static void SetupCache();
|
| -
|
| - private:
|
| - static const int kNumCachedOperands = 128;
|
| - static LDoubleStackSlot cache[];
|
| -
|
| - LDoubleStackSlot() : LOperand() { }
|
| - explicit LDoubleStackSlot(int index) : LOperand(DOUBLE_STACK_SLOT, index) { }
|
| -};
|
| -
|
| -
|
| -class LRegister: public LOperand {
|
| - public:
|
| - static LRegister* Create(int index) {
|
| - ASSERT(index >= 0);
|
| - if (index < kNumCachedOperands) return &cache[index];
|
| - return new LRegister(index);
|
| - }
|
| -
|
| - static LRegister* cast(LOperand* op) {
|
| - ASSERT(op->IsRegister());
|
| - return reinterpret_cast<LRegister*>(op);
|
| - }
|
| -
|
| - static void SetupCache();
|
| -
|
| - private:
|
| - static const int kNumCachedOperands = 16;
|
| - static LRegister cache[];
|
| -
|
| - LRegister() : LOperand() { }
|
| - explicit LRegister(int index) : LOperand(REGISTER, index) { }
|
| -};
|
| -
|
| -
|
| -class LDoubleRegister: public LOperand {
|
| - public:
|
| - static LDoubleRegister* Create(int index) {
|
| - ASSERT(index >= 0);
|
| - if (index < kNumCachedOperands) return &cache[index];
|
| - return new LDoubleRegister(index);
|
| - }
|
| -
|
| - static LDoubleRegister* cast(LOperand* op) {
|
| - ASSERT(op->IsDoubleRegister());
|
| - return reinterpret_cast<LDoubleRegister*>(op);
|
| - }
|
| -
|
| - static void SetupCache();
|
| -
|
| - private:
|
| - static const int kNumCachedOperands = 16;
|
| - static LDoubleRegister cache[];
|
| -
|
| - LDoubleRegister() : LOperand() { }
|
| - explicit LDoubleRegister(int index) : LOperand(DOUBLE_REGISTER, index) { }
|
| -};
|
| -
|
| -
|
| -// A register-allocator view of a Lithium instruction. It contains the id of
|
| -// the output operand and a list of input operand uses.
|
| -class InstructionSummary: public ZoneObject {
|
| - public:
|
| - InstructionSummary()
|
| - : output_operand_(NULL), input_count_(0), operands_(4), is_call_(false) {}
|
| -
|
| - // Output operands.
|
| - LOperand* Output() const { return output_operand_; }
|
| - void SetOutput(LOperand* output) {
|
| - ASSERT(output_operand_ == NULL);
|
| - output_operand_ = output;
|
| - }
|
| -
|
| - // Input operands.
|
| - int InputCount() const { return input_count_; }
|
| - LOperand* InputAt(int i) const {
|
| - ASSERT(i < input_count_);
|
| - return operands_[i];
|
| - }
|
| - void AddInput(LOperand* input) {
|
| - operands_.InsertAt(input_count_, input);
|
| - input_count_++;
|
| - }
|
| -
|
| - // Temporary operands.
|
| - int TempCount() const { return operands_.length() - input_count_; }
|
| - LOperand* TempAt(int i) const { return operands_[i + input_count_]; }
|
| - void AddTemp(LOperand* temp) { operands_.Add(temp); }
|
| -
|
| - void MarkAsCall() { is_call_ = true; }
|
| - bool IsCall() const { return is_call_; }
|
| -
|
| - private:
|
| - LOperand* output_operand_;
|
| - int input_count_;
|
| - ZoneList<LOperand*> operands_;
|
| - bool is_call_;
|
| -};
|
| -
|
| // Representation of the non-empty interval [start,end[.
|
| class UseInterval: public ZoneObject {
|
| public:
|
| @@ -559,27 +245,14 @@
|
| // Representation of a use position.
|
| class UsePosition: public ZoneObject {
|
| public:
|
| - UsePosition(LifetimePosition pos, LOperand* operand)
|
| - : operand_(operand),
|
| - hint_(NULL),
|
| - pos_(pos),
|
| - next_(NULL),
|
| - requires_reg_(false),
|
| - register_beneficial_(true) {
|
| - if (operand_ != NULL && operand_->IsUnallocated()) {
|
| - LUnallocated* unalloc = LUnallocated::cast(operand_);
|
| - requires_reg_ = unalloc->HasRegisterPolicy();
|
| - register_beneficial_ = !unalloc->HasAnyPolicy();
|
| - }
|
| - ASSERT(pos_.IsValid());
|
| - }
|
| + UsePosition(LifetimePosition pos, LOperand* operand);
|
|
|
| LOperand* operand() const { return operand_; }
|
| bool HasOperand() const { return operand_ != NULL; }
|
|
|
| LOperand* hint() const { return hint_; }
|
| void set_hint(LOperand* hint) { hint_ = hint; }
|
| - bool HasHint() const { return hint_ != NULL && !hint_->IsUnallocated(); }
|
| + bool HasHint() const;
|
| bool RequiresRegister() const;
|
| bool RegisterIsBeneficial() const;
|
|
|
| @@ -605,21 +278,7 @@
|
| public:
|
| static const int kInvalidAssignment = 0x7fffffff;
|
|
|
| - explicit LiveRange(int id)
|
| - : id_(id),
|
| - spilled_(false),
|
| - assigned_register_(kInvalidAssignment),
|
| - assigned_register_kind_(NONE),
|
| - last_interval_(NULL),
|
| - first_interval_(NULL),
|
| - first_pos_(NULL),
|
| - parent_(NULL),
|
| - next_(NULL),
|
| - current_interval_(NULL),
|
| - last_processed_use_(NULL),
|
| - spill_start_index_(kMaxInt) {
|
| - spill_operand_ = new LUnallocated(LUnallocated::IGNORE);
|
| - }
|
| + explicit LiveRange(int id);
|
|
|
| UseInterval* first_interval() const { return first_interval_; }
|
| UsePosition* first_pos() const { return first_pos_; }
|
| @@ -634,19 +293,8 @@
|
| LOperand* CreateAssignedOperand();
|
| int assigned_register() const { return assigned_register_; }
|
| int spill_start_index() const { return spill_start_index_; }
|
| - void set_assigned_register(int reg, RegisterKind register_kind) {
|
| - ASSERT(!HasRegisterAssigned() && !IsSpilled());
|
| - assigned_register_ = reg;
|
| - assigned_register_kind_ = register_kind;
|
| - ConvertOperands();
|
| - }
|
| - void MakeSpilled() {
|
| - ASSERT(!IsSpilled());
|
| - ASSERT(TopLevel()->HasAllocatedSpillOperand());
|
| - spilled_ = true;
|
| - assigned_register_ = kInvalidAssignment;
|
| - ConvertOperands();
|
| - }
|
| + void set_assigned_register(int reg, RegisterKind register_kind);
|
| + void MakeSpilled();
|
|
|
| // Returns use position in this live range that follows both start
|
| // and last processed use position.
|
| @@ -695,16 +343,9 @@
|
| return last_interval_->end();
|
| }
|
|
|
| - bool HasAllocatedSpillOperand() const {
|
| - return spill_operand_ != NULL && !spill_operand_->IsUnallocated();
|
| - }
|
| + bool HasAllocatedSpillOperand() const;
|
| LOperand* GetSpillOperand() const { return spill_operand_; }
|
| - void SetSpillOperand(LOperand* operand) {
|
| - ASSERT(!operand->IsUnallocated());
|
| - ASSERT(spill_operand_ != NULL);
|
| - ASSERT(spill_operand_->IsUnallocated());
|
| - spill_operand_->ConvertTo(operand->kind(), operand->index());
|
| - }
|
| + void SetSpillOperand(LOperand* operand);
|
|
|
| void SetSpillStartIndex(int start) {
|
| spill_start_index_ = Min(start, spill_start_index_);
|
| @@ -715,7 +356,6 @@
|
| bool Covers(LifetimePosition position);
|
| LifetimePosition FirstIntersection(LiveRange* other);
|
|
|
| -
|
| // Add a new interval or a new use position to this live range.
|
| void EnsureInterval(LifetimePosition start, LifetimePosition end);
|
| void AddUseInterval(LifetimePosition start, LifetimePosition end);
|
| @@ -792,9 +432,6 @@
|
| public:
|
| explicit LAllocator(int first_virtual_register, HGraph* graph)
|
| : chunk_(NULL),
|
| - summaries_(0),
|
| - next_summary_(NULL),
|
| - summary_stack_(2),
|
| live_in_sets_(0),
|
| live_ranges_(16),
|
| fixed_live_ranges_(8),
|
| @@ -821,24 +458,12 @@
|
| // Record a temporary operand.
|
| void RecordTemporary(LUnallocated* operand);
|
|
|
| - // Marks the current instruction as a call.
|
| - void MarkAsCall();
|
| -
|
| // Checks whether the value of a given virtual register is tagged.
|
| bool HasTaggedValue(int virtual_register) const;
|
|
|
| // Returns the register kind required by the given virtual register.
|
| RegisterKind RequiredRegisterKind(int virtual_register) const;
|
|
|
| - // Begin a new instruction.
|
| - void BeginInstruction();
|
| -
|
| - // Summarize the current instruction.
|
| - void SummarizeInstruction(int index);
|
| -
|
| - // Summarize the current instruction.
|
| - void OmitInstruction();
|
| -
|
| // Control max function size.
|
| static int max_initial_value_ids();
|
|
|
| @@ -886,8 +511,8 @@
|
| void AddInitialIntervals(HBasicBlock* block, BitVector* live_out);
|
| void ProcessInstructions(HBasicBlock* block, BitVector* live);
|
| void MeetRegisterConstraints(HBasicBlock* block);
|
| - void MeetConstraintsBetween(InstructionSummary* first,
|
| - InstructionSummary* second,
|
| + void MeetConstraintsBetween(LInstruction* first,
|
| + LInstruction* second,
|
| int gap_index);
|
| void ResolvePhis(HBasicBlock* block);
|
|
|
| @@ -952,7 +577,6 @@
|
|
|
| void Spill(LiveRange* range);
|
| bool IsBlockBoundary(LifetimePosition pos);
|
| - void AddGapMove(int pos, LiveRange* prev, LiveRange* next);
|
|
|
| // Helper methods for resolving control flow.
|
| void ResolveControlFlow(LiveRange* range,
|
| @@ -966,12 +590,6 @@
|
| // Return the block which contains give lifetime position.
|
| HBasicBlock* GetBlock(LifetimePosition pos);
|
|
|
| - // Current active summary.
|
| - InstructionSummary* current_summary() const { return summary_stack_.last(); }
|
| -
|
| - // Get summary for given instruction index.
|
| - InstructionSummary* GetSummary(int index) const { return summaries_[index]; }
|
| -
|
| // Helper methods for the fixed registers.
|
| int RegisterCount() const;
|
| static int FixedLiveRangeID(int index) { return -index - 1; }
|
| @@ -980,16 +598,18 @@
|
| LiveRange* FixedDoubleLiveRangeFor(int index);
|
| LiveRange* LiveRangeFor(int index);
|
| HPhi* LookupPhi(LOperand* operand) const;
|
| - LGap* GetLastGap(HBasicBlock* block) const;
|
| + LGap* GetLastGap(HBasicBlock* block);
|
|
|
| const char* RegisterName(int allocation_index);
|
|
|
| - LChunk* chunk_;
|
| - ZoneList<InstructionSummary*> summaries_;
|
| - InstructionSummary* next_summary_;
|
| + inline bool IsGapAt(int index);
|
|
|
| - ZoneList<InstructionSummary*> summary_stack_;
|
| + inline LInstruction* InstructionAt(int index);
|
|
|
| + inline LGap* GapAt(int index);
|
| +
|
| + LChunk* chunk_;
|
| +
|
| // During liveness analysis keep a mapping from block id to live_in sets
|
| // for blocks already analyzed.
|
| ZoneList<BitVector*> live_in_sets_;
|
|
|