| OLD | NEW |
| 1 // Copyright 2010 the V8 project authors. All rights reserved. | 1 // Copyright 2010 the V8 project authors. All rights reserved. |
| 2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
| 3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
| 4 // met: | 4 // met: |
| 5 // | 5 // |
| 6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
| 7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
| 8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
| 9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
| 10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
| (...skipping 12 matching lines...) Expand all Loading... |
| 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 27 | 27 |
| 28 | 28 |
| 29 #ifndef V8_MIPS_VIRTUAL_FRAME_MIPS_H_ | 29 #ifndef V8_MIPS_VIRTUAL_FRAME_MIPS_H_ |
| 30 #define V8_MIPS_VIRTUAL_FRAME_MIPS_H_ | 30 #define V8_MIPS_VIRTUAL_FRAME_MIPS_H_ |
| 31 | 31 |
| 32 #include "register-allocator.h" | 32 #include "register-allocator.h" |
| 33 #include "scopes.h" | |
| 34 | 33 |
| 35 namespace v8 { | 34 namespace v8 { |
| 36 namespace internal { | 35 namespace internal { |
| 37 | 36 |
| 37 // This dummy class is only used to create invalid virtual frames. |
| 38 extern class InvalidVirtualFrameInitializer {}* kInvalidVirtualFrameInitializer; |
| 39 |
| 38 | 40 |
| 39 // ------------------------------------------------------------------------- | 41 // ------------------------------------------------------------------------- |
| 40 // Virtual frames | 42 // Virtual frames |
| 41 // | 43 // |
| 42 // The virtual frame is an abstraction of the physical stack frame. It | 44 // The virtual frame is an abstraction of the physical stack frame. It |
| 43 // encapsulates the parameters, frame-allocated locals, and the expression | 45 // encapsulates the parameters, frame-allocated locals, and the expression |
| 44 // stack. It supports push/pop operations on the expression stack, as well | 46 // stack. It supports push/pop operations on the expression stack, as well |
| 45 // as random access to the expression stack elements, locals, and | 47 // as random access to the expression stack elements, locals, and |
| 46 // parameters. | 48 // parameters. |
| 47 | 49 |
| 48 class VirtualFrame : public ZoneObject { | 50 class VirtualFrame : public ZoneObject { |
| 49 public: | 51 public: |
| 52 class RegisterAllocationScope; |
| 50 // A utility class to introduce a scope where the virtual frame is | 53 // A utility class to introduce a scope where the virtual frame is |
| 51 // expected to remain spilled. The constructor spills the code | 54 // expected to remain spilled. The constructor spills the code |
| 52 // generator's current frame, but no attempt is made to require it | 55 // generator's current frame, and keeps it spilled. |
| 53 // to stay spilled. It is intended as documentation while the code | |
| 54 // generator is being transformed. | |
| 55 class SpilledScope BASE_EMBEDDED { | 56 class SpilledScope BASE_EMBEDDED { |
| 56 public: | 57 public: |
| 58 explicit SpilledScope(VirtualFrame* frame) |
| 59 : old_is_spilled_( |
| 60 Isolate::Current()->is_virtual_frame_in_spilled_scope()) { |
| 61 if (frame != NULL) { |
| 62 if (!old_is_spilled_) { |
| 63 frame->SpillAll(); |
| 64 } else { |
| 65 frame->AssertIsSpilled(); |
| 66 } |
| 67 } |
| 68 Isolate::Current()->set_is_virtual_frame_in_spilled_scope(true); |
| 69 } |
| 70 ~SpilledScope() { |
| 71 Isolate::Current()->set_is_virtual_frame_in_spilled_scope( |
| 72 old_is_spilled_); |
| 73 } |
| 74 static bool is_spilled() { |
| 75 return Isolate::Current()->is_virtual_frame_in_spilled_scope(); |
| 76 } |
| 77 |
| 78 private: |
| 79 int old_is_spilled_; |
| 80 |
| 57 SpilledScope() {} | 81 SpilledScope() {} |
| 82 |
| 83 friend class RegisterAllocationScope; |
| 84 }; |
| 85 |
| 86 class RegisterAllocationScope BASE_EMBEDDED { |
| 87 public: |
| 88 // A utility class to introduce a scope where the virtual frame |
| 89 // is not spilled, ie. where register allocation occurs. Eventually |
| 90 // when RegisterAllocationScope is ubiquitous it can be removed |
| 91 // along with the (by then unused) SpilledScope class. |
| 92 inline explicit RegisterAllocationScope(CodeGenerator* cgen); |
| 93 inline ~RegisterAllocationScope(); |
| 94 |
| 95 private: |
| 96 CodeGenerator* cgen_; |
| 97 bool old_is_spilled_; |
| 98 |
| 99 RegisterAllocationScope() {} |
| 58 }; | 100 }; |
| 59 | 101 |
| 60 // An illegal index into the virtual frame. | 102 // An illegal index into the virtual frame. |
| 61 static const int kIllegalIndex = -1; | 103 static const int kIllegalIndex = -1; |
| 62 | 104 |
| 63 // Construct an initial virtual frame on entry to a JS function. | 105 // Construct an initial virtual frame on entry to a JS function. |
| 64 inline VirtualFrame(); | 106 inline VirtualFrame(); |
| 65 | 107 |
| 108 // Construct an invalid virtual frame, used by JumpTargets. |
| 109 inline VirtualFrame(InvalidVirtualFrameInitializer* dummy); |
| 110 |
| 66 // Construct a virtual frame as a clone of an existing one. | 111 // Construct a virtual frame as a clone of an existing one. |
| 67 explicit inline VirtualFrame(VirtualFrame* original); | 112 explicit inline VirtualFrame(VirtualFrame* original); |
| 68 | 113 |
| 69 CodeGenerator* cgen() { return CodeGeneratorScope::Current(); } | 114 inline CodeGenerator* cgen() const; |
| 70 MacroAssembler* masm() { return cgen()->masm(); } | 115 inline MacroAssembler* masm(); |
| 71 | |
| 72 // Create a duplicate of an existing valid frame element. | |
| 73 FrameElement CopyElementAt(int index, | |
| 74 NumberInfo info = NumberInfo::Unknown()); | |
| 75 | 116 |
| 76 // The number of elements on the virtual frame. | 117 // The number of elements on the virtual frame. |
| 77 int element_count() { return elements_.length(); } | 118 int element_count() const { return element_count_; } |
| 78 | 119 |
| 79 // The height of the virtual expression stack. | 120 // The height of the virtual expression stack. |
| 80 int height() { | 121 inline int height() const; |
| 81 return element_count() - expression_base_index(); | |
| 82 } | |
| 83 | |
| 84 int register_location(int num) { | |
| 85 ASSERT(num >= 0 && num < RegisterAllocator::kNumRegisters); | |
| 86 return register_locations_[num]; | |
| 87 } | |
| 88 | |
| 89 int register_location(Register reg) { | |
| 90 return register_locations_[RegisterAllocator::ToNumber(reg)]; | |
| 91 } | |
| 92 | |
| 93 void set_register_location(Register reg, int index) { | |
| 94 register_locations_[RegisterAllocator::ToNumber(reg)] = index; | |
| 95 } | |
| 96 | 122 |
| 97 bool is_used(int num) { | 123 bool is_used(int num) { |
| 98 ASSERT(num >= 0 && num < RegisterAllocator::kNumRegisters); | 124 switch (num) { |
| 99 return register_locations_[num] != kIllegalIndex; | 125 case 0: { // a0. |
| 100 } | 126 return kA0InUse[top_of_stack_state_]; |
| 101 | 127 } |
| 102 bool is_used(Register reg) { | 128 case 1: { // a1. |
| 103 return register_locations_[RegisterAllocator::ToNumber(reg)] | 129 return kA1InUse[top_of_stack_state_]; |
| 104 != kIllegalIndex; | 130 } |
| 131 case 2: |
| 132 case 3: |
| 133 case 4: |
| 134 case 5: |
| 135 case 6: { // a2 to a3, t0 to t2. |
| 136 ASSERT(num - kFirstAllocatedRegister < kNumberOfAllocatedRegisters); |
| 137 ASSERT(num >= kFirstAllocatedRegister); |
| 138 if ((register_allocation_map_ & |
| 139 (1 << (num - kFirstAllocatedRegister))) == 0) { |
| 140 return false; |
| 141 } else { |
| 142 return true; |
| 143 } |
| 144 } |
| 145 default: { |
| 146 ASSERT(num < kFirstAllocatedRegister || |
| 147 num >= kFirstAllocatedRegister + kNumberOfAllocatedRegisters); |
| 148 return false; |
| 149 } |
| 150 } |
| 105 } | 151 } |
| 106 | 152 |
| 107 // Add extra in-memory elements to the top of the frame to match an actual | 153 // Add extra in-memory elements to the top of the frame to match an actual |
| 108 // frame (eg, the frame after an exception handler is pushed). No code is | 154 // frame (eg, the frame after an exception handler is pushed). No code is |
| 109 // emitted. | 155 // emitted. |
| 110 void Adjust(int count); | 156 void Adjust(int count); |
| 111 | 157 |
| 112 // Forget elements from the top of the frame to match an actual frame (eg, | 158 // Forget elements from the top of the frame to match an actual frame (eg, |
| 113 // the frame after a runtime call). No code is emitted. | 159 // the frame after a runtime call). No code is emitted except to bring the |
| 114 void Forget(int count) { | 160 // frame to a spilled state. |
| 115 ASSERT(count >= 0); | 161 void Forget(int count); |
| 116 ASSERT(stack_pointer_ == element_count() - 1); | |
| 117 stack_pointer_ -= count; | |
| 118 // On mips, all elements are in memory, so there is no extra bookkeeping | |
| 119 // (registers, copies, etc.) beyond dropping the elements. | |
| 120 elements_.Rewind(stack_pointer_ + 1); | |
| 121 } | |
| 122 | 162 |
| 123 // Forget count elements from the top of the frame and adjust the stack | |
| 124 // pointer downward. This is used, for example, before merging frames at | |
| 125 // break, continue, and return targets. | |
| 126 void ForgetElements(int count); | |
| 127 | 163 |
| 128 // Spill all values from the frame to memory. | 164 // Spill all values from the frame to memory. |
| 129 void SpillAll(); | 165 void SpillAll(); |
| 130 | 166 |
| 167 void AssertIsSpilled() const { |
| 168 ASSERT(top_of_stack_state_ == NO_TOS_REGISTERS); |
| 169 ASSERT(register_allocation_map_ == 0); |
| 170 } |
| 171 |
| 172 void AssertIsNotSpilled() { |
| 173 ASSERT(!SpilledScope::is_spilled()); |
| 174 } |
| 175 |
| 131 // Spill all occurrences of a specific register from the frame. | 176 // Spill all occurrences of a specific register from the frame. |
| 132 void Spill(Register reg) { | 177 void Spill(Register reg) { |
| 133 if (is_used(reg)) SpillElementAt(register_location(reg)); | 178 UNIMPLEMENTED(); |
| 134 } | 179 } |
| 135 | 180 |
| 136 // Spill all occurrences of an arbitrary register if possible. Return the | 181 // Spill all occurrences of an arbitrary register if possible. Return the |
| 137 // register spilled or no_reg if it was not possible to free any register | 182 // register spilled or no_reg if it was not possible to free any register |
| 138 // (ie, they all have frame-external references). | 183 // (ie, they all have frame-external references). Unimplemented. |
| 139 Register SpillAnyRegister(); | 184 Register SpillAnyRegister(); |
| 140 | 185 |
| 141 // Prepare this virtual frame for merging to an expected frame by | |
| 142 // performing some state changes that do not require generating | |
| 143 // code. It is guaranteed that no code will be generated. | |
| 144 void PrepareMergeTo(VirtualFrame* expected); | |
| 145 | |
| 146 // Make this virtual frame have a state identical to an expected virtual | 186 // Make this virtual frame have a state identical to an expected virtual |
| 147 // frame. As a side effect, code may be emitted to make this frame match | 187 // frame. As a side effect, code may be emitted to make this frame match |
| 148 // the expected one. | 188 // the expected one. |
| 149 void MergeTo(VirtualFrame* expected); | 189 void MergeTo(const VirtualFrame* expected, |
| 190 Condition cond = al, |
| 191 Register r1 = no_reg, |
| 192 const Operand& r2 = Operand(no_reg)); |
| 193 |
| 194 void MergeTo(VirtualFrame* expected, |
| 195 Condition cond = al, |
| 196 Register r1 = no_reg, |
| 197 const Operand& r2 = Operand(no_reg)); |
| 198 |
| 199 // Checks whether this frame can be branched to by the other frame. |
| 200 bool IsCompatibleWith(const VirtualFrame* other) const { |
| 201 return (tos_known_smi_map_ & (~other->tos_known_smi_map_)) == 0; |
| 202 } |
| 203 |
| 204 inline void ForgetTypeInfo() { |
| 205 tos_known_smi_map_ = 0; |
| 206 } |
| 150 | 207 |
| 151 // Detach a frame from its code generator, perhaps temporarily. This | 208 // Detach a frame from its code generator, perhaps temporarily. This |
| 152 // tells the register allocator that it is free to use frame-internal | 209 // tells the register allocator that it is free to use frame-internal |
| 153 // registers. Used when the code generator's frame is switched from this | 210 // registers. Used when the code generator's frame is switched from this |
| 154 // one to NULL by an unconditional jump. | 211 // one to NULL by an unconditional jump. |
| 155 void DetachFromCodeGenerator() { | 212 void DetachFromCodeGenerator() { |
| 156 RegisterAllocator* cgen_allocator = cgen()->allocator(); | |
| 157 for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) { | |
| 158 if (is_used(i)) cgen_allocator->Unuse(i); | |
| 159 } | |
| 160 } | 213 } |
| 161 | 214 |
| 162 // (Re)attach a frame to its code generator. This informs the register | 215 // (Re)attach a frame to its code generator. This informs the register |
| 163 // allocator that the frame-internal register references are active again. | 216 // allocator that the frame-internal register references are active again. |
| 164 // Used when a code generator's frame is switched from NULL to this one by | 217 // Used when a code generator's frame is switched from NULL to this one by |
| 165 // binding a label. | 218 // binding a label. |
| 166 void AttachToCodeGenerator() { | 219 void AttachToCodeGenerator() { |
| 167 RegisterAllocator* cgen_allocator = cgen()->allocator(); | |
| 168 for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) { | |
| 169 if (is_used(i)) cgen_allocator->Unuse(i); | |
| 170 } | |
| 171 } | 220 } |
| 172 | 221 |
| 173 // Emit code for the physical JS entry and exit frame sequences. After | 222 // Emit code for the physical JS entry and exit frame sequences. After |
| 174 // calling Enter, the virtual frame is ready for use; and after calling | 223 // calling Enter, the virtual frame is ready for use; and after calling |
| 175 // Exit it should not be used. Note that Enter does not allocate space in | 224 // Exit it should not be used. Note that Enter does not allocate space in |
| 176 // the physical frame for storing frame-allocated locals. | 225 // the physical frame for storing frame-allocated locals. |
| 177 void Enter(); | 226 void Enter(); |
| 178 void Exit(); | 227 void Exit(); |
| 179 | 228 |
| 180 // Prepare for returning from the frame by spilling locals and | 229 // Prepare for returning from the frame by elements in the virtual frame. |
| 181 // dropping all non-locals elements in the virtual frame. This | 230 // This avoids generating unnecessary merge code when jumping to the shared |
| 182 // avoids generating unnecessary merge code when jumping to the | 231 // return site. No spill code emitted. Value to return should be in v0. |
| 183 // shared return site. Emits code for spills. | 232 inline void PrepareForReturn(); |
| 184 void PrepareForReturn(); | 233 |
| 234 // Number of local variables after when we use a loop for allocating. |
| 235 static const int kLocalVarBound = 5; |
| 185 | 236 |
| 186 // Allocate and initialize the frame-allocated locals. | 237 // Allocate and initialize the frame-allocated locals. |
| 187 void AllocateStackSlots(); | 238 void AllocateStackSlots(); |
| 188 | 239 |
| 189 // The current top of the expression stack as an assembly operand. | 240 // The current top of the expression stack as an assembly operand. |
| 190 MemOperand Top() { return MemOperand(sp, 0); } | 241 MemOperand Top() { |
| 242 AssertIsSpilled(); |
| 243 return MemOperand(sp, 0); |
| 244 } |
| 191 | 245 |
| 192 // An element of the expression stack as an assembly operand. | 246 // An element of the expression stack as an assembly operand. |
| 193 MemOperand ElementAt(int index) { | 247 MemOperand ElementAt(int index) { |
| 194 return MemOperand(sp, index * kPointerSize); | 248 int adjusted_index = index - kVirtualElements[top_of_stack_state_]; |
| 249 ASSERT(adjusted_index >= 0); |
| 250 return MemOperand(sp, adjusted_index * kPointerSize); |
| 195 } | 251 } |
| 196 | 252 |
| 197 // Random-access store to a frame-top relative frame element. The result | 253 bool KnownSmiAt(int index) { |
| 198 // becomes owned by the frame and is invalidated. | 254 if (index >= kTOSKnownSmiMapSize) return false; |
| 199 void SetElementAt(int index, Result* value); | 255 return (tos_known_smi_map_ & (1 << index)) != 0; |
| 200 | |
| 201 // Set a frame element to a constant. The index is frame-top relative. | |
| 202 void SetElementAt(int index, Handle<Object> value) { | |
| 203 Result temp(value); | |
| 204 SetElementAt(index, &temp); | |
| 205 } | 256 } |
| 206 | |
| 207 void PushElementAt(int index) { | |
| 208 PushFrameSlotAt(element_count() - index - 1); | |
| 209 } | |
| 210 | |
| 211 // A frame-allocated local as an assembly operand. | 257 // A frame-allocated local as an assembly operand. |
| 212 MemOperand LocalAt(int index) { | 258 inline MemOperand LocalAt(int index); |
| 213 ASSERT(0 <= index); | |
| 214 ASSERT(index < local_count()); | |
| 215 return MemOperand(s8_fp, kLocal0Offset - index * kPointerSize); | |
| 216 } | |
| 217 | |
| 218 // Push a copy of the value of a local frame slot on top of the frame. | |
| 219 void PushLocalAt(int index) { | |
| 220 PushFrameSlotAt(local0_index() + index); | |
| 221 } | |
| 222 | |
| 223 // Push the value of a local frame slot on top of the frame and invalidate | |
| 224 // the local slot. The slot should be written to before trying to read | |
| 225 // from it again. | |
| 226 void TakeLocalAt(int index) { | |
| 227 TakeFrameSlotAt(local0_index() + index); | |
| 228 } | |
| 229 | |
| 230 // Store the top value on the virtual frame into a local frame slot. The | |
| 231 // value is left in place on top of the frame. | |
| 232 void StoreToLocalAt(int index) { | |
| 233 StoreToFrameSlotAt(local0_index() + index); | |
| 234 } | |
| 235 | 259 |
| 236 // Push the address of the receiver slot on the frame. | 260 // Push the address of the receiver slot on the frame. |
| 237 void PushReceiverSlotAddress(); | 261 void PushReceiverSlotAddress(); |
| 238 | 262 |
| 239 // The function frame slot. | 263 // The function frame slot. |
| 240 MemOperand Function() { return MemOperand(s8_fp, kFunctionOffset); } | 264 MemOperand Function() { return MemOperand(fp, kFunctionOffset); } |
| 241 | |
| 242 // Push the function on top of the frame. | |
| 243 void PushFunction() { PushFrameSlotAt(function_index()); } | |
| 244 | 265 |
| 245 // The context frame slot. | 266 // The context frame slot. |
| 246 MemOperand Context() { return MemOperand(s8_fp, kContextOffset); } | 267 MemOperand Context() { return MemOperand(fp, kContextOffset); } |
| 247 | |
| 248 // Save the value of the cp register to the context frame slot. | |
| 249 void SaveContextRegister(); | |
| 250 | |
| 251 // Restore the cp register from the value of the context frame | |
| 252 // slot. | |
| 253 void RestoreContextRegister(); | |
| 254 | 268 |
| 255 // A parameter as an assembly operand. | 269 // A parameter as an assembly operand. |
| 256 MemOperand ParameterAt(int index) { | 270 inline MemOperand ParameterAt(int index); |
| 257 // Index -1 corresponds to the receiver. | |
| 258 ASSERT(-1 <= index); // -1 is the receiver. | |
| 259 ASSERT(index <= parameter_count()); | |
| 260 uint16_t a = 0; // Number of argument slots. | |
| 261 return MemOperand(s8_fp, (1 + parameter_count() + a - index) *kPointerSize); | |
| 262 } | |
| 263 | |
| 264 // Push a copy of the value of a parameter frame slot on top of the frame. | |
| 265 void PushParameterAt(int index) { | |
| 266 PushFrameSlotAt(param0_index() + index); | |
| 267 } | |
| 268 | |
| 269 // Push the value of a paramter frame slot on top of the frame and | |
| 270 // invalidate the parameter slot. The slot should be written to before | |
| 271 // trying to read from it again. | |
| 272 void TakeParameterAt(int index) { | |
| 273 TakeFrameSlotAt(param0_index() + index); | |
| 274 } | |
| 275 | |
| 276 // Store the top value on the virtual frame into a parameter frame slot. | |
| 277 // The value is left in place on top of the frame. | |
| 278 void StoreToParameterAt(int index) { | |
| 279 StoreToFrameSlotAt(param0_index() + index); | |
| 280 } | |
| 281 | 271 |
| 282 // The receiver frame slot. | 272 // The receiver frame slot. |
| 283 MemOperand Receiver() { return ParameterAt(-1); } | 273 inline MemOperand Receiver(); |
| 284 | 274 |
| 285 // Push a try-catch or try-finally handler on top of the virtual frame. | 275 // Push a try-catch or try-finally handler on top of the virtual frame. |
| 286 void PushTryHandler(HandlerType type); | 276 void PushTryHandler(HandlerType type); |
| 287 | 277 |
| 288 // Call stub given the number of arguments it expects on (and | 278 // Call stub given the number of arguments it expects on (and |
| 289 // removes from) the stack. | 279 // removes from) the stack. |
| 290 void CallStub(CodeStub* stub, int arg_count) { | 280 inline void CallStub(CodeStub* stub, int arg_count); |
| 291 PrepareForCall(arg_count, arg_count); | |
| 292 RawCallStub(stub); | |
| 293 } | |
| 294 | 281 |
| 295 void CallStub(CodeStub* stub, Result* arg); | 282 // Call JS function from top of the stack with arguments |
| 296 | 283 // taken from the stack. |
| 297 void CallStub(CodeStub* stub, Result* arg0, Result* arg1); | 284 void CallJSFunction(int arg_count); |
| 298 | 285 |
| 299 // Call runtime given the number of arguments expected on (and | 286 // Call runtime given the number of arguments expected on (and |
| 300 // removed from) the stack. | 287 // removed from) the stack. |
| 301 void CallRuntime(Runtime::Function* f, int arg_count); | 288 void CallRuntime(const Runtime::Function* f, int arg_count); |
| 302 void CallRuntime(Runtime::FunctionId id, int arg_count); | 289 void CallRuntime(Runtime::FunctionId id, int arg_count); |
| 303 | 290 |
| 304 // Call runtime with sp aligned to 8 bytes. | 291 #ifdef ENABLE_DEBUGGER_SUPPORT |
| 305 void CallAlignedRuntime(Runtime::Function* f, int arg_count); | 292 void DebugBreak(); |
| 306 void CallAlignedRuntime(Runtime::FunctionId id, int arg_count); | 293 #endif |
| 307 | 294 |
| 308 // Invoke builtin given the number of arguments it expects on (and | 295 // Invoke builtin given the number of arguments it expects on (and |
| 309 // removes from) the stack. | 296 // removes from) the stack. |
| 310 void InvokeBuiltin(Builtins::JavaScript id, | 297 void InvokeBuiltin(Builtins::JavaScript id, |
| 311 InvokeJSFlags flag, | 298 InvokeJSFlags flag, |
| 312 Result* arg_count_register, | |
| 313 int arg_count); | 299 int arg_count); |
| 314 | 300 |
| 301 // Call load IC. Receiver is on the stack and is consumed. Result is returned |
| 302 // in v0. |
| 303 void CallLoadIC(Handle<String> name, RelocInfo::Mode mode); |
| 304 |
| 305 // Call store IC. If the load is contextual, value is found on top of the |
| 306 // frame. If not, value and receiver are on the frame. Both are consumed. |
| 307 // Result is returned in v0. |
| 308 void CallStoreIC(Handle<String> name, bool is_contextual); |
| 309 |
| 310 // Call keyed load IC. Key and receiver are on the stack. Both are consumed. |
| 311 // Result is returned in v0. |
| 312 void CallKeyedLoadIC(); |
| 313 |
| 314 // Call keyed store IC. Value, key and receiver are on the stack. All three |
| 315 // are consumed. Result is returned in v0 (and a0). |
| 316 void CallKeyedStoreIC(); |
| 317 |
| 315 // Call into an IC stub given the number of arguments it removes | 318 // Call into an IC stub given the number of arguments it removes |
| 316 // from the stack. Register arguments are passed as results and | 319 // from the stack. Register arguments to the IC stub are implicit, |
| 317 // consumed by the call. | 320 // and depend on the type of IC stub. |
| 318 void CallCodeObject(Handle<Code> ic, | 321 void CallCodeObject(Handle<Code> ic, |
| 319 RelocInfo::Mode rmode, | 322 RelocInfo::Mode rmode, |
| 320 int dropped_args); | 323 int dropped_args); |
| 321 void CallCodeObject(Handle<Code> ic, | |
| 322 RelocInfo::Mode rmode, | |
| 323 Result* arg, | |
| 324 int dropped_args); | |
| 325 void CallCodeObject(Handle<Code> ic, | |
| 326 RelocInfo::Mode rmode, | |
| 327 Result* arg0, | |
| 328 Result* arg1, | |
| 329 int dropped_args, | |
| 330 bool set_auto_args_slots = false); | |
| 331 | 324 |
| 332 // Drop a number of elements from the top of the expression stack. May | 325 // Drop a number of elements from the top of the expression stack. May |
| 333 // emit code to affect the physical frame. Does not clobber any registers | 326 // emit code to affect the physical frame. Does not clobber any registers |
| 334 // excepting possibly the stack pointer. | 327 // excepting possibly the stack pointer. |
| 335 void Drop(int count); | 328 void Drop(int count); |
| 336 // Similar to VirtualFrame::Drop but we don't modify the actual stack. | |
| 337 // This is because we need to manually restore sp to the correct position. | |
| 338 void DropFromVFrameOnly(int count); | |
| 339 | 329 |
| 340 // Drop one element. | 330 // Drop one element. |
| 341 void Drop() { Drop(1); } | 331 void Drop() { Drop(1); } |
| 342 void DropFromVFrameOnly() { DropFromVFrameOnly(1); } | |
| 343 | 332 |
| 344 // Duplicate the top element of the frame. | 333 // Pop an element from the top of the expression stack. Discards |
| 345 void Dup() { PushFrameSlotAt(element_count() - 1); } | 334 // the result. |
| 335 void Pop(); |
| 346 | 336 |
| 347 // Pop an element from the top of the expression stack. Returns a | 337 // Pop an element from the top of the expression stack. The register |
| 348 // Result, which may be a constant or a register. | 338 // will be one normally used for the top of stack register allocation |
| 349 Result Pop(); | 339 // so you can't hold on to it if you push on the stack. |
| 340 Register PopToRegister(Register but_not_to_this_one = no_reg); |
| 341 |
| 342 // Look at the top of the stack. The register returned is aliased and |
| 343 // must be copied to a scratch register before modification. |
| 344 Register Peek(); |
| 345 |
| 346 // Look at the value beneath the top of the stack. The register returned is |
| 347 // aliased and must be copied to a scratch register before modification. |
| 348 Register Peek2(); |
| 349 |
| 350 // Duplicate the top of stack. |
| 351 void Dup(); |
| 352 |
| 353 // Duplicate the two elements on top of stack. |
| 354 void Dup2(); |
| 355 |
| 356 // Flushes all registers, but it puts a copy of the top-of-stack in a0. |
| 357 void SpillAllButCopyTOSToA0(); |
| 358 |
| 359 // Flushes all registers, but it puts a copy of the top-of-stack in a1. |
| 360 void SpillAllButCopyTOSToA1(); |
| 361 |
| 362 // Flushes all registers, but it puts a copy of the top-of-stack in a1 |
| 363 // and the next value on the stack in a0. |
| 364 void SpillAllButCopyTOSToA1A0(); |
| 350 | 365 |
| 351 // Pop and save an element from the top of the expression stack and | 366 // Pop and save an element from the top of the expression stack and |
| 352 // emit a corresponding pop instruction. | 367 // emit a corresponding pop instruction. |
| 353 void EmitPop(Register reg); | 368 void EmitPop(Register reg); |
| 354 // Same but for multiple registers | 369 // Same but for multiple registers |
| 355 void EmitMultiPop(RegList regs); | 370 void EmitMultiPop(RegList regs); |
| 356 void EmitMultiPopReversed(RegList regs); | 371 void EmitMultiPopReversed(RegList regs); |
| 357 | 372 |
| 373 |
| 374 // Takes the top two elements and puts them in a0 (top element) and a1 |
| 375 // (second element). |
| 376 void PopToA1A0(); |
| 377 |
| 378 // Takes the top element and puts it in a1. |
| 379 void PopToA1(); |
| 380 |
| 381 // Takes the top element and puts it in a0. |
| 382 void PopToA0(); |
| 383 |
| 358 // Push an element on top of the expression stack and emit a | 384 // Push an element on top of the expression stack and emit a |
| 359 // corresponding push instruction. | 385 // corresponding push instruction. |
| 360 void EmitPush(Register reg); | 386 void EmitPush(Register reg, TypeInfo type_info = TypeInfo::Unknown()); |
| 387 void EmitPush(Operand operand, TypeInfo type_info = TypeInfo::Unknown()); |
| 388 void EmitPush(MemOperand operand, TypeInfo type_info = TypeInfo::Unknown()); |
| 389 void EmitPushRoot(Heap::RootListIndex index); |
| 390 |
| 391 // Overwrite the nth thing on the stack. If the nth position is in a |
| 392 // register then this turns into a Move, otherwise an sw. Afterwards |
| 393 // you can still use the register even if it is a register that can be |
| 394 // used for TOS (a0 or a1). |
| 395 void SetElementAt(Register reg, int this_far_down); |
| 396 |
| 397 // Get a register which is free and which must be immediately used to |
| 398 // push on the top of the stack. |
| 399 Register GetTOSRegister(); |
| 400 |
| 361 // Same but for multiple registers. | 401 // Same but for multiple registers. |
| 362 void EmitMultiPush(RegList regs); | 402 void EmitMultiPush(RegList regs); |
| 363 void EmitMultiPushReversed(RegList regs); | 403 void EmitMultiPushReversed(RegList regs); |
| 364 | 404 |
| 365 // Push an element on the virtual frame. | 405 static Register scratch0() { return t4; } |
| 366 inline void Push(Register reg, NumberInfo info = NumberInfo::Unknown()); | 406 static Register scratch1() { return t5; } |
| 367 inline void Push(Handle<Object> value); | 407 static Register scratch2() { return t6; } |
| 368 inline void Push(Smi* value); | |
| 369 | |
| 370 // Pushing a result invalidates it (its contents become owned by the frame). | |
| 371 void Push(Result* result) { | |
| 372 if (result->is_register()) { | |
| 373 Push(result->reg()); | |
| 374 } else { | |
| 375 ASSERT(result->is_constant()); | |
| 376 Push(result->handle()); | |
| 377 } | |
| 378 result->Unuse(); | |
| 379 } | |
| 380 | |
| 381 // Nip removes zero or more elements from immediately below the top | |
| 382 // of the frame, leaving the previous top-of-frame value on top of | |
| 383 // the frame. Nip(k) is equivalent to x = Pop(), Drop(k), Push(x). | |
| 384 inline void Nip(int num_dropped); | |
| 385 | |
| 386 // This pushes 4 arguments slots on the stack and saves asked 'a' registers | |
| 387 // 'a' registers are arguments register a0 to a3. | |
| 388 void EmitArgumentSlots(RegList reglist); | |
| 389 | |
| 390 inline void SetTypeForLocalAt(int index, NumberInfo info); | |
| 391 inline void SetTypeForParamAt(int index, NumberInfo info); | |
| 392 | 408 |
| 393 private: | 409 private: |
| 394 static const int kLocal0Offset = JavaScriptFrameConstants::kLocal0Offset; | 410 static const int kLocal0Offset = JavaScriptFrameConstants::kLocal0Offset; |
| 395 static const int kFunctionOffset = JavaScriptFrameConstants::kFunctionOffset; | 411 static const int kFunctionOffset = JavaScriptFrameConstants::kFunctionOffset; |
| 396 static const int kContextOffset = StandardFrameConstants::kContextOffset; | 412 static const int kContextOffset = StandardFrameConstants::kContextOffset; |
| 397 | 413 |
| 398 static const int kHandlerSize = StackHandlerConstants::kSize / kPointerSize; | 414 static const int kHandlerSize = StackHandlerConstants::kSize / kPointerSize; |
| 399 static const int kPreallocatedElements = 5 + 8; // 8 expression stack slots. | 415 static const int kPreallocatedElements = 5 + 8; // 8 expression stack slots. |
| 400 | 416 |
| 401 ZoneList<FrameElement> elements_; | 417 // 5 states for the top of stack, which can be in memory or in a0 and a1. |
| 418 enum TopOfStack { NO_TOS_REGISTERS, A0_TOS, A1_TOS, A1_A0_TOS, A0_A1_TOS, |
| 419 TOS_STATES}; |
| 420 static const int kMaxTOSRegisters = 2; |
| 421 |
| 422 static const bool kA0InUse[TOS_STATES]; |
| 423 static const bool kA1InUse[TOS_STATES]; |
| 424 static const int kVirtualElements[TOS_STATES]; |
| 425 static const TopOfStack kStateAfterPop[TOS_STATES]; |
| 426 static const TopOfStack kStateAfterPush[TOS_STATES]; |
| 427 static const Register kTopRegister[TOS_STATES]; |
| 428 static const Register kBottomRegister[TOS_STATES]; |
| 429 |
| 430 // We allocate up to 5 locals in registers. |
| 431 static const int kNumberOfAllocatedRegisters = 5; |
| 432 // r2 to r6 are allocated to locals. |
| 433 static const int kFirstAllocatedRegister = 2; |
| 434 |
| 435 static const Register kAllocatedRegisters[kNumberOfAllocatedRegisters]; |
| 436 |
| 437 static Register AllocatedRegister(int r) { |
| 438 ASSERT(r >= 0 && r < kNumberOfAllocatedRegisters); |
| 439 return kAllocatedRegisters[r]; |
| 440 } |
| 441 |
| 442 // The number of elements on the stack frame. |
| 443 int element_count_; |
| 444 TopOfStack top_of_stack_state_:3; |
| 445 int register_allocation_map_:kNumberOfAllocatedRegisters; |
| 446 static const int kTOSKnownSmiMapSize = 4; |
| 447 unsigned tos_known_smi_map_:kTOSKnownSmiMapSize; |
| 402 | 448 |
| 403 // The index of the element that is at the processor's stack pointer | 449 // The index of the element that is at the processor's stack pointer |
| 404 // (the sp register). | 450 // (the sp register). For now since everything is in memory it is given |
| 405 int stack_pointer_; | 451 // by the number of elements on the not-very-virtual stack frame. |
| 406 | 452 int stack_pointer() { return element_count_ - 1; } |
| 407 // The index of the register frame element using each register, or | |
| 408 // kIllegalIndex if a register is not on the frame. | |
| 409 int register_locations_[RegisterAllocator::kNumRegisters]; | |
| 410 | 453 |
| 411 // The number of frame-allocated locals and parameters respectively. | 454 // The number of frame-allocated locals and parameters respectively. |
| 412 int parameter_count() { return cgen()->scope()->num_parameters(); } | 455 inline int parameter_count() const; |
| 413 int local_count() { return cgen()->scope()->num_stack_slots(); } | 456 inline int local_count() const; |
| 414 | 457 |
| 415 // The index of the element that is at the processor's frame pointer | 458 // The index of the element that is at the processor's frame pointer |
| 416 // (the fp register). The parameters, receiver, function, and context | 459 // (the fp register). The parameters, receiver, function, and context |
| 417 // are below the frame pointer. | 460 // are below the frame pointer. |
| 418 int frame_pointer() { return parameter_count() + 3; } | 461 inline int frame_pointer() const; |
| 419 | 462 |
| 420 // The index of the first parameter. The receiver lies below the first | 463 // The index of the first parameter. The receiver lies below the first |
| 421 // parameter. | 464 // parameter. |
| 422 int param0_index() { return 1; } | 465 int param0_index() { return 1; } |
| 423 | 466 |
| 424 // The index of the context slot in the frame. It is immediately | 467 // The index of the context slot in the frame. It is immediately |
| 425 // below the frame pointer. | 468 // below the frame pointer. |
| 426 int context_index() { return frame_pointer() - 1; } | 469 inline int context_index(); |
| 427 | 470 |
| 428 // The index of the function slot in the frame. It is below the frame | 471 // The index of the function slot in the frame. It is below the frame |
| 429 // pointer and context slot. | 472 // pointer and context slot. |
| 430 int function_index() { return frame_pointer() - 2; } | 473 inline int function_index(); |
| 431 | 474 |
| 432 // The index of the first local. Between the frame pointer and the | 475 // The index of the first local. Between the frame pointer and the |
| 433 // locals lies the return address. | 476 // locals lies the return address. |
| 434 int local0_index() { return frame_pointer() + 2; } | 477 inline int local0_index() const; |
| 435 | 478 |
| 436 // The index of the base of the expression stack. | 479 // The index of the base of the expression stack. |
| 437 int expression_base_index() { return local0_index() + local_count(); } | 480 inline int expression_base_index() const; |
| 438 | 481 |
| 439 // Convert a frame index into a frame pointer relative offset into the | 482 // Convert a frame index into a frame pointer relative offset into the |
| 440 // actual stack. | 483 // actual stack. |
| 441 int fp_relative(int index) { | 484 inline int fp_relative(int index); |
| 442 ASSERT(index < element_count()); | |
| 443 ASSERT(frame_pointer() < element_count()); // FP is on the frame. | |
| 444 return (frame_pointer() - index) * kPointerSize; | |
| 445 } | |
| 446 | |
| 447 // Record an occurrence of a register in the virtual frame. This has the | |
| 448 // effect of incrementing the register's external reference count and | |
| 449 // of updating the index of the register's location in the frame. | |
| 450 void Use(Register reg, int index) { | |
| 451 ASSERT(!is_used(reg)); | |
| 452 set_register_location(reg, index); | |
| 453 cgen()->allocator()->Use(reg); | |
| 454 } | |
| 455 | |
| 456 // Record that a register reference has been dropped from the frame. This | |
| 457 // decrements the register's external reference count and invalidates the | |
| 458 // index of the register's location in the frame. | |
| 459 void Unuse(Register reg) { | |
| 460 ASSERT(is_used(reg)); | |
| 461 set_register_location(reg, kIllegalIndex); | |
| 462 cgen()->allocator()->Unuse(reg); | |
| 463 } | |
| 464 | |
| 465 // Spill the element at a particular index---write it to memory if | |
| 466 // necessary, free any associated register, and forget its value if | |
| 467 // constant. | |
| 468 void SpillElementAt(int index); | |
| 469 | |
| 470 // Sync the element at a particular index. If it is a register or | |
| 471 // constant that disagrees with the value on the stack, write it to memory. | |
| 472 // Keep the element type as register or constant, and clear the dirty bit. | |
| 473 void SyncElementAt(int index); | |
| 474 | |
| 475 // Sync the range of elements in [begin, end] with memory. | |
| 476 void SyncRange(int begin, int end); | |
| 477 | |
| 478 // Sync a single unsynced element that lies beneath or at the stack pointer. | |
| 479 void SyncElementBelowStackPointer(int index); | |
| 480 | |
| 481 // Sync a single unsynced element that lies just above the stack pointer. | |
| 482 void SyncElementByPushing(int index); | |
| 483 | |
| 484 // Push a copy of a frame slot (typically a local or parameter) on top of | |
| 485 // the frame. | |
| 486 inline void PushFrameSlotAt(int index); | |
| 487 | |
| 488 // Push a the value of a frame slot (typically a local or parameter) on | |
| 489 // top of the frame and invalidate the slot. | |
| 490 void TakeFrameSlotAt(int index); | |
| 491 | |
| 492 // Store the value on top of the frame to a frame slot (typically a local | |
| 493 // or parameter). | |
| 494 void StoreToFrameSlotAt(int index); | |
| 495 | 485 |
| 496 // Spill all elements in registers. Spill the top spilled_args elements | 486 // Spill all elements in registers. Spill the top spilled_args elements |
| 497 // on the frame. Sync all other frame elements. | 487 // on the frame. Sync all other frame elements. |
| 498 // Then drop dropped_args elements from the virtual frame, to match | 488 // Then drop dropped_args elements from the virtual frame, to match |
| 499 // the effect of an upcoming call that will drop them from the stack. | 489 // the effect of an upcoming call that will drop them from the stack. |
| 500 void PrepareForCall(int spilled_args, int dropped_args); | 490 void PrepareForCall(int spilled_args, int dropped_args); |
| 501 | 491 |
| 502 // Move frame elements currently in registers or constants, that | 492 // If all top-of-stack registers are in use then the lowest one is pushed |
| 503 // should be in memory in the expected frame, to memory. | 493 // onto the physical stack and made free. |
| 504 void MergeMoveRegistersToMemory(VirtualFrame* expected); | 494 void EnsureOneFreeTOSRegister(); |
| 505 | 495 |
| 506 // Make the register-to-register moves necessary to | 496 // Emit instructions to get the top of stack state from where we are to where |
| 507 // merge this frame with the expected frame. | 497 // we want to be. |
| 508 // Register to memory moves must already have been made, | 498 void MergeTOSTo(TopOfStack expected_state, |
| 509 // and memory to register moves must follow this call. | 499 Condition cond = al, |
| 510 // This is because some new memory-to-register moves are | 500 Register r1 = no_reg, |
| 511 // created in order to break cycles of register moves. | 501 const Operand& r2 = Operand(no_reg)); |
| 512 // Used in the implementation of MergeTo(). | |
| 513 void MergeMoveRegistersToRegisters(VirtualFrame* expected); | |
| 514 | 502 |
| 515 // Make the memory-to-register and constant-to-register moves | 503 inline bool Equals(const VirtualFrame* other); |
| 516 // needed to make this frame equal the expected frame. | |
| 517 // Called after all register-to-memory and register-to-register | |
| 518 // moves have been made. After this function returns, the frames | |
| 519 // should be equal. | |
| 520 void MergeMoveMemoryToRegisters(VirtualFrame* expected); | |
| 521 | 504 |
| 522 // Invalidates a frame slot (puts an invalid frame element in it). | 505 inline void LowerHeight(int count) { |
| 523 // Copies on the frame are correctly handled, and if this slot was | 506 element_count_ -= count; |
| 524 // the backing store of copies, the index of the new backing store | 507 if (count >= kTOSKnownSmiMapSize) { |
| 525 // is returned. Otherwise, returns kIllegalIndex. | 508 tos_known_smi_map_ = 0; |
| 526 // Register counts are correctly updated. | 509 } else { |
| 527 int InvalidateFrameSlotAt(int index); | 510 tos_known_smi_map_ >>= count; |
| 511 } |
| 512 } |
| 528 | 513 |
| 529 // Call a code stub that has already been prepared for calling (via | 514 inline void RaiseHeight(int count, unsigned known_smi_map = 0) { |
| 530 // PrepareForCall). | 515 ASSERT(known_smi_map < (1u << count)); |
| 531 void RawCallStub(CodeStub* stub); | 516 element_count_ += count; |
| 532 | 517 if (count >= kTOSKnownSmiMapSize) { |
| 533 // Calls a code object which has already been prepared for calling | 518 tos_known_smi_map_ = known_smi_map; |
| 534 // (via PrepareForCall). | 519 } else { |
| 535 void RawCallCodeObject(Handle<Code> code, RelocInfo::Mode rmode); | 520 tos_known_smi_map_ = ((tos_known_smi_map_ << count) | known_smi_map); |
| 536 | 521 } |
| 537 inline bool Equals(VirtualFrame* other); | 522 } |
| 538 | |
| 539 // Classes that need raw access to the elements_ array. | |
| 540 friend class DeferredCode; | |
| 541 friend class JumpTarget; | 523 friend class JumpTarget; |
| 542 }; | 524 }; |
| 543 | 525 |
| 544 | 526 |
| 545 } } // namespace v8::internal | 527 } } // namespace v8::internal |
| 546 | 528 |
| 547 #endif // V8_MIPS_VIRTUAL_FRAME_MIPS_H_ | 529 #endif // V8_MIPS_VIRTUAL_FRAME_MIPS_H_ |
| 548 | 530 |
| OLD | NEW |