blob: 113ccc692f7bcc01aebf6bccb88520e363a92717 [file] [log] [blame]
ager@chromium.org71daaf62009-04-01 07:22:49 +00001// Copyright 2009 the V8 project authors. All rights reserved.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
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.
27
28#ifndef V8_VIRTUAL_FRAME_IA32_H_
29#define V8_VIRTUAL_FRAME_IA32_H_
30
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000031#include "register-allocator.h"
32
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000033namespace v8 { namespace internal {
34
35// -------------------------------------------------------------------------
36// Virtual frames
37//
38// The virtual frame is an abstraction of the physical stack frame. It
39// encapsulates the parameters, frame-allocated locals, and the expression
40// stack. It supports push/pop operations on the expression stack, as well
41// as random access to the expression stack elements, locals, and
42// parameters.
43
44class VirtualFrame : public Malloced {
45 public:
46 // A utility class to introduce a scope where the virtual frame is
47 // expected to remain spilled. The constructor spills the code
48 // generator's current frame, but no attempt is made to require it
49 // to stay spilled. It is intended as documentation while the code
50 // generator is being transformed.
51 class SpilledScope BASE_EMBEDDED {
52 public:
53 explicit SpilledScope(CodeGenerator* cgen);
54
55 ~SpilledScope();
56
57 private:
58 CodeGenerator* cgen_;
59 bool previous_state_;
60 };
61
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000062 // An illegal index into the virtual frame.
63 static const int kIllegalIndex = -1;
64
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000065 // Construct an initial virtual frame on entry to a JS function.
66 explicit VirtualFrame(CodeGenerator* cgen);
67
68 // Construct a virtual frame as a clone of an existing one.
69 explicit VirtualFrame(VirtualFrame* original);
70
71 // Create a duplicate of an existing valid frame element.
72 FrameElement CopyElementAt(int index);
73
74 // The height of the virtual expression stack.
75 int height() const {
76 return elements_.length() - expression_base_index();
77 }
78
ager@chromium.org41826e72009-03-30 13:30:57 +000079 int register_index(Register reg) {
80 return register_locations_[reg.code()];
81 }
82
83 bool is_used(int reg_code) {
84 return register_locations_[reg_code] != kIllegalIndex;
85 }
86
87 bool is_used(Register reg) {
88 return is_used(reg.code());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000089 }
90
91 // Add extra in-memory elements to the top of the frame to match an actual
92 // frame (eg, the frame after an exception handler is pushed). No code is
93 // emitted.
94 void Adjust(int count);
95
96 // Forget count elements from the top of the frame all in-memory
97 // (including synced) and adjust the stack pointer downward, to
98 // match an external frame effect (examples include a call removing
99 // its arguments, and exiting a try/catch removing an exception
100 // handler). No code will be emitted.
101 void Forget(int count);
102
103 // Forget count elements from the top of the frame without adjusting
104 // the stack pointer downward. This is used, for example, before
105 // merging frames at break, continue, and return targets.
106 void ForgetElements(int count);
107
108 // Spill all values from the frame to memory.
109 void SpillAll();
110
111 // Spill all occurrences of a specific register from the frame.
112 void Spill(Register reg);
113
114 // Spill all occurrences of an arbitrary register if possible. Return the
115 // register spilled or no_reg if it was not possible to free any register
116 // (ie, they all have frame-external references).
117 Register SpillAnyRegister();
118
119 // Prepare this virtual frame for merging to an expected frame by
120 // performing some state changes that do not require generating
121 // code. It is guaranteed that no code will be generated.
122 void PrepareMergeTo(VirtualFrame* expected);
123
124 // Make this virtual frame have a state identical to an expected virtual
125 // frame. As a side effect, code may be emitted to make this frame match
126 // the expected one.
127 void MergeTo(VirtualFrame* expected);
128
129 // Detach a frame from its code generator, perhaps temporarily. This
130 // tells the register allocator that it is free to use frame-internal
131 // registers. Used when the code generator's frame is switched from this
132 // one to NULL by an unconditional jump.
133 void DetachFromCodeGenerator();
134
135 // (Re)attach a frame to its code generator. This informs the register
136 // allocator that the frame-internal register references are active again.
137 // Used when a code generator's frame is switched from NULL to this one by
138 // binding a label.
139 void AttachToCodeGenerator();
140
141 // Emit code for the physical JS entry and exit frame sequences. After
142 // calling Enter, the virtual frame is ready for use; and after calling
143 // Exit it should not be used. Note that Enter does not allocate space in
144 // the physical frame for storing frame-allocated locals.
145 void Enter();
146 void Exit();
147
148 // Prepare for returning from the frame by spilling locals. This
149 // avoids generating unnecessary merge code when jumping to the
150 // shared return site. Emits code for spills.
151 void PrepareForReturn();
152
153 // Allocate and initialize the frame-allocated locals.
154 void AllocateStackSlots(int count);
155
156 // An element of the expression stack as an assembly operand.
157 Operand ElementAt(int index) const {
158 return Operand(esp, index * kPointerSize);
159 }
160
161 // Random-access store to a frame-top relative frame element. The result
162 // becomes owned by the frame and is invalidated.
163 void SetElementAt(int index, Result* value);
164
165 // Set a frame element to a constant. The index is frame-top relative.
166 void SetElementAt(int index, Handle<Object> value) {
167 Result temp(value, cgen_);
168 SetElementAt(index, &temp);
169 }
170
171 void PushElementAt(int index) {
172 PushFrameSlotAt(elements_.length() - index - 1);
173 }
174
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +0000175 void StoreToElementAt(int index) {
176 StoreToFrameSlotAt(elements_.length() - index - 1);
177 }
178
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000179 // A frame-allocated local as an assembly operand.
180 Operand LocalAt(int index) const {
181 ASSERT(0 <= index);
182 ASSERT(index < local_count_);
183 return Operand(ebp, kLocal0Offset - index * kPointerSize);
184 }
185
186 // Push a copy of the value of a local frame slot on top of the frame.
187 void PushLocalAt(int index) {
188 PushFrameSlotAt(local0_index() + index);
189 }
190
191 // Push the value of a local frame slot on top of the frame and invalidate
192 // the local slot. The slot should be written to before trying to read
193 // from it again.
194 void TakeLocalAt(int index) {
195 TakeFrameSlotAt(local0_index() + index);
196 }
197
198 // Store the top value on the virtual frame into a local frame slot. The
199 // value is left in place on top of the frame.
200 void StoreToLocalAt(int index) {
201 StoreToFrameSlotAt(local0_index() + index);
202 }
203
204 // Push the address of the receiver slot on the frame.
205 void PushReceiverSlotAddress();
206
207 // Push the function on top of the frame.
208 void PushFunction() { PushFrameSlotAt(function_index()); }
209
210 // Save the value of the esi register to the context frame slot.
211 void SaveContextRegister();
212
213 // Restore the esi register from the value of the context frame
214 // slot.
215 void RestoreContextRegister();
216
217 // A parameter as an assembly operand.
218 Operand ParameterAt(int index) const {
219 ASSERT(-1 <= index); // -1 is the receiver.
220 ASSERT(index < parameter_count_);
221 return Operand(ebp, (1 + parameter_count_ - index) * kPointerSize);
222 }
223
224 // Push a copy of the value of a parameter frame slot on top of the frame.
225 void PushParameterAt(int index) {
226 PushFrameSlotAt(param0_index() + index);
227 }
228
229 // Push the value of a paramter frame slot on top of the frame and
230 // invalidate the parameter slot. The slot should be written to before
231 // trying to read from it again.
232 void TakeParameterAt(int index) {
233 TakeFrameSlotAt(param0_index() + index);
234 }
235
236 // Store the top value on the virtual frame into a parameter frame slot.
237 // The value is left in place on top of the frame.
238 void StoreToParameterAt(int index) {
239 StoreToFrameSlotAt(param0_index() + index);
240 }
241
242 // The receiver frame slot.
243 Operand Receiver() const { return ParameterAt(-1); }
244
245 // Push a try-catch or try-finally handler on top of the virtual frame.
246 void PushTryHandler(HandlerType type);
247
ager@chromium.org41826e72009-03-30 13:30:57 +0000248 // Call stub given the number of arguments it expects on (and
249 // removes from) the stack.
250 Result CallStub(CodeStub* stub, int arg_count);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000251
ager@chromium.org41826e72009-03-30 13:30:57 +0000252 // Call stub that takes a single argument passed in eax. The
253 // argument is given as a result which does not have to be eax or
254 // even a register. The argument is consumed by the call.
255 Result CallStub(CodeStub* stub, Result* arg);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000256
ager@chromium.org41826e72009-03-30 13:30:57 +0000257 // Call stub that takes a pair of arguments passed in edx (arg0) and
258 // eax (arg1). The arguments are given as results which do not have
259 // to be in the proper registers or even in registers. The
260 // arguments are consumed by the call.
261 Result CallStub(CodeStub* stub, Result* arg0, Result* arg1);
262
263 // Call runtime given the number of arguments expected on (and
264 // removed from) the stack.
265 Result CallRuntime(Runtime::Function* f, int arg_count);
266 Result CallRuntime(Runtime::FunctionId id, int arg_count);
267
268 // Invoke builtin given the number of arguments it expects on (and
269 // removes from) the stack.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000270 Result InvokeBuiltin(Builtins::JavaScript id,
271 InvokeFlag flag,
ager@chromium.org41826e72009-03-30 13:30:57 +0000272 int arg_count);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000273
ager@chromium.org41826e72009-03-30 13:30:57 +0000274 // Call load IC. Name and receiver are found on top of the frame.
275 // Receiver is not dropped.
276 Result CallLoadIC(RelocInfo::Mode mode);
277
278 // Call keyed load IC. Key and receiver are found on top of the
279 // frame. They are not dropped.
280 Result CallKeyedLoadIC(RelocInfo::Mode mode);
281
282 // Call store IC. Name, value, and receiver are found on top of the
283 // frame. Receiver is not dropped.
284 Result CallStoreIC();
285
286 // Call keyed store IC. Value, key, and receiver are found on top
287 // of the frame. Key and receiver are not dropped.
288 Result CallKeyedStoreIC();
289
290 // Call call IC. Arguments, reciever, and function name are found
291 // on top of the frame. Function name slot is not dropped. The
292 // argument count does not include the receiver.
293 Result CallCallIC(RelocInfo::Mode mode, int arg_count, int loop_nesting);
294
295 // Allocate and call JS function as constructor. Arguments,
296 // receiver (global object), and function are found on top of the
297 // frame. Function is not dropped. The argument count does not
298 // include the receiver.
299 Result CallConstructor(int arg_count);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000300
301 // Drop a number of elements from the top of the expression stack. May
302 // emit code to affect the physical frame. Does not clobber any registers
303 // excepting possibly the stack pointer.
304 void Drop(int count);
305
306 // Drop one element.
307 void Drop() { Drop(1); }
308
309 // Duplicate the top element of the frame.
310 void Dup() { PushFrameSlotAt(elements_.length() - 1); }
311
312 // Pop an element from the top of the expression stack. Returns a
313 // Result, which may be a constant or a register.
314 Result Pop();
315
316 // Pop and save an element from the top of the expression stack and
317 // emit a corresponding pop instruction.
318 void EmitPop(Register reg);
319 void EmitPop(Operand operand);
320
321 // Push an element on top of the expression stack and emit a
322 // corresponding push instruction.
323 void EmitPush(Register reg);
324 void EmitPush(Operand operand);
325 void EmitPush(Immediate immediate);
326
327 // Push an element on the virtual frame.
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +0000328 void Push(Register reg, StaticType static_type = StaticType());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000329 void Push(Handle<Object> value);
330 void Push(Smi* value) { Push(Handle<Object>(value)); }
331
332 // Pushing a result invalidates it (its contents become owned by the
333 // frame).
334 void Push(Result* result);
335
336 // Nip removes zero or more elements from immediately below the top
337 // of the frame, leaving the previous top-of-frame value on top of
338 // the frame. Nip(k) is equivalent to x = Pop(), Drop(k), Push(x).
339 void Nip(int num_dropped);
340
341 private:
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000342 static const int kLocal0Offset = JavaScriptFrameConstants::kLocal0Offset;
343 static const int kFunctionOffset = JavaScriptFrameConstants::kFunctionOffset;
344 static const int kContextOffset = StandardFrameConstants::kContextOffset;
345
346 static const int kHandlerSize = StackHandlerConstants::kSize / kPointerSize;
ager@chromium.org71daaf62009-04-01 07:22:49 +0000347 static const int kPreallocatedElements = 5 + 8; // 8 expression stack slots.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000348
349 CodeGenerator* cgen_;
350 MacroAssembler* masm_;
351
352 List<FrameElement> elements_;
353
354 // The number of frame-allocated locals and parameters respectively.
355 int parameter_count_;
356 int local_count_;
357
358 // The index of the element that is at the processor's stack pointer
359 // (the esp register).
360 int stack_pointer_;
361
362 // The index of the element that is at the processor's frame pointer
363 // (the ebp register).
364 int frame_pointer_;
365
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000366 // The index of the register frame element using each register, or
367 // kIllegalIndex if a register is not on the frame.
368 int register_locations_[kNumRegisters];
369
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000370 // The index of the first parameter. The receiver lies below the first
371 // parameter.
372 int param0_index() const { return 1; }
373
374 // The index of the context slot in the frame.
375 int context_index() const {
376 ASSERT(frame_pointer_ != kIllegalIndex);
377 return frame_pointer_ + 1;
378 }
379
380 // The index of the function slot in the frame. It lies above the context
381 // slot.
382 int function_index() const {
383 ASSERT(frame_pointer_ != kIllegalIndex);
384 return frame_pointer_ + 2;
385 }
386
387 // The index of the first local. Between the parameters and the locals
388 // lie the return address, the saved frame pointer, the context, and the
389 // function.
390 int local0_index() const {
391 ASSERT(frame_pointer_ != kIllegalIndex);
392 return frame_pointer_ + 3;
393 }
394
395 // The index of the base of the expression stack.
396 int expression_base_index() const { return local0_index() + local_count_; }
397
398 // Convert a frame index into a frame pointer relative offset into the
399 // actual stack.
400 int fp_relative(int index) const {
401 return (frame_pointer_ - index) * kPointerSize;
402 }
403
404 // Record an occurrence of a register in the virtual frame. This has the
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000405 // effect of incrementing the register's external reference count and
406 // of updating the index of the register's location in the frame.
407 void Use(Register reg, int index);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000408
409 // Record that a register reference has been dropped from the frame. This
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000410 // decrements the register's external reference count and invalidates the
411 // index of the register's location in the frame.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000412 void Unuse(Register reg);
413
414 // Spill the element at a particular index---write it to memory if
415 // necessary, free any associated register, and forget its value if
416 // constant.
417 void SpillElementAt(int index);
418
419 // Sync the element at a particular index. If it is a register or
420 // constant that disagrees with the value on the stack, write it to memory.
421 // Keep the element type as register or constant, and clear the dirty bit.
422 void SyncElementAt(int index);
423
424 // Sync the range of elements in [begin, end).
425 void SyncRange(int begin, int end);
426
ager@chromium.org41826e72009-03-30 13:30:57 +0000427 // Sync a single unsynced element that lies beneath or at the stack pointer.
428 void SyncElementBelowStackPointer(int index);
429
430 // Sync a single unsynced element that lies just above the stack pointer.
431 void SyncElementByPushing(int index);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000432
433 // Push a copy of a frame slot (typically a local or parameter) on top of
434 // the frame.
435 void PushFrameSlotAt(int index);
436
437 // Push a the value of a frame slot (typically a local or parameter) on
438 // top of the frame and invalidate the slot.
439 void TakeFrameSlotAt(int index);
440
441 // Store the value on top of the frame to a frame slot (typically a local
442 // or parameter).
443 void StoreToFrameSlotAt(int index);
444
445 // Spill all elements in registers. Spill the top spilled_args elements
446 // on the frame. Sync all other frame elements.
447 // Then drop dropped_args elements from the virtual frame, to match
448 // the effect of an upcoming call that will drop them from the stack.
449 void PrepareForCall(int spilled_args, int dropped_args);
450
451 // Move frame elements currently in registers or constants, that
452 // should be in memory in the expected frame, to memory.
453 void MergeMoveRegistersToMemory(VirtualFrame* expected);
454
455 // Make the register-to-register moves necessary to
456 // merge this frame with the expected frame.
457 // Register to memory moves must already have been made,
458 // and memory to register moves must follow this call.
459 // This is because some new memory-to-register moves are
460 // created in order to break cycles of register moves.
461 // Used in the implementation of MergeTo().
462 void MergeMoveRegistersToRegisters(VirtualFrame* expected);
463
464 // Make the memory-to-register and constant-to-register moves
465 // needed to make this frame equal the expected frame.
466 // Called after all register-to-memory and register-to-register
467 // moves have been made. After this function returns, the frames
468 // should be equal.
469 void MergeMoveMemoryToRegisters(VirtualFrame* expected);
470
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000471 // Invalidates a frame slot (puts an invalid frame element in it).
472 // Copies on the frame are correctly handled, and if this slot was
473 // the backing store of copies, the index of the new backing store
474 // is returned. Otherwise, returns kIllegalIndex.
475 // Register counts are correctly updated.
476 int InvalidateFrameSlotAt(int index);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000477
478 // Call a code stub that has already been prepared for calling (via
479 // PrepareForCall).
ager@chromium.org41826e72009-03-30 13:30:57 +0000480 Result RawCallStub(CodeStub* stub);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000481
482 // Calls a code object which has already been prepared for calling
483 // (via PrepareForCall).
484 Result RawCallCodeObject(Handle<Code> code, RelocInfo::Mode rmode);
485
486 bool Equals(VirtualFrame* other);
487
488 friend class JumpTarget;
489};
490
491} } // namespace v8::internal
492
493#endif // V8_VIRTUAL_FRAME_IA32_H_