Author: [EMAIL PROTECTED] Date: Fri Nov 28 00:41:54 2008 New Revision: 861
Added: branches/experimental/toiger/src/register-allocator-ia32.cc branches/experimental/toiger/src/register-allocator-ia32.h branches/experimental/toiger/src/register-allocator.h Modified: branches/experimental/toiger/src/SConscript branches/experimental/toiger/src/codegen-ia32.cc branches/experimental/toiger/src/codegen-ia32.h branches/experimental/toiger/src/virtual-frame-ia32.cc branches/experimental/toiger/src/virtual-frame-ia32.h Log: Begin counting references to registers, both in the frame and out. Allocation/deallocation is explicit. We still almost always spill everything and then use registers directly (without allocating them). None of this has been ported to the ARM. Review URL: http://codereview.chromium.org/11396 Modified: branches/experimental/toiger/src/SConscript ============================================================================== --- branches/experimental/toiger/src/SConscript (original) +++ branches/experimental/toiger/src/SConscript Fri Nov 28 00:41:54 2008 @@ -54,10 +54,10 @@ 'macro-assembler-arm.cc', 'stub-cache-arm.cc', 'virtual-frame-arm.cc' ], 'arch:ia32': [ - 'assembler-ia32.cc', 'builtins-ia32.cc', 'codegen-ia32.cc', - 'cpu-ia32.cc', 'disasm-ia32.cc', 'frames-ia32.cc', 'ic-ia32.cc', - 'jump-target-ia32.cc', 'macro-assembler-ia32.cc', 'stub-cache-ia32.cc', - 'virtual-frame-ia32.cc' + 'assembler-ia32.cc', 'builtins-ia32.cc', 'codegen-ia32.cc', 'cpu-ia32.cc', + 'disasm-ia32.cc', 'frames-ia32.cc', 'ic-ia32.cc', 'jump-target-ia32.cc', + 'macro-assembler-ia32.cc', 'register-allocator-ia32.cc', + 'stub-cache-ia32.cc', 'virtual-frame-ia32.cc' ], 'simulator:arm': ['simulator-arm.cc'], 'os:linux': ['platform-linux.cc'], Modified: branches/experimental/toiger/src/codegen-ia32.cc ============================================================================== --- branches/experimental/toiger/src/codegen-ia32.cc (original) +++ branches/experimental/toiger/src/codegen-ia32.cc Fri Nov 28 00:41:54 2008 @@ -80,6 +80,7 @@ masm_(new MacroAssembler(NULL, buffer_size)), scope_(NULL), frame_(NULL), + allocator_(NULL), cc_reg_(no_condition), state_(NULL), is_inside_try_(false), @@ -106,6 +107,9 @@ scope_ = fun->scope(); ASSERT(frame_ == NULL); set_frame(new VirtualFrame(this)); + ASSERT(allocator_ == NULL); + RegisterAllocator register_allocator(this); + allocator_ = ®ister_allocator; cc_reg_ = no_condition; function_return_.set_code_generator(this); function_return_is_shadowed_ = false; @@ -123,54 +127,39 @@ // edi: caller's parameter pointer // esi: callee's context + allocator_->Initialize(); frame_->Enter(); // tos: code slot #ifdef DEBUG if (strlen(FLAG_stop_at) > 0 && fun->name()->IsEqualTo(CStrVector(FLAG_stop_at))) { + frame_->SpillAll(); __ int3(); } #endif - // This section now only allocates and copies the formals into the - // arguments object. It saves the address in ecx, which is saved - // at any point before either garbage collection or ecx is - // overwritten. The flag arguments_array_allocated communicates - // with the store into the arguments variable and guards the lazy - // pushes of ecx to TOS. The flag arguments_array_saved notes - // when the push has happened. - bool arguments_object_allocated = false; - bool arguments_object_saved = false; - - // Allocate arguments object. - // The arguments object pointer needs to be saved in ecx, since we need - // to store arguments into the context. + // Allocate space for locals and initialize them. + frame_->AllocateStackSlots(scope_->num_stack_slots()); + + // Allocate the arguments object and copy the parameters into it. if (scope_->arguments() != NULL) { ASSERT(scope_->arguments_shadow() != NULL); - Comment cmnt(masm_, "[ allocate arguments object"); + Comment cmnt(masm_, "[ Allocate arguments object"); ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT); + frame_->SpillAll(); __ lea(eax, frame_->Receiver()); frame_->EmitPush(frame_->Function()); frame_->EmitPush(eax); frame_->EmitPush(Immediate(Smi::FromInt(scope_->num_parameters()))); frame_->CallStub(&stub, 3); - __ mov(ecx, Operand(eax)); - arguments_object_allocated = true; + frame_->Push(eax); } - // Allocate space for locals and initialize them. - frame_->AllocateStackSlots(scope_->num_stack_slots()); - if (scope_->num_heap_slots() > 0) { - frame_->SpillAll(); Comment cmnt(masm_, "[ allocate local context"); - // Save the arguments object pointer, if any. - if (arguments_object_allocated && !arguments_object_saved) { - frame_->EmitPush(ecx); - arguments_object_saved = true; - } // Allocate local context. // Get outer context and create a new context based on it. + frame_->SpillAll(); frame_->EmitPush(frame_->Function()); frame_->CallRuntime(Runtime::kNewContext, 1); // eax holds the result @@ -205,6 +194,7 @@ Variable* par = scope_->parameter(i); Slot* slot = par->slot(); if (slot != NULL && slot->type() == Slot::CONTEXT) { + frame_->SpillAll(); ASSERT(!scope_->is_global_scope()); // no parameters in global scope __ mov(eax, frame_->ParameterAt(i)); // Loads ecx with context; used below in RecordWrite. @@ -222,27 +212,16 @@ // Store the arguments object. This must happen after context // initialization because the arguments object may be stored in the // context. - if (arguments_object_allocated) { - ASSERT(scope_->arguments() != NULL); - ASSERT(scope_->arguments_shadow() != NULL); + if (scope_->arguments() != NULL) { + frame_->SpillAll(); Comment cmnt(masm_, "[ store arguments object"); { Reference shadow_ref(this, scope_->arguments_shadow()); ASSERT(shadow_ref.is_slot()); { Reference arguments_ref(this, scope_->arguments()); ASSERT(arguments_ref.is_slot()); - // If the newly-allocated arguments object is already on the - // stack, we make use of the convenient property that references - // representing slots take up no space on the expression stack - // (ie, it doesn't matter that the stored value is actually below - // the reference). - // - // If the newly-allocated argument object is not already on - // the stack, we rely on the property that loading a - // zero-sized reference will not clobber the ecx register. - if (!arguments_object_saved) { - frame_->SpillAll(); - frame_->EmitPush(ecx); - } + // Here we rely on the convenient property that references to slot + // take up zero space in the frame (ie, it doesn't matter that the + // stored value is actually below the reference on the frame). arguments_ref.SetValue(NOT_CONST_INIT); } shadow_ref.SetValue(NOT_CONST_INIT); @@ -268,6 +247,7 @@ frame_->CallRuntime(Runtime::kTraceEnter, 0); // Ignore the return value. } + frame_->SpillAll(); CheckStack(); // Compile the body of the function in a vanilla state. Don't @@ -302,13 +282,16 @@ loop_nesting_ -= fun->loop_nesting(); // Code generation state must be reset. + ASSERT(state_ == NULL); + ASSERT(loop_nesting() == 0); ASSERT(!function_return_is_shadowed_); function_return_.Unuse(); - scope_ = NULL; - delete_frame(); ASSERT(!has_cc()); - ASSERT(state_ == NULL); - ASSERT(loop_nesting() == 0); + // There is no need to delete the register allocator, it is a + // stack-allocated local. + allocator_ = NULL; + delete_frame(); + scope_ = NULL; } @@ -1195,8 +1178,8 @@ void CodeGenerator::SmiComparison(Condition cc, - Handle<Object> value, - bool strict) { + Handle<Object> value, + bool strict) { // Strict only makes sense for equality comparisons. ASSERT(!strict || cc == equal); Modified: branches/experimental/toiger/src/codegen-ia32.h ============================================================================== --- branches/experimental/toiger/src/codegen-ia32.h (original) +++ branches/experimental/toiger/src/codegen-ia32.h Fri Nov 28 00:41:54 2008 @@ -29,6 +29,7 @@ #define V8_CODEGEN_IA32_H_ #include "scopes.h" +#include "register-allocator.h" namespace v8 { namespace internal { @@ -173,14 +174,14 @@ MacroAssembler* masm() { return masm_; } VirtualFrame* frame() const { return frame_; } - void set_frame(VirtualFrame* frame) { frame_ = frame; } - void delete_frame() { delete frame_; frame_ = NULL; } + RegisterAllocator* allocator() const { return allocator_; } + CodeGenState* state() { return state_; } void set_state(CodeGenState* state) { state_ = state; } @@ -389,6 +390,7 @@ // Code generation state Scope* scope_; VirtualFrame* frame_; + RegisterAllocator* allocator_; Condition cc_reg_; CodeGenState* state_; bool is_inside_try_; Added: branches/experimental/toiger/src/register-allocator-ia32.cc ============================================================================== --- (empty file) +++ branches/experimental/toiger/src/register-allocator-ia32.cc Fri Nov 28 00:41:54 2008 @@ -0,0 +1,66 @@ +// Copyright 2008 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. + +#include "v8.h" + +#include "codegen.h" +#include "register-allocator.h" + +namespace v8 { namespace internal { + +void RegisterAllocator::Initialize() { + registers_.Reset(); + registers_.Use(esp); + registers_.Use(ebp); + registers_.Use(esi); + registers_.Use(edi); +} + + +Register RegisterAllocator::Allocate() { + // Return the first free register, if any. + for (int i = 0; i < num_registers(); i++) { + if (!registers_.is_used(i)) { + Register result = { i }; + registers_.Use(result); + return result; + } + } + + // Ask the current frame to spill a register. + ASSERT(code_generator_->frame() != NULL); + Register result = code_generator_->frame()->SpillAnyRegister(); + if (!result.is(no_reg)) { + ASSERT(!registers_.is_used(result.code())); + registers_.Use(result); + } + + return result; +} + + +} } // namespace v8::internal Added: branches/experimental/toiger/src/register-allocator-ia32.h ============================================================================== --- (empty file) +++ branches/experimental/toiger/src/register-allocator-ia32.h Fri Nov 28 00:41:54 2008 @@ -0,0 +1,111 @@ +// Copyright 2008 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_REGISTER_ALLOCATOR_IA32_H_ +#define V8_REGISTER_ALLOCATOR_IA32_H_ + +#include "macro-assembler.h" + +namespace v8 { namespace internal { + +// ------------------------------------------------------------------------- +// Register file +// +// The register file tracks reference counts for the processor registers. +// It is used by both the register allocator and the virtual frame. + +class RegisterFile BASE_EMBEDDED { + public: + RegisterFile() { Reset(); } + + void Reset() { + for (int i = 0; i < kNumRegisters; i++) { + ref_counts_[i] = 0; + } + } + + // Predicates and accessors for the reference counts. They take a + // register code rather than a register because they are frequently used + // in a loop over the register codes. + bool is_used(int reg_code) const { return ref_counts_[reg_code] > 0; } + int count(int reg_code) const { return ref_counts_[reg_code]; } + + // Record a use of a register by incrementing its reference count. + void Use(Register reg) { + ref_counts_[reg.code()]++; + } + + // Record that a register will no longer be used by decrementing its + // reference count. + void Unuse(Register reg) { + ASSERT(is_used(reg.code())); + if (is_used(reg.code())) { + ref_counts_[reg.code()]--; + } + } + + static const int kNumRegisters = 8; + + private: + int ref_counts_[kNumRegisters]; +}; + + +// ------------------------------------------------------------------------- +// Register allocator +// + +class RegisterAllocator BASE_EMBEDDED { + public: + RegisterAllocator(CodeGenerator* cgen) : code_generator_(cgen) {} + + int num_registers() const { return RegisterFile::kNumRegisters; } + + int count(int reg_code) { return registers_.count(reg_code); } + + // Explicitly record a reference to a register. + void Use(Register reg) { registers_.Use(reg); } + + // Explicitly record that a register will no lonber be used. + void Unuse(Register reg) { registers_.Unuse(reg); } + + // Initialize the register allocator for entry to a JS function. On + // entry, esp, ebp, esi, and edi are externally referenced (ie, outside + // the virtual frame); and the other registers are free. + void Initialize(); + + // Allocate a free register if possible or fail by returning no_reg. + Register Allocate(); + + private: + CodeGenerator* code_generator_; + RegisterFile registers_; +}; + +} } // namespace v8::internal + +#endif // V8_REGISTER_ALLOCATOR_IA32_H_ Added: branches/experimental/toiger/src/register-allocator.h ============================================================================== --- (empty file) +++ branches/experimental/toiger/src/register-allocator.h Fri Nov 28 00:41:54 2008 @@ -0,0 +1,36 @@ +// Copyright 2008 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_REGISTER_ALLOCATOR_H_ +#define V8_REGISTER_ALLOCATOR_H_ + +#if defined(ARM) || defined(__arm__) || defined(__thumb__) +#else // ia32 +#include "register-allocator-ia32.h" +#endif + +#endif // V8_REGISTER_ALLOCATOR_H_ Modified: branches/experimental/toiger/src/virtual-frame-ia32.cc ============================================================================== --- branches/experimental/toiger/src/virtual-frame-ia32.cc (original) +++ branches/experimental/toiger/src/virtual-frame-ia32.cc Fri Nov 28 00:41:54 2008 @@ -41,26 +41,30 @@ // On entry to a function, the virtual frame already contains the receiver, // the parameters, and a return address. All frame elements are in memory. VirtualFrame::VirtualFrame(CodeGenerator* cgen) - : masm_(cgen->masm()), + : cgen_(cgen), + masm_(cgen->masm()), elements_(0), parameter_count_(cgen->scope()->num_parameters()), local_count_(0), stack_pointer_(parameter_count_ + 1), // 0-based index of TOS. frame_pointer_(kIllegalIndex) { + FrameElement memory_element; for (int i = 0; i < parameter_count_ + 2; i++) { - elements_.Add(FrameElement()); + elements_.Add(memory_element); } } // When cloned, a frame is a deep copy of the original. VirtualFrame::VirtualFrame(VirtualFrame* original) - : masm_(original->masm_), + : cgen_(original->cgen_), + masm_(original->masm_), elements_(original->elements_.length()), parameter_count_(original->parameter_count_), local_count_(original->local_count_), stack_pointer_(original->stack_pointer_), - frame_pointer_(original->frame_pointer_) { + frame_pointer_(original->frame_pointer_), + frame_registers_(original->frame_registers_) { // Copy all the elements from the original. for (int i = 0; i < original->elements_.length(); i++) { elements_.Add(original->elements_[i]); @@ -94,18 +98,33 @@ stack_pointer_ -= count; for (int i = 0; i < count; i++) { - elements_.RemoveLast(); + FrameElement last = elements_.RemoveLast(); + if (last.is_register()) { + Unuse(last.reg()); + } } } +void VirtualFrame::Use(Register reg) { + frame_registers_.Use(reg); + cgen_->allocator()->Use(reg); +} + + +void VirtualFrame::Unuse(Register reg) { + frame_registers_.Unuse(reg); + cgen_->allocator()->Unuse(reg); +} + + // Clear the dirty bit for the element at a given index. We can only // allocate space in the actual frame for the virtual element immediately // above the stack pointer. void VirtualFrame::SyncElementAt(int index) { FrameElement element = elements_[index]; - if (element.is_dirty()) { + if (!element.is_synced()) { if (index <= stack_pointer_) { // Write elements below the stack pointer to their (already allocated) // actual frame location. @@ -128,7 +147,55 @@ __ push(element.reg()); } } + + elements_[index].set_sync(); + } +} + + +// Spill any register if possible, making its reference count zero. +Register VirtualFrame::SpillAnyRegister() { + // Find the leftmost (ordered by register code), least + // internally-referenced register whose internal reference count matches + // its external reference count (so that spilling it from the frame frees + // it for use). + int min_count = kMaxInt; + int best_register_code = no_reg.code(); + + for (int i = 0; i < RegisterFile::kNumRegisters; i++) { + int count = frame_registers_.count(i); + if (count < min_count && count == cgen_->allocator()->count(i)) { + min_count = count; + best_register_code = i; + } + } + + if (best_register_code != no_reg.code()) { + // Spill all occurrences of the register. There are min_count + // occurrences, stop when we've spilled them all to avoid syncing + // elements unnecessarily. + int i = 0; + while (min_count > 0) { + ASSERT(i < elements_.length()); + if (elements_[i].is_register() && + elements_[i].reg().code() == best_register_code) { + // Found an instance of the best_register being used in the frame. + // Spill it. + SpillElementAt(i); + min_count--; + } else { + if (i > stack_pointer_) { + // Make sure to materialize elements on the virtual frame in + // memory. We rely on this to spill occurrences of the register + // lying above the current virtual stack pointer. + SyncElementAt(i); + } + } + } } + + Register result = { best_register_code }; + return result; } @@ -138,10 +205,21 @@ void VirtualFrame::SpillElementAt(int index) { SyncElementAt(index); // The element is now in memory. + if (elements_[index].is_register()) { + Unuse(elements_[index].reg()); + } elements_[index] = FrameElement(); } +// Clear the dirty bits for all elements. +void VirtualFrame::SyncAll() { + for (int i = 0; i < elements_.length(); i++) { + SyncElementAt(i); + } +} + + // Make the type of all elements be MEMORY. void VirtualFrame::SpillAll() { for (int i = 0; i < elements_.length(); i++) { @@ -186,6 +264,7 @@ void VirtualFrame::MergeTo(VirtualFrame* expected) { + ASSERT(cgen_ == expected->cgen_); ASSERT(masm_ == expected->masm_); ASSERT(elements_.length() == expected->elements_.length()); ASSERT(parameter_count_ == expected->parameter_count_); @@ -204,6 +283,7 @@ void VirtualFrame::Enter() { + // Registers live on entry: esp, ebp, esi, edi. Comment cmnt(masm_, "[ Enter JS frame"); EmitPush(ebp); @@ -211,23 +291,12 @@ __ mov(ebp, Operand(esp)); // Store the context and the function in the frame. - FrameElement context(esi); - context.clear_dirty(); - elements_.Add(context); - stack_pointer_++; - __ push(esi); - - FrameElement function(edi); - function.clear_dirty(); - elements_.Add(function); - stack_pointer_++; - __ push(edi); + Push(esi); + // The frame owns the register reference now. + cgen_->allocator()->Unuse(esi); - // Clear the function slot when generating debug code. - if (FLAG_debug_code) { - SpillElementAt(stack_pointer_); - __ Set(edi, Immediate(reinterpret_cast<int>(kZapValue))); - } + Push(edi); + cgen_->allocator()->Unuse(edi); } @@ -244,7 +313,10 @@ __ mov(esp, Operand(ebp)); stack_pointer_ = frame_pointer_; for (int i = elements_.length() - 1; i > stack_pointer_; i--) { - elements_.RemoveLast(); + FrameElement last = elements_.RemoveLast(); + if (last.is_register()) { + Unuse(last.reg()); + } } frame_pointer_ = kIllegalIndex; @@ -260,14 +332,17 @@ Comment cmnt(masm_, "[ Allocate space for locals"); // The locals are constants (the undefined value), but we sync them with // the actual frame to allocate space for spilling them. - FrameElement initial_value(Factory::undefined_value()); - initial_value.clear_dirty(); - __ Set(eax, Immediate(Factory::undefined_value())); + SyncAll(); + Handle<Object> undefined = Factory::undefined_value(); + FrameElement initial_value(undefined, FrameElement::SYNCED); + Register tmp = cgen_->allocator()->Allocate(); + __ Set(tmp, Immediate(undefined)); for (int i = 0; i < count; i++) { elements_.Add(initial_value); stack_pointer_++; - __ push(eax); + __ push(tmp); } + cgen_->allocator()->Unuse(tmp); } } @@ -323,7 +398,10 @@ // Discard elements above the stack pointer. while (count > 0 && stack_pointer_ < elements_.length() - 1) { - elements_.RemoveLast(); + FrameElement last = elements_.RemoveLast(); + if (last.is_register()) { + Unuse(last.reg()); + } } // Discard the rest of the elements and lower the stack pointer. @@ -371,9 +449,23 @@ void VirtualFrame::EmitPush(Immediate immediate) { ASSERT(stack_pointer_ == elements_.length() - 1); - elements_.Add(FrameElement()); + FrameElement memory_element; + elements_.Add(memory_element); stack_pointer_++; __ push(immediate); +} + + +void VirtualFrame::Push(Register reg) { + FrameElement register_element(reg, FrameElement::NOT_SYNCED); + Use(reg); + elements_.Add(register_element); +} + + +void VirtualFrame::Push(Handle<Object> value) { + FrameElement constant_element(value, FrameElement::NOT_SYNCED); + elements_.Add(constant_element); } Modified: branches/experimental/toiger/src/virtual-frame-ia32.h ============================================================================== --- branches/experimental/toiger/src/virtual-frame-ia32.h (original) +++ branches/experimental/toiger/src/virtual-frame-ia32.h Fri Nov 28 00:41:54 2008 @@ -29,44 +29,53 @@ #define V8_VIRTUAL_FRAME_IA32_H_ #include "macro-assembler.h" +#include "register-allocator.h" namespace v8 { namespace internal { // ------------------------------------------------------------------------- // Virtual frame elements // -// The internal elements of the virtual frames. Elements are (currently) of -// only one kind, in-memory. Their actual location is given by their -// position in the virtual frame. +// The internal elements of the virtual frames. There are several kinds of +// elements: +// * Memory: an element that resides in the actual frame. Its address is +// given by its position in the virtual frame. +// * Register: an element that resides in a register. +// * Constant: an element whose value is known at compile time. class FrameElement BASE_EMBEDDED { public: + enum SyncFlag { SYNCED, NOT_SYNCED }; + + // Construct an in-memory frame element. FrameElement() { - type_ = TypeField::encode(MEMORY) | DirtyField::encode(false); - // Memory elements have no useful data. + type_ = TypeField::encode(MEMORY) | SyncField::encode(SYNCED); + // In-memory elements have no useful data. data_.reg_ = no_reg; } - explicit FrameElement(Register reg) { - type_ = TypeField::encode(REGISTER) | DirtyField::encode(true); + // Construct an in-register frame element. + FrameElement(Register reg, SyncFlag is_synced) { + type_ = TypeField::encode(REGISTER) | SyncField::encode(is_synced); data_.reg_ = reg; } - explicit FrameElement(Handle<Object> value) { - type_ = TypeField::encode(CONSTANT) | DirtyField::encode(true); + // Construct a frame element whose value is known at compile time. + FrameElement(Handle<Object> value, SyncFlag is_synced) { + type_ = TypeField::encode(CONSTANT) | SyncField::encode(is_synced); data_.handle_ = value.location(); } - bool is_dirty() const { return DirtyField::decode(type_); } + bool is_synced() const { return SyncField::decode(type_) == SYNCED; } - void set_dirty() { + void set_sync() { ASSERT(type() != MEMORY); - type_ = type_ | DirtyField::encode(true); + type_ = type_ | SyncField::encode(SYNCED); } - void clear_dirty() { + void clear_sync() { ASSERT(type() != MEMORY); - type_ = type_ & ~DirtyField::mask(); + type_ = type_ & ~SyncField::mask(); } bool is_register() const { return type() == REGISTER; } @@ -86,7 +95,7 @@ enum Type { MEMORY, REGISTER, CONSTANT }; // BitField is <type, shift, size>. - class DirtyField : public BitField<bool, 0, 1> {}; + class SyncField : public BitField<SyncFlag, 0, 1> {}; class TypeField : public BitField<Type, 1, 32 - 1> {}; Type type() const { return TypeField::decode(type_); } @@ -137,6 +146,10 @@ // Spill all values from the frame to memory. void SpillAll(); + // Spill a register if possible. Return the register spilled or no_reg if + // it was not possible to spill one. + Register SpillAnyRegister(); + // Ensure that this frame is in a state where an arbitrary frame of the // right size could be merged to it. May emit code. void EnsureMergable(); @@ -231,6 +244,10 @@ void EmitPush(Operand operand); void EmitPush(Immediate immediate); + // Push an element on the virtual frame. + void Push(Register reg); + void Push(Handle<Object> value); + private: // An illegal index into the virtual frame. static const int kIllegalIndex = -1; @@ -241,6 +258,7 @@ static const int kHandlerSize = StackHandlerConstants::kSize / kPointerSize; + CodeGenerator* cgen_; MacroAssembler* masm_; List<FrameElement> elements_; @@ -256,14 +274,34 @@ // (the ebp register). int frame_pointer_; + // The frame has an embedded register file that it uses to track registers + // used in the frame. + RegisterFile frame_registers_; + // The index of the first parameter. The receiver lies below the first // parameter. int param0_index() const { return 1; } + // The index of the context slot in the frame. + int context_index() const { + ASSERT(frame_pointer_ != kIllegalIndex); + return frame_pointer_ + 1; + } + + // The index of the function slot in the frame. It lies above the context + // slot. + int function_index() const { + ASSERT(frame_pointer_ != kIllegalIndex); + return frame_pointer_ + 2; + } + // The index of the first local. Between the parameters and the locals // lie the return address, the saved frame pointer, the context, and the // function. - int local0_index() const { return param0_index() + parameter_count_ + 4; } + int local0_index() const { + ASSERT(frame_pointer_ != kIllegalIndex); + return frame_pointer_ + 3; + } // The index of the base of the expression stack. int expression_base_index() const { return local0_index() + local_count_; } @@ -274,6 +312,15 @@ return (frame_pointer_ - index) * kPointerSize; } + // Record an occurrence of a register in the virtual frame. This has the + // effect of incrementing both the register's frame-internal reference + // count and its external reference count. + void Use(Register reg); + + // Record that a register reference has been dropped from the frame. This + // decrements both the register's internal and external reference counts. + void Unuse(Register reg); + // Sync the element at a particular index---write it to memory if // necessary, but do not free any associated register or forget its value // if constant. Space should have already been allocated in the actual @@ -285,6 +332,9 @@ // constant. Space should have already been allocated in the actual frame // for all the elements below this one (at least). void SpillElementAt(int index); + + // Sync all elements in the frame. + void SyncAll(); // Spill the topmost elements of the frame to memory (eg, they are the // arguments to a call) and all registers. --~--~---------~--~----~------------~-------~--~----~ v8-dev mailing list v8-dev@googlegroups.com http://groups.google.com/group/v8-dev -~----------~----~----~----~------~----~------~--~---