diff options
Diffstat (limited to 'src/3rdparty/v8/src/ia32/virtual-frame-ia32.h')
-rw-r--r-- | src/3rdparty/v8/src/ia32/virtual-frame-ia32.h | 650 |
1 files changed, 650 insertions, 0 deletions
diff --git a/src/3rdparty/v8/src/ia32/virtual-frame-ia32.h b/src/3rdparty/v8/src/ia32/virtual-frame-ia32.h new file mode 100644 index 0000000..504a8fc --- /dev/null +++ b/src/3rdparty/v8/src/ia32/virtual-frame-ia32.h @@ -0,0 +1,650 @@ +// Copyright 2009 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_IA32_VIRTUAL_FRAME_IA32_H_ +#define V8_IA32_VIRTUAL_FRAME_IA32_H_ + +#include "codegen.h" +#include "register-allocator.h" +#include "scopes.h" +#include "type-info.h" + +namespace v8 { +namespace internal { + +// ------------------------------------------------------------------------- +// Virtual frames +// +// The virtual frame is an abstraction of the physical stack frame. It +// encapsulates the parameters, frame-allocated locals, and the expression +// stack. It supports push/pop operations on the expression stack, as well +// as random access to the expression stack elements, locals, and +// parameters. + +class VirtualFrame: public ZoneObject { + public: + // A utility class to introduce a scope where the virtual frame is + // expected to remain spilled. The constructor spills the code + // generator's current frame, but no attempt is made to require it + // to stay spilled. It is intended as documentation while the code + // generator is being transformed. + class SpilledScope BASE_EMBEDDED { + public: + SpilledScope() : previous_state_(cgen()->in_spilled_code()) { + ASSERT(cgen()->has_valid_frame()); + cgen()->frame()->SpillAll(); + cgen()->set_in_spilled_code(true); + } + + ~SpilledScope() { + cgen()->set_in_spilled_code(previous_state_); + } + + private: + bool previous_state_; + + CodeGenerator* cgen() { + return CodeGeneratorScope::Current(Isolate::Current()); + } + }; + + // An illegal index into the virtual frame. + static const int kIllegalIndex = -1; + + // Construct an initial virtual frame on entry to a JS function. + inline VirtualFrame(); + + // Construct a virtual frame as a clone of an existing one. + explicit inline VirtualFrame(VirtualFrame* original); + + CodeGenerator* cgen() { + return CodeGeneratorScope::Current(Isolate::Current()); + } + + MacroAssembler* masm() { return cgen()->masm(); } + + // Create a duplicate of an existing valid frame element. + FrameElement CopyElementAt(int index, + TypeInfo info = TypeInfo::Uninitialized()); + + // The number of elements on the virtual frame. + int element_count() { return elements_.length(); } + + // The height of the virtual expression stack. + int height() { return element_count() - expression_base_index(); } + + int register_location(int num) { + ASSERT(num >= 0 && num < RegisterAllocator::kNumRegisters); + return register_locations_[num]; + } + + inline int register_location(Register reg); + + inline void set_register_location(Register reg, int index); + + bool is_used(int num) { + ASSERT(num >= 0 && num < RegisterAllocator::kNumRegisters); + return register_locations_[num] != kIllegalIndex; + } + + inline bool is_used(Register reg); + + // Add extra in-memory elements to the top of the frame to match an actual + // frame (eg, the frame after an exception handler is pushed). No code is + // emitted. + void Adjust(int count); + + // Forget count elements from the top of the frame all in-memory + // (including synced) and adjust the stack pointer downward, to + // match an external frame effect (examples include a call removing + // its arguments, and exiting a try/catch removing an exception + // handler). No code will be emitted. + void Forget(int count) { + ASSERT(count >= 0); + ASSERT(stack_pointer_ == element_count() - 1); + stack_pointer_ -= count; + ForgetElements(count); + } + + // Forget count elements from the top of the frame without adjusting + // the stack pointer downward. This is used, for example, before + // merging frames at break, continue, and return targets. + void ForgetElements(int count); + + // Spill all values from the frame to memory. + inline void SpillAll(); + + // Spill all occurrences of a specific register from the frame. + void Spill(Register reg) { + if (is_used(reg)) SpillElementAt(register_location(reg)); + } + + // Make the two registers distinct and spill them. Returns the second + // register. If the registers were not distinct then it returns the new + // second register. + Result MakeDistinctAndSpilled(Result* left, Result* right) { + Spill(left->reg()); + Spill(right->reg()); + if (left->reg().is(right->reg())) { + RegisterAllocator* allocator = cgen()->allocator(); + Result fresh = allocator->Allocate(); + ASSERT(fresh.is_valid()); + masm()->mov(fresh.reg(), right->reg()); + return fresh; + } + return *right; + } + + // Spill all occurrences of an arbitrary register if possible. Return the + // register spilled or no_reg if it was not possible to free any register + // (ie, they all have frame-external references). + Register SpillAnyRegister(); + + // Spill the top element of the frame. + void SpillTop() { SpillElementAt(element_count() - 1); } + + // Sync the range of elements in [begin, end] with memory. + void SyncRange(int begin, int end); + + // Make this frame so that an arbitrary frame of the same height can + // be merged to it. Copies and constants are removed from the frame. + void MakeMergable(); + + // Prepare this virtual frame for merging to an expected frame by + // performing some state changes that do not require generating + // code. It is guaranteed that no code will be generated. + void PrepareMergeTo(VirtualFrame* expected); + + // Make this virtual frame have a state identical to an expected virtual + // frame. As a side effect, code may be emitted to make this frame match + // the expected one. + void MergeTo(VirtualFrame* expected); + + // Detach a frame from its code generator, perhaps temporarily. This + // tells the register allocator that it is free to use frame-internal + // registers. Used when the code generator's frame is switched from this + // one to NULL by an unconditional jump. + void DetachFromCodeGenerator() { + RegisterAllocator* cgen_allocator = cgen()->allocator(); + for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) { + if (is_used(i)) cgen_allocator->Unuse(i); + } + } + + // (Re)attach a frame to its code generator. This informs the register + // allocator that the frame-internal register references are active again. + // Used when a code generator's frame is switched from NULL to this one by + // binding a label. + void AttachToCodeGenerator() { + RegisterAllocator* cgen_allocator = cgen()->allocator(); + for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) { + if (is_used(i)) cgen_allocator->Use(i); + } + } + + // Emit code for the physical JS entry and exit frame sequences. After + // calling Enter, the virtual frame is ready for use; and after calling + // Exit it should not be used. Note that Enter does not allocate space in + // the physical frame for storing frame-allocated locals. + void Enter(); + void Exit(); + + // Prepare for returning from the frame by spilling locals. This + // avoids generating unnecessary merge code when jumping to the + // shared return site. Emits code for spills. + inline void PrepareForReturn(); + + // Number of local variables after when we use a loop for allocating. + static const int kLocalVarBound = 10; + + // Allocate and initialize the frame-allocated locals. + void AllocateStackSlots(); + + // An element of the expression stack as an assembly operand. + Operand ElementAt(int index) const { + return Operand(esp, index * kPointerSize); + } + + // Random-access store to a frame-top relative frame element. The result + // becomes owned by the frame and is invalidated. + void SetElementAt(int index, Result* value); + + // Set a frame element to a constant. The index is frame-top relative. + inline void SetElementAt(int index, Handle<Object> value); + + void PushElementAt(int index) { + PushFrameSlotAt(element_count() - index - 1); + } + + void StoreToElementAt(int index) { + StoreToFrameSlotAt(element_count() - index - 1); + } + + // A frame-allocated local as an assembly operand. + Operand LocalAt(int index) { + ASSERT(0 <= index); + ASSERT(index < local_count()); + return Operand(ebp, kLocal0Offset - index * kPointerSize); + } + + // Push a copy of the value of a local frame slot on top of the frame. + void PushLocalAt(int index) { + PushFrameSlotAt(local0_index() + index); + } + + // Push a copy of the value of a local frame slot on top of the frame. + void UntaggedPushLocalAt(int index) { + UntaggedPushFrameSlotAt(local0_index() + index); + } + + // Push the value of a local frame slot on top of the frame and invalidate + // the local slot. The slot should be written to before trying to read + // from it again. + void TakeLocalAt(int index) { + TakeFrameSlotAt(local0_index() + index); + } + + // Store the top value on the virtual frame into a local frame slot. The + // value is left in place on top of the frame. + void StoreToLocalAt(int index) { + StoreToFrameSlotAt(local0_index() + index); + } + + // Push the address of the receiver slot on the frame. + void PushReceiverSlotAddress(); + + // Push the function on top of the frame. + void PushFunction() { + PushFrameSlotAt(function_index()); + } + + // Save the value of the esi register to the context frame slot. + void SaveContextRegister(); + + // Restore the esi register from the value of the context frame + // slot. + void RestoreContextRegister(); + + // A parameter as an assembly operand. + Operand ParameterAt(int index) { + ASSERT(-1 <= index); // -1 is the receiver. + ASSERT(index < parameter_count()); + return Operand(ebp, (1 + parameter_count() - index) * kPointerSize); + } + + // Push a copy of the value of a parameter frame slot on top of the frame. + void PushParameterAt(int index) { + PushFrameSlotAt(param0_index() + index); + } + + // Push a copy of the value of a parameter frame slot on top of the frame. + void UntaggedPushParameterAt(int index) { + UntaggedPushFrameSlotAt(param0_index() + index); + } + + // Push the value of a paramter frame slot on top of the frame and + // invalidate the parameter slot. The slot should be written to before + // trying to read from it again. + void TakeParameterAt(int index) { + TakeFrameSlotAt(param0_index() + index); + } + + // Store the top value on the virtual frame into a parameter frame slot. + // The value is left in place on top of the frame. + void StoreToParameterAt(int index) { + StoreToFrameSlotAt(param0_index() + index); + } + + // The receiver frame slot. + Operand Receiver() { + return ParameterAt(-1); + } + + // Push a try-catch or try-finally handler on top of the virtual frame. + void PushTryHandler(HandlerType type); + + // Call stub given the number of arguments it expects on (and + // removes from) the stack. + inline Result CallStub(CodeStub* stub, int arg_count); + + // Call stub that takes a single argument passed in eax. The + // argument is given as a result which does not have to be eax or + // even a register. The argument is consumed by the call. + Result CallStub(CodeStub* stub, Result* arg); + + // Call stub that takes a pair of arguments passed in edx (arg0) and + // eax (arg1). The arguments are given as results which do not have + // to be in the proper registers or even in registers. The + // arguments are consumed by the call. + Result CallStub(CodeStub* stub, Result* arg0, Result* arg1); + + // Call JS function from top of the stack with arguments + // taken from the stack. + Result CallJSFunction(int arg_count); + + // Call runtime given the number of arguments expected on (and + // removed from) the stack. + Result CallRuntime(const Runtime::Function* f, int arg_count); + Result CallRuntime(Runtime::FunctionId id, int arg_count); + +#ifdef ENABLE_DEBUGGER_SUPPORT + void DebugBreak(); +#endif + + // Invoke builtin given the number of arguments it expects on (and + // removes from) the stack. + Result InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag, int arg_count); + + // Call load IC. Name and receiver are found on top of the frame. + // Both are dropped. + Result CallLoadIC(RelocInfo::Mode mode); + + // Call keyed load IC. Key and receiver are found on top of the + // frame. Both are dropped. + Result CallKeyedLoadIC(RelocInfo::Mode mode); + + // Call store IC. If the load is contextual, value is found on top of the + // frame. If not, value and receiver are on the frame. Both are dropped. + Result CallStoreIC(Handle<String> name, bool is_contextual, + StrictModeFlag strict_mode); + + // Call keyed store IC. Value, key, and receiver are found on top + // of the frame. All three are dropped. + Result CallKeyedStoreIC(StrictModeFlag strict_mode); + + // Call call IC. Function name, arguments, and receiver are found on top + // of the frame and dropped by the call. The argument count does not + // include the receiver. + Result CallCallIC(RelocInfo::Mode mode, int arg_count, int loop_nesting); + + // Call keyed call IC. Same calling convention as CallCallIC. + Result CallKeyedCallIC(RelocInfo::Mode mode, int arg_count, int loop_nesting); + + // Allocate and call JS function as constructor. Arguments, + // receiver (global object), and function are found on top of the + // frame. Function is not dropped. The argument count does not + // include the receiver. + Result CallConstructor(int arg_count); + + // Drop a number of elements from the top of the expression stack. May + // emit code to affect the physical frame. Does not clobber any registers + // excepting possibly the stack pointer. + void Drop(int count); + + // Drop one element. + void Drop() { + Drop(1); + } + + // Duplicate the top element of the frame. + void Dup() { + PushFrameSlotAt(element_count() - 1); + } + + // Pop an element from the top of the expression stack. Returns a + // Result, which may be a constant or a register. + Result Pop(); + + // Pop and save an element from the top of the expression stack and + // emit a corresponding pop instruction. + void EmitPop(Register reg); + void EmitPop(Operand operand); + + // Push an element on top of the expression stack and emit a + // corresponding push instruction. + void EmitPush(Register reg, + TypeInfo info = TypeInfo::Unknown()); + void EmitPush(Operand operand, + TypeInfo info = TypeInfo::Unknown()); + void EmitPush(Immediate immediate, + TypeInfo info = TypeInfo::Unknown()); + + inline bool ConstantPoolOverflowed(); + + // Push an element on the virtual frame. + void Push(Handle<Object> value); + inline void Push(Register reg, TypeInfo info = TypeInfo::Unknown()); + inline void Push(Smi* value); + + void PushUntaggedElement(Handle<Object> value); + + // Pushing a result invalidates it (its contents become owned by the + // frame). + void Push(Result* result) { + // This assert will trigger if you try to push the same value twice. + ASSERT(result->is_valid()); + if (result->is_register()) { + Push(result->reg(), result->type_info()); + } else { + ASSERT(result->is_constant()); + Push(result->handle()); + } + if (cgen()->in_safe_int32_mode()) { + ASSERT(result->is_untagged_int32()); + elements_[element_count() - 1].set_untagged_int32(true); + } + result->Unuse(); + } + + // Pushing an expression expects that the expression is trivial (according + // to Expression::IsTrivial). + void Push(Expression* expr); + + // Nip removes zero or more elements from immediately below the top + // of the frame, leaving the previous top-of-frame value on top of + // the frame. Nip(k) is equivalent to x = Pop(), Drop(k), Push(x). + inline void Nip(int num_dropped); + + // Check that the frame has no elements containing untagged int32 elements. + bool HasNoUntaggedInt32Elements() { + for (int i = 0; i < element_count(); ++i) { + if (elements_[i].is_untagged_int32()) return false; + } + return true; + } + + // Update the type information of a variable frame element directly. + inline void SetTypeForLocalAt(int index, TypeInfo info); + inline void SetTypeForParamAt(int index, TypeInfo info); + + private: + static const int kLocal0Offset = JavaScriptFrameConstants::kLocal0Offset; + static const int kFunctionOffset = JavaScriptFrameConstants::kFunctionOffset; + static const int kContextOffset = StandardFrameConstants::kContextOffset; + + static const int kHandlerSize = StackHandlerConstants::kSize / kPointerSize; + static const int kPreallocatedElements = 5 + 8; // 8 expression stack slots. + + ZoneList<FrameElement> elements_; + + // The index of the element that is at the processor's stack pointer + // (the esp register). + int stack_pointer_; + + // The index of the register frame element using each register, or + // kIllegalIndex if a register is not on the frame. + int register_locations_[RegisterAllocator::kNumRegisters]; + + // The number of frame-allocated locals and parameters respectively. + inline int parameter_count(); + + inline int local_count(); + + // The index of the element that is at the processor's frame pointer + // (the ebp register). The parameters, receiver, and return address + // are below the frame pointer. + int frame_pointer() { + return parameter_count() + 2; + } + + // The index of the first parameter. The receiver lies below the first + // parameter. + int param0_index() { + return 1; + } + + // The index of the context slot in the frame. It is immediately + // above the frame pointer. + int context_index() { + return frame_pointer() + 1; + } + + // The index of the function slot in the frame. It is above the frame + // pointer and the context slot. + int function_index() { + return frame_pointer() + 2; + } + + // The index of the first local. Between the frame pointer and the + // locals lie the context and the function. + int local0_index() { + return frame_pointer() + 3; + } + + // The index of the base of the expression stack. + int expression_base_index() { + return local0_index() + local_count(); + } + + // Convert a frame index into a frame pointer relative offset into the + // actual stack. + int fp_relative(int index) { + ASSERT(index < element_count()); + ASSERT(frame_pointer() < element_count()); // FP is on the frame. + return (frame_pointer() - index) * kPointerSize; + } + + // Record an occurrence of a register in the virtual frame. This has the + // effect of incrementing the register's external reference count and + // of updating the index of the register's location in the frame. + void Use(Register reg, int index) { + ASSERT(!is_used(reg)); + set_register_location(reg, index); + cgen()->allocator()->Use(reg); + } + + // Record that a register reference has been dropped from the frame. This + // decrements the register's external reference count and invalidates the + // index of the register's location in the frame. + void Unuse(Register reg) { + ASSERT(is_used(reg)); + set_register_location(reg, kIllegalIndex); + cgen()->allocator()->Unuse(reg); + } + + // Spill the element at a particular index---write it to memory if + // necessary, free any associated register, and forget its value if + // constant. + void SpillElementAt(int index); + + // Sync the element at a particular index. If it is a register or + // constant that disagrees with the value on the stack, write it to memory. + // Keep the element type as register or constant, and clear the dirty bit. + void SyncElementAt(int index); + + // Sync a single unsynced element that lies beneath or at the stack pointer. + void SyncElementBelowStackPointer(int index); + + // Sync a single unsynced element that lies just above the stack pointer. + void SyncElementByPushing(int index); + + // Push a copy of a frame slot (typically a local or parameter) on top of + // the frame. + inline void PushFrameSlotAt(int index); + + // Push a copy of a frame slot (typically a local or parameter) on top of + // the frame, at an untagged int32 value. Bails out if the value is not + // an int32. + void UntaggedPushFrameSlotAt(int index); + + // Push a the value of a frame slot (typically a local or parameter) on + // top of the frame and invalidate the slot. + void TakeFrameSlotAt(int index); + + // Store the value on top of the frame to a frame slot (typically a local + // or parameter). + void StoreToFrameSlotAt(int index); + + // Spill all elements in registers. Spill the top spilled_args elements + // on the frame. Sync all other frame elements. + // Then drop dropped_args elements from the virtual frame, to match + // the effect of an upcoming call that will drop them from the stack. + void PrepareForCall(int spilled_args, int dropped_args); + + // Move frame elements currently in registers or constants, that + // should be in memory in the expected frame, to memory. + void MergeMoveRegistersToMemory(VirtualFrame* expected); + + // Make the register-to-register moves necessary to + // merge this frame with the expected frame. + // Register to memory moves must already have been made, + // and memory to register moves must follow this call. + // This is because some new memory-to-register moves are + // created in order to break cycles of register moves. + // Used in the implementation of MergeTo(). + void MergeMoveRegistersToRegisters(VirtualFrame* expected); + + // Make the memory-to-register and constant-to-register moves + // needed to make this frame equal the expected frame. + // Called after all register-to-memory and register-to-register + // moves have been made. After this function returns, the frames + // should be equal. + void MergeMoveMemoryToRegisters(VirtualFrame* expected); + + // Invalidates a frame slot (puts an invalid frame element in it). + // Copies on the frame are correctly handled, and if this slot was + // the backing store of copies, the index of the new backing store + // is returned. Otherwise, returns kIllegalIndex. + // Register counts are correctly updated. + int InvalidateFrameSlotAt(int index); + + // This function assumes that a and b are the only results that could be in + // the registers a_reg or b_reg. Other results can be live, but must not + // be in the registers a_reg or b_reg. The results a and b are invalidated. + void MoveResultsToRegisters(Result* a, + Result* b, + Register a_reg, + Register b_reg); + + // Call a code stub that has already been prepared for calling (via + // PrepareForCall). + Result RawCallStub(CodeStub* stub); + + // Calls a code object which has already been prepared for calling + // (via PrepareForCall). + Result RawCallCodeObject(Handle<Code> code, RelocInfo::Mode rmode); + + inline bool Equals(VirtualFrame* other); + + // Classes that need raw access to the elements_ array. + friend class FrameRegisterState; + friend class JumpTarget; +}; + +} } // namespace v8::internal + +#endif // V8_IA32_VIRTUAL_FRAME_IA32_H_ |