Chromium Code Reviews| Index: src/arm/virtual-frame-arm.h |
| =================================================================== |
| --- src/arm/virtual-frame-arm.h (revision 4341) |
| +++ src/arm/virtual-frame-arm.h (working copy) |
| @@ -45,16 +45,71 @@ |
| class VirtualFrame : public ZoneObject { |
| public: |
| + class RegisterAllocationScope; |
| // A utility class to introduce a scope where the virtual frame is |
| // expected to remain spilled. The constructor spills the code |
| - // generator's current frame, but no attempt is made to require it |
| - // to stay spilled. It is intended as documentation while the code |
| - // generator is being transformed. |
| + // generator's current frame, and keeps it spilled. |
| class SpilledScope BASE_EMBEDDED { |
| public: |
| - SpilledScope() {} |
| + inline SpilledScope(VirtualFrame* frame) |
|
Kasper Lund
2010/04/07 08:09:50
Why put inline here? It needs to be explicit thoug
Erik Corry
2010/04/07 12:49:17
Done.
|
| + : old_is_spilled_(is_spilled_) { |
| + if (frame != NULL) { |
| + if (!is_spilled_) { |
| + frame->SpillAll(); |
| + } else { |
| + frame->AssertIsSpilled(); |
| + } |
| + } |
| + is_spilled_ = true; |
| + } |
| + inline ~SpilledScope() { |
|
Kasper Lund
2010/04/07 08:09:50
Why put inline here?
Erik Corry
2010/04/07 12:49:17
Done.
|
| + is_spilled_ = old_is_spilled_; |
| + } |
| + static inline bool is_spilled() { return is_spilled_; } |
|
Kasper Lund
2010/04/07 08:09:50
Why put inline here?
Erik Corry
2010/04/07 12:49:17
Done.
|
| + |
| + private: |
| + static bool is_spilled_; |
| + int old_is_spilled_; |
| + |
| + SpilledScope() { } |
| + |
| + friend class RegisterAllocationScope; |
| }; |
| + class RegisterAllocationScope BASE_EMBEDDED { |
| + public: |
| + // A utility class to introduce a scope where the virtual frame |
| + // is not spilled, ie where register allocation occurs. Eventually |
|
Søren Thygesen Gjesse
2010/04/07 07:53:37
Dot after ie?
Erik Corry
2010/04/07 12:49:17
Done.
|
| + // when RegisterAllocationScope is ubiquitous it can be removed |
| + // along with the (by then unused) SpilledScope class. |
| + explicit RegisterAllocationScope(CodeGenerator* cgen) |
| + : cgen_(cgen), |
| + old_is_spilled_(SpilledScope::is_spilled_) { |
| + SpilledScope::is_spilled_ = false; |
| + if (old_is_spilled_) { |
| + VirtualFrame* frame = cgen->frame(); |
| + if (frame != NULL) { |
| + frame->AssertIsSpilled(); |
| + } |
| + } |
| + } |
| + ~RegisterAllocationScope() { |
| + SpilledScope::is_spilled_ = old_is_spilled_; |
| + if (old_is_spilled_) { |
| + VirtualFrame* frame = cgen_->frame(); |
| + if (frame != NULL) { |
| + frame->SpillAll(); |
| + } |
| + } |
| + } |
| + |
| + private: |
| + CodeGenerator* cgen_; |
| + bool old_is_spilled_; |
| + |
| + RegisterAllocationScope() { } |
| + }; |
| + |
| // An illegal index into the virtual frame. |
| static const int kIllegalIndex = -1; |
| @@ -75,27 +130,38 @@ |
| return element_count() - expression_base_index(); |
| } |
| - int register_location(int num) { |
| - ASSERT(num >= 0 && num < RegisterAllocator::kNumRegisters); |
| - return register_locations_[num]; |
| - } |
| - |
| - int register_location(Register reg) { |
| - return register_locations_[RegisterAllocator::ToNumber(reg)]; |
| - } |
| - |
| - void set_register_location(Register reg, int index) { |
| - register_locations_[RegisterAllocator::ToNumber(reg)] = index; |
| - } |
| - |
| bool is_used(int num) { |
| - ASSERT(num >= 0 && num < RegisterAllocator::kNumRegisters); |
| - return register_locations_[num] != kIllegalIndex; |
| + switch (num) { |
| + case 0: { // r0. |
| + return kR0InUse[top_of_stack_state_]; |
| + } |
| + case 1: { // r1. |
| + return kR1InUse[top_of_stack_state_]; |
| + } |
| + case 2: |
| + case 3: |
| + case 4: |
| + case 5: |
| + case 6: { // r2 to r6. |
| + ASSERT(num - kFirstAllocatedRegister < kNumberOfAllocatedRegisters); |
| + ASSERT(num >= kFirstAllocatedRegister); |
| + if ((register_allocation_map_ & |
| + (1 << (num - kFirstAllocatedRegister))) == 0) { |
| + return false; |
| + } else { |
| + return true; |
| + } |
| + } |
| + default: { |
| + ASSERT(num < kFirstAllocatedRegister || |
| + num >= kFirstAllocatedRegister + kNumberOfAllocatedRegisters); |
| + return false; |
| + } |
| + } |
| } |
| bool is_used(Register reg) { |
| - return register_locations_[RegisterAllocator::ToNumber(reg)] |
| - != kIllegalIndex; |
| + return is_used(RegisterAllocator::ToNumber(reg)); |
| } |
| // Add extra in-memory elements to the top of the frame to match an actual |
| @@ -104,39 +170,55 @@ |
| void Adjust(int count); |
| // Forget elements from the top of the frame to match an actual frame (eg, |
| - // the frame after a runtime call). No code is emitted. |
| + // the frame after a runtime call). No code is emitted except to bring the |
| + // frame to a spilled state. |
| void Forget(int count) { |
| - ASSERT(count >= 0); |
| - ASSERT(stack_pointer_ == element_count() - 1); |
| - stack_pointer_ -= count; |
| - // On ARM, all elements are in memory, so there is no extra bookkeeping |
| - // (registers, copies, etc.) beyond dropping the elements. |
| + SpillAll(); |
| element_count_ -= count; |
| } |
| - // Forget count elements from the top of the frame and adjust the stack |
| - // pointer downward. This is used, for example, before merging frames at |
| - // break, continue, and return targets. |
| - void ForgetElements(int count); |
| - |
| // Spill all values from the frame to memory. |
| - inline void SpillAll(); |
| + inline void SpillAll() { |
| + switch (top_of_stack_state_) { |
| + case R0_R1_TOS: |
|
Søren Thygesen Gjesse
2010/04/07 07:53:37
Maybe it is just me, but my initial assumption was
Erik Corry
2010/04/07 12:49:17
Reversed. Lets see how that works.
|
| + masm()->push(r0); |
| + // Fall through. |
| + case R1_TOS: |
| + masm()->push(r1); |
| + top_of_stack_state_ = NO_TOS_REGISTERS; |
| + break; |
| + case R1_R0_TOS: |
| + masm()->push(r1); |
| + // Fall through. |
| + case R0_TOS: |
| + masm()->push(r0); |
| + top_of_stack_state_ = NO_TOS_REGISTERS; |
| + // Fall through. |
| + case NO_TOS_REGISTERS: |
| + break; |
| + } |
| + ASSERT(register_allocation_map_ == 0); // Not yet implemented. |
| + } |
| + inline void AssertIsSpilled() { |
|
Kasper Lund
2010/04/07 08:09:50
Remove inline?
Erik Corry
2010/04/07 12:49:17
Done.
|
| + ASSERT(top_of_stack_state_ == NO_TOS_REGISTERS); |
| + ASSERT(register_allocation_map_ == 0); |
| + } |
| + |
| + inline void AssertIsNotSpilled() { |
|
Kasper Lund
2010/04/07 08:09:50
Remove inline?
Erik Corry
2010/04/07 12:49:17
Done.
|
| + ASSERT(!SpilledScope::is_spilled()); |
| + } |
| + |
| // Spill all occurrences of a specific register from the frame. |
| void Spill(Register reg) { |
| - if (is_used(reg)) SpillElementAt(register_location(reg)); |
| + UNIMPLEMENTED(); |
| } |
| // Spill all occurrences of an arbitrary register if possible. Return the |
| // register spilled or no_reg if it was not possible to free any register |
| - // (ie, they all have frame-external references). |
| + // (ie, they all have frame-external references). Unimplemented. |
| Register SpillAnyRegister(); |
| - // Prepare this virtual frame for merging to an expected frame by |
| - // performing some state changes that do not require generating |
| - // code. It is guaranteed that no code will be generated. |
| - void PrepareMergeTo(VirtualFrame* expected); |
| - |
| // Make this virtual frame have a state identical to an expected virtual |
| // frame. As a side effect, code may be emitted to make this frame match |
| // the expected one. |
| @@ -147,10 +229,7 @@ |
| // registers. Used when the code generator's frame is switched from this |
| // one to NULL by an unconditional jump. |
| void DetachFromCodeGenerator() { |
| - RegisterAllocator* cgen_allocator = cgen()->allocator(); |
| - for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) { |
| - if (is_used(i)) cgen_allocator->Unuse(i); |
| - } |
| + AssertIsSpilled(); |
| } |
| // (Re)attach a frame to its code generator. This informs the register |
| @@ -158,10 +237,7 @@ |
| // Used when a code generator's frame is switched from NULL to this one by |
| // binding a label. |
| void AttachToCodeGenerator() { |
| - RegisterAllocator* cgen_allocator = cgen()->allocator(); |
| - for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) { |
| - if (is_used(i)) cgen_allocator->Unuse(i); |
| - } |
| + AssertIsSpilled(); |
| } |
| // Emit code for the physical JS entry and exit frame sequences. After |
| @@ -184,23 +260,17 @@ |
| void AllocateStackSlots(); |
| // The current top of the expression stack as an assembly operand. |
| - MemOperand Top() { return MemOperand(sp, 0); } |
| + MemOperand Top() { |
| + AssertIsSpilled(); |
| + return MemOperand(sp, 0); |
| + } |
| // An element of the expression stack as an assembly operand. |
| MemOperand ElementAt(int index) { |
| + AssertIsSpilled(); |
| return MemOperand(sp, index * kPointerSize); |
| } |
| - // Random-access store to a frame-top relative frame element. The result |
| - // becomes owned by the frame and is invalidated. |
| - void SetElementAt(int index, Result* value); |
| - |
| - // Set a frame element to a constant. The index is frame-top relative. |
| - void SetElementAt(int index, Handle<Object> value) { |
| - Result temp(value); |
| - SetElementAt(index, &temp); |
| - } |
| - |
| // A frame-allocated local as an assembly operand. |
| MemOperand LocalAt(int index) { |
| ASSERT(0 <= index); |
| @@ -208,13 +278,6 @@ |
| return MemOperand(fp, kLocal0Offset - index * kPointerSize); |
| } |
| - // Push the value of a local frame slot on top of the frame and invalidate |
| - // the local slot. The slot should be written to before trying to read |
| - // from it again. |
| - void TakeLocalAt(int index) { |
| - TakeFrameSlotAt(local0_index() + index); |
| - } |
| - |
| // Push the address of the receiver slot on the frame. |
| void PushReceiverSlotAddress(); |
| @@ -224,13 +287,6 @@ |
| // The context frame slot. |
| MemOperand Context() { return MemOperand(fp, kContextOffset); } |
| - // Save the value of the esi register to the context frame slot. |
| - void SaveContextRegister(); |
| - |
| - // Restore the esi register from the value of the context frame |
| - // slot. |
| - void RestoreContextRegister(); |
| - |
| // A parameter as an assembly operand. |
| MemOperand ParameterAt(int index) { |
| // Index -1 corresponds to the receiver. |
| @@ -239,19 +295,6 @@ |
| return MemOperand(fp, (1 + parameter_count() - index) * kPointerSize); |
| } |
| - // Push the value of a paramter frame slot on top of the frame and |
| - // invalidate the parameter slot. The slot should be written to before |
| - // trying to read from it again. |
| - void TakeParameterAt(int index) { |
| - TakeFrameSlotAt(param0_index() + index); |
| - } |
| - |
| - // Store the top value on the virtual frame into a parameter frame slot. |
| - // The value is left in place on top of the frame. |
| - void StoreToParameterAt(int index) { |
| - StoreToFrameSlotAt(param0_index() + index); |
| - } |
| - |
| // The receiver frame slot. |
| MemOperand Receiver() { return ParameterAt(-1); } |
| @@ -261,7 +304,7 @@ |
| // Call stub given the number of arguments it expects on (and |
| // removes from) the stack. |
| void CallStub(CodeStub* stub, int arg_count) { |
| - Forget(arg_count); |
| + if (arg_count != 0) Forget(arg_count); |
| ASSERT(cgen()->HasValidEntryRegisters()); |
| masm()->CallStub(stub); |
| } |
| @@ -296,35 +339,51 @@ |
| // Drop one element. |
| void Drop() { Drop(1); } |
| - // Pop an element from the top of the expression stack. Returns a |
| - // Result, which may be a constant or a register. |
| - Result Pop(); |
| + // Pop an element from the top of the expression stack. Discards |
| + // the result. |
| + void Pop(); |
| + // Pop an element from the top of the expression stack. The register |
| + // will be one normally used for the top of stack register allocation |
| + // so you can't hold on to it if you push on the stack. |
| + Register PopToRegister(Register but_not_to_this_one = no_reg); |
| + |
| + // Look at the top of the stack. The register returned is aliased and |
| + // must be copied to a scratch register before modification. |
| + Register Peek(); |
| + |
| // Pop and save an element from the top of the expression stack and |
| // emit a corresponding pop instruction. |
| void EmitPop(Register reg); |
| + // Takes the top two elements and puts them in r0 (top element) and r1 |
| + // (second element). |
| + void PopToR1R0(); |
| + |
| + // Takes the top element and puts it in r1. |
| + void PopToR1(); |
| + |
| + // Takes the top element and puts it in r0. |
| + void PopToR0(); |
| + |
| // Push an element on top of the expression stack and emit a |
| // corresponding push instruction. |
| void EmitPush(Register reg); |
| + void EmitPush(MemOperand operand); |
| + // Get a register which is free and which must be immediately used to |
| + // push on the top of the stack. |
| + Register GetTOSRegister(); |
| + |
| // Push multiple registers on the stack and the virtual frame |
| // Register are selected by setting bit in src_regs and |
| // are pushed in decreasing order: r15 .. r0. |
| void EmitPushMultiple(int count, int src_regs); |
| - // Push an element on the virtual frame. |
| - inline void Push(Handle<Object> value); |
| - inline void Push(Smi* value); |
| + static inline Register scratch0() { return r7; } |
|
Kasper Lund
2010/04/07 08:09:50
inline?
Erik Corry
2010/04/07 12:49:17
Done.
|
| + static inline Register scratch1() { return r8; } |
| + static inline Register scratch2() { return r9; } |
| - // Nip removes zero or more elements from immediately below the top |
| - // of the frame, leaving the previous top-of-frame value on top of |
| - // the frame. Nip(k) is equivalent to x = Pop(), Drop(k), Push(x). |
| - inline void Nip(int num_dropped); |
| - |
| - inline void SetTypeForLocalAt(int index, TypeInfo info); |
| - inline void SetTypeForParamAt(int index, TypeInfo info); |
| - |
| private: |
| static const int kLocal0Offset = JavaScriptFrameConstants::kLocal0Offset; |
| static const int kFunctionOffset = JavaScriptFrameConstants::kFunctionOffset; |
| @@ -333,17 +392,41 @@ |
| static const int kHandlerSize = StackHandlerConstants::kSize / kPointerSize; |
| static const int kPreallocatedElements = 5 + 8; // 8 expression stack slots. |
| + // 5 states for the top of stack, which can be in memory or in r0 and r1. |
| + enum TopOfStack { NO_TOS_REGISTERS, R0_TOS, R1_TOS, R0_R1_TOS, R1_R0_TOS, |
| + TOS_STATES}; |
| + static const int kMaxTOSRegisters = 2; |
| + |
|
Søren Thygesen Gjesse
2010/04/07 10:46:51
Most of the names of these constants are self-expl
Erik Corry
2010/04/07 12:49:17
Done (with a rename and a comment in the .cc file)
|
| + static const bool kR0InUse[TOS_STATES]; |
| + static const bool kR1InUse[TOS_STATES]; |
| + static const int kVirtualElements[TOS_STATES]; |
| + static const TopOfStack kPopState[TOS_STATES]; |
| + static const TopOfStack kPushState[TOS_STATES]; |
| + static const Register kTopRegister[TOS_STATES]; |
| + static const Register kBottomRegister[TOS_STATES]; |
| + |
| + // We allocate up to 5 locals in registers. |
| + static const int kNumberOfAllocatedRegisters = 5; |
| + // r2 to r6 are allocated to locals. |
| + static const int kFirstAllocatedRegister = 2; |
| + |
| + static const Register kAllocatedRegisters[kNumberOfAllocatedRegisters]; |
| + |
| + static Register AllocatedRegister(int r) { |
| + ASSERT(r >= 0 && r < kNumberOfAllocatedRegisters); |
| + return kAllocatedRegisters[r]; |
| + } |
| + |
| // The number of elements on the stack frame. |
| int element_count_; |
| + TopOfStack top_of_stack_state_:3; |
| + int register_allocation_map_:kNumberOfAllocatedRegisters; |
| // The index of the element that is at the processor's stack pointer |
| - // (the sp register). |
| - int stack_pointer_; |
| + // (the sp register). For now since everything is in memory it is given |
| + // by the number of elements on the not-very-virtual stack frame. |
| + int stack_pointer() { return element_count_ - 1; } |
| - // The index of the register frame element using each register, or |
| - // kIllegalIndex if a register is not on the frame. |
| - int register_locations_[RegisterAllocator::kNumRegisters]; |
| - |
| // The number of frame-allocated locals and parameters respectively. |
| int parameter_count() { return cgen()->scope()->num_parameters(); } |
| int local_count() { return cgen()->scope()->num_stack_slots(); } |
| @@ -380,81 +463,12 @@ |
| return (frame_pointer() - index) * kPointerSize; |
| } |
| - // Record an occurrence of a register in the virtual frame. This has the |
| - // effect of incrementing the register's external reference count and |
| - // of updating the index of the register's location in the frame. |
| - void Use(Register reg, int index) { |
| - ASSERT(!is_used(reg)); |
| - set_register_location(reg, index); |
| - cgen()->allocator()->Use(reg); |
| - } |
| - |
| - // Record that a register reference has been dropped from the frame. This |
| - // decrements the register's external reference count and invalidates the |
| - // index of the register's location in the frame. |
| - void Unuse(Register reg) { |
| - ASSERT(is_used(reg)); |
| - set_register_location(reg, kIllegalIndex); |
| - cgen()->allocator()->Unuse(reg); |
| - } |
| - |
| - // Spill the element at a particular index---write it to memory if |
| - // necessary, free any associated register, and forget its value if |
| - // constant. |
| - void SpillElementAt(int index); |
| - |
| - // Sync the element at a particular index. If it is a register or |
| - // constant that disagrees with the value on the stack, write it to memory. |
| - // Keep the element type as register or constant, and clear the dirty bit. |
| - void SyncElementAt(int index); |
| - |
| - // Sync a single unsynced element that lies beneath or at the stack pointer. |
| - void SyncElementBelowStackPointer(int index); |
| - |
| - // Sync a single unsynced element that lies just above the stack pointer. |
| - void SyncElementByPushing(int index); |
| - |
| - // Push a the value of a frame slot (typically a local or parameter) on |
| - // top of the frame and invalidate the slot. |
| - void TakeFrameSlotAt(int index); |
| - |
| - // Store the value on top of the frame to a frame slot (typically a local |
| - // or parameter). |
| - void StoreToFrameSlotAt(int index); |
| - |
| // Spill all elements in registers. Spill the top spilled_args elements |
| // on the frame. Sync all other frame elements. |
| // Then drop dropped_args elements from the virtual frame, to match |
| // the effect of an upcoming call that will drop them from the stack. |
| void PrepareForCall(int spilled_args, int dropped_args); |
| - // Move frame elements currently in registers or constants, that |
| - // should be in memory in the expected frame, to memory. |
| - void MergeMoveRegistersToMemory(VirtualFrame* expected); |
| - |
| - // Make the register-to-register moves necessary to |
| - // merge this frame with the expected frame. |
| - // Register to memory moves must already have been made, |
| - // and memory to register moves must follow this call. |
| - // This is because some new memory-to-register moves are |
| - // created in order to break cycles of register moves. |
| - // Used in the implementation of MergeTo(). |
| - void MergeMoveRegistersToRegisters(VirtualFrame* expected); |
| - |
| - // Make the memory-to-register and constant-to-register moves |
| - // needed to make this frame equal the expected frame. |
| - // Called after all register-to-memory and register-to-register |
| - // moves have been made. After this function returns, the frames |
| - // should be equal. |
| - void MergeMoveMemoryToRegisters(VirtualFrame* expected); |
| - |
| - // Invalidates a frame slot (puts an invalid frame element in it). |
| - // Copies on the frame are correctly handled, and if this slot was |
| - // the backing store of copies, the index of the new backing store |
| - // is returned. Otherwise, returns kIllegalIndex. |
| - // Register counts are correctly updated. |
| - int InvalidateFrameSlotAt(int index); |
| - |
| inline bool Equals(VirtualFrame* other); |
| friend class JumpTarget; |