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 |