blob: af3c08ce770c4c54f7465ebdf9be0b257ae1fbc7 [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_ARM_H_
29#define V8_VIRTUAL_FRAME_ARM_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) {
kasperl@chromium.org2d18d102009-04-15 13:27:32 +000088 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 elements from the top of the frame to match an actual frame (eg,
97 // the frame after a runtime call). No code is emitted.
98 void Forget(int count);
99
100 // Forget count elements from the top of the frame without adjusting
101 // the stack pointer downward. This is used, for example, before
102 // merging frames at break, continue, and return targets.
103 void ForgetElements(int count);
104
105 // Spill all values from the frame to memory.
106 void SpillAll();
107
108 // Spill all occurrences of a specific register from the frame.
109 void Spill(Register reg);
110
111 // Spill all occurrences of an arbitrary register if possible. Return the
112 // register spilled or no_reg if it was not possible to free any register
113 // (ie, they all have frame-external references).
114 Register SpillAnyRegister();
115
116 // Prepare this virtual frame for merging to an expected frame by
117 // performing some state changes that do not require generating
118 // code. It is guaranteed that no code will be generated.
119 void PrepareMergeTo(VirtualFrame* expected);
120
121 // Make this virtual frame have a state identical to an expected virtual
122 // frame. As a side effect, code may be emitted to make this frame match
123 // the expected one.
124 void MergeTo(VirtualFrame* expected);
125
126 // Detach a frame from its code generator, perhaps temporarily. This
127 // tells the register allocator that it is free to use frame-internal
128 // registers. Used when the code generator's frame is switched from this
129 // one to NULL by an unconditional jump.
130 void DetachFromCodeGenerator();
131
132 // (Re)attach a frame to its code generator. This informs the register
133 // allocator that the frame-internal register references are active again.
134 // Used when a code generator's frame is switched from NULL to this one by
135 // binding a label.
136 void AttachToCodeGenerator();
137
138 // Emit code for the physical JS entry and exit frame sequences. After
139 // calling Enter, the virtual frame is ready for use; and after calling
140 // Exit it should not be used. Note that Enter does not allocate space in
141 // the physical frame for storing frame-allocated locals.
142 void Enter();
143 void Exit();
144
145 // Prepare for returning from the frame by spilling locals and
146 // dropping all non-locals elements in the virtual frame. This
147 // avoids generating unnecessary merge code when jumping to the
148 // shared return site. Emits code for spills.
149 void PrepareForReturn();
150
151 // Allocate and initialize the frame-allocated locals.
152 void AllocateStackSlots(int count);
153
154 // The current top of the expression stack as an assembly operand.
155 MemOperand Top() const { return MemOperand(sp, 0); }
156
157 // An element of the expression stack as an assembly operand.
158 MemOperand ElementAt(int index) const {
159 return MemOperand(sp, index * kPointerSize);
160 }
161
162 // Random-access store to a frame-top relative frame element. The result
163 // becomes owned by the frame and is invalidated.
164 void SetElementAt(int index, Result* value);
165
166 // Set a frame element to a constant. The index is frame-top relative.
167 void SetElementAt(int index, Handle<Object> value) {
168 Result temp(value, cgen_);
169 SetElementAt(index, &temp);
170 }
171
172 void PushElementAt(int index) {
173 PushFrameSlotAt(elements_.length() - index - 1);
174 }
175
176 // A frame-allocated local as an assembly operand.
177 MemOperand LocalAt(int index) const {
178 ASSERT(0 <= index);
179 ASSERT(index < local_count_);
180 return MemOperand(fp, kLocal0Offset - index * kPointerSize);
181 }
182
183 // Push a copy of the value of a local frame slot on top of the frame.
184 void PushLocalAt(int index) {
185 PushFrameSlotAt(local0_index() + index);
186 }
187
188 // Push the value of a local frame slot on top of the frame and invalidate
189 // the local slot. The slot should be written to before trying to read
190 // from it again.
191 void TakeLocalAt(int index) {
192 TakeFrameSlotAt(local0_index() + index);
193 }
194
195 // Store the top value on the virtual frame into a local frame slot. The
196 // value is left in place on top of the frame.
197 void StoreToLocalAt(int index) {
198 StoreToFrameSlotAt(local0_index() + index);
199 }
200
201 // Push the address of the receiver slot on the frame.
202 void PushReceiverSlotAddress();
203
204 // The function frame slot.
205 MemOperand Function() const { return MemOperand(fp, kFunctionOffset); }
206
207 // Push the function on top of the frame.
208 void PushFunction() { PushFrameSlotAt(function_index()); }
209
210 // The context frame slot.
211 MemOperand Context() const { return MemOperand(fp, kContextOffset); }
212
213 // Save the value of the esi register to the context frame slot.
214 void SaveContextRegister();
215
216 // Restore the esi register from the value of the context frame
217 // slot.
218 void RestoreContextRegister();
219
220 // A parameter as an assembly operand.
221 MemOperand ParameterAt(int index) const {
222 // Index -1 corresponds to the receiver.
223 ASSERT(-1 <= index && index <= parameter_count_);
224 return MemOperand(fp, (1 + parameter_count_ - index) * kPointerSize);
225 }
226
227 // Push a copy of the value of a parameter frame slot on top of the frame.
228 void PushParameterAt(int index) {
229 PushFrameSlotAt(param0_index() + index);
230 }
231
232 // Push the value of a paramter frame slot on top of the frame and
233 // invalidate the parameter slot. The slot should be written to before
234 // trying to read from it again.
235 void TakeParameterAt(int index) {
236 TakeFrameSlotAt(param0_index() + index);
237 }
238
239 // Store the top value on the virtual frame into a parameter frame slot.
240 // The value is left in place on top of the frame.
241 void StoreToParameterAt(int index) {
242 StoreToFrameSlotAt(param0_index() + index);
243 }
244
245 // The receiver frame slot.
246 MemOperand Receiver() const { return ParameterAt(-1); }
247
248 // Push a try-catch or try-finally handler on top of the virtual frame.
249 void PushTryHandler(HandlerType type);
250
ager@chromium.org41826e72009-03-30 13:30:57 +0000251 // Call stub given the number of arguments it expects on (and
252 // removes from) the stack.
253 Result CallStub(CodeStub* stub, int arg_count);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000254
ager@chromium.org41826e72009-03-30 13:30:57 +0000255 // Call stub that expects its argument in r0. The argument is given
256 // as a result which must be the register r0.
257 Result CallStub(CodeStub* stub, Result* arg);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000258
ager@chromium.org41826e72009-03-30 13:30:57 +0000259 // Call stub that expects its arguments in r1 and r0. The arguments
260 // are given as results which must be the appropriate registers.
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 InvokeJSFlags flag,
272 Result* arg_count_register,
ager@chromium.org41826e72009-03-30 13:30:57 +0000273 int arg_count);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000274
ager@chromium.org41826e72009-03-30 13:30:57 +0000275 // Call into an IC stub given the number of arguments it removes
276 // from the stack. Register arguments are passed as results and
277 // consumed by the call.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000278 Result CallCodeObject(Handle<Code> ic,
279 RelocInfo::Mode rmode,
280 int dropped_args);
281 Result CallCodeObject(Handle<Code> ic,
282 RelocInfo::Mode rmode,
283 Result* arg,
284 int dropped_args);
285 Result CallCodeObject(Handle<Code> ic,
286 RelocInfo::Mode rmode,
287 Result* arg0,
288 Result* arg1,
289 int dropped_args);
290
291 // Drop a number of elements from the top of the expression stack. May
292 // emit code to affect the physical frame. Does not clobber any registers
293 // excepting possibly the stack pointer.
294 void Drop(int count);
295
296 // Drop one element.
297 void Drop() { Drop(1); }
298
299 // Duplicate the top element of the frame.
300 void Dup() { PushFrameSlotAt(elements_.length() - 1); }
301
302 // Pop an element from the top of the expression stack. Returns a
303 // Result, which may be a constant or a register.
304 Result Pop();
305
306 // Pop and save an element from the top of the expression stack and
307 // emit a corresponding pop instruction.
308 void EmitPop(Register reg);
309
310 // Push an element on top of the expression stack and emit a
311 // corresponding push instruction.
312 void EmitPush(Register reg);
313
314 // Push an element on the virtual frame.
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +0000315 void Push(Register reg, StaticType static_type = StaticType());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000316 void Push(Handle<Object> value);
317 void Push(Smi* value) { Push(Handle<Object>(value)); }
318
kasperl@chromium.org8ccb0be2009-04-07 07:21:39 +0000319 // Pushing a result invalidates it (its contents become owned by the frame).
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000320 void Push(Result* result);
321
322 // Nip removes zero or more elements from immediately below the top
323 // of the frame, leaving the previous top-of-frame value on top of
324 // the frame. Nip(k) is equivalent to x = Pop(), Drop(k), Push(x).
325 void Nip(int num_dropped);
326
327 private:
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000328 static const int kLocal0Offset = JavaScriptFrameConstants::kLocal0Offset;
329 static const int kFunctionOffset = JavaScriptFrameConstants::kFunctionOffset;
330 static const int kContextOffset = StandardFrameConstants::kContextOffset;
331
332 static const int kHandlerSize = StackHandlerConstants::kSize / kPointerSize;
ager@chromium.org71daaf62009-04-01 07:22:49 +0000333 static const int kPreallocatedElements = 5 + 8; // 8 expression stack slots.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000334
335 CodeGenerator* cgen_;
336 MacroAssembler* masm_;
337
338 List<FrameElement> elements_;
339
340 // The number of frame-allocated locals and parameters respectively.
341 int parameter_count_;
342 int local_count_;
343
344 // The index of the element that is at the processor's stack pointer
345 // (the sp register).
346 int stack_pointer_;
347
348 // The index of the element that is at the processor's frame pointer
349 // (the fp register).
350 int frame_pointer_;
351
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000352 // The index of the register frame element using each register, or
353 // kIllegalIndex if a register is not on the frame.
354 int register_locations_[kNumRegisters];
355
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000356 // The index of the first parameter. The receiver lies below the first
357 // parameter.
358 int param0_index() const { return 1; }
359
360 // The index of the context slot in the frame.
361 int context_index() const {
362 ASSERT(frame_pointer_ != kIllegalIndex);
363 return frame_pointer_ - 1;
364 }
365
366 // The index of the function slot in the frame. It lies above the context
367 // slot.
368 int function_index() const {
369 ASSERT(frame_pointer_ != kIllegalIndex);
370 return frame_pointer_ - 2;
371 }
372
373 // The index of the first local. Between the parameters and the locals
374 // lie the return address, the saved frame pointer, the context, and the
375 // function.
376 int local0_index() const {
377 ASSERT(frame_pointer_ != kIllegalIndex);
378 return frame_pointer_ + 2;
379 }
380
381 // The index of the base of the expression stack.
382 int expression_base_index() const { return local0_index() + local_count_; }
383
384 // Convert a frame index into a frame pointer relative offset into the
385 // actual stack.
386 int fp_relative(int index) const {
387 return (frame_pointer_ - index) * kPointerSize;
388 }
389
390 // Record an occurrence of a register in the virtual frame. This has the
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000391 // effect of incrementing the register's external reference count and
392 // of updating the index of the register's location in the frame.
393 void Use(Register reg, int index);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000394
395 // Record that a register reference has been dropped from the frame. This
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000396 // decrements the register's external reference count and invalidates the
397 // index of the register's location in the frame.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000398 void Unuse(Register reg);
399
400 // Spill the element at a particular index---write it to memory if
401 // necessary, free any associated register, and forget its value if
402 // constant.
403 void SpillElementAt(int index);
404
405 // Sync the element at a particular index. If it is a register or
406 // constant that disagrees with the value on the stack, write it to memory.
407 // Keep the element type as register or constant, and clear the dirty bit.
408 void SyncElementAt(int index);
409
410 // Sync the range of elements in [begin, end).
411 void SyncRange(int begin, int end);
412
ager@chromium.org41826e72009-03-30 13:30:57 +0000413 // Sync a single unsynced element that lies beneath or at the stack pointer.
414 void SyncElementBelowStackPointer(int index);
415
416 // Sync a single unsynced element that lies just above the stack pointer.
417 void SyncElementByPushing(int index);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000418
419 // Push a copy of a frame slot (typically a local or parameter) on top of
420 // the frame.
421 void PushFrameSlotAt(int index);
422
423 // Push a the value of a frame slot (typically a local or parameter) on
424 // top of the frame and invalidate the slot.
425 void TakeFrameSlotAt(int index);
426
427 // Store the value on top of the frame to a frame slot (typically a local
428 // or parameter).
429 void StoreToFrameSlotAt(int index);
430
431 // Spill all elements in registers. Spill the top spilled_args elements
432 // on the frame. Sync all other frame elements.
433 // Then drop dropped_args elements from the virtual frame, to match
434 // the effect of an upcoming call that will drop them from the stack.
435 void PrepareForCall(int spilled_args, int dropped_args);
436
437 // Move frame elements currently in registers or constants, that
438 // should be in memory in the expected frame, to memory.
439 void MergeMoveRegistersToMemory(VirtualFrame* expected);
440
441 // Make the register-to-register moves necessary to
442 // merge this frame with the expected frame.
443 // Register to memory moves must already have been made,
444 // and memory to register moves must follow this call.
445 // This is because some new memory-to-register moves are
446 // created in order to break cycles of register moves.
447 // Used in the implementation of MergeTo().
448 void MergeMoveRegistersToRegisters(VirtualFrame* expected);
449
450 // Make the memory-to-register and constant-to-register moves
451 // needed to make this frame equal the expected frame.
452 // Called after all register-to-memory and register-to-register
453 // moves have been made. After this function returns, the frames
454 // should be equal.
455 void MergeMoveMemoryToRegisters(VirtualFrame* expected);
456
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000457 // Invalidates a frame slot (puts an invalid frame element in it).
458 // Copies on the frame are correctly handled, and if this slot was
459 // the backing store of copies, the index of the new backing store
460 // is returned. Otherwise, returns kIllegalIndex.
461 // Register counts are correctly updated.
462 int InvalidateFrameSlotAt(int index);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000463
464 // Call a code stub that has already been prepared for calling (via
465 // PrepareForCall).
ager@chromium.org41826e72009-03-30 13:30:57 +0000466 Result RawCallStub(CodeStub* stub);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000467
468 // Calls a code object which has already been prepared for calling
469 // (via PrepareForCall).
470 Result RawCallCodeObject(Handle<Code> code, RelocInfo::Mode rmode);
471
472 bool Equals(VirtualFrame* other);
473
474 friend class JumpTarget;
475};
476
477
478} } // namespace v8::internal
479
480#endif // V8_VIRTUAL_FRAME_ARM_H_