diff options
Diffstat (limited to 'deps/v8/src')
246 files changed, 10837 insertions, 5186 deletions
diff --git a/deps/v8/src/SConscript b/deps/v8/src/SConscript index 59c452b7ca..64d20631ca 100755 --- a/deps/v8/src/SConscript +++ b/deps/v8/src/SConscript @@ -43,14 +43,14 @@ SOURCES = { 'flags.cc', 'frames.cc', 'func-name-inferrer.cc', 'global-handles.cc', 'handles.cc', 'hashmap.cc', 'heap.cc', 'ic.cc', 'interpreter-irregexp.cc', 'jsregexp.cc', - 'jump-target.cc', 'log.cc', 'mark-compact.cc', 'messages.cc', 'objects.cc', - 'oprofile-agent.cc', 'parser.cc', 'property.cc', 'regexp-macro-assembler.cc', - 'regexp-macro-assembler-irregexp.cc', 'regexp-stack.cc', - 'register-allocator.cc', 'rewriter.cc', 'runtime.cc', 'scanner.cc', - 'scopeinfo.cc', 'scopes.cc', 'serialize.cc', 'snapshot-common.cc', - 'spaces.cc', 'string-stream.cc', 'stub-cache.cc', 'token.cc', 'top.cc', - 'unicode.cc', 'usage-analyzer.cc', 'utils.cc', 'v8-counters.cc', - 'v8.cc', 'v8threads.cc', 'variables.cc', 'version.cc', + 'jump-target.cc', 'log.cc', 'log-utils.cc', 'mark-compact.cc', 'messages.cc', + 'objects.cc', 'oprofile-agent.cc', 'parser.cc', 'property.cc', + 'regexp-macro-assembler.cc', 'regexp-macro-assembler-irregexp.cc', + 'regexp-stack.cc', 'register-allocator.cc', 'rewriter.cc', 'runtime.cc', + 'scanner.cc', 'scopeinfo.cc', 'scopes.cc', 'serialize.cc', + 'snapshot-common.cc', 'spaces.cc', 'string-stream.cc', 'stub-cache.cc', + 'token.cc', 'top.cc', 'unicode.cc', 'usage-analyzer.cc', 'utils.cc', + 'v8-counters.cc', 'v8.cc', 'v8threads.cc', 'variables.cc', 'version.cc', 'virtual-frame.cc', 'zone.cc' ], 'arch:arm': [ diff --git a/deps/v8/src/accessors.cc b/deps/v8/src/accessors.cc index 4cd93be870..ac6cdf95ac 100644 --- a/deps/v8/src/accessors.cc +++ b/deps/v8/src/accessors.cc @@ -34,7 +34,8 @@ #include "top.h" #include "zone-inl.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { template <class C> @@ -288,6 +289,24 @@ const AccessorDescriptor Accessors::ScriptType = { // +// Accessors::ScriptCompilationType +// + + +Object* Accessors::ScriptGetCompilationType(Object* object, void*) { + Object* script = JSValue::cast(object)->value(); + return Script::cast(script)->compilation_type(); +} + + +const AccessorDescriptor Accessors::ScriptCompilationType = { + ScriptGetCompilationType, + IllegalSetter, + 0 +}; + + +// // Accessors::ScriptGetLineEnds // @@ -313,9 +332,8 @@ const AccessorDescriptor Accessors::ScriptLineEnds = { Object* Accessors::ScriptGetContextData(Object* object, void*) { - HandleScope scope; - Handle<Script> script(Script::cast(JSValue::cast(object)->value())); - return script->context_data(); + Object* script = JSValue::cast(object)->value(); + return Script::cast(script)->context_data(); } @@ -327,6 +345,54 @@ const AccessorDescriptor Accessors::ScriptContextData = { // +// Accessors::ScriptGetEvalFromFunction +// + + +Object* Accessors::ScriptGetEvalFromFunction(Object* object, void*) { + Object* script = JSValue::cast(object)->value(); + return Script::cast(script)->eval_from_function(); +} + + +const AccessorDescriptor Accessors::ScriptEvalFromFunction = { + ScriptGetEvalFromFunction, + IllegalSetter, + 0 +}; + + +// +// Accessors::ScriptGetEvalFromPosition +// + + +Object* Accessors::ScriptGetEvalFromPosition(Object* object, void*) { + HandleScope scope; + Handle<Script> script(Script::cast(JSValue::cast(object)->value())); + + // If this is not a script compiled through eval there is no eval position. + int compilation_type = Smi::cast(script->compilation_type())->value(); + if (compilation_type != Script::COMPILATION_TYPE_EVAL) { + return Heap::undefined_value(); + } + + // Get the function from where eval was called and find the source position + // from the instruction offset. + Handle<Code> code(JSFunction::cast(script->eval_from_function())->code()); + return Smi::FromInt(code->SourcePosition(code->instruction_start() + + script->eval_from_instructions_offset()->value())); +} + + +const AccessorDescriptor Accessors::ScriptEvalFromPosition = { + ScriptGetEvalFromPosition, + IllegalSetter, + 0 +}; + + +// // Accessors::FunctionPrototype // diff --git a/deps/v8/src/accessors.h b/deps/v8/src/accessors.h index 1dd8fdd2f2..51d322ec8c 100644 --- a/deps/v8/src/accessors.h +++ b/deps/v8/src/accessors.h @@ -28,27 +28,31 @@ #ifndef V8_ACCESSORS_H_ #define V8_ACCESSORS_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // The list of accessor descriptors. This is a second-order macro // taking a macro to be applied to all accessor descriptor names. #define ACCESSOR_DESCRIPTOR_LIST(V) \ - V(FunctionPrototype) \ - V(FunctionLength) \ - V(FunctionName) \ - V(FunctionArguments) \ - V(FunctionCaller) \ - V(ArrayLength) \ - V(StringLength) \ - V(ScriptSource) \ - V(ScriptName) \ - V(ScriptId) \ - V(ScriptLineOffset) \ - V(ScriptColumnOffset) \ - V(ScriptData) \ - V(ScriptType) \ - V(ScriptLineEnds) \ - V(ScriptContextData) \ + V(FunctionPrototype) \ + V(FunctionLength) \ + V(FunctionName) \ + V(FunctionArguments) \ + V(FunctionCaller) \ + V(ArrayLength) \ + V(StringLength) \ + V(ScriptSource) \ + V(ScriptName) \ + V(ScriptId) \ + V(ScriptLineOffset) \ + V(ScriptColumnOffset) \ + V(ScriptData) \ + V(ScriptType) \ + V(ScriptCompilationType) \ + V(ScriptLineEnds) \ + V(ScriptContextData) \ + V(ScriptEvalFromFunction) \ + V(ScriptEvalFromPosition) \ V(ObjectPrototype) // Accessors contains all predefined proxy accessors. @@ -88,8 +92,11 @@ class Accessors : public AllStatic { static Object* ScriptGetColumnOffset(Object* object, void*); static Object* ScriptGetData(Object* object, void*); static Object* ScriptGetType(Object* object, void*); + static Object* ScriptGetCompilationType(Object* object, void*); static Object* ScriptGetLineEnds(Object* object, void*); static Object* ScriptGetContextData(Object* object, void*); + static Object* ScriptGetEvalFromFunction(Object* object, void*); + static Object* ScriptGetEvalFromPosition(Object* object, void*); static Object* ObjectGetPrototype(Object* receiver, void*); static Object* ObjectSetPrototype(JSObject* receiver, Object* value, void*); diff --git a/deps/v8/src/allocation.cc b/deps/v8/src/allocation.cc index 3d26123bfa..41724b68cf 100644 --- a/deps/v8/src/allocation.cc +++ b/deps/v8/src/allocation.cc @@ -29,7 +29,8 @@ #include "v8.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { void* Malloced::New(size_t size) { diff --git a/deps/v8/src/allocation.h b/deps/v8/src/allocation.h index a690f08357..586c4fd0d1 100644 --- a/deps/v8/src/allocation.h +++ b/deps/v8/src/allocation.h @@ -28,7 +28,8 @@ #ifndef V8_ALLOCATION_H_ #define V8_ALLOCATION_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // A class that controls whether allocation is allowed. This is for diff --git a/deps/v8/src/api.cc b/deps/v8/src/api.cc index c250412e4c..7b7f290816 100644 --- a/deps/v8/src/api.cc +++ b/deps/v8/src/api.cc @@ -99,7 +99,6 @@ static i::HandleScopeImplementer thread_local; // --- E x c e p t i o n B e h a v i o r --- -static bool has_shut_down = false; static FatalErrorCallback exception_behavior = NULL; @@ -123,7 +122,7 @@ static FatalErrorCallback& GetFatalErrorHandler() { // When V8 cannot allocated memory FatalProcessOutOfMemory is called. // The default fatal error handler is called and execution is stopped. void i::V8::FatalProcessOutOfMemory(const char* location) { - has_shut_down = true; + i::V8::SetFatalError(); FatalErrorCallback callback = GetFatalErrorHandler(); { LEAVE_V8; @@ -142,13 +141,13 @@ void V8::SetFatalErrorHandler(FatalErrorCallback that) { bool Utils::ReportApiFailure(const char* location, const char* message) { FatalErrorCallback callback = GetFatalErrorHandler(); callback(location, message); - has_shut_down = true; + i::V8::SetFatalError(); return false; } bool V8::IsDead() { - return has_shut_down; + return i::V8::IsDead(); } @@ -186,7 +185,8 @@ static bool ReportEmptyHandle(const char* location) { * yet been done. */ static inline bool IsDeadCheck(const char* location) { - return has_shut_down ? ReportV8Dead(location) : false; + return !i::V8::IsRunning() + && i::V8::IsDead() ? ReportV8Dead(location) : false; } @@ -205,9 +205,14 @@ static inline bool EmptyCheck(const char* location, const v8::Data* obj) { static i::StringInputBuffer write_input_buffer; -static void EnsureInitialized(const char* location) { - if (IsDeadCheck(location)) return; - ApiCheck(v8::V8::Initialize(), location, "Error initializing V8"); +static inline bool EnsureInitialized(const char* location) { + if (i::V8::IsRunning()) { + return true; + } + if (IsDeadCheck(location)) { + return false; + } + return ApiCheck(v8::V8::Initialize(), location, "Error initializing V8"); } @@ -225,29 +230,25 @@ void ImplementationUtilities::ZapHandleRange(void** begin, void** end) { v8::Handle<v8::Primitive> ImplementationUtilities::Undefined() { - if (IsDeadCheck("v8::Undefined()")) return v8::Handle<v8::Primitive>(); - EnsureInitialized("v8::Undefined()"); + if (!EnsureInitialized("v8::Undefined()")) return v8::Handle<v8::Primitive>(); return v8::Handle<Primitive>(ToApi<Primitive>(i::Factory::undefined_value())); } v8::Handle<v8::Primitive> ImplementationUtilities::Null() { - if (IsDeadCheck("v8::Null()")) return v8::Handle<v8::Primitive>(); - EnsureInitialized("v8::Null()"); + if (!EnsureInitialized("v8::Null()")) return v8::Handle<v8::Primitive>(); return v8::Handle<Primitive>(ToApi<Primitive>(i::Factory::null_value())); } v8::Handle<v8::Boolean> ImplementationUtilities::True() { - if (IsDeadCheck("v8::True()")) return v8::Handle<v8::Boolean>(); - EnsureInitialized("v8::True()"); + if (!EnsureInitialized("v8::True()")) return v8::Handle<v8::Boolean>(); return v8::Handle<v8::Boolean>(ToApi<Boolean>(i::Factory::true_value())); } v8::Handle<v8::Boolean> ImplementationUtilities::False() { - if (IsDeadCheck("v8::False()")) return v8::Handle<v8::Boolean>(); - EnsureInitialized("v8::False()"); + if (!EnsureInitialized("v8::False()")) return v8::Handle<v8::Boolean>(); return v8::Handle<v8::Boolean>(ToApi<Boolean>(i::Factory::false_value())); } @@ -373,21 +374,21 @@ void V8::ClearWeak(void** obj) { bool V8::IsGlobalNearDeath(void** obj) { LOG_API("IsGlobalNearDeath"); - if (has_shut_down) return false; + if (!i::V8::IsRunning()) return false; return i::GlobalHandles::IsNearDeath(reinterpret_cast<i::Object**>(obj)); } bool V8::IsGlobalWeak(void** obj) { LOG_API("IsGlobalWeak"); - if (has_shut_down) return false; + if (!i::V8::IsRunning()) return false; return i::GlobalHandles::IsWeak(reinterpret_cast<i::Object**>(obj)); } void V8::DisposeGlobal(void** obj) { LOG_API("DisposeGlobal"); - if (has_shut_down) return; + if (!i::V8::IsRunning()) return; i::Object** ptr = reinterpret_cast<i::Object**>(obj); if ((*ptr)->IsGlobalContext()) i::Heap::NotifyContextDisposed(); i::GlobalHandles::Destroy(ptr); @@ -415,7 +416,8 @@ int HandleScope::NumberOfHandles() { void** v8::HandleScope::CreateHandle(void* value) { - return i::HandleScope::CreateHandle(value); + return reinterpret_cast<void**>( + i::HandleScope::CreateHandle(reinterpret_cast<i::Object*>(value))); } @@ -431,7 +433,7 @@ void Context::Enter() { void Context::Exit() { - if (has_shut_down) return; + if (!i::V8::IsRunning()) return; if (!ApiCheck(thread_local.LeaveLastContext(), "v8::Context::Exit()", "Cannot exit non-entered context")) { @@ -1890,6 +1892,19 @@ bool v8::Object::ForceSet(v8::Handle<Value> key, } +bool v8::Object::ForceDelete(v8::Handle<Value> key) { + ON_BAILOUT("v8::Object::ForceDelete()", return false); + ENTER_V8; + i::Handle<i::JSObject> self = Utils::OpenHandle(this); + i::Handle<i::Object> key_obj = Utils::OpenHandle(*key); + EXCEPTION_PREAMBLE(); + i::Handle<i::Object> obj = i::ForceDeleteProperty(self, key_obj); + has_pending_exception = obj.is_null(); + EXCEPTION_BAILOUT_CHECK(false); + return obj->IsTrue(); +} + + Local<Value> v8::Object::Get(v8::Handle<Value> key) { ON_BAILOUT("v8::Object::Get()", return Local<v8::Value>()); ENTER_V8; @@ -2450,7 +2465,7 @@ void v8::Object::SetInternalField(int index, v8::Handle<Value> value) { // --- E n v i r o n m e n t --- bool v8::V8::Initialize() { - if (i::V8::HasBeenSetup()) return true; + if (i::V8::IsRunning()) return true; ENTER_V8; HandleScope scope; if (i::Snapshot::Initialize()) { @@ -2612,6 +2627,13 @@ v8::Local<v8::Context> Context::GetCurrent() { } +v8::Local<v8::Context> Context::GetCalling() { + if (IsDeadCheck("v8::Context::GetCalling()")) return Local<Context>(); + i::Handle<i::Context> context(i::Top::GetCallingGlobalContext()); + return Utils::ToLocal(context); +} + + v8::Local<v8::Object> Context::Global() { if (IsDeadCheck("v8::Context::Global()")) return Local<v8::Object>(); i::Object** ctx = reinterpret_cast<i::Object**>(this); @@ -3116,16 +3138,28 @@ void V8::PauseProfiler() { #endif } + void V8::ResumeProfiler() { #ifdef ENABLE_LOGGING_AND_PROFILING i::Logger::ResumeProfiler(); #endif } + +bool V8::IsProfilerPaused() { +#ifdef ENABLE_LOGGING_AND_PROFILING + return i::Logger::IsProfilerPaused(); +#else + return true; +#endif +} + + int V8::GetLogLines(int from_pos, char* dest_buf, int max_size) { #ifdef ENABLE_LOGGING_AND_PROFILING return i::Logger::GetLogLines(from_pos, dest_buf, max_size); #endif + return 0; } String::Utf8Value::Utf8Value(v8::Handle<v8::Value> obj) { @@ -3312,7 +3346,7 @@ bool Debug::SetDebugEventListener(v8::Handle<v8::Object> that, void Debug::DebugBreak() { - if (!i::V8::HasBeenSetup()) return; + if (!i::V8::IsRunning()) return; i::StackGuard::DebugBreak(); } @@ -3354,7 +3388,7 @@ void Debug::SetMessageHandler2(v8::Debug::MessageHandler2 handler) { void Debug::SendCommand(const uint16_t* command, int length, ClientData* client_data) { - if (!i::V8::HasBeenSetup()) return; + if (!i::V8::IsRunning()) return; i::Debugger::ProcessCommand(i::Vector<const uint16_t>(command, length), client_data); } @@ -3370,7 +3404,7 @@ void Debug::SetHostDispatchHandler(HostDispatchHandler handler, Handle<Value> Debug::Call(v8::Handle<v8::Function> fun, v8::Handle<v8::Value> data) { - if (!i::V8::HasBeenSetup()) return Handle<Value>(); + if (!i::V8::IsRunning()) return Handle<Value>(); ON_BAILOUT("v8::Debug::Call()", return Handle<Value>()); ENTER_V8; i::Handle<i::Object> result; diff --git a/deps/v8/src/arguments.h b/deps/v8/src/arguments.h index 2ec68ed20b..80f90063ba 100644 --- a/deps/v8/src/arguments.h +++ b/deps/v8/src/arguments.h @@ -28,7 +28,8 @@ #ifndef V8_ARGUMENTS_H_ #define V8_ARGUMENTS_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Arguments provides access to runtime call parameters. // diff --git a/deps/v8/src/arm/assembler-arm-inl.h b/deps/v8/src/arm/assembler-arm-inl.h index fe64761e3b..824a5fda52 100644 --- a/deps/v8/src/arm/assembler-arm-inl.h +++ b/deps/v8/src/arm/assembler-arm-inl.h @@ -41,7 +41,8 @@ #include "cpu.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { Condition NegateCondition(Condition cc) { ASSERT(cc != al); diff --git a/deps/v8/src/arm/assembler-arm.cc b/deps/v8/src/arm/assembler-arm.cc index 191c865a82..6ec8f460bc 100644 --- a/deps/v8/src/arm/assembler-arm.cc +++ b/deps/v8/src/arm/assembler-arm.cc @@ -39,7 +39,8 @@ #include "arm/assembler-arm-inl.h" #include "serialize.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // ----------------------------------------------------------------------------- // Implementation of Register and CRegister @@ -211,6 +212,7 @@ enum { // Instruction bit masks RdMask = 15 << 12, // in str instruction CondMask = 15 << 28, + CoprocessorMask = 15 << 8, OpCodeMask = 15 << 21, // in data-processing instructions Imm24Mask = (1 << 24) - 1, Off12Mask = (1 << 12) - 1, @@ -616,7 +618,8 @@ void Assembler::addrmod4(Instr instr, Register rn, RegList rl) { void Assembler::addrmod5(Instr instr, CRegister crd, const MemOperand& x) { // unindexed addressing is not encoded by this function - ASSERT((instr & ~(CondMask | P | U | N | W | L)) == (B27 | B26)); + ASSERT_EQ((B27 | B26), + (instr & ~(CondMask | CoprocessorMask | P | U | N | W | L))); ASSERT(x.rn_.is_valid() && !x.rm_.is_valid()); int am = x.am_; int offset_8 = x.offset_; diff --git a/deps/v8/src/arm/assembler-arm.h b/deps/v8/src/arm/assembler-arm.h index d7535e0da4..eeab4a72cc 100644 --- a/deps/v8/src/arm/assembler-arm.h +++ b/deps/v8/src/arm/assembler-arm.h @@ -42,7 +42,8 @@ #include "assembler.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // CPU Registers. // @@ -83,8 +84,6 @@ struct Register { }; -const int kNumRegisters = 16; - extern Register no_reg; extern Register r0; extern Register r1; @@ -622,8 +621,8 @@ class Assembler : public Malloced { // Pseudo instructions void nop() { mov(r0, Operand(r0)); } - void push(Register src) { - str(src, MemOperand(sp, 4, NegPreIndex), al); + void push(Register src, Condition cond = al) { + str(src, MemOperand(sp, 4, NegPreIndex), cond); } void pop(Register dst) { diff --git a/deps/v8/src/arm/builtins-arm.cc b/deps/v8/src/arm/builtins-arm.cc index 9c7a42ab17..588798bdea 100644 --- a/deps/v8/src/arm/builtins-arm.cc +++ b/deps/v8/src/arm/builtins-arm.cc @@ -31,7 +31,8 @@ #include "debug.h" #include "runtime.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { #define __ ACCESS_MASM(masm) @@ -187,7 +188,7 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) { // Set expected number of arguments to zero (not changing r0). __ mov(r2, Operand(0)); - __ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION); + __ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR); __ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)), RelocInfo::CODE_TARGET); } diff --git a/deps/v8/src/arm/codegen-arm-inl.h b/deps/v8/src/arm/codegen-arm-inl.h new file mode 100644 index 0000000000..544331a522 --- /dev/null +++ b/deps/v8/src/arm/codegen-arm-inl.h @@ -0,0 +1,46 @@ +// 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_ARM_CODEGEN_ARM_INL_H_ +#define V8_ARM_CODEGEN_ARM_INL_H_ + +namespace v8 { +namespace internal { + +#define __ ACCESS_MASM(masm_) + +// Platform-specific inline functions. + +void DeferredCode::Jump() { __ jmp(&entry_label_); } +void DeferredCode::Branch(Condition cc) { __ b(cc, &entry_label_); } + +#undef __ + +} } // namespace v8::internal + +#endif // V8_ARM_CODEGEN_ARM_INL_H_ diff --git a/deps/v8/src/arm/codegen-arm.cc b/deps/v8/src/arm/codegen-arm.cc index 1930a7c2f2..7428d3b597 100644 --- a/deps/v8/src/arm/codegen-arm.cc +++ b/deps/v8/src/arm/codegen-arm.cc @@ -36,10 +36,39 @@ #include "scopes.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { #define __ ACCESS_MASM(masm_) +// ------------------------------------------------------------------------- +// Platform-specific DeferredCode functions. + +void DeferredCode::SaveRegisters() { + for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) { + int action = registers_[i]; + if (action == kPush) { + __ push(RegisterAllocator::ToRegister(i)); + } else if (action != kIgnore && (action & kSyncedFlag) == 0) { + __ str(RegisterAllocator::ToRegister(i), MemOperand(fp, action)); + } + } +} + + +void DeferredCode::RestoreRegisters() { + // Restore registers in reverse order due to the stack. + for (int i = RegisterAllocator::kNumRegisters - 1; i >= 0; i--) { + int action = registers_[i]; + if (action == kPush) { + __ pop(RegisterAllocator::ToRegister(i)); + } else if (action != kIgnore) { + action &= ~kSyncedFlag; + __ ldr(RegisterAllocator::ToRegister(i), MemOperand(fp, action)); + } + } +} + // ------------------------------------------------------------------------- // CodeGenState implementation. @@ -108,7 +137,7 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) { RegisterAllocator register_allocator(this); allocator_ = ®ister_allocator; ASSERT(frame_ == NULL); - frame_ = new VirtualFrame(this); + frame_ = new VirtualFrame(); cc_reg_ = al; set_in_spilled_code(false); { @@ -133,13 +162,13 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) { #endif // Allocate space for locals and initialize them. - frame_->AllocateStackSlots(scope_->num_stack_slots()); + frame_->AllocateStackSlots(); // Initialize the function return target after the locals are set // up, because it needs the expected frame height from the frame. - function_return_.Initialize(this, JumpTarget::BIDIRECTIONAL); + function_return_.set_direction(JumpTarget::BIDIRECTIONAL); function_return_is_shadowed_ = false; - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; if (scope_->num_heap_slots() > 0) { // Allocate local context. // Get outer context and create a new context based on it. @@ -148,7 +177,7 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) { frame_->CallRuntime(Runtime::kNewContext, 1); // r0 holds the result #ifdef DEBUG - JumpTarget verified_true(this); + JumpTarget verified_true; __ cmp(r0, Operand(cp)); verified_true.Branch(eq); __ stop("NewContext: r0 is expected to be the same as cp"); @@ -288,9 +317,7 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) { DeleteFrame(); // Process any deferred code using the register allocator. - if (HasStackOverflow()) { - ClearDeferred(); - } else { + if (!HasStackOverflow()) { ProcessDeferred(); } @@ -456,14 +483,14 @@ void CodeGenerator::Load(Expression* x, TypeofState typeof_state) { int original_height = frame_->height(); #endif ASSERT(!in_spilled_code()); - JumpTarget true_target(this); - JumpTarget false_target(this); + JumpTarget true_target; + JumpTarget false_target; LoadCondition(x, typeof_state, &true_target, &false_target, false); if (has_cc()) { // Convert cc_reg_ into a boolean value. - JumpTarget loaded(this); - JumpTarget materialize_true(this); + JumpTarget loaded; + JumpTarget materialize_true; materialize_true.Branch(cc_reg_); __ mov(r0, Operand(Factory::false_value())); frame_->EmitPush(r0); @@ -478,7 +505,7 @@ void CodeGenerator::Load(Expression* x, TypeofState typeof_state) { if (true_target.is_linked() || false_target.is_linked()) { // We have at least one condition value that has been "translated" // into a branch, thus it needs to be loaded explicitly. - JumpTarget loaded(this); + JumpTarget loaded; if (frame_ != NULL) { loaded.Jump(); // Don't lose the current TOS. } @@ -510,14 +537,14 @@ void CodeGenerator::Load(Expression* x, TypeofState typeof_state) { void CodeGenerator::LoadGlobal() { - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; __ ldr(r0, GlobalObject()); frame_->EmitPush(r0); } void CodeGenerator::LoadGlobalReceiver(Register scratch) { - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; __ ldr(scratch, ContextOperand(cp, Context::GLOBAL_INDEX)); __ ldr(scratch, FieldMemOperand(scratch, GlobalObject::kGlobalReceiverOffset)); @@ -529,7 +556,7 @@ void CodeGenerator::LoadGlobalReceiver(Register scratch) { // that we have the INSIDE_TYPEOF typeof state. => Need to handle global // variables w/o reference errors elsewhere. void CodeGenerator::LoadTypeofExpression(Expression* x) { - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Variable* variable = x->AsVariableProxy()->AsVariable(); if (variable != NULL && !variable->is_this() && variable->is_global()) { // NOTE: This is somewhat nasty. We force the compiler to load @@ -559,7 +586,7 @@ Reference::~Reference() { void CodeGenerator::LoadReference(Reference* ref) { - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ LoadReference"); Expression* e = ref->expression(); Property* property = e->AsProperty(); @@ -602,7 +629,7 @@ void CodeGenerator::LoadReference(Reference* ref) { void CodeGenerator::UnloadReference(Reference* ref) { - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; // Pop a reference from the stack while preserving TOS. Comment cmnt(masm_, "[ UnloadReference"); int size = ref->size(); @@ -619,7 +646,7 @@ void CodeGenerator::UnloadReference(Reference* ref) { // may jump to 'false_target' in case the register converts to 'false'. void CodeGenerator::ToBoolean(JumpTarget* true_target, JumpTarget* false_target) { - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; // Note: The generated code snippet does not change stack variables. // Only the condition code should be set. frame_->EmitPop(r0); @@ -701,7 +728,7 @@ class GenericBinaryOpStub : public CodeStub { void CodeGenerator::GenericBinaryOperation(Token::Value op, OverwriteMode overwrite_mode) { - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; // sp[0] : y // sp[1] : x // result : r0 @@ -756,13 +783,11 @@ void CodeGenerator::GenericBinaryOperation(Token::Value op, class DeferredInlineSmiOperation: public DeferredCode { public: - DeferredInlineSmiOperation(CodeGenerator* generator, - Token::Value op, + DeferredInlineSmiOperation(Token::Value op, int value, bool reversed, OverwriteMode overwrite_mode) - : DeferredCode(generator), - op_(op), + : op_(op), value_(value), reversed_(reversed), overwrite_mode_(overwrite_mode) { @@ -780,17 +805,13 @@ class DeferredInlineSmiOperation: public DeferredCode { void DeferredInlineSmiOperation::Generate() { - enter()->Bind(); - VirtualFrame::SpilledScope spilled_scope(generator()); - switch (op_) { case Token::ADD: { + // Revert optimistic add. if (reversed_) { - // revert optimistic add __ sub(r0, r0, Operand(Smi::FromInt(value_))); __ mov(r1, Operand(Smi::FromInt(value_))); } else { - // revert optimistic add __ sub(r1, r0, Operand(Smi::FromInt(value_))); __ mov(r0, Operand(Smi::FromInt(value_))); } @@ -798,8 +819,8 @@ void DeferredInlineSmiOperation::Generate() { } case Token::SUB: { + // Revert optimistic sub. if (reversed_) { - // revert optimistic sub __ rsb(r0, r0, Operand(Smi::FromInt(value_))); __ mov(r1, Operand(Smi::FromInt(value_))); } else { @@ -828,24 +849,19 @@ void DeferredInlineSmiOperation::Generate() { __ mov(r1, Operand(r0)); __ mov(r0, Operand(Smi::FromInt(value_))); } else { - UNREACHABLE(); // should have been handled in SmiOperation + UNREACHABLE(); // Should have been handled in SmiOperation. } break; } default: - // other cases should have been handled before this point. + // Other cases should have been handled before this point. UNREACHABLE(); break; } - GenericBinaryOpStub igostub(op_, overwrite_mode_); - Result arg0 = generator()->allocator()->Allocate(r1); - ASSERT(arg0.is_valid()); - Result arg1 = generator()->allocator()->Allocate(r0); - ASSERT(arg1.is_valid()); - generator()->frame()->CallStub(&igostub, &arg0, &arg1); - exit_.Jump(); + GenericBinaryOpStub stub(op_, overwrite_mode_); + __ CallStub(&stub); } @@ -853,7 +869,7 @@ void CodeGenerator::SmiOperation(Token::Value op, Handle<Object> value, bool reversed, OverwriteMode mode) { - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; // NOTE: This is an attempt to inline (a bit) more of the code for // some possible smi operations (like + and -) when (at least) one // of the operands is a literal smi. With this optimization, the @@ -865,34 +881,34 @@ void CodeGenerator::SmiOperation(Token::Value op, int int_value = Smi::cast(*value)->value(); - JumpTarget exit(this); + JumpTarget exit; frame_->EmitPop(r0); switch (op) { case Token::ADD: { DeferredCode* deferred = - new DeferredInlineSmiOperation(this, op, int_value, reversed, mode); + new DeferredInlineSmiOperation(op, int_value, reversed, mode); __ add(r0, r0, Operand(value), SetCC); - deferred->enter()->Branch(vs); + deferred->Branch(vs); __ tst(r0, Operand(kSmiTagMask)); - deferred->enter()->Branch(ne); + deferred->Branch(ne); deferred->BindExit(); break; } case Token::SUB: { DeferredCode* deferred = - new DeferredInlineSmiOperation(this, op, int_value, reversed, mode); + new DeferredInlineSmiOperation(op, int_value, reversed, mode); - if (!reversed) { - __ sub(r0, r0, Operand(value), SetCC); - } else { + if (reversed) { __ rsb(r0, r0, Operand(value), SetCC); + } else { + __ sub(r0, r0, Operand(value), SetCC); } - deferred->enter()->Branch(vs); + deferred->Branch(vs); __ tst(r0, Operand(kSmiTagMask)); - deferred->enter()->Branch(ne); + deferred->Branch(ne); deferred->BindExit(); break; } @@ -901,9 +917,9 @@ void CodeGenerator::SmiOperation(Token::Value op, case Token::BIT_XOR: case Token::BIT_AND: { DeferredCode* deferred = - new DeferredInlineSmiOperation(this, op, int_value, reversed, mode); + new DeferredInlineSmiOperation(op, int_value, reversed, mode); __ tst(r0, Operand(kSmiTagMask)); - deferred->enter()->Branch(ne); + deferred->Branch(ne); switch (op) { case Token::BIT_OR: __ orr(r0, r0, Operand(value)); break; case Token::BIT_XOR: __ eor(r0, r0, Operand(value)); break; @@ -926,16 +942,16 @@ void CodeGenerator::SmiOperation(Token::Value op, } else { int shift_value = int_value & 0x1f; // least significant 5 bits DeferredCode* deferred = - new DeferredInlineSmiOperation(this, op, shift_value, false, mode); + new DeferredInlineSmiOperation(op, shift_value, false, mode); __ tst(r0, Operand(kSmiTagMask)); - deferred->enter()->Branch(ne); + deferred->Branch(ne); __ mov(r2, Operand(r0, ASR, kSmiTagSize)); // remove tags switch (op) { case Token::SHL: { __ mov(r2, Operand(r2, LSL, shift_value)); // check that the *unsigned* result fits in a smi __ add(r3, r2, Operand(0x40000000), SetCC); - deferred->enter()->Branch(mi); + deferred->Branch(mi); break; } case Token::SHR: { @@ -950,7 +966,7 @@ void CodeGenerator::SmiOperation(Token::Value op, // smi tagging these two cases can only happen with shifts // by 0 or 1 when handed a valid smi __ and_(r3, r2, Operand(0xc0000000), SetCC); - deferred->enter()->Branch(ne); + deferred->Branch(ne); break; } case Token::SAR: { @@ -987,7 +1003,7 @@ void CodeGenerator::SmiOperation(Token::Value op, void CodeGenerator::Comparison(Condition cc, bool strict) { - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; // sp[0] : y // sp[1] : x // result : cc register @@ -995,8 +1011,8 @@ void CodeGenerator::Comparison(Condition cc, bool strict) { // Strict only makes sense for equality comparisons. ASSERT(!strict || cc == eq); - JumpTarget exit(this); - JumpTarget smi(this); + JumpTarget exit; + JumpTarget smi; // Implement '>' and '<=' by reversal to obtain ECMA-262 conversion order. if (cc == gt || cc == le) { cc = ReverseCondition(cc); @@ -1057,12 +1073,14 @@ void CodeGenerator::Comparison(Condition cc, bool strict) { class CallFunctionStub: public CodeStub { public: - explicit CallFunctionStub(int argc) : argc_(argc) {} + CallFunctionStub(int argc, InLoopFlag in_loop) + : argc_(argc), in_loop_(in_loop) {} void Generate(MacroAssembler* masm); private: int argc_; + InLoopFlag in_loop_; #if defined(DEBUG) void Print() { PrintF("CallFunctionStub (argc %d)\n", argc_); } @@ -1070,13 +1088,14 @@ class CallFunctionStub: public CodeStub { Major MajorKey() { return CallFunction; } int MinorKey() { return argc_; } + InLoopFlag InLoop() { return in_loop_; } }; // Call the function on the stack with the given arguments. void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args, int position) { - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; // Push the arguments ("left-to-right") on the stack. int arg_count = args->length(); for (int i = 0; i < arg_count; i++) { @@ -1087,7 +1106,8 @@ void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args, CodeForSourcePosition(position); // Use the shared code stub to call the function. - CallFunctionStub call_function(arg_count); + InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP; + CallFunctionStub call_function(arg_count, in_loop); frame_->CallStub(&call_function, arg_count + 1); // Restore context and pop function from the stack. @@ -1097,7 +1117,7 @@ void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args, void CodeGenerator::Branch(bool if_true, JumpTarget* target) { - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; ASSERT(has_cc()); Condition cc = if_true ? cc_reg_ : NegateCondition(cc_reg_); target->Branch(cc); @@ -1106,7 +1126,7 @@ void CodeGenerator::Branch(bool if_true, JumpTarget* target) { void CodeGenerator::CheckStack() { - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; if (FLAG_check_stack) { Comment cmnt(masm_, "[ check stack"); StackCheckStub stub; @@ -1141,7 +1161,7 @@ void CodeGenerator::VisitStatements(ZoneList<Statement*>* statements) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; for (int i = 0; frame_ != NULL && i < statements->length(); i++) { VisitAndSpill(statements->at(i)); } @@ -1153,10 +1173,10 @@ void CodeGenerator::VisitBlock(Block* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ Block"); CodeForStatementPosition(node); - node->break_target()->Initialize(this); + node->break_target()->set_direction(JumpTarget::FORWARD_ONLY); VisitStatementsAndSpill(node->statements()); if (node->break_target()->is_linked()) { node->break_target()->Bind(); @@ -1167,7 +1187,7 @@ void CodeGenerator::VisitBlock(Block* node) { void CodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) { - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; __ mov(r0, Operand(pairs)); frame_->EmitPush(r0); frame_->EmitPush(cp); @@ -1182,7 +1202,7 @@ void CodeGenerator::VisitDeclaration(Declaration* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ Declaration"); CodeForStatementPosition(node); Variable* var = node->proxy()->var(); @@ -1254,7 +1274,7 @@ void CodeGenerator::VisitExpressionStatement(ExpressionStatement* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ ExpressionStatement"); CodeForStatementPosition(node); Expression* expression = node->expression(); @@ -1269,7 +1289,7 @@ void CodeGenerator::VisitEmptyStatement(EmptyStatement* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "// EmptyStatement"); CodeForStatementPosition(node); // nothing to do @@ -1281,7 +1301,7 @@ void CodeGenerator::VisitIfStatement(IfStatement* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ IfStatement"); // Generate different code depending on which parts of the if statement // are present or not. @@ -1290,11 +1310,11 @@ void CodeGenerator::VisitIfStatement(IfStatement* node) { CodeForStatementPosition(node); - JumpTarget exit(this); + JumpTarget exit; if (has_then_stm && has_else_stm) { Comment cmnt(masm_, "[ IfThenElse"); - JumpTarget then(this); - JumpTarget else_(this); + JumpTarget then; + JumpTarget else_; // if (cond) LoadConditionAndSpill(node->condition(), NOT_INSIDE_TYPEOF, &then, &else_, true); @@ -1318,7 +1338,7 @@ void CodeGenerator::VisitIfStatement(IfStatement* node) { } else if (has_then_stm) { Comment cmnt(masm_, "[ IfThen"); ASSERT(!has_else_stm); - JumpTarget then(this); + JumpTarget then; // if (cond) LoadConditionAndSpill(node->condition(), NOT_INSIDE_TYPEOF, &then, &exit, true); @@ -1334,7 +1354,7 @@ void CodeGenerator::VisitIfStatement(IfStatement* node) { } else if (has_else_stm) { Comment cmnt(masm_, "[ IfElse"); ASSERT(!has_then_stm); - JumpTarget else_(this); + JumpTarget else_; // if (!cond) LoadConditionAndSpill(node->condition(), NOT_INSIDE_TYPEOF, &exit, &else_, true); @@ -1371,7 +1391,7 @@ void CodeGenerator::VisitIfStatement(IfStatement* node) { void CodeGenerator::VisitContinueStatement(ContinueStatement* node) { - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ ContinueStatement"); CodeForStatementPosition(node); node->target()->continue_target()->Jump(); @@ -1379,7 +1399,7 @@ void CodeGenerator::VisitContinueStatement(ContinueStatement* node) { void CodeGenerator::VisitBreakStatement(BreakStatement* node) { - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ BreakStatement"); CodeForStatementPosition(node); node->target()->break_target()->Jump(); @@ -1387,7 +1407,7 @@ void CodeGenerator::VisitBreakStatement(BreakStatement* node) { void CodeGenerator::VisitReturnStatement(ReturnStatement* node) { - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ ReturnStatement"); if (function_return_is_shadowed_) { @@ -1414,7 +1434,7 @@ void CodeGenerator::VisitWithEnterStatement(WithEnterStatement* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ WithEnterStatement"); CodeForStatementPosition(node); LoadAndSpill(node->expression()); @@ -1424,7 +1444,7 @@ void CodeGenerator::VisitWithEnterStatement(WithEnterStatement* node) { frame_->CallRuntime(Runtime::kPushContext, 1); } #ifdef DEBUG - JumpTarget verified_true(this); + JumpTarget verified_true; __ cmp(r0, Operand(cp)); verified_true.Branch(eq); __ stop("PushContext: r0 is expected to be the same as cp"); @@ -1440,7 +1460,7 @@ void CodeGenerator::VisitWithExitStatement(WithExitStatement* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ WithExitStatement"); CodeForStatementPosition(node); // Pop context. @@ -1467,9 +1487,9 @@ void CodeGenerator::GenerateFastCaseSwitchJumpTable( Label* default_label, Vector<Label*> case_targets, Vector<Label> case_labels) { - VirtualFrame::SpilledScope spilled_scope(this); - JumpTarget setup_default(this); - JumpTarget is_smi(this); + VirtualFrame::SpilledScope spilled_scope; + JumpTarget setup_default; + JumpTarget is_smi; // A non-null default label pointer indicates a default case among // the case labels. Otherwise we use the break target as a @@ -1529,8 +1549,6 @@ void CodeGenerator::GenerateFastCaseSwitchJumpTable( if (node->break_target()->is_linked()) { node->break_target()->Bind(); } - - delete start_frame; } @@ -1538,10 +1556,10 @@ void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ SwitchStatement"); CodeForStatementPosition(node); - node->break_target()->Initialize(this); + node->break_target()->set_direction(JumpTarget::FORWARD_ONLY); LoadAndSpill(node->tag()); if (TryGenerateFastCaseSwitchStatement(node)) { @@ -1549,10 +1567,10 @@ void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) { return; } - JumpTarget next_test(this); - JumpTarget fall_through(this); - JumpTarget default_entry(this); - JumpTarget default_exit(this, JumpTarget::BIDIRECTIONAL); + JumpTarget next_test; + JumpTarget fall_through; + JumpTarget default_entry; + JumpTarget default_exit(JumpTarget::BIDIRECTIONAL); ZoneList<CaseClause*>* cases = node->cases(); int length = cases->length(); CaseClause* default_clause = NULL; @@ -1632,10 +1650,10 @@ void CodeGenerator::VisitLoopStatement(LoopStatement* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ LoopStatement"); CodeForStatementPosition(node); - node->break_target()->Initialize(this); + node->break_target()->set_direction(JumpTarget::FORWARD_ONLY); // Simple condition analysis. ALWAYS_TRUE and ALWAYS_FALSE represent a // known result for the test expression, with no side effects. @@ -1656,19 +1674,19 @@ void CodeGenerator::VisitLoopStatement(LoopStatement* node) { switch (node->type()) { case LoopStatement::DO_LOOP: { - JumpTarget body(this, JumpTarget::BIDIRECTIONAL); + JumpTarget body(JumpTarget::BIDIRECTIONAL); // Label the top of the loop for the backward CFG edge. If the test // is always true we can use the continue target, and if the test is // always false there is no need. if (info == ALWAYS_TRUE) { - node->continue_target()->Initialize(this, JumpTarget::BIDIRECTIONAL); + node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL); node->continue_target()->Bind(); } else if (info == ALWAYS_FALSE) { - node->continue_target()->Initialize(this); + node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY); } else { ASSERT(info == DONT_KNOW); - node->continue_target()->Initialize(this); + node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY); body.Bind(); } @@ -1715,11 +1733,11 @@ void CodeGenerator::VisitLoopStatement(LoopStatement* node) { // Label the top of the loop with the continue target for the backward // CFG edge. - node->continue_target()->Initialize(this, JumpTarget::BIDIRECTIONAL); + node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL); node->continue_target()->Bind(); if (info == DONT_KNOW) { - JumpTarget body(this); + JumpTarget body; LoadConditionAndSpill(node->cond(), NOT_INSIDE_TYPEOF, &body, node->break_target(), true); if (has_valid_frame()) { @@ -1745,7 +1763,7 @@ void CodeGenerator::VisitLoopStatement(LoopStatement* node) { } case LoopStatement::FOR_LOOP: { - JumpTarget loop(this, JumpTarget::BIDIRECTIONAL); + JumpTarget loop(JumpTarget::BIDIRECTIONAL); if (node->init() != NULL) { VisitAndSpill(node->init()); @@ -1757,16 +1775,16 @@ void CodeGenerator::VisitLoopStatement(LoopStatement* node) { // If there is no update statement, label the top of the loop with the // continue target, otherwise with the loop target. if (node->next() == NULL) { - node->continue_target()->Initialize(this, JumpTarget::BIDIRECTIONAL); + node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL); node->continue_target()->Bind(); } else { - node->continue_target()->Initialize(this); + node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY); loop.Bind(); } // If the test is always true, there is no need to compile it. if (info == DONT_KNOW) { - JumpTarget body(this); + JumpTarget body; LoadConditionAndSpill(node->cond(), NOT_INSIDE_TYPEOF, &body, node->break_target(), true); if (has_valid_frame()) { @@ -1822,16 +1840,16 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) { int original_height = frame_->height(); #endif ASSERT(!in_spilled_code()); - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ ForInStatement"); CodeForStatementPosition(node); - JumpTarget primitive(this); - JumpTarget jsobject(this); - JumpTarget fixed_array(this); - JumpTarget entry(this, JumpTarget::BIDIRECTIONAL); - JumpTarget end_del_check(this); - JumpTarget exit(this); + JumpTarget primitive; + JumpTarget jsobject; + JumpTarget fixed_array; + JumpTarget entry(JumpTarget::BIDIRECTIONAL); + JumpTarget end_del_check; + JumpTarget exit; // Get the object to enumerate over (converted to JSObject). LoadAndSpill(node->enumerable()); @@ -1916,8 +1934,8 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) { // sp[4] : enumerable // Grab the current frame's height for the break and continue // targets only after all the state is pushed on the frame. - node->break_target()->Initialize(this); - node->continue_target()->Initialize(this); + node->break_target()->set_direction(JumpTarget::FORWARD_ONLY); + node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY); __ ldr(r0, frame_->ElementAt(0)); // load the current count __ ldr(r1, frame_->ElementAt(1)); // load the length @@ -2016,12 +2034,12 @@ void CodeGenerator::VisitTryCatch(TryCatch* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ TryCatch"); CodeForStatementPosition(node); - JumpTarget try_block(this); - JumpTarget exit(this); + JumpTarget try_block; + JumpTarget exit; try_block.Call(); // --- Catch block --- @@ -2132,7 +2150,6 @@ void CodeGenerator::VisitTryCatch(TryCatch* node) { } shadows[i]->other_target()->Jump(); } - delete shadows[i]; } exit.Bind(); @@ -2144,7 +2161,7 @@ void CodeGenerator::VisitTryFinally(TryFinally* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ TryFinally"); CodeForStatementPosition(node); @@ -2153,8 +2170,8 @@ void CodeGenerator::VisitTryFinally(TryFinally* node) { // break/continue from within the try block. enum { FALLING, THROWING, JUMPING }; - JumpTarget try_block(this); - JumpTarget finally_block(this); + JumpTarget try_block; + JumpTarget finally_block; try_block.Call(); @@ -2299,7 +2316,7 @@ void CodeGenerator::VisitTryFinally(TryFinally* node) { JumpTarget* original = shadows[i]->other_target(); __ cmp(r2, Operand(Smi::FromInt(JUMPING + i))); if (!function_return_is_shadowed_ && i == kReturnShadowIndex) { - JumpTarget skip(this); + JumpTarget skip; skip.Branch(ne); frame_->PrepareForReturn(); original->Jump(); @@ -2308,12 +2325,11 @@ void CodeGenerator::VisitTryFinally(TryFinally* node) { original->Branch(eq); } } - delete shadows[i]; } if (has_valid_frame()) { // Check if we need to rethrow the exception. - JumpTarget exit(this); + JumpTarget exit; __ cmp(r2, Operand(Smi::FromInt(THROWING))); exit.Branch(ne); @@ -2332,7 +2348,7 @@ void CodeGenerator::VisitDebuggerStatement(DebuggerStatement* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ DebuggerStatament"); CodeForStatementPosition(node); #ifdef ENABLE_DEBUGGER_SUPPORT @@ -2344,7 +2360,7 @@ void CodeGenerator::VisitDebuggerStatement(DebuggerStatement* node) { void CodeGenerator::InstantiateBoilerplate(Handle<JSFunction> boilerplate) { - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; ASSERT(boilerplate->IsBoilerplate()); // Push the boilerplate on the stack. @@ -2362,7 +2378,7 @@ void CodeGenerator::VisitFunctionLiteral(FunctionLiteral* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ FunctionLiteral"); // Build the function boilerplate and instantiate it. @@ -2382,7 +2398,7 @@ void CodeGenerator::VisitFunctionBoilerplateLiteral( #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ FunctionBoilerplateLiteral"); InstantiateBoilerplate(node->boilerplate()); ASSERT(frame_->height() == original_height + 1); @@ -2393,11 +2409,11 @@ void CodeGenerator::VisitConditional(Conditional* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ Conditional"); - JumpTarget then(this); - JumpTarget else_(this); - JumpTarget exit(this); + JumpTarget then; + JumpTarget else_; + JumpTarget exit; LoadConditionAndSpill(node->condition(), NOT_INSIDE_TYPEOF, &then, &else_, true); Branch(false, &else_); @@ -2412,12 +2428,12 @@ void CodeGenerator::VisitConditional(Conditional* node) { void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) { - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; if (slot->type() == Slot::LOOKUP) { ASSERT(slot->var()->is_dynamic()); - JumpTarget slow(this); - JumpTarget done(this); + JumpTarget slow; + JumpTarget done; // Generate fast-case code for variables that might be shadowed by // eval-introduced variables. Eval is used a lot without @@ -2565,7 +2581,7 @@ void CodeGenerator::VisitSlot(Slot* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ Slot"); LoadFromSlot(node, typeof_state()); ASSERT(frame_->height() == original_height + 1); @@ -2576,7 +2592,7 @@ void CodeGenerator::VisitVariableProxy(VariableProxy* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ VariableProxy"); Variable* var = node->var(); @@ -2596,7 +2612,7 @@ void CodeGenerator::VisitLiteral(Literal* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ Literal"); __ mov(r0, Operand(node->handle())); frame_->EmitPush(r0); @@ -2608,7 +2624,7 @@ void CodeGenerator::VisitRegExpLiteral(RegExpLiteral* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ RexExp Literal"); // Retrieve the literal array and check the allocated entry. @@ -2624,7 +2640,7 @@ void CodeGenerator::VisitRegExpLiteral(RegExpLiteral* node) { FixedArray::kHeaderSize + node->literal_index() * kPointerSize; __ ldr(r2, FieldMemOperand(r1, literal_offset)); - JumpTarget done(this); + JumpTarget done; __ cmp(r2, Operand(Factory::undefined_value())); done.Branch(ne); @@ -2653,8 +2669,7 @@ void CodeGenerator::VisitRegExpLiteral(RegExpLiteral* node) { // therefore context dependent. class DeferredObjectLiteral: public DeferredCode { public: - DeferredObjectLiteral(CodeGenerator* generator, ObjectLiteral* node) - : DeferredCode(generator), node_(node) { + explicit DeferredObjectLiteral(ObjectLiteral* node) : node_(node) { set_comment("[ DeferredObjectLiteral"); } @@ -2667,26 +2682,20 @@ class DeferredObjectLiteral: public DeferredCode { void DeferredObjectLiteral::Generate() { // Argument is passed in r1. - enter()->Bind(); - VirtualFrame::SpilledScope spilled_scope(generator()); // If the entry is undefined we call the runtime system to compute // the literal. - - VirtualFrame* frame = generator()->frame(); // Literal array (0). - frame->EmitPush(r1); + __ push(r1); // Literal index (1). __ mov(r0, Operand(Smi::FromInt(node_->literal_index()))); - frame->EmitPush(r0); + __ push(r0); // Constant properties (2). __ mov(r0, Operand(node_->constant_properties())); - frame->EmitPush(r0); - Result boilerplate = - frame->CallRuntime(Runtime::kCreateObjectLiteralBoilerplate, 3); - __ mov(r2, Operand(boilerplate.reg())); + __ push(r0); + __ CallRuntime(Runtime::kCreateObjectLiteralBoilerplate, 3); + __ mov(r2, Operand(r0)); // Result is returned in r2. - exit_.Jump(); } @@ -2694,10 +2703,10 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ ObjectLiteral"); - DeferredObjectLiteral* deferred = new DeferredObjectLiteral(this, node); + DeferredObjectLiteral* deferred = new DeferredObjectLiteral(node); // Retrieve the literal array and check the allocated entry. @@ -2715,7 +2724,7 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) { // Check whether we need to materialize the object literal boilerplate. // If so, jump to the deferred code. __ cmp(r2, Operand(Factory::undefined_value())); - deferred->enter()->Branch(eq); + deferred->Branch(eq); deferred->BindExit(); // Push the object literal boilerplate. @@ -2782,8 +2791,7 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) { // therefore context dependent. class DeferredArrayLiteral: public DeferredCode { public: - DeferredArrayLiteral(CodeGenerator* generator, ArrayLiteral* node) - : DeferredCode(generator), node_(node) { + explicit DeferredArrayLiteral(ArrayLiteral* node) : node_(node) { set_comment("[ DeferredArrayLiteral"); } @@ -2796,26 +2804,20 @@ class DeferredArrayLiteral: public DeferredCode { void DeferredArrayLiteral::Generate() { // Argument is passed in r1. - enter()->Bind(); - VirtualFrame::SpilledScope spilled_scope(generator()); // If the entry is undefined we call the runtime system to computed // the literal. - - VirtualFrame* frame = generator()->frame(); // Literal array (0). - frame->EmitPush(r1); + __ push(r1); // Literal index (1). __ mov(r0, Operand(Smi::FromInt(node_->literal_index()))); - frame->EmitPush(r0); + __ push(r0); // Constant properties (2). __ mov(r0, Operand(node_->literals())); - frame->EmitPush(r0); - Result boilerplate = - frame->CallRuntime(Runtime::kCreateArrayLiteralBoilerplate, 3); - __ mov(r2, Operand(boilerplate.reg())); + __ push(r0); + __ CallRuntime(Runtime::kCreateArrayLiteralBoilerplate, 3); + __ mov(r2, Operand(r0)); // Result is returned in r2. - exit_.Jump(); } @@ -2823,10 +2825,10 @@ void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ ArrayLiteral"); - DeferredArrayLiteral* deferred = new DeferredArrayLiteral(this, node); + DeferredArrayLiteral* deferred = new DeferredArrayLiteral(node); // Retrieve the literal array and check the allocated entry. @@ -2844,7 +2846,7 @@ void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) { // Check whether we need to materialize the object literal boilerplate. // If so, jump to the deferred code. __ cmp(r2, Operand(Factory::undefined_value())); - deferred->enter()->Branch(eq); + deferred->Branch(eq); deferred->BindExit(); // Push the object literal boilerplate. @@ -2897,7 +2899,7 @@ void CodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* node) { int original_height = frame_->height(); #endif ASSERT(!in_spilled_code()); - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; // Call runtime routine to allocate the catch extension object and // assign the exception value to the catch variable. Comment cmnt(masm_, "[ CatchExtensionObject"); @@ -2914,7 +2916,7 @@ void CodeGenerator::VisitAssignment(Assignment* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ Assignment"); CodeForStatementPosition(node); @@ -2982,7 +2984,7 @@ void CodeGenerator::VisitThrow(Throw* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ Throw"); LoadAndSpill(node->exception()); @@ -2997,7 +2999,7 @@ void CodeGenerator::VisitProperty(Property* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ Property"); { Reference property(this, node); @@ -3011,7 +3013,7 @@ void CodeGenerator::VisitCall(Call* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ Call"); ZoneList<Expression*>* args = node->arguments(); @@ -3054,7 +3056,8 @@ void CodeGenerator::VisitCall(Call* node) { } // Setup the receiver register and call the IC initialization code. - Handle<Code> stub = ComputeCallInitialize(arg_count); + InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP; + Handle<Code> stub = ComputeCallInitialize(arg_count, in_loop); CodeForSourcePosition(node->position()); frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET_CONTEXT, arg_count + 1); @@ -3105,7 +3108,8 @@ void CodeGenerator::VisitCall(Call* node) { } // Set the receiver register and call the IC initialization code. - Handle<Code> stub = ComputeCallInitialize(arg_count); + InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP; + Handle<Code> stub = ComputeCallInitialize(arg_count, in_loop); CodeForSourcePosition(node->position()); frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET, arg_count + 1); __ ldr(cp, frame_->Context()); @@ -3160,7 +3164,7 @@ void CodeGenerator::VisitCallEval(CallEval* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ CallEval"); // In a call to eval, we first call %ResolvePossiblyDirectEval to resolve @@ -3203,7 +3207,8 @@ void CodeGenerator::VisitCallEval(CallEval* node) { // Call the function. CodeForSourcePosition(node->position()); - CallFunctionStub call_function(arg_count); + InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP; + CallFunctionStub call_function(arg_count, in_loop); frame_->CallStub(&call_function, arg_count + 1); __ ldr(cp, frame_->Context()); @@ -3218,7 +3223,7 @@ void CodeGenerator::VisitCallNew(CallNew* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ CallNew"); CodeForStatementPosition(node); @@ -3268,9 +3273,9 @@ void CodeGenerator::VisitCallNew(CallNew* node) { void CodeGenerator::GenerateValueOf(ZoneList<Expression*>* args) { - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; ASSERT(args->length() == 1); - JumpTarget leave(this); + JumpTarget leave; LoadAndSpill(args->at(0)); frame_->EmitPop(r0); // r0 contains object. // if (object->IsSmi()) return the object. @@ -3290,9 +3295,9 @@ void CodeGenerator::GenerateValueOf(ZoneList<Expression*>* args) { void CodeGenerator::GenerateSetValueOf(ZoneList<Expression*>* args) { - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; ASSERT(args->length() == 2); - JumpTarget leave(this); + JumpTarget leave; LoadAndSpill(args->at(0)); // Load the object. LoadAndSpill(args->at(1)); // Load the value. frame_->EmitPop(r0); // r0 contains value @@ -3318,7 +3323,7 @@ void CodeGenerator::GenerateSetValueOf(ZoneList<Expression*>* args) { void CodeGenerator::GenerateIsSmi(ZoneList<Expression*>* args) { - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; ASSERT(args->length() == 1); LoadAndSpill(args->at(0)); frame_->EmitPop(r0); @@ -3328,7 +3333,7 @@ void CodeGenerator::GenerateIsSmi(ZoneList<Expression*>* args) { void CodeGenerator::GenerateLog(ZoneList<Expression*>* args) { - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; // See comment in CodeGenerator::GenerateLog in codegen-ia32.cc. ASSERT_EQ(args->length(), 3); #ifdef ENABLE_LOGGING_AND_PROFILING @@ -3344,7 +3349,7 @@ void CodeGenerator::GenerateLog(ZoneList<Expression*>* args) { void CodeGenerator::GenerateIsNonNegativeSmi(ZoneList<Expression*>* args) { - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; ASSERT(args->length() == 1); LoadAndSpill(args->at(0)); frame_->EmitPop(r0); @@ -3357,7 +3362,7 @@ void CodeGenerator::GenerateIsNonNegativeSmi(ZoneList<Expression*>* args) { // undefined in order to trigger the slow case, Runtime_StringCharCodeAt. // It is not yet implemented on ARM, so it always goes to the slow case. void CodeGenerator::GenerateFastCharCodeAt(ZoneList<Expression*>* args) { - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; ASSERT(args->length() == 2); __ mov(r0, Operand(Factory::undefined_value())); frame_->EmitPush(r0); @@ -3365,10 +3370,10 @@ void CodeGenerator::GenerateFastCharCodeAt(ZoneList<Expression*>* args) { void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) { - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; ASSERT(args->length() == 1); LoadAndSpill(args->at(0)); - JumpTarget answer(this); + JumpTarget answer; // We need the CC bits to come out as not_equal in the case where the // object is a smi. This can't be done with the usual test opcode so // we use XOR to get the right CC bits. @@ -3387,7 +3392,7 @@ void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) { void CodeGenerator::GenerateArgumentsLength(ZoneList<Expression*>* args) { - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; ASSERT(args->length() == 0); // Seed the result with the formal parameters count, which will be used @@ -3402,7 +3407,7 @@ void CodeGenerator::GenerateArgumentsLength(ZoneList<Expression*>* args) { void CodeGenerator::GenerateArgumentsAccess(ZoneList<Expression*>* args) { - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; ASSERT(args->length() == 1); // Satisfy contract with ArgumentsAccessStub: @@ -3419,7 +3424,7 @@ void CodeGenerator::GenerateArgumentsAccess(ZoneList<Expression*>* args) { void CodeGenerator::GenerateObjectEquals(ZoneList<Expression*>* args) { - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; ASSERT(args->length() == 2); // Load the two objects into registers and perform the comparison. @@ -3436,7 +3441,7 @@ void CodeGenerator::VisitCallRuntime(CallRuntime* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; if (CheckForInlineRuntimeCall(node)) { ASSERT((has_cc() && frame_->height() == original_height) || (!has_cc() && frame_->height() == original_height + 1)); @@ -3465,7 +3470,8 @@ void CodeGenerator::VisitCallRuntime(CallRuntime* node) { if (function == NULL) { // Call the JS runtime function. - Handle<Code> stub = ComputeCallInitialize(arg_count); + InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP; + Handle<Code> stub = ComputeCallInitialize(arg_count, in_loop); frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET, arg_count + 1); __ ldr(cp, frame_->Context()); frame_->Drop(); @@ -3483,7 +3489,7 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ UnaryOperation"); Token::Value op = node->op(); @@ -3572,8 +3578,8 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) { case Token::BIT_NOT: { // smi check - JumpTarget smi_label(this); - JumpTarget continue_label(this); + JumpTarget smi_label; + JumpTarget continue_label; __ tst(r0, Operand(kSmiTagMask)); smi_label.Branch(eq); @@ -3599,7 +3605,7 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) { case Token::ADD: { // Smi check. - JumpTarget continue_label(this); + JumpTarget continue_label; __ tst(r0, Operand(kSmiTagMask)); continue_label.Branch(eq); frame_->EmitPush(r0); @@ -3624,7 +3630,7 @@ void CodeGenerator::VisitCountOperation(CountOperation* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ CountOperation"); bool is_postfix = node->is_postfix(); @@ -3653,8 +3659,8 @@ void CodeGenerator::VisitCountOperation(CountOperation* node) { target.GetValueAndSpill(NOT_INSIDE_TYPEOF); frame_->EmitPop(r0); - JumpTarget slow(this); - JumpTarget exit(this); + JumpTarget slow; + JumpTarget exit; // Load the value (1) into register r1. __ mov(r1, Operand(Smi::FromInt(1))); @@ -3726,7 +3732,7 @@ void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ BinaryOperation"); Token::Value op = node->op(); @@ -3743,7 +3749,7 @@ void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) { // of compiling the binary operation is materialized or not. if (op == Token::AND) { - JumpTarget is_true(this); + JumpTarget is_true; LoadConditionAndSpill(node->left(), NOT_INSIDE_TYPEOF, &is_true, @@ -3761,8 +3767,8 @@ void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) { false); } else { - JumpTarget pop_and_continue(this); - JumpTarget exit(this); + JumpTarget pop_and_continue; + JumpTarget exit; __ ldr(r0, frame_->Top()); // dup the stack top frame_->EmitPush(r0); @@ -3785,7 +3791,7 @@ void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) { } } else if (op == Token::OR) { - JumpTarget is_false(this); + JumpTarget is_false; LoadConditionAndSpill(node->left(), NOT_INSIDE_TYPEOF, true_target(), @@ -3803,8 +3809,8 @@ void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) { false); } else { - JumpTarget pop_and_continue(this); - JumpTarget exit(this); + JumpTarget pop_and_continue; + JumpTarget exit; __ ldr(r0, frame_->Top()); frame_->EmitPush(r0); @@ -3876,7 +3882,7 @@ void CodeGenerator::VisitThisFunction(ThisFunction* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; __ ldr(r0, frame_->Function()); frame_->EmitPush(r0); ASSERT(frame_->height() == original_height + 1); @@ -3887,7 +3893,7 @@ void CodeGenerator::VisitCompareOperation(CompareOperation* node) { #ifdef DEBUG int original_height = frame_->height(); #endif - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ CompareOperation"); // Get the expressions from the node. @@ -4245,7 +4251,7 @@ void Reference::SetValue(InitState init_state) { } else { ASSERT(!slot->var()->is_dynamic()); - JumpTarget exit(cgen_); + JumpTarget exit; if (init_state == CONST_INIT) { ASSERT(slot->var()->mode() == Variable::CONST); // Only the first const initialization must be executed (the slot @@ -4335,6 +4341,45 @@ void Reference::SetValue(InitState init_state) { } +static void AllocateHeapNumber( + MacroAssembler* masm, + Label* need_gc, // Jump here if young space is full. + Register result_reg, // The tagged address of the new heap number. + Register allocation_top_addr_reg, // A scratch register. + Register scratch2) { // Another scratch register. + ExternalReference allocation_top = + ExternalReference::new_space_allocation_top_address(); + ExternalReference allocation_limit = + ExternalReference::new_space_allocation_limit_address(); + + // allocat := the address of the allocation top variable. + __ mov(allocation_top_addr_reg, Operand(allocation_top)); + // result_reg := the old allocation top. + __ ldr(result_reg, MemOperand(allocation_top_addr_reg)); + // scratch2 := the address of the allocation limit. + __ mov(scratch2, Operand(allocation_limit)); + // scratch2 := the allocation limit. + __ ldr(scratch2, MemOperand(scratch2)); + // result_reg := the new allocation top. + __ add(result_reg, result_reg, Operand(HeapNumber::kSize)); + // Compare new new allocation top and limit. + __ cmp(result_reg, Operand(scratch2)); + // Branch if out of space in young generation. + __ b(hi, need_gc); + // Store new allocation top. + __ str(result_reg, MemOperand(allocation_top_addr_reg)); // store new top + // Tag and adjust back to start of new object. + __ sub(result_reg, result_reg, Operand(HeapNumber::kSize - kHeapObjectTag)); + // Get heap number map into scratch2. + __ mov(scratch2, Operand(Factory::heap_number_map())); + // Store heap number map in new object. + __ str(scratch2, FieldMemOperand(result_reg, HeapObject::kMapOffset)); +} + + +// We fall into this code if the operands were Smis, but the result was +// not (eg. overflow). We branch into this code (to the not_smi label) if +// the operands were not both Smi. static void HandleBinaryOpSlowCases(MacroAssembler* masm, Label* not_smi, const Builtins::JavaScript& builtin, @@ -4342,73 +4387,74 @@ static void HandleBinaryOpSlowCases(MacroAssembler* masm, int swi_number, OverwriteMode mode) { Label slow; - if (mode == NO_OVERWRITE) { - __ bind(not_smi); - } __ bind(&slow); __ push(r1); __ push(r0); __ mov(r0, Operand(1)); // Set number of arguments. __ InvokeBuiltin(builtin, JUMP_JS); // Tail call. - // Could it be a double-double op? If we already have a place to put - // the answer then we can do the op and skip the builtin and runtime call. - if (mode != NO_OVERWRITE) { - __ bind(not_smi); - __ tst(r0, Operand(kSmiTagMask)); - __ b(eq, &slow); // We can't handle a Smi-double combination yet. - __ tst(r1, Operand(kSmiTagMask)); - __ b(eq, &slow); // We can't handle a Smi-double combination yet. - // Get map of r0 into r2. - __ ldr(r2, FieldMemOperand(r0, HeapObject::kMapOffset)); - // Get type of r0 into r3. - __ ldrb(r3, FieldMemOperand(r2, Map::kInstanceTypeOffset)); - __ cmp(r3, Operand(HEAP_NUMBER_TYPE)); - __ b(ne, &slow); - // Get type of r1 into r3. - __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset)); - // Check they are both the same map (heap number map). - __ cmp(r2, r3); - __ b(ne, &slow); - // Both are doubles. - // Calling convention says that second double is in r2 and r3. - __ ldr(r2, FieldMemOperand(r0, HeapNumber::kValueOffset)); - __ ldr(r3, FieldMemOperand(r0, HeapNumber::kValueOffset + kPointerSize)); + __ bind(not_smi); + __ tst(r0, Operand(kSmiTagMask)); + __ b(eq, &slow); // We can't handle a Smi-double combination yet. + __ tst(r1, Operand(kSmiTagMask)); + __ b(eq, &slow); // We can't handle a Smi-double combination yet. + // Get map of r0 into r2. + __ ldr(r2, FieldMemOperand(r0, HeapObject::kMapOffset)); + // Get type of r0 into r3. + __ ldrb(r3, FieldMemOperand(r2, Map::kInstanceTypeOffset)); + __ cmp(r3, Operand(HEAP_NUMBER_TYPE)); + __ b(ne, &slow); + // Get type of r1 into r3. + __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset)); + // Check they are both the same map (heap number map). + __ cmp(r2, r3); + __ b(ne, &slow); + // Both are doubles. + // Calling convention says that second double is in r2 and r3. + __ ldr(r2, FieldMemOperand(r0, HeapNumber::kValueOffset)); + __ ldr(r3, FieldMemOperand(r0, HeapNumber::kValueOffset + kPointerSize)); + + if (mode == NO_OVERWRITE) { + // Get address of new heap number into r5. + AllocateHeapNumber(masm, &slow, r5, r6, r7); __ push(lr); - if (mode == OVERWRITE_LEFT) { - __ push(r1); - } else { - __ push(r0); - } - // Calling convention says that first double is in r0 and r1. - __ ldr(r0, FieldMemOperand(r1, HeapNumber::kValueOffset)); - __ ldr(r1, FieldMemOperand(r1, HeapNumber::kValueOffset + kPointerSize)); - // Call C routine that may not cause GC or other trouble. - __ mov(r5, Operand(ExternalReference::double_fp_operation(operation))); + __ push(r5); + } else if (mode == OVERWRITE_LEFT) { + __ push(lr); + __ push(r1); + } else { + ASSERT(mode == OVERWRITE_RIGHT); + __ push(lr); + __ push(r0); + } + // Calling convention says that first double is in r0 and r1. + __ ldr(r0, FieldMemOperand(r1, HeapNumber::kValueOffset)); + __ ldr(r1, FieldMemOperand(r1, HeapNumber::kValueOffset + kPointerSize)); + // Call C routine that may not cause GC or other trouble. + __ mov(r5, Operand(ExternalReference::double_fp_operation(operation))); #if !defined(__arm__) - // Notify the simulator that we are calling an add routine in C. - __ swi(swi_number); + // Notify the simulator that we are calling an add routine in C. + __ swi(swi_number); #else - // Actually call the add routine written in C. - __ Call(r5); + // Actually call the add routine written in C. + __ Call(r5); #endif - // Store answer in the overwritable heap number. - __ pop(r4); + // Store answer in the overwritable heap number. + __ pop(r4); #if !defined(__ARM_EABI__) && defined(__arm__) - // Double returned in fp coprocessor register 0 and 1, encoded as register - // cr8. Offsets must be divisible by 4 for coprocessor so we need to - // substract the tag from r4. - __ sub(r5, r4, Operand(kHeapObjectTag)); - __ stc(p1, cr8, MemOperand(r5, HeapNumber::kValueOffset)); + // Double returned in fp coprocessor register 0 and 1, encoded as register + // cr8. Offsets must be divisible by 4 for coprocessor so we need to + // substract the tag from r4. + __ sub(r5, r4, Operand(kHeapObjectTag)); + __ stc(p1, cr8, MemOperand(r5, HeapNumber::kValueOffset)); #else - // Double returned in fp coprocessor register 0 and 1. - __ str(r0, FieldMemOperand(r4, HeapNumber::kValueOffset)); - __ str(r1, FieldMemOperand(r4, HeapNumber::kValueOffset + kPointerSize)); + // Double returned in fp coprocessor register 0 and 1. + __ str(r0, FieldMemOperand(r4, HeapNumber::kValueOffset)); + __ str(r1, FieldMemOperand(r4, HeapNumber::kValueOffset + kPointerSize)); #endif - __ mov(r0, Operand(r4)); - // And we are done. - __ pop(pc); - } + __ mov(r0, Operand(r4)); + // And we are done. + __ pop(pc); } diff --git a/deps/v8/src/arm/codegen-arm.h b/deps/v8/src/arm/codegen-arm.h index c098acdd77..a8cb777d79 100644 --- a/deps/v8/src/arm/codegen-arm.h +++ b/deps/v8/src/arm/codegen-arm.h @@ -28,7 +28,8 @@ #ifndef V8_ARM_CODEGEN_ARM_H_ #define V8_ARM_CODEGEN_ARM_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Forward declarations class DeferredCode; @@ -193,8 +194,7 @@ class CodeGenerator: public AstVisitor { // Accessors Scope* scope() const { return scope_; } - // Clearing and generating deferred code. - void ClearDeferred(); + // Generating deferred code. void ProcessDeferred(); bool is_eval() { return is_eval_; } @@ -205,6 +205,8 @@ class CodeGenerator: public AstVisitor { JumpTarget* true_target() const { return state_->true_target(); } JumpTarget* false_target() const { return state_->false_target(); } + // We don't track loop nesting level on ARM yet. + int loop_nesting() const { return 0; } // Node visitors. void VisitStatements(ZoneList<Statement*>* statements); @@ -317,8 +319,7 @@ class CodeGenerator: public AstVisitor { Handle<JSFunction> BuildBoilerplate(FunctionLiteral* node); void ProcessDeclarations(ZoneList<Declaration*>* declarations); - Handle<Code> ComputeCallInitialize(int argc); - Handle<Code> ComputeCallInitializeInLoop(int argc); + Handle<Code> ComputeCallInitialize(int argc, InLoopFlag in_loop); // Declare global variables and functions in the given array of // name/value pairs. diff --git a/deps/v8/src/arm/constants-arm.h b/deps/v8/src/arm/constants-arm.h index 66c6a8d864..99eab238c7 100644 --- a/deps/v8/src/arm/constants-arm.h +++ b/deps/v8/src/arm/constants-arm.h @@ -28,7 +28,8 @@ #ifndef V8_ARM_CONSTANTS_ARM_H_ #define V8_ARM_CONSTANTS_ARM_H_ -namespace assembler { namespace arm { +namespace assembler { +namespace arm { // Defines constants and accessor classes to assemble, disassemble and // simulate ARM instructions. diff --git a/deps/v8/src/arm/cpu-arm.cc b/deps/v8/src/arm/cpu-arm.cc index 7369661290..71da1ecc90 100644 --- a/deps/v8/src/arm/cpu-arm.cc +++ b/deps/v8/src/arm/cpu-arm.cc @@ -34,7 +34,8 @@ #include "cpu.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { void CPU::Setup() { // Nothing to do. diff --git a/deps/v8/src/arm/debug-arm.cc b/deps/v8/src/arm/debug-arm.cc index f86f981cbb..bcfab6c809 100644 --- a/deps/v8/src/arm/debug-arm.cc +++ b/deps/v8/src/arm/debug-arm.cc @@ -30,7 +30,8 @@ #include "codegen-inl.h" #include "debug.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { #ifdef ENABLE_DEBUGGER_SUPPORT // Currently debug break is not supported in frame exit code on ARM. diff --git a/deps/v8/src/arm/disasm-arm.cc b/deps/v8/src/arm/disasm-arm.cc index 3b7474dbac..f56a599f81 100644 --- a/deps/v8/src/arm/disasm-arm.cc +++ b/deps/v8/src/arm/disasm-arm.cc @@ -62,7 +62,8 @@ #include "platform.h" -namespace assembler { namespace arm { +namespace assembler { +namespace arm { namespace v8i = v8::internal; diff --git a/deps/v8/src/arm/frames-arm.cc b/deps/v8/src/arm/frames-arm.cc index d26198ae1e..6fde4b73c0 100644 --- a/deps/v8/src/arm/frames-arm.cc +++ b/deps/v8/src/arm/frames-arm.cc @@ -31,7 +31,8 @@ #include "arm/assembler-arm-inl.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { StackFrame::Type StackFrame::ComputeType(State* state) { diff --git a/deps/v8/src/arm/frames-arm.h b/deps/v8/src/arm/frames-arm.h index 9a18f3d93b..a67b18a2b6 100644 --- a/deps/v8/src/arm/frames-arm.h +++ b/deps/v8/src/arm/frames-arm.h @@ -28,7 +28,8 @@ #ifndef V8_ARM_FRAMES_ARM_H_ #define V8_ARM_FRAMES_ARM_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // The ARM ABI does not specify the usage of register r9, which may be reserved diff --git a/deps/v8/src/arm/ic-arm.cc b/deps/v8/src/arm/ic-arm.cc index b07c4742db..9b45c46a8c 100644 --- a/deps/v8/src/arm/ic-arm.cc +++ b/deps/v8/src/arm/ic-arm.cc @@ -32,7 +32,8 @@ #include "runtime.h" #include "stub-cache.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // ---------------------------------------------------------------------------- @@ -211,7 +212,7 @@ void CallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) { // Probe the stub cache. Code::Flags flags = - Code::ComputeFlags(Code::CALL_IC, MONOMORPHIC, NORMAL, argc); + Code::ComputeFlags(Code::CALL_IC, NOT_IN_LOOP, MONOMORPHIC, NORMAL, argc); StubCache::GenerateProbe(masm, flags, r1, r2, r3); // If the stub cache probing failed, the receiver might be a value. @@ -422,7 +423,9 @@ void LoadIC::GenerateMegamorphic(MacroAssembler* masm) { __ ldr(r0, MemOperand(sp, 0)); // Probe the stub cache. - Code::Flags flags = Code::ComputeFlags(Code::LOAD_IC, MONOMORPHIC); + Code::Flags flags = Code::ComputeFlags(Code::LOAD_IC, + NOT_IN_LOOP, + MONOMORPHIC); StubCache::GenerateProbe(masm, flags, r0, r2, r3); // Cache miss: Jump to runtime. @@ -755,7 +758,9 @@ void StoreIC::GenerateMegamorphic(MacroAssembler* masm) { // Get the receiver from the stack and probe the stub cache. __ ldr(r1, MemOperand(sp)); - Code::Flags flags = Code::ComputeFlags(Code::STORE_IC, MONOMORPHIC); + Code::Flags flags = Code::ComputeFlags(Code::STORE_IC, + NOT_IN_LOOP, + MONOMORPHIC); StubCache::GenerateProbe(masm, flags, r1, r2, r3); // Cache miss: Jump to runtime. diff --git a/deps/v8/src/arm/jump-target-arm.cc b/deps/v8/src/arm/jump-target-arm.cc index 6d375e5cee..65e7eafa6f 100644 --- a/deps/v8/src/arm/jump-target-arm.cc +++ b/deps/v8/src/arm/jump-target-arm.cc @@ -28,46 +28,47 @@ #include "v8.h" #include "codegen-inl.h" +#include "jump-target-inl.h" #include "register-allocator-inl.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // ------------------------------------------------------------------------- // JumpTarget implementation. -#define __ ACCESS_MASM(masm_) +#define __ ACCESS_MASM(cgen()->masm()) void JumpTarget::DoJump() { - ASSERT(cgen_ != NULL); - ASSERT(cgen_->has_valid_frame()); + ASSERT(cgen()->has_valid_frame()); // Live non-frame registers are not allowed at unconditional jumps // because we have no way of invalidating the corresponding results // which are still live in the C++ code. - ASSERT(cgen_->HasValidEntryRegisters()); + ASSERT(cgen()->HasValidEntryRegisters()); if (is_bound()) { // Backward jump. There is an expected frame to merge to. ASSERT(direction_ == BIDIRECTIONAL); - cgen_->frame()->MergeTo(entry_frame_); - cgen_->DeleteFrame(); + cgen()->frame()->PrepareMergeTo(entry_frame_); + cgen()->frame()->MergeTo(entry_frame_); + cgen()->DeleteFrame(); __ jmp(&entry_label_); } else { + // Preconfigured entry frame is not used on ARM. + ASSERT(entry_frame_ == NULL); // Forward jump. The current frame is added to the end of the list // of frames reaching the target block and a jump to the merge code // is emitted. - AddReachingFrame(cgen_->frame()); + AddReachingFrame(cgen()->frame()); RegisterFile empty; - cgen_->SetFrame(NULL, &empty); + cgen()->SetFrame(NULL, &empty); __ jmp(&merge_labels_.last()); } - - is_linked_ = !is_bound_; } void JumpTarget::DoBranch(Condition cc, Hint ignored) { - ASSERT(cgen_ != NULL); - ASSERT(cgen_->has_valid_frame()); + ASSERT(cgen()->has_valid_frame()); if (is_bound()) { ASSERT(direction_ == BIDIRECTIONAL); @@ -77,29 +78,29 @@ void JumpTarget::DoBranch(Condition cc, Hint ignored) { // Swap the current frame for a copy (we do the swapping to get // the off-frame registers off the fall through) to use for the // branch. - VirtualFrame* fall_through_frame = cgen_->frame(); + VirtualFrame* fall_through_frame = cgen()->frame(); VirtualFrame* branch_frame = new VirtualFrame(fall_through_frame); - RegisterFile non_frame_registers = RegisterAllocator::Reserved(); - cgen_->SetFrame(branch_frame, &non_frame_registers); + RegisterFile non_frame_registers; + cgen()->SetFrame(branch_frame, &non_frame_registers); // Check if we can avoid merge code. - cgen_->frame()->PrepareMergeTo(entry_frame_); - if (cgen_->frame()->Equals(entry_frame_)) { + cgen()->frame()->PrepareMergeTo(entry_frame_); + if (cgen()->frame()->Equals(entry_frame_)) { // Branch right in to the block. - cgen_->DeleteFrame(); + cgen()->DeleteFrame(); __ b(cc, &entry_label_); - cgen_->SetFrame(fall_through_frame, &non_frame_registers); + cgen()->SetFrame(fall_through_frame, &non_frame_registers); return; } // Check if we can reuse existing merge code. for (int i = 0; i < reaching_frames_.length(); i++) { if (reaching_frames_[i] != NULL && - cgen_->frame()->Equals(reaching_frames_[i])) { + cgen()->frame()->Equals(reaching_frames_[i])) { // Branch to the merge code. - cgen_->DeleteFrame(); + cgen()->DeleteFrame(); __ b(cc, &merge_labels_[i]); - cgen_->SetFrame(fall_through_frame, &non_frame_registers); + cgen()->SetFrame(fall_through_frame, &non_frame_registers); return; } } @@ -108,19 +109,20 @@ void JumpTarget::DoBranch(Condition cc, Hint ignored) { // around the merge code on the fall through path. Label original_fall_through; __ b(NegateCondition(cc), &original_fall_through); - cgen_->frame()->MergeTo(entry_frame_); - cgen_->DeleteFrame(); + cgen()->frame()->MergeTo(entry_frame_); + cgen()->DeleteFrame(); __ b(&entry_label_); - cgen_->SetFrame(fall_through_frame, &non_frame_registers); + cgen()->SetFrame(fall_through_frame, &non_frame_registers); __ bind(&original_fall_through); } else { + // Preconfigured entry frame is not used on ARM. + ASSERT(entry_frame_ == NULL); // Forward branch. A copy of the current frame is added to the end // of the list of frames reaching the target block and a branch to // the merge code is emitted. - AddReachingFrame(new VirtualFrame(cgen_->frame())); + AddReachingFrame(new VirtualFrame(cgen()->frame())); __ b(cc, &merge_labels_.last()); - is_linked_ = true; } } @@ -132,70 +134,63 @@ void JumpTarget::Call() { // at the label (which should be the only one) is the spilled current // frame plus an in-memory return address. The "fall-through" frame // at the return site is the spilled current frame. - ASSERT(cgen_ != NULL); - ASSERT(cgen_->has_valid_frame()); + ASSERT(cgen()->has_valid_frame()); // There are no non-frame references across the call. - ASSERT(cgen_->HasValidEntryRegisters()); + ASSERT(cgen()->HasValidEntryRegisters()); ASSERT(!is_linked()); - cgen_->frame()->SpillAll(); - VirtualFrame* target_frame = new VirtualFrame(cgen_->frame()); + cgen()->frame()->SpillAll(); + VirtualFrame* target_frame = new VirtualFrame(cgen()->frame()); target_frame->Adjust(1); + // We do not expect a call with a preconfigured entry frame. + ASSERT(entry_frame_ == NULL); AddReachingFrame(target_frame); __ bl(&merge_labels_.last()); - - is_linked_ = !is_bound_; } void JumpTarget::DoBind(int mergable_elements) { - ASSERT(cgen_ != NULL); ASSERT(!is_bound()); // Live non-frame registers are not allowed at the start of a basic // block. - ASSERT(!cgen_->has_valid_frame() || cgen_->HasValidEntryRegisters()); + ASSERT(!cgen()->has_valid_frame() || cgen()->HasValidEntryRegisters()); if (direction_ == FORWARD_ONLY) { // A simple case: no forward jumps and no possible backward jumps. if (!is_linked()) { // The stack pointer can be floating above the top of the // virtual frame before the bind. Afterward, it should not. - ASSERT(cgen_->has_valid_frame()); - VirtualFrame* frame = cgen_->frame(); - int difference = - frame->stack_pointer_ - (frame->elements_.length() - 1); + ASSERT(cgen()->has_valid_frame()); + VirtualFrame* frame = cgen()->frame(); + int difference = frame->stack_pointer_ - (frame->element_count() - 1); if (difference > 0) { frame->stack_pointer_ -= difference; __ add(sp, sp, Operand(difference * kPointerSize)); } - - is_bound_ = true; + __ bind(&entry_label_); return; } // Another simple case: no fall through, a single forward jump, // and no possible backward jumps. - if (!cgen_->has_valid_frame() && reaching_frames_.length() == 1) { + if (!cgen()->has_valid_frame() && reaching_frames_.length() == 1) { // Pick up the only reaching frame, take ownership of it, and // use it for the block about to be emitted. VirtualFrame* frame = reaching_frames_[0]; - RegisterFile reserved = RegisterAllocator::Reserved(); - cgen_->SetFrame(frame, &reserved); + RegisterFile empty; + cgen()->SetFrame(frame, &empty); reaching_frames_[0] = NULL; __ bind(&merge_labels_[0]); // The stack pointer can be floating above the top of the // virtual frame before the bind. Afterward, it should not. - int difference = - frame->stack_pointer_ - (frame->elements_.length() - 1); + int difference = frame->stack_pointer_ - (frame->element_count() - 1); if (difference > 0) { frame->stack_pointer_ -= difference; __ add(sp, sp, Operand(difference * kPointerSize)); } - - is_linked_ = false; - is_bound_ = true; + __ bind(&entry_label_); return; } } @@ -203,15 +198,17 @@ void JumpTarget::DoBind(int mergable_elements) { // If there is a current frame, record it as the fall-through. It // is owned by the reaching frames for now. bool had_fall_through = false; - if (cgen_->has_valid_frame()) { + if (cgen()->has_valid_frame()) { had_fall_through = true; - AddReachingFrame(cgen_->frame()); + AddReachingFrame(cgen()->frame()); // Return value ignored. RegisterFile empty; - cgen_->SetFrame(NULL, &empty); + cgen()->SetFrame(NULL, &empty); } // Compute the frame to use for entry to the block. - ComputeEntryFrame(mergable_elements); + if (entry_frame_ == NULL) { + ComputeEntryFrame(mergable_elements); + } // Some moves required to merge to an expected frame require purely // frame state changes, and do not require any code generation. @@ -242,17 +239,17 @@ void JumpTarget::DoBind(int mergable_elements) { // binding site or as the fall through from a previous merge // code block. Jump around the code we are about to // generate. - if (cgen_->has_valid_frame()) { - cgen_->DeleteFrame(); + if (cgen()->has_valid_frame()) { + cgen()->DeleteFrame(); __ b(&entry_label_); } // Pick up the frame for this block. Assume ownership if // there cannot be backward jumps. - RegisterFile reserved = RegisterAllocator::Reserved(); + RegisterFile empty; if (direction_ == BIDIRECTIONAL) { - cgen_->SetFrame(new VirtualFrame(frame), &reserved); + cgen()->SetFrame(new VirtualFrame(frame), &empty); } else { - cgen_->SetFrame(frame, &reserved); + cgen()->SetFrame(frame, &empty); reaching_frames_[i] = NULL; } __ bind(&merge_labels_[i]); @@ -261,23 +258,22 @@ void JumpTarget::DoBind(int mergable_elements) { // looking for any that can share merge code with this one. for (int j = 0; j < i; j++) { VirtualFrame* other = reaching_frames_[j]; - if (other != NULL && other->Equals(cgen_->frame())) { + if (other != NULL && other->Equals(cgen()->frame())) { // Set the reaching frame element to null to avoid // processing it later, and then bind its entry label. - delete other; reaching_frames_[j] = NULL; __ bind(&merge_labels_[j]); } } // Emit the merge code. - cgen_->frame()->MergeTo(entry_frame_); + cgen()->frame()->MergeTo(entry_frame_); } else if (i == reaching_frames_.length() - 1 && had_fall_through) { // If this is the fall through, and it didn't need merge // code, we need to pick up the frame so we can jump around // subsequent merge blocks if necessary. - RegisterFile reserved = RegisterAllocator::Reserved(); - cgen_->SetFrame(frame, &reserved); + RegisterFile empty; + cgen()->SetFrame(frame, &empty); reaching_frames_[i] = NULL; } } @@ -286,22 +282,17 @@ void JumpTarget::DoBind(int mergable_elements) { // The code generator may not have a current frame if there was no // fall through and none of the reaching frames needed merging. // In that case, clone the entry frame as the current frame. - if (!cgen_->has_valid_frame()) { - RegisterFile reserved_registers = RegisterAllocator::Reserved(); - cgen_->SetFrame(new VirtualFrame(entry_frame_), &reserved_registers); + if (!cgen()->has_valid_frame()) { + RegisterFile empty; + cgen()->SetFrame(new VirtualFrame(entry_frame_), &empty); } - // There is certainly a current frame equal to the entry frame. - // Bind the entry frame label. - __ bind(&entry_label_); - // There may be unprocessed reaching frames that did not need // merge code. They will have unbound merge labels. Bind their // merge labels to be the same as the entry label and deallocate // them. for (int i = 0; i < reaching_frames_.length(); i++) { if (!merge_labels_[i].is_bound()) { - delete reaching_frames_[i]; reaching_frames_[i] = NULL; __ bind(&merge_labels_[i]); } @@ -318,15 +309,13 @@ void JumpTarget::DoBind(int mergable_elements) { // Use a copy of the reaching frame so the original can be saved // for possible reuse as a backward merge block. - RegisterFile reserved = RegisterAllocator::Reserved(); - cgen_->SetFrame(new VirtualFrame(reaching_frames_[0]), &reserved); + RegisterFile empty; + cgen()->SetFrame(new VirtualFrame(reaching_frames_[0]), &empty); __ bind(&merge_labels_[0]); - cgen_->frame()->MergeTo(entry_frame_); - __ bind(&entry_label_); + cgen()->frame()->MergeTo(entry_frame_); } - is_linked_ = false; - is_bound_ = true; + __ bind(&entry_label_); } #undef __ diff --git a/deps/v8/src/arm/macro-assembler-arm.cc b/deps/v8/src/arm/macro-assembler-arm.cc index 365c1ad7f2..4e24063c94 100644 --- a/deps/v8/src/arm/macro-assembler-arm.cc +++ b/deps/v8/src/arm/macro-assembler-arm.cc @@ -32,7 +32,8 @@ #include "debug.h" #include "runtime.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Give alias names to registers Register cp = { 8 }; // JavaScript context pointer @@ -58,7 +59,10 @@ MacroAssembler::MacroAssembler(void* buffer, int size) // We do not support thumb inter-working with an arm architecture not supporting // the blx instruction (below v5t) #if defined(__THUMB_INTERWORK__) -#if !defined(__ARM_ARCH_5T__) && !defined(__ARM_ARCH_5TE__) +#if !defined(__ARM_ARCH_5T__) && \ + !defined(__ARM_ARCH_5TE__) && \ + !defined(__ARM_ARCH_7A__) && \ + !defined(__ARM_ARCH_7__) // add tests for other versions above v5t as required #error "for thumb inter-working we require architecture v5t or above" #endif @@ -291,6 +295,12 @@ void MacroAssembler::LeaveFrame(StackFrame::Type type) { void MacroAssembler::EnterExitFrame(StackFrame::Type type) { ASSERT(type == StackFrame::EXIT || type == StackFrame::EXIT_DEBUG); + + // Compute the argv pointer and keep it in a callee-saved register. + // r0 is argc. + add(r6, sp, Operand(r0, LSL, kPointerSizeLog2)); + sub(r6, r6, Operand(kPointerSize)); + // Compute parameter pointer before making changes and save it as ip // register so that it is restored as sp register on exit, thereby // popping the args. @@ -298,6 +308,17 @@ void MacroAssembler::EnterExitFrame(StackFrame::Type type) { // ip = sp + kPointerSize * #args; add(ip, sp, Operand(r0, LSL, kPointerSizeLog2)); + // Align the stack at this point. After this point we have 5 pushes, + // so in fact we have to unalign here! See also the assert on the + // alignment immediately below. + if (OS::ActivationFrameAlignment() != kPointerSize) { + // This code needs to be made more general if this assert doesn't hold. + ASSERT(OS::ActivationFrameAlignment() == 2 * kPointerSize); + mov(r7, Operand(Smi::FromInt(0))); + tst(sp, Operand(OS::ActivationFrameAlignment() - 1)); + push(r7, eq); // Conditional push instruction. + } + // Push in reverse order: caller_fp, sp_on_exit, and caller_pc. stm(db_w, sp, fp.bit() | ip.bit() | lr.bit()); mov(fp, Operand(sp)); // setup new frame pointer @@ -316,9 +337,6 @@ void MacroAssembler::EnterExitFrame(StackFrame::Type type) { mov(r4, Operand(r0)); mov(r5, Operand(r1)); - // Compute the argv pointer and keep it in a callee-saved register. - add(r6, fp, Operand(r4, LSL, kPointerSizeLog2)); - add(r6, r6, Operand(ExitFrameConstants::kPPDisplacement - kPointerSize)); #ifdef ENABLE_DEBUGGER_SUPPORT // Save the state of all registers to the stack from the memory diff --git a/deps/v8/src/arm/macro-assembler-arm.h b/deps/v8/src/arm/macro-assembler-arm.h index e336757e0a..27eeab2e9a 100644 --- a/deps/v8/src/arm/macro-assembler-arm.h +++ b/deps/v8/src/arm/macro-assembler-arm.h @@ -30,7 +30,8 @@ #include "assembler.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Give alias names to registers diff --git a/deps/v8/src/arm/regexp-macro-assembler-arm.cc b/deps/v8/src/arm/regexp-macro-assembler-arm.cc index bf07f0e3d9..78ebc7e801 100644 --- a/deps/v8/src/arm/regexp-macro-assembler-arm.cc +++ b/deps/v8/src/arm/regexp-macro-assembler-arm.cc @@ -30,7 +30,8 @@ #include "regexp-macro-assembler.h" #include "arm/regexp-macro-assembler-arm.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { RegExpMacroAssemblerARM::RegExpMacroAssemblerARM() { UNIMPLEMENTED(); diff --git a/deps/v8/src/arm/regexp-macro-assembler-arm.h b/deps/v8/src/arm/regexp-macro-assembler-arm.h index 2f38bb73ef..de5518379c 100644 --- a/deps/v8/src/arm/regexp-macro-assembler-arm.h +++ b/deps/v8/src/arm/regexp-macro-assembler-arm.h @@ -28,7 +28,8 @@ #ifndef V8_ARM_REGEXP_MACRO_ASSEMBLER_ARM_H_ #define V8_ARM_REGEXP_MACRO_ASSEMBLER_ARM_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { class RegExpMacroAssemblerARM: public RegExpMacroAssembler { public: diff --git a/deps/v8/src/arm/register-allocator-arm-inl.h b/deps/v8/src/arm/register-allocator-arm-inl.h new file mode 100644 index 0000000000..d98818f0f6 --- /dev/null +++ b/deps/v8/src/arm/register-allocator-arm-inl.h @@ -0,0 +1,103 @@ +// 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_ARM_REGISTER_ALLOCATOR_ARM_INL_H_ +#define V8_ARM_REGISTER_ALLOCATOR_ARM_INL_H_ + +#include "v8.h" + +namespace v8 { +namespace internal { + +// ------------------------------------------------------------------------- +// RegisterAllocator implementation. + +bool RegisterAllocator::IsReserved(Register reg) { + return reg.is(cp) || reg.is(fp) || reg.is(sp) || reg.is(pc); +} + + + +// The register allocator uses small integers to represent the +// non-reserved assembler registers. The mapping is: +// +// r0 <-> 0 +// r1 <-> 1 +// r2 <-> 2 +// r3 <-> 3 +// r4 <-> 4 +// r5 <-> 5 +// r6 <-> 6 +// r7 <-> 7 +// r9 <-> 8 +// r10 <-> 9 +// ip <-> 10 +// lr <-> 11 + +int RegisterAllocator::ToNumber(Register reg) { + ASSERT(reg.is_valid() && !IsReserved(reg)); + static int numbers[] = { + 0, // r0 + 1, // r1 + 2, // r2 + 3, // r3 + 4, // r4 + 5, // r5 + 6, // r6 + 7, // r7 + -1, // cp + 8, // r9 + 9, // r10 + -1, // fp + 10, // ip + -1, // sp + 11, // lr + -1 // pc + }; + return numbers[reg.code()]; +} + + +Register RegisterAllocator::ToRegister(int num) { + ASSERT(num >= 0 && num < kNumRegisters); + static Register registers[] = + { r0, r1, r2, r3, r4, r5, r6, r7, r9, r10, ip, lr }; + return registers[num]; +} + + +void RegisterAllocator::Initialize() { + Reset(); + // The non-reserved r1 and lr registers are live on JS function entry. + Use(r1); // JS function. + Use(lr); // Return address. +} + + +} } // namespace v8::internal + +#endif // V8_ARM_REGISTER_ALLOCATOR_ARM_INL_H_ diff --git a/deps/v8/src/arm/register-allocator-arm.cc b/deps/v8/src/arm/register-allocator-arm.cc index d468c84e3d..ad0c7f9d46 100644 --- a/deps/v8/src/arm/register-allocator-arm.cc +++ b/deps/v8/src/arm/register-allocator-arm.cc @@ -30,7 +30,8 @@ #include "codegen-inl.h" #include "register-allocator-inl.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // ------------------------------------------------------------------------- // Result implementation. @@ -48,56 +49,10 @@ void Result::ToRegister(Register target) { // ------------------------------------------------------------------------- // RegisterAllocator implementation. -RegisterFile RegisterAllocator::Reserved() { - RegisterFile reserved; - reserved.Use(sp); - reserved.Use(fp); - reserved.Use(cp); - reserved.Use(pc); - return reserved; -} - - -void RegisterAllocator::UnuseReserved(RegisterFile* register_file) { - register_file->ref_counts_[sp.code()] = 0; - register_file->ref_counts_[fp.code()] = 0; - register_file->ref_counts_[cp.code()] = 0; - register_file->ref_counts_[pc.code()] = 0; -} - - -bool RegisterAllocator::IsReserved(int reg_code) { - return (reg_code == sp.code()) - || (reg_code == fp.code()) - || (reg_code == cp.code()) - || (reg_code == pc.code()); -} - - -void RegisterAllocator::Initialize() { - Reset(); - // The following registers are live on function entry, saved in the - // frame, and available for allocation during execution. - Use(r1); // JS function. - Use(lr); // Return address. -} - - -void RegisterAllocator::Reset() { - registers_.Reset(); - // The following registers are live on function entry and reserved - // during execution. - Use(sp); // Stack pointer. - Use(fp); // Frame pointer (caller's frame pointer on entry). - Use(cp); // Context context (callee's context on entry). - Use(pc); // Program counter. -} - - Result RegisterAllocator::AllocateByteRegisterWithoutSpilling() { - UNIMPLEMENTED(); - Result invalid(cgen_); - return invalid; + // No byte registers on ARM. + UNREACHABLE(); + return Result(); } diff --git a/deps/v8/src/arm/register-allocator-arm.h b/deps/v8/src/arm/register-allocator-arm.h new file mode 100644 index 0000000000..f953ed9f1d --- /dev/null +++ b/deps/v8/src/arm/register-allocator-arm.h @@ -0,0 +1,43 @@ +// 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_ARM_REGISTER_ALLOCATOR_ARM_H_ +#define V8_ARM_REGISTER_ALLOCATOR_ARM_H_ + +namespace v8 { +namespace internal { + +class RegisterAllocatorConstants : public AllStatic { + public: + static const int kNumRegisters = 12; + static const int kInvalidRegister = -1; +}; + + +} } // namespace v8::internal + +#endif // V8_ARM_REGISTER_ALLOCATOR_ARM_H_ diff --git a/deps/v8/src/arm/simulator-arm.cc b/deps/v8/src/arm/simulator-arm.cc index 9737e95399..b8b66636c4 100644 --- a/deps/v8/src/arm/simulator-arm.cc +++ b/deps/v8/src/arm/simulator-arm.cc @@ -36,7 +36,8 @@ #if !defined(__arm__) // Only build the simulator if not compiling for real ARM hardware. -namespace assembler { namespace arm { +namespace assembler { +namespace arm { using ::v8::internal::Object; using ::v8::internal::PrintF; diff --git a/deps/v8/src/arm/simulator-arm.h b/deps/v8/src/arm/simulator-arm.h index 2029fd3bc7..d4a395acaf 100644 --- a/deps/v8/src/arm/simulator-arm.h +++ b/deps/v8/src/arm/simulator-arm.h @@ -66,7 +66,8 @@ #include "constants-arm.h" -namespace assembler { namespace arm { +namespace assembler { +namespace arm { class Simulator { public: diff --git a/deps/v8/src/arm/stub-cache-arm.cc b/deps/v8/src/arm/stub-cache-arm.cc index 56afa02884..c09f9e3b60 100644 --- a/deps/v8/src/arm/stub-cache-arm.cc +++ b/deps/v8/src/arm/stub-cache-arm.cc @@ -1,4 +1,4 @@ -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2006-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: @@ -31,7 +31,8 @@ #include "codegen-inl.h" #include "stub-cache.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { #define __ ACCESS_MASM(masm) @@ -61,7 +62,7 @@ static void ProbeTable(MacroAssembler* masm, // Check that the flags match what we're looking for. __ ldr(offset, FieldMemOperand(offset, Code::kFlagsOffset)); - __ and_(offset, offset, Operand(~Code::kFlagsTypeMask)); + __ and_(offset, offset, Operand(~Code::kFlagsNotUsedInLookup)); __ cmp(offset, Operand(flags)); __ b(ne, &miss); @@ -245,6 +246,7 @@ void StubCompiler::GenerateLoadCallback(MacroAssembler* masm, void StubCompiler::GenerateLoadInterceptor(MacroAssembler* masm, JSObject* object, JSObject* holder, + Smi* lookup_hint, Register receiver, Register name, Register scratch1, @@ -262,11 +264,13 @@ void StubCompiler::GenerateLoadInterceptor(MacroAssembler* masm, __ push(receiver); // receiver __ push(reg); // holder __ push(name); // name + __ mov(scratch1, Operand(lookup_hint)); + __ push(scratch1); // Do tail-call to the runtime system. ExternalReference load_ic_property = ExternalReference(IC_Utility(IC::kLoadInterceptorProperty)); - __ TailCallRuntime(load_ic_property, 3); + __ TailCallRuntime(load_ic_property, 4); } @@ -494,7 +498,9 @@ Object* StubCompiler::CompileLazyCompile(Code::Flags flags) { Object* CallStubCompiler::CompileCallField(Object* object, JSObject* holder, int index, - String* name) { + String* name, + Code::Flags flags) { + ASSERT_EQ(FIELD, Code::ExtractTypeFromFlags(flags)); // ----------- S t a t e ------------- // -- lr: return address // ----------------------------------- @@ -538,14 +544,16 @@ Object* CallStubCompiler::CompileCallField(Object* object, __ Jump(ic, RelocInfo::CODE_TARGET); // Return the generated code. - return GetCode(FIELD, name); + return GetCodeWithFlags(flags, name); } Object* CallStubCompiler::CompileCallConstant(Object* object, JSObject* holder, JSFunction* function, - CheckType check) { + CheckType check, + Code::Flags flags) { + ASSERT_EQ(CONSTANT_FUNCTION, Code::ExtractTypeFromFlags(flags)); // ----------- S t a t e ------------- // -- lr: return address // ----------------------------------- @@ -663,7 +671,7 @@ Object* CallStubCompiler::CompileCallConstant(Object* object, if (function->shared()->name()->IsString()) { function_name = String::cast(function->shared()->name()); } - return GetCode(CONSTANT_FUNCTION, function_name); + return GetCodeWithFlags(flags, function_name); } @@ -904,7 +912,15 @@ Object* LoadStubCompiler::CompileLoadInterceptor(JSObject* object, __ ldr(r0, MemOperand(sp, 0)); - GenerateLoadInterceptor(masm(), object, holder, r0, r2, r3, r1, &miss); + GenerateLoadInterceptor(masm(), + object, + holder, + holder->InterceptorPropertyLookupHint(name), + r0, + r2, + r3, + r1, + &miss); __ bind(&miss); GenerateLoadMiss(masm(), Code::LOAD_IC); @@ -1010,7 +1026,15 @@ Object* KeyedLoadStubCompiler::CompileLoadInterceptor(JSObject* receiver, __ cmp(r2, Operand(Handle<String>(name))); __ b(ne, &miss); - GenerateLoadInterceptor(masm(), receiver, holder, r0, r2, r3, r1, &miss); + GenerateLoadInterceptor(masm(), + receiver, + holder, + Smi::FromInt(JSObject::kLookupInHolder), + r0, + r2, + r3, + r1, + &miss); __ bind(&miss); GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); diff --git a/deps/v8/src/arm/virtual-frame-arm.cc b/deps/v8/src/arm/virtual-frame-arm.cc index 43100f1ec7..952738329b 100644 --- a/deps/v8/src/arm/virtual-frame-arm.cc +++ b/deps/v8/src/arm/virtual-frame-arm.cc @@ -31,31 +31,25 @@ #include "register-allocator-inl.h" #include "scopes.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // ------------------------------------------------------------------------- // VirtualFrame implementation. -#define __ ACCESS_MASM(masm_) +#define __ ACCESS_MASM(masm()) // On entry to a function, the virtual frame already contains the // receiver and the parameters. All initial frame elements are in // memory. -VirtualFrame::VirtualFrame(CodeGenerator* cgen) - : cgen_(cgen), - masm_(cgen->masm()), - elements_(cgen->scope()->num_parameters() - + cgen->scope()->num_stack_slots() - + kPreallocatedElements), - parameter_count_(cgen->scope()->num_parameters()), - local_count_(0), - stack_pointer_(parameter_count_), // 0-based index of TOS. - frame_pointer_(kIllegalIndex) { - for (int i = 0; i < parameter_count_ + 1; i++) { +VirtualFrame::VirtualFrame() + : elements_(parameter_count() + local_count() + kPreallocatedElements), + stack_pointer_(parameter_count()) { // 0-based index of TOS. + for (int i = 0; i <= stack_pointer_; i++) { elements_.Add(FrameElement::MemoryElement()); } - for (int i = 0; i < kNumRegisters; i++) { + for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) { register_locations_[i] = kIllegalIndex; } } @@ -82,10 +76,10 @@ void VirtualFrame::SyncRange(int begin, int end) { void VirtualFrame::MergeTo(VirtualFrame* expected) { - Comment cmnt(masm_, "[ Merge frame"); + Comment cmnt(masm(), "[ Merge frame"); // We should always be merging the code generator's current frame to an // expected frame. - ASSERT(cgen_->frame() == this); + ASSERT(cgen()->frame() == this); // Adjust the stack pointer upward (toward the top of the virtual // frame) if necessary. @@ -102,7 +96,7 @@ void VirtualFrame::MergeTo(VirtualFrame* expected) { // Fix any sync bit problems from the bottom-up, stopping when we // hit the stack pointer or the top of the frame if the stack // pointer is floating above the frame. - int limit = Min(stack_pointer_, elements_.length() - 1); + int limit = Min(static_cast<int>(stack_pointer_), element_count() - 1); for (int i = 0; i <= limit; i++) { FrameElement source = elements_[i]; FrameElement target = expected->elements_[i]; @@ -134,7 +128,7 @@ void VirtualFrame::MergeMoveRegistersToMemory(VirtualFrame* expected) { // On ARM, all elements are in memory. #ifdef DEBUG - int start = Min(stack_pointer_, elements_.length() - 1); + int start = Min(static_cast<int>(stack_pointer_), element_count() - 1); for (int i = start; i >= 0; i--) { ASSERT(elements_[i].is_memory()); ASSERT(expected->elements_[i].is_memory()); @@ -147,12 +141,12 @@ void VirtualFrame::MergeMoveRegistersToRegisters(VirtualFrame* expected) { } -void VirtualFrame::MergeMoveMemoryToRegisters(VirtualFrame *expected) { +void VirtualFrame::MergeMoveMemoryToRegisters(VirtualFrame* expected) { } void VirtualFrame::Enter() { - Comment cmnt(masm_, "[ Enter JS frame"); + Comment cmnt(masm(), "[ Enter JS frame"); #ifdef DEBUG // Verify that r1 contains a JS function. The following code relies @@ -175,15 +169,14 @@ void VirtualFrame::Enter() { Adjust(4); __ stm(db_w, sp, r1.bit() | cp.bit() | fp.bit() | lr.bit()); // Adjust FP to point to saved FP. - frame_pointer_ = elements_.length() - 2; __ add(fp, sp, Operand(2 * kPointerSize)); - cgen_->allocator()->Unuse(r1); - cgen_->allocator()->Unuse(lr); + cgen()->allocator()->Unuse(r1); + cgen()->allocator()->Unuse(lr); } void VirtualFrame::Exit() { - Comment cmnt(masm_, "[ Exit JS frame"); + Comment cmnt(masm(), "[ Exit JS frame"); // Drop the execution stack down to the frame pointer and restore the caller // frame pointer and return address. __ mov(sp, fp); @@ -191,12 +184,11 @@ void VirtualFrame::Exit() { } -void VirtualFrame::AllocateStackSlots(int count) { - ASSERT(height() == 0); - local_count_ = count; - Adjust(count); +void VirtualFrame::AllocateStackSlots() { + int count = local_count(); if (count > 0) { - Comment cmnt(masm_, "[ Allocate space for locals"); + Comment cmnt(masm(), "[ Allocate space for locals"); + Adjust(count); // Initialize stack slots with 'undefined' value. __ mov(ip, Operand(Factory::undefined_value())); for (int i = 0; i < count; i++) { @@ -246,9 +238,9 @@ void VirtualFrame::PushTryHandler(HandlerType type) { Result VirtualFrame::RawCallStub(CodeStub* stub) { - ASSERT(cgen_->HasValidEntryRegisters()); + ASSERT(cgen()->HasValidEntryRegisters()); __ CallStub(stub); - Result result = cgen_->allocator()->Allocate(r0); + Result result = cgen()->allocator()->Allocate(r0); ASSERT(result.is_valid()); return result; } @@ -271,9 +263,9 @@ Result VirtualFrame::CallStub(CodeStub* stub, Result* arg0, Result* arg1) { Result VirtualFrame::CallRuntime(Runtime::Function* f, int arg_count) { PrepareForCall(arg_count, arg_count); - ASSERT(cgen_->HasValidEntryRegisters()); + ASSERT(cgen()->HasValidEntryRegisters()); __ CallRuntime(f, arg_count); - Result result = cgen_->allocator()->Allocate(r0); + Result result = cgen()->allocator()->Allocate(r0); ASSERT(result.is_valid()); return result; } @@ -281,9 +273,9 @@ Result VirtualFrame::CallRuntime(Runtime::Function* f, int arg_count) { Result VirtualFrame::CallRuntime(Runtime::FunctionId id, int arg_count) { PrepareForCall(arg_count, arg_count); - ASSERT(cgen_->HasValidEntryRegisters()); + ASSERT(cgen()->HasValidEntryRegisters()); __ CallRuntime(id, arg_count); - Result result = cgen_->allocator()->Allocate(r0); + Result result = cgen()->allocator()->Allocate(r0); ASSERT(result.is_valid()); return result; } @@ -297,16 +289,16 @@ Result VirtualFrame::InvokeBuiltin(Builtins::JavaScript id, PrepareForCall(arg_count, arg_count); arg_count_register->Unuse(); __ InvokeBuiltin(id, flags); - Result result = cgen_->allocator()->Allocate(r0); + Result result = cgen()->allocator()->Allocate(r0); return result; } Result VirtualFrame::RawCallCodeObject(Handle<Code> code, RelocInfo::Mode rmode) { - ASSERT(cgen_->HasValidEntryRegisters()); + ASSERT(cgen()->HasValidEntryRegisters()); __ Call(code, rmode); - Result result = cgen_->allocator()->Allocate(r0); + Result result = cgen()->allocator()->Allocate(r0); ASSERT(result.is_valid()); return result; } @@ -401,7 +393,7 @@ Result VirtualFrame::CallCodeObject(Handle<Code> code, void VirtualFrame::Drop(int count) { ASSERT(height() >= count); - int num_virtual_elements = (elements_.length() - 1) - stack_pointer_; + int num_virtual_elements = (element_count() - 1) - stack_pointer_; // Emit code to lower the stack pointer if necessary. if (num_virtual_elements < count) { @@ -422,13 +414,12 @@ void VirtualFrame::Drop(int count) { Result VirtualFrame::Pop() { UNIMPLEMENTED(); - Result invalid(cgen_); - return invalid; + return Result(); } void VirtualFrame::EmitPop(Register reg) { - ASSERT(stack_pointer_ == elements_.length() - 1); + ASSERT(stack_pointer_ == element_count() - 1); stack_pointer_--; elements_.RemoveLast(); __ pop(reg); @@ -436,7 +427,7 @@ void VirtualFrame::EmitPop(Register reg) { void VirtualFrame::EmitPush(Register reg) { - ASSERT(stack_pointer_ == elements_.length() - 1); + ASSERT(stack_pointer_ == element_count() - 1); elements_.Add(FrameElement::MemoryElement()); stack_pointer_++; __ push(reg); diff --git a/deps/v8/src/arm/virtual-frame-arm.h b/deps/v8/src/arm/virtual-frame-arm.h index 371a23e939..ebebd534a7 100644 --- a/deps/v8/src/arm/virtual-frame-arm.h +++ b/deps/v8/src/arm/virtual-frame-arm.h @@ -29,8 +29,10 @@ #define V8_ARM_VIRTUAL_FRAME_ARM_H_ #include "register-allocator.h" +#include "scopes.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // ------------------------------------------------------------------------- // Virtual frames @@ -41,7 +43,7 @@ namespace v8 { namespace internal { // as random access to the expression stack elements, locals, and // parameters. -class VirtualFrame : public Malloced { +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 @@ -50,42 +52,66 @@ class VirtualFrame : public Malloced { // generator is being transformed. class SpilledScope BASE_EMBEDDED { public: - explicit SpilledScope(CodeGenerator* cgen); + SpilledScope() : previous_state_(cgen()->in_spilled_code()) { + ASSERT(cgen()->has_valid_frame()); + cgen()->frame()->SpillAll(); + cgen()->set_in_spilled_code(true); + } - ~SpilledScope(); + ~SpilledScope() { + cgen()->set_in_spilled_code(previous_state_); + } private: - CodeGenerator* cgen_; bool previous_state_; + + CodeGenerator* cgen() { return CodeGeneratorScope::Current(); } }; // An illegal index into the virtual frame. static const int kIllegalIndex = -1; // Construct an initial virtual frame on entry to a JS function. - explicit VirtualFrame(CodeGenerator* cgen); + VirtualFrame(); // Construct a virtual frame as a clone of an existing one. explicit VirtualFrame(VirtualFrame* original); + CodeGenerator* cgen() { return CodeGeneratorScope::Current(); } + MacroAssembler* masm() { return cgen()->masm(); } + // Create a duplicate of an existing valid frame element. FrameElement CopyElementAt(int index); + // The number of elements on the virtual frame. + int element_count() { return elements_.length(); } + // The height of the virtual expression stack. - int height() const { - return elements_.length() - expression_base_index(); + int height() { + return element_count() - expression_base_index(); + } + + int register_location(int num) { + ASSERT(num >= 0 && num < RegisterAllocator::kNumRegisters); + return register_locations_[num]; + } + + int register_location(Register reg) { + return register_locations_[RegisterAllocator::ToNumber(reg)]; } - int register_index(Register reg) { - return register_locations_[reg.code()]; + void set_register_location(Register reg, int index) { + register_locations_[RegisterAllocator::ToNumber(reg)] = index; } - bool is_used(int reg_code) { - return register_locations_[reg_code] != kIllegalIndex; + bool is_used(int num) { + ASSERT(num >= 0 && num < RegisterAllocator::kNumRegisters); + return register_locations_[num] != kIllegalIndex; } bool is_used(Register reg) { - return is_used(reg.code()); + return register_locations_[RegisterAllocator::ToNumber(reg)] + != kIllegalIndex; } // Add extra in-memory elements to the top of the frame to match an actual @@ -95,7 +121,12 @@ class VirtualFrame : public Malloced { // Forget elements from the top of the frame to match an actual frame (eg, // the frame after a runtime call). No code is emitted. - void Forget(int count); + 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 @@ -106,7 +137,9 @@ class VirtualFrame : public Malloced { void SpillAll(); // Spill all occurrences of a specific register from the frame. - void Spill(Register reg); + void Spill(Register reg) { + if (is_used(reg)) SpillElementAt(register_location(reg)); + } // 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 @@ -127,13 +160,23 @@ class VirtualFrame : public Malloced { // 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(); + 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(); + void AttachToCodeGenerator() { + RegisterAllocator* cgen_allocator = cgen()->allocator(); + for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) { + if (is_used(i)) cgen_allocator->Unuse(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 @@ -149,13 +192,13 @@ class VirtualFrame : public Malloced { void PrepareForReturn(); // Allocate and initialize the frame-allocated locals. - void AllocateStackSlots(int count); + void AllocateStackSlots(); // The current top of the expression stack as an assembly operand. - MemOperand Top() const { return MemOperand(sp, 0); } + MemOperand Top() { return MemOperand(sp, 0); } // An element of the expression stack as an assembly operand. - MemOperand ElementAt(int index) const { + MemOperand ElementAt(int index) { return MemOperand(sp, index * kPointerSize); } @@ -165,18 +208,18 @@ class VirtualFrame : public Malloced { // Set a frame element to a constant. The index is frame-top relative. void SetElementAt(int index, Handle<Object> value) { - Result temp(value, cgen_); + Result temp(value); SetElementAt(index, &temp); } void PushElementAt(int index) { - PushFrameSlotAt(elements_.length() - index - 1); + PushFrameSlotAt(element_count() - index - 1); } // A frame-allocated local as an assembly operand. - MemOperand LocalAt(int index) const { + MemOperand LocalAt(int index) { ASSERT(0 <= index); - ASSERT(index < local_count_); + ASSERT(index < local_count()); return MemOperand(fp, kLocal0Offset - index * kPointerSize); } @@ -202,13 +245,13 @@ class VirtualFrame : public Malloced { void PushReceiverSlotAddress(); // The function frame slot. - MemOperand Function() const { return MemOperand(fp, kFunctionOffset); } + MemOperand Function() { return MemOperand(fp, kFunctionOffset); } // Push the function on top of the frame. void PushFunction() { PushFrameSlotAt(function_index()); } // The context frame slot. - MemOperand Context() const { return MemOperand(fp, kContextOffset); } + MemOperand Context() { return MemOperand(fp, kContextOffset); } // Save the value of the esi register to the context frame slot. void SaveContextRegister(); @@ -218,10 +261,11 @@ class VirtualFrame : public Malloced { void RestoreContextRegister(); // A parameter as an assembly operand. - MemOperand ParameterAt(int index) const { + MemOperand ParameterAt(int index) { // Index -1 corresponds to the receiver. - ASSERT(-1 <= index && index <= parameter_count_); - return MemOperand(fp, (1 + parameter_count_ - index) * kPointerSize); + ASSERT(-1 <= index); // -1 is the receiver. + ASSERT(index <= parameter_count()); + return MemOperand(fp, (1 + parameter_count() - index) * kPointerSize); } // Push a copy of the value of a parameter frame slot on top of the frame. @@ -243,14 +287,17 @@ class VirtualFrame : public Malloced { } // The receiver frame slot. - MemOperand Receiver() const { return ParameterAt(-1); } + MemOperand 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. - Result CallStub(CodeStub* stub, int arg_count); + Result CallStub(CodeStub* stub, int arg_count) { + PrepareForCall(arg_count, arg_count); + return RawCallStub(stub); + } // Call stub that expects its argument in r0. The argument is given // as a result which must be the register r0. @@ -297,7 +344,7 @@ class VirtualFrame : public Malloced { void Drop() { Drop(1); } // Duplicate the top element of the frame. - void Dup() { PushFrameSlotAt(elements_.length() - 1); } + 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. @@ -317,7 +364,15 @@ class VirtualFrame : public Malloced { void Push(Smi* value) { Push(Handle<Object>(value)); } // Pushing a result invalidates it (its contents become owned by the frame). - void Push(Result* result); + void Push(Result* result) { + if (result->is_register()) { + Push(result->reg(), result->static_type()); + } else { + ASSERT(result->is_constant()); + Push(result->handle()); + } + result->Unuse(); + } // Nip removes zero or more elements from immediately below the top // of the frame, leaving the previous top-of-frame value on top of @@ -332,70 +387,69 @@ class VirtualFrame : public Malloced { static const int kHandlerSize = StackHandlerConstants::kSize / kPointerSize; static const int kPreallocatedElements = 5 + 8; // 8 expression stack slots. - CodeGenerator* cgen_; - MacroAssembler* masm_; - - List<FrameElement> elements_; - - // The number of frame-allocated locals and parameters respectively. - int parameter_count_; - int local_count_; + ZoneList<FrameElement> elements_; // The index of the element that is at the processor's stack pointer // (the sp register). int stack_pointer_; - // The index of the element that is at the processor's frame pointer - // (the fp register). - int frame_pointer_; - // The index of the register frame element using each register, or // kIllegalIndex if a register is not on the frame. - int register_locations_[kNumRegisters]; + int register_locations_[RegisterAllocator::kNumRegisters]; + + // The number of frame-allocated locals and parameters respectively. + int parameter_count() { return cgen()->scope()->num_parameters(); } + int local_count() { return cgen()->scope()->num_stack_slots(); } + + // The index of the element that is at the processor's frame pointer + // (the fp register). The parameters, receiver, function, and context + // are below the frame pointer. + int frame_pointer() { return parameter_count() + 3; } // The index of the first parameter. The receiver lies below the first // parameter. - int param0_index() const { return 1; } + int param0_index() { 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 context slot in the frame. It is immediately + // below the frame pointer. + int context_index() { 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 function slot in the frame. It is below the frame + // pointer and context slot. + int function_index() { 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 { - ASSERT(frame_pointer_ != kIllegalIndex); - return frame_pointer_ + 2; - } + // The index of the first local. Between the frame pointer and the + // locals lies the return address. + int local0_index() { return frame_pointer() + 2; } // The index of the base of the expression stack. - int expression_base_index() const { return local0_index() + local_count_; } + 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) const { - return (frame_pointer_ - index) * kPointerSize; + 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); + 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); + 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 @@ -407,7 +461,7 @@ class VirtualFrame : public Malloced { // Keep the element type as register or constant, and clear the dirty bit. void SyncElementAt(int index); - // Sync the range of elements in [begin, end). + // Sync the range of elements in [begin, end] with memory. void SyncRange(int begin, int end); // Sync a single unsynced element that lies beneath or at the stack pointer. @@ -471,6 +525,8 @@ class VirtualFrame : public Malloced { bool Equals(VirtualFrame* other); + // Classes that need raw access to the elements_ array. + friend class DeferredCode; friend class JumpTarget; }; diff --git a/deps/v8/src/assembler.cc b/deps/v8/src/assembler.cc index ec0e4fd145..5dba75d2d1 100644 --- a/deps/v8/src/assembler.cc +++ b/deps/v8/src/assembler.cc @@ -43,7 +43,8 @@ #include "stub-cache.h" #include "regexp-stack.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // ----------------------------------------------------------------------------- @@ -90,13 +91,13 @@ int Label::pos() const { // bits, the lowest 7 bits written first. // // data-jump + pos: 00 1110 11, -// signed int, lowest byte written first +// signed intptr_t, lowest byte written first // // data-jump + st.pos: 01 1110 11, -// signed int, lowest byte written first +// signed intptr_t, lowest byte written first // // data-jump + comm.: 10 1110 11, -// signed int, lowest byte written first +// signed intptr_t, lowest byte written first // const int kMaxRelocModes = 14; @@ -158,7 +159,7 @@ void RelocInfoWriter::WriteTaggedPC(uint32_t pc_delta, int tag) { } -void RelocInfoWriter::WriteTaggedData(int32_t data_delta, int tag) { +void RelocInfoWriter::WriteTaggedData(intptr_t data_delta, int tag) { *--pos_ = data_delta << kPositionTypeTagBits | tag; } @@ -178,11 +179,12 @@ void RelocInfoWriter::WriteExtraTaggedPC(uint32_t pc_delta, int extra_tag) { } -void RelocInfoWriter::WriteExtraTaggedData(int32_t data_delta, int top_tag) { +void RelocInfoWriter::WriteExtraTaggedData(intptr_t data_delta, int top_tag) { WriteExtraTag(kDataJumpTag, top_tag); - for (int i = 0; i < kIntSize; i++) { + for (int i = 0; i < kIntptrSize; i++) { *--pos_ = data_delta; - data_delta = ArithmeticShiftRight(data_delta, kBitsPerByte); + // Signed right shift is arithmetic shift. Tested in test-utils.cc. + data_delta = data_delta >> kBitsPerByte; } } @@ -205,11 +207,13 @@ void RelocInfoWriter::Write(const RelocInfo* rinfo) { WriteTaggedPC(pc_delta, kCodeTargetTag); } else if (RelocInfo::IsPosition(rmode)) { // Use signed delta-encoding for data. - int32_t data_delta = rinfo->data() - last_data_; + intptr_t data_delta = rinfo->data() - last_data_; int pos_type_tag = rmode == RelocInfo::POSITION ? kNonstatementPositionTag : kStatementPositionTag; // Check if data is small enough to fit in a tagged byte. - if (is_intn(data_delta, kSmallDataBits)) { + // We cannot use is_intn because data_delta is not an int32_t. + if (data_delta >= -(1 << (kSmallDataBits-1)) && + data_delta < 1 << (kSmallDataBits-1)) { WriteTaggedPC(pc_delta, kPositionTag); WriteTaggedData(data_delta, pos_type_tag); last_data_ = rinfo->data(); @@ -263,9 +267,9 @@ inline void RelocIterator::AdvanceReadPC() { void RelocIterator::AdvanceReadData() { - int32_t x = 0; - for (int i = 0; i < kIntSize; i++) { - x |= *--pos_ << i * kBitsPerByte; + intptr_t x = 0; + for (int i = 0; i < kIntptrSize; i++) { + x |= static_cast<intptr_t>(*--pos_) << i * kBitsPerByte; } rinfo_.data_ += x; } @@ -294,7 +298,8 @@ inline int RelocIterator::GetPositionTypeTag() { inline void RelocIterator::ReadTaggedData() { int8_t signed_b = *pos_; - rinfo_.data_ += ArithmeticShiftRight(signed_b, kPositionTypeTagBits); + // Signed right shift is arithmetic shift. Tested in test-utils.cc. + rinfo_.data_ += signed_b >> kPositionTypeTagBits; } diff --git a/deps/v8/src/assembler.h b/deps/v8/src/assembler.h index 8abdbc767c..66f952adbd 100644 --- a/deps/v8/src/assembler.h +++ b/deps/v8/src/assembler.h @@ -40,7 +40,8 @@ #include "zone-inl.h" #include "token.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // ----------------------------------------------------------------------------- @@ -271,8 +272,8 @@ class RelocInfoWriter BASE_EMBEDDED { inline uint32_t WriteVariableLengthPCJump(uint32_t pc_delta); inline void WriteTaggedPC(uint32_t pc_delta, int tag); inline void WriteExtraTaggedPC(uint32_t pc_delta, int extra_tag); - inline void WriteExtraTaggedData(int32_t data_delta, int top_tag); - inline void WriteTaggedData(int32_t data_delta, int tag); + inline void WriteExtraTaggedData(intptr_t data_delta, int top_tag); + inline void WriteTaggedData(intptr_t data_delta, int tag); inline void WriteExtraTag(int extra_tag, int top_tag); byte* pos_; @@ -423,8 +424,6 @@ class ExternalReference BASE_EMBEDDED { // ----------------------------------------------------------------------------- // Utility functions -// Move these into inline file? - static inline bool is_intn(int x, int n) { return -(1 << (n-1)) <= x && x < (1 << (n-1)); } @@ -436,9 +435,11 @@ static inline bool is_uintn(int x, int n) { return (x & -(1 << n)) == 0; } +static inline bool is_uint2(int x) { return is_uintn(x, 2); } static inline bool is_uint3(int x) { return is_uintn(x, 3); } static inline bool is_uint4(int x) { return is_uintn(x, 4); } static inline bool is_uint5(int x) { return is_uintn(x, 5); } +static inline bool is_uint6(int x) { return is_uintn(x, 6); } static inline bool is_uint8(int x) { return is_uintn(x, 8); } static inline bool is_uint12(int x) { return is_uintn(x, 12); } static inline bool is_uint16(int x) { return is_uintn(x, 16); } diff --git a/deps/v8/src/ast.cc b/deps/v8/src/ast.cc index d19e3b3e04..eef8da7151 100644 --- a/deps/v8/src/ast.cc +++ b/deps/v8/src/ast.cc @@ -31,7 +31,8 @@ #include "scopes.h" #include "string-stream.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { VariableProxySentinel VariableProxySentinel::this_proxy_(true); diff --git a/deps/v8/src/ast.h b/deps/v8/src/ast.h index 6a2f671052..80a4aa5f2e 100644 --- a/deps/v8/src/ast.h +++ b/deps/v8/src/ast.h @@ -37,7 +37,8 @@ #include "jsregexp.h" #include "jump-target.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // The abstract syntax tree is an intermediate, light-weight // representation of the parsed JavaScript code suitable for diff --git a/deps/v8/src/bootstrapper.cc b/deps/v8/src/bootstrapper.cc index 09cf68dea2..89c92b0697 100644 --- a/deps/v8/src/bootstrapper.cc +++ b/deps/v8/src/bootstrapper.cc @@ -37,7 +37,8 @@ #include "macro-assembler.h" #include "natives.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // A SourceCodeCache uses a FixedArray to store pairs of // (AsciiString*, JSFunction*), mapping names of native code files @@ -46,7 +47,7 @@ namespace v8 { namespace internal { // generate an index for each native JS file. class SourceCodeCache BASE_EMBEDDED { public: - explicit SourceCodeCache(ScriptType type): type_(type) { } + explicit SourceCodeCache(Script::Type type): type_(type) { } void Initialize(bool create_heap_objects) { if (create_heap_objects) { @@ -88,13 +89,13 @@ class SourceCodeCache BASE_EMBEDDED { } private: - ScriptType type_; + Script::Type type_; FixedArray* cache_; DISALLOW_COPY_AND_ASSIGN(SourceCodeCache); }; -static SourceCodeCache natives_cache(SCRIPT_TYPE_NATIVE); -static SourceCodeCache extensions_cache(SCRIPT_TYPE_EXTENSION); +static SourceCodeCache natives_cache(Script::TYPE_NATIVE); +static SourceCodeCache extensions_cache(Script::TYPE_EXTENSION); Handle<String> Bootstrapper::NativesSourceLookup(int index) { @@ -521,7 +522,7 @@ void Genesis::CreateRoots(v8::Handle<v8::ObjectTemplate> global_template, empty_function->set_code(*code); Handle<String> source = Factory::NewStringFromAscii(CStrVector("() {}")); Handle<Script> script = Factory::NewScript(source); - script->set_type(Smi::FromInt(SCRIPT_TYPE_NATIVE)); + script->set_type(Smi::FromInt(Script::TYPE_NATIVE)); empty_function->shared()->set_script(*script); empty_function->shared()->set_start_position(0); empty_function->shared()->set_end_position(source->length()); @@ -820,14 +821,28 @@ void Genesis::CreateRoots(v8::Handle<v8::ObjectTemplate> global_template, global_context()->set_context_extension_function(*context_extension_fun); } - // Setup the call-as-function delegate. - Handle<Code> code = - Handle<Code>(Builtins::builtin(Builtins::HandleApiCallAsFunction)); - Handle<JSFunction> delegate = - Factory::NewFunction(Factory::empty_symbol(), JS_OBJECT_TYPE, - JSObject::kHeaderSize, code, true); - global_context()->set_call_as_function_delegate(*delegate); - delegate->shared()->DontAdaptArguments(); + + { + // Setup the call-as-function delegate. + Handle<Code> code = + Handle<Code>(Builtins::builtin(Builtins::HandleApiCallAsFunction)); + Handle<JSFunction> delegate = + Factory::NewFunction(Factory::empty_symbol(), JS_OBJECT_TYPE, + JSObject::kHeaderSize, code, true); + global_context()->set_call_as_function_delegate(*delegate); + delegate->shared()->DontAdaptArguments(); + } + + { + // Setup the call-as-constructor delegate. + Handle<Code> code = + Handle<Code>(Builtins::builtin(Builtins::HandleApiCallAsConstructor)); + Handle<JSFunction> delegate = + Factory::NewFunction(Factory::empty_symbol(), JS_OBJECT_TYPE, + JSObject::kHeaderSize, code, true); + global_context()->set_call_as_constructor_delegate(*delegate); + delegate->shared()->DontAdaptArguments(); + } global_context()->set_special_function_table(Heap::empty_fixed_array()); @@ -1047,6 +1062,14 @@ bool Genesis::InstallNatives() { Factory::LookupAsciiSymbol("type"), proxy_type, common_attributes); + Handle<Proxy> proxy_compilation_type = + Factory::NewProxy(&Accessors::ScriptCompilationType); + script_descriptors = + Factory::CopyAppendProxyDescriptor( + script_descriptors, + Factory::LookupAsciiSymbol("compilation_type"), + proxy_compilation_type, + common_attributes); Handle<Proxy> proxy_line_ends = Factory::NewProxy(&Accessors::ScriptLineEnds); script_descriptors = @@ -1063,16 +1086,38 @@ bool Genesis::InstallNatives() { Factory::LookupAsciiSymbol("context_data"), proxy_context_data, common_attributes); + Handle<Proxy> proxy_eval_from_function = + Factory::NewProxy(&Accessors::ScriptEvalFromFunction); + script_descriptors = + Factory::CopyAppendProxyDescriptor( + script_descriptors, + Factory::LookupAsciiSymbol("eval_from_function"), + proxy_eval_from_function, + common_attributes); + Handle<Proxy> proxy_eval_from_position = + Factory::NewProxy(&Accessors::ScriptEvalFromPosition); + script_descriptors = + Factory::CopyAppendProxyDescriptor( + script_descriptors, + Factory::LookupAsciiSymbol("eval_from_position"), + proxy_eval_from_position, + common_attributes); Handle<Map> script_map = Handle<Map>(script_fun->initial_map()); script_map->set_instance_descriptors(*script_descriptors); // Allocate the empty script. Handle<Script> script = Factory::NewScript(Factory::empty_string()); - script->set_type(Smi::FromInt(SCRIPT_TYPE_NATIVE)); + script->set_type(Smi::FromInt(Script::TYPE_NATIVE)); global_context()->set_empty_script(*script); } +#ifdef V8_HOST_ARCH_64_BIT + // TODO(X64): Reenable remaining initialization when code generation works. + return true; +#endif // V8_HOST_ARCH_64_BIT + + if (FLAG_natives_file == NULL) { // Without natives file, install default natives. for (int i = Natives::GetDelayCount(); @@ -1509,8 +1554,8 @@ Genesis::Genesis(Handle<Object> global_object, current_ = this; result_ = NULL; - // If V8 hasn't been and cannot be initialized, just return. - if (!V8::HasBeenSetup() && !V8::Initialize(NULL)) return; + // If V8 isn't running and cannot be initialized, just return. + if (!V8::IsRunning() && !V8::Initialize(NULL)) return; // Before creating the roots we must save the context and restore it // on all function exits. @@ -1518,6 +1563,7 @@ Genesis::Genesis(Handle<Object> global_object, SaveContext context; CreateRoots(global_template, global_object); + if (!InstallNatives()) return; MakeFunctionInstancePrototypeWritable(); diff --git a/deps/v8/src/bootstrapper.h b/deps/v8/src/bootstrapper.h index e2883dc0f1..0d743e388f 100644 --- a/deps/v8/src/bootstrapper.h +++ b/deps/v8/src/bootstrapper.h @@ -29,7 +29,8 @@ #ifndef V8_BOOTSTRAPPER_H_ #define V8_BOOTSTRAPPER_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // The Boostrapper is the public interface for creating a JavaScript global // context. diff --git a/deps/v8/src/builtins.cc b/deps/v8/src/builtins.cc index b27974ffd9..1c43f7a4ba 100644 --- a/deps/v8/src/builtins.cc +++ b/deps/v8/src/builtins.cc @@ -32,7 +32,8 @@ #include "builtins.h" #include "ic-inl.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // ---------------------------------------------------------------------------- // Support macros for defining builtins in C. @@ -394,12 +395,18 @@ BUILTIN(HandleApiCall) { BUILTIN_END -// Handle calls to non-function objects created through the API that -// support calls. -BUILTIN(HandleApiCallAsFunction) { - // Non-functions are never called as constructors. +// Helper function to handle calls to non-function objects created through the +// API. The object can be called as either a constructor (using new) or just as +// a function (without new). +static Object* HandleApiCallAsFunctionOrConstructor(bool is_construct_call, + int __argc__, + Object** __argv__) { + // Non-functions are never called as constructors. Even if this is an object + // called as a constructor the delegate call is not a construct call. ASSERT(!CalledAsConstructor()); + Handle<Object> receiver(&__argv__[0]); + // Get the object called. JSObject* obj = JSObject::cast(*receiver); @@ -431,7 +438,7 @@ BUILTIN(HandleApiCallAsFunction) { data, self, callee, - false, + is_construct_call, reinterpret_cast<void**>(__argv__ - 1), __argc__ - 1); v8::Handle<v8::Value> value; @@ -450,6 +457,21 @@ BUILTIN(HandleApiCallAsFunction) { RETURN_IF_SCHEDULED_EXCEPTION(); return result; } + + +// Handle calls to non-function objects created through the API. This delegate +// function is used when the call is a normal function call. +BUILTIN(HandleApiCallAsFunction) { + return HandleApiCallAsFunctionOrConstructor(false, __argc__, __argv__); +} +BUILTIN_END + + +// Handle calls to non-function objects created through the API. This delegate +// function is used when the call is a construct call. +BUILTIN(HandleApiCallAsConstructor) { + return HandleApiCallAsFunctionOrConstructor(true, __argc__, __argv__); +} BUILTIN_END @@ -644,12 +666,12 @@ void Builtins::Setup(bool create_heap_objects) { Code::ComputeFlags(Code::BUILTIN) \ }, -#define DEF_FUNCTION_PTR_A(name, kind, state) \ - { FUNCTION_ADDR(Generate_##name), \ - NULL, \ - #name, \ - name, \ - Code::ComputeFlags(Code::kind, state) \ +#define DEF_FUNCTION_PTR_A(name, kind, state) \ + { FUNCTION_ADDR(Generate_##name), \ + NULL, \ + #name, \ + name, \ + Code::ComputeFlags(Code::kind, NOT_IN_LOOP, state) \ }, // Define array of pointers to generators and C builtin functions. diff --git a/deps/v8/src/builtins.h b/deps/v8/src/builtins.h index 4e74a3cc40..6e0f832565 100644 --- a/deps/v8/src/builtins.h +++ b/deps/v8/src/builtins.h @@ -28,7 +28,8 @@ #ifndef V8_BUILTINS_H_ #define V8_BUILTINS_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Define list of builtins implemented in C. #define BUILTIN_LIST_C(V) \ @@ -42,7 +43,8 @@ namespace v8 { namespace internal { V(ArrayPop) \ \ V(HandleApiCall) \ - V(HandleApiCallAsFunction) + V(HandleApiCallAsFunction) \ + V(HandleApiCallAsConstructor) // Define list of builtins implemented in assembly. @@ -99,35 +101,36 @@ namespace v8 { namespace internal { #endif // Define list of builtins implemented in JavaScript. -#define BUILTINS_LIST_JS(V) \ - V(EQUALS, 1) \ - V(STRICT_EQUALS, 1) \ - V(COMPARE, 2) \ - V(ADD, 1) \ - V(SUB, 1) \ - V(MUL, 1) \ - V(DIV, 1) \ - V(MOD, 1) \ - V(BIT_OR, 1) \ - V(BIT_AND, 1) \ - V(BIT_XOR, 1) \ - V(UNARY_MINUS, 0) \ - V(BIT_NOT, 0) \ - V(SHL, 1) \ - V(SAR, 1) \ - V(SHR, 1) \ - V(DELETE, 1) \ - V(IN, 1) \ - V(INSTANCE_OF, 1) \ - V(GET_KEYS, 0) \ - V(FILTER_KEY, 1) \ - V(CALL_NON_FUNCTION, 0) \ - V(TO_OBJECT, 0) \ - V(TO_NUMBER, 0) \ - V(TO_STRING, 0) \ - V(STRING_ADD_LEFT, 1) \ - V(STRING_ADD_RIGHT, 1) \ - V(APPLY_PREPARE, 1) \ +#define BUILTINS_LIST_JS(V) \ + V(EQUALS, 1) \ + V(STRICT_EQUALS, 1) \ + V(COMPARE, 2) \ + V(ADD, 1) \ + V(SUB, 1) \ + V(MUL, 1) \ + V(DIV, 1) \ + V(MOD, 1) \ + V(BIT_OR, 1) \ + V(BIT_AND, 1) \ + V(BIT_XOR, 1) \ + V(UNARY_MINUS, 0) \ + V(BIT_NOT, 0) \ + V(SHL, 1) \ + V(SAR, 1) \ + V(SHR, 1) \ + V(DELETE, 1) \ + V(IN, 1) \ + V(INSTANCE_OF, 1) \ + V(GET_KEYS, 0) \ + V(FILTER_KEY, 1) \ + V(CALL_NON_FUNCTION, 0) \ + V(CALL_NON_FUNCTION_AS_CONSTRUCTOR, 0) \ + V(TO_OBJECT, 0) \ + V(TO_NUMBER, 0) \ + V(TO_STRING, 0) \ + V(STRING_ADD_LEFT, 1) \ + V(STRING_ADD_RIGHT, 1) \ + V(APPLY_PREPARE, 1) \ V(APPLY_OVERFLOW, 1) diff --git a/deps/v8/src/bytecodes-irregexp.h b/deps/v8/src/bytecodes-irregexp.h index 94f37a8db5..bcb34c8992 100644 --- a/deps/v8/src/bytecodes-irregexp.h +++ b/deps/v8/src/bytecodes-irregexp.h @@ -29,7 +29,8 @@ #ifndef V8_BYTECODES_IRREGEXP_H_ #define V8_BYTECODES_IRREGEXP_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { static const int BYTECODE_MASK = 0xff; diff --git a/deps/v8/src/char-predicates-inl.h b/deps/v8/src/char-predicates-inl.h index 217db9c331..fadbc9afbe 100644 --- a/deps/v8/src/char-predicates-inl.h +++ b/deps/v8/src/char-predicates-inl.h @@ -30,7 +30,8 @@ #include "char-predicates.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { inline bool IsCarriageReturn(uc32 c) { diff --git a/deps/v8/src/char-predicates.h b/deps/v8/src/char-predicates.h index 63e83b4d3f..dac1eb8fef 100644 --- a/deps/v8/src/char-predicates.h +++ b/deps/v8/src/char-predicates.h @@ -28,7 +28,8 @@ #ifndef V8_CHAR_PREDICATES_H_ #define V8_CHAR_PREDICATES_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Unicode character predicates as defined by ECMA-262, 3rd, // used for lexical analysis. diff --git a/deps/v8/src/code-stubs.cc b/deps/v8/src/code-stubs.cc index 06c4dcdc20..b14ede18a4 100644 --- a/deps/v8/src/code-stubs.cc +++ b/deps/v8/src/code-stubs.cc @@ -32,7 +32,8 @@ #include "factory.h" #include "macro-assembler.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { Handle<Code> CodeStub::GetCode() { uint32_t key = GetKey(); @@ -58,7 +59,7 @@ Handle<Code> CodeStub::GetCode() { masm.GetCode(&desc); // Copy the generated code into a heap object, and store the major key. - Code::Flags flags = Code::ComputeFlags(Code::STUB); + Code::Flags flags = Code::ComputeFlags(Code::STUB, InLoop()); Handle<Code> code = Factory::NewCode(desc, NULL, flags, masm.CodeObject()); code->set_major_key(MajorKey()); diff --git a/deps/v8/src/code-stubs.h b/deps/v8/src/code-stubs.h index 67634aa13a..183a64abe7 100644 --- a/deps/v8/src/code-stubs.h +++ b/deps/v8/src/code-stubs.h @@ -28,7 +28,8 @@ #ifndef V8_CODE_STUBS_H_ #define V8_CODE_STUBS_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Stub is base classes of all stubs. @@ -82,6 +83,10 @@ class CodeStub BASE_EMBEDDED { virtual Major MajorKey() = 0; virtual int MinorKey() = 0; + // The CallFunctionStub needs to override this so it can encode whether a + // lazily generated function should be fully optimized or not. + virtual InLoopFlag InLoop() { return NOT_IN_LOOP; } + // Returns a name for logging/debugging purposes. virtual const char* GetName() { return MajorName(MajorKey()); } diff --git a/deps/v8/src/code.h b/deps/v8/src/code.h index 87e0794904..072344b67d 100644 --- a/deps/v8/src/code.h +++ b/deps/v8/src/code.h @@ -28,7 +28,8 @@ #ifndef V8_CODE_H_ #define V8_CODE_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Wrapper class for passing expected and actual parameter counts as diff --git a/deps/v8/src/codegen-inl.h b/deps/v8/src/codegen-inl.h index c42f5ac64c..bee237d8c9 100644 --- a/deps/v8/src/codegen-inl.h +++ b/deps/v8/src/codegen-inl.h @@ -30,9 +30,23 @@ #define V8_CODEGEN_INL_H_ #include "codegen.h" +#include "register-allocator-inl.h" + +#if V8_TARGET_ARCH_IA32 +#include "ia32/codegen-ia32-inl.h" +#elif V8_TARGET_ARCH_X64 +#include "x64/codegen-x64-inl.h" +#elif V8_TARGET_ARCH_ARM +#include "arm/codegen-arm-inl.h" +#else +#error Unsupported target architecture. +#endif + -namespace v8 { namespace internal { +namespace v8 { +namespace internal { +#define __ ACCESS_MASM(masm_) // ----------------------------------------------------------------------------- // Support for "structured" code comments. @@ -44,15 +58,12 @@ namespace v8 { namespace internal { class Comment BASE_EMBEDDED { public: - Comment(MacroAssembler* masm, const char* msg) - : masm_(masm), - msg_(msg) { - masm_->RecordComment(msg); + Comment(MacroAssembler* masm, const char* msg) : masm_(masm), msg_(msg) { + __ RecordComment(msg); } ~Comment() { - if (msg_[0] == '[') - masm_->RecordComment("]"); + if (msg_[0] == '[') __ RecordComment("]"); } private: @@ -69,6 +80,8 @@ class Comment BASE_EMBEDDED { #endif // DEBUG +#undef __ + } } // namespace v8::internal diff --git a/deps/v8/src/codegen.cc b/deps/v8/src/codegen.cc index 40c2583f4f..f46269fe97 100644 --- a/deps/v8/src/codegen.cc +++ b/deps/v8/src/codegen.cc @@ -38,27 +38,41 @@ #include "scopeinfo.h" #include "stub-cache.h" -namespace v8 { namespace internal { - -DeferredCode::DeferredCode(CodeGenerator* generator) - : generator_(generator), - masm_(generator->masm()), - enter_(generator), - exit_(generator, JumpTarget::BIDIRECTIONAL), - statement_position_(masm_->current_statement_position()), - position_(masm_->current_position()) { - generator->AddDeferred(this); +namespace v8 { +namespace internal { + + +CodeGenerator* CodeGeneratorScope::top_ = NULL; + + +DeferredCode::DeferredCode() + : masm_(CodeGeneratorScope::Current()->masm()), + statement_position_(masm_->current_statement_position()), + position_(masm_->current_position()) { ASSERT(statement_position_ != RelocInfo::kNoPosition); ASSERT(position_ != RelocInfo::kNoPosition); + + CodeGeneratorScope::Current()->AddDeferred(this); #ifdef DEBUG comment_ = ""; #endif -} - -void CodeGenerator::ClearDeferred() { - for (int i = 0; i < deferred_.length(); i++) { - deferred_[i]->Clear(); + // Copy the register locations from the code generator's frame. + // These are the registers that will be spilled on entry to the + // deferred code and restored on exit. + VirtualFrame* frame = CodeGeneratorScope::Current()->frame(); + int sp_offset = frame->fp_relative(frame->stack_pointer_); + for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) { + int loc = frame->register_location(i); + if (loc == VirtualFrame::kIllegalIndex) { + registers_[i] = kIgnore; + } else if (frame->elements_[loc].is_synced()) { + // Needs to be restored on exit but not saved on entry. + registers_[i] = frame->fp_relative(loc) | kSyncedFlag; + } else { + int offset = frame->fp_relative(loc); + registers_[i] = (offset < sp_offset) ? kPush : offset; + } } } @@ -66,17 +80,19 @@ void CodeGenerator::ClearDeferred() { void CodeGenerator::ProcessDeferred() { while (!deferred_.is_empty()) { DeferredCode* code = deferred_.RemoveLast(); - MacroAssembler* masm = code->masm(); + ASSERT(masm_ == code->masm()); // Record position of deferred code stub. - masm->RecordStatementPosition(code->statement_position()); + masm_->RecordStatementPosition(code->statement_position()); if (code->position() != RelocInfo::kNoPosition) { - masm->RecordPosition(code->position()); + masm_->RecordPosition(code->position()); } // Generate the code. - Comment cmnt(masm, code->comment()); + Comment cmnt(masm_, code->comment()); + masm_->bind(code->entry_label()); + code->SaveRegisters(); code->Generate(); - ASSERT(code->enter()->is_bound()); - code->Clear(); + code->RestoreRegisters(); + masm_->jmp(code->exit_label()); } } @@ -104,7 +120,6 @@ void CodeGenerator::SetFrame(VirtualFrame* new_frame, void CodeGenerator::DeleteFrame() { if (has_valid_frame()) { frame_->DetachFromCodeGenerator(); - delete frame_; frame_ = NULL; } } @@ -155,17 +170,21 @@ Handle<Code> CodeGenerator::MakeCode(FunctionLiteral* flit, // Generate code. const int initial_buffer_size = 4 * KB; CodeGenerator cgen(initial_buffer_size, script, is_eval); + CodeGeneratorScope scope(&cgen); cgen.GenCode(flit); if (cgen.HasStackOverflow()) { ASSERT(!Top::has_pending_exception()); return Handle<Code>::null(); } - // Allocate and install the code. + // Allocate and install the code. Time the rest of this function as + // code creation. + HistogramTimerScope timer(&Counters::code_creation); CodeDesc desc; cgen.masm()->GetCode(&desc); - ScopeInfo<> sinfo(flit->scope()); - Code::Flags flags = Code::ComputeFlags(Code::FUNCTION); + ZoneScopeInfo sinfo(flit->scope()); + InLoopFlag in_loop = (cgen.loop_nesting() != 0) ? IN_LOOP : NOT_IN_LOOP; + Code::Flags flags = Code::ComputeFlags(Code::FUNCTION, in_loop); Handle<Code> code = Factory::NewCode(desc, &sinfo, flags, @@ -206,7 +225,7 @@ Handle<Code> CodeGenerator::MakeCode(FunctionLiteral* flit, bool CodeGenerator::ShouldGenerateLog(Expression* type) { ASSERT(type != NULL); - if (!Logger::is_enabled()) return false; + if (!Logger::IsEnabled()) return false; Handle<String> name = Handle<String>::cast(type->AsLiteral()->handle()); if (FLAG_log_regexp) { static Vector<const char> kRegexp = CStrVector("regexp"); @@ -317,17 +336,18 @@ Handle<JSFunction> CodeGenerator::BuildBoilerplate(FunctionLiteral* node) { } -Handle<Code> CodeGenerator::ComputeCallInitialize(int argc) { - CALL_HEAP_FUNCTION(StubCache::ComputeCallInitialize(argc), Code); -} - - -Handle<Code> CodeGenerator::ComputeCallInitializeInLoop(int argc) { - // Force the creation of the corresponding stub outside loops, - // because it will be used when clearing the ICs later - when we - // don't know if we're inside a loop or not. - ComputeCallInitialize(argc); - CALL_HEAP_FUNCTION(StubCache::ComputeCallInitializeInLoop(argc), Code); +Handle<Code> CodeGenerator::ComputeCallInitialize( + int argc, + InLoopFlag in_loop) { + if (in_loop == IN_LOOP) { + // Force the creation of the corresponding stub outside loops, + // because it may be used when clearing the ICs later - it is + // possible for a series of IC transitions to lose the in-loop + // information, and the IC clearing code can't generate a stub + // that it needs so we need to ensure it is generated already. + ComputeCallInitialize(argc, NOT_IN_LOOP); + } + CALL_HEAP_FUNCTION(StubCache::ComputeCallInitialize(argc, in_loop), Code); } @@ -507,8 +527,8 @@ void CodeGenerator::GenerateFastCaseSwitchCases( // frame. Otherwise, we have to merge the existing one to the // start frame as part of the previous case. if (!has_valid_frame()) { - RegisterFile non_frame_registers = RegisterAllocator::Reserved(); - SetFrame(new VirtualFrame(start_frame), &non_frame_registers); + RegisterFile empty; + SetFrame(new VirtualFrame(start_frame), &empty); } else { frame_->MergeTo(start_frame); } diff --git a/deps/v8/src/codegen.h b/deps/v8/src/codegen.h index a6cd693ebf..e1758e1a9f 100644 --- a/deps/v8/src/codegen.h +++ b/deps/v8/src/codegen.h @@ -52,7 +52,6 @@ // CodeGenerator // ~CodeGenerator // ProcessDeferred -// ClearDeferred // GenCode // BuildBoilerplate // ComputeCallInitialize @@ -86,14 +85,34 @@ enum OverwriteMode { NO_OVERWRITE, OVERWRITE_LEFT, OVERWRITE_RIGHT }; #include "arm/codegen-arm.h" #endif -namespace v8 { namespace internal { +#include "register-allocator.h" +namespace v8 { +namespace internal { -// Use lazy compilation; defaults to true. -// NOTE: Do not remove non-lazy compilation until we can properly -// install extensions with lazy compilation enabled. At the -// moment, this doesn't work for the extensions in Google3, -// and we can only run the tests with --nolazy. + +// Code generation can be nested. Code generation scopes form a stack +// of active code generators. +class CodeGeneratorScope BASE_EMBEDDED { + public: + explicit CodeGeneratorScope(CodeGenerator* cgen) { + previous_ = top_; + top_ = cgen; + } + + ~CodeGeneratorScope() { + top_ = previous_; + } + + static CodeGenerator* Current() { + ASSERT(top_ != NULL); + return top_; + } + + private: + static CodeGenerator* top_; + CodeGenerator* previous_; +}; // Deferred code objects are small pieces of code that are compiled @@ -101,52 +120,56 @@ namespace v8 { namespace internal { // paths thereby avoiding expensive jumps around uncommon code parts. class DeferredCode: public ZoneObject { public: - explicit DeferredCode(CodeGenerator* generator); + DeferredCode(); virtual ~DeferredCode() { } virtual void Generate() = 0; - // Unuse the entry and exit targets, deallocating all virtual frames - // held by them. It will be impossible to emit a (correct) jump - // into or out of the deferred code after clearing. - void Clear() { - enter_.Unuse(); - exit_.Unuse(); - } - - MacroAssembler* masm() const { return masm_; } - CodeGenerator* generator() const { return generator_; } - - JumpTarget* enter() { return &enter_; } - void BindExit() { exit_.Bind(0); } - void BindExit(Result* result) { exit_.Bind(result, 1); } - void BindExit(Result* result0, Result* result1) { - exit_.Bind(result0, result1, 2); - } - void BindExit(Result* result0, Result* result1, Result* result2) { - exit_.Bind(result0, result1, result2, 3); - } + MacroAssembler* masm() { return masm_; } int statement_position() const { return statement_position_; } int position() const { return position_; } + Label* entry_label() { return &entry_label_; } + Label* exit_label() { return &exit_label_; } + #ifdef DEBUG void set_comment(const char* comment) { comment_ = comment; } const char* comment() const { return comment_; } #else - inline void set_comment(const char* comment) { } + void set_comment(const char* comment) { } const char* comment() const { return ""; } #endif + inline void Jump(); + inline void Branch(Condition cc); + void BindExit() { masm_->bind(&exit_label_); } + + void SaveRegisters(); + void RestoreRegisters(); + protected: - CodeGenerator* const generator_; - MacroAssembler* const masm_; - JumpTarget enter_; - JumpTarget exit_; + MacroAssembler* masm_; private: + // Constants indicating special actions. They should not be multiples + // of kPointerSize so they will not collide with valid offsets from + // the frame pointer. + static const int kIgnore = -1; + static const int kPush = 1; + + // This flag is ored with a valid offset from the frame pointer, so + // it should fit in the low zero bits of a valid offset. + static const int kSyncedFlag = 2; + int statement_position_; int position_; + + Label entry_label_; + Label exit_label_; + + int registers_[RegisterAllocator::kNumRegisters]; + #ifdef DEBUG const char* comment_; #endif diff --git a/deps/v8/src/compilation-cache.cc b/deps/v8/src/compilation-cache.cc index 4c02d86ce2..421b6766fe 100644 --- a/deps/v8/src/compilation-cache.cc +++ b/deps/v8/src/compilation-cache.cc @@ -29,15 +29,30 @@ #include "compilation-cache.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { enum { - NUMBER_OF_ENTRY_KINDS = CompilationCache::LAST_ENTRY + 1 + // The number of script generations tell how many GCs a script can + // survive in the compilation cache, before it will be flushed if it + // hasn't been used. + NUMBER_OF_SCRIPT_GENERATIONS = 5, + + // The compilation cache consists of tables - one for each entry + // kind plus extras for the script generations. + NUMBER_OF_TABLE_ENTRIES = + CompilationCache::LAST_ENTRY + NUMBER_OF_SCRIPT_GENERATIONS }; +// Current enable state of the compilation cache. +static bool enabled = true; +static inline bool IsEnabled() { + return FLAG_compilation_cache && enabled; +} + // Keep separate tables for the different entry kinds. -static Object* tables[NUMBER_OF_ENTRY_KINDS] = { 0, }; +static Object* tables[NUMBER_OF_TABLE_ENTRIES] = { 0, }; static Handle<CompilationCacheTable> AllocateTable(int size) { @@ -46,14 +61,15 @@ static Handle<CompilationCacheTable> AllocateTable(int size) { } -static Handle<CompilationCacheTable> GetTable(CompilationCache::Entry entry) { +static Handle<CompilationCacheTable> GetTable(int index) { + ASSERT(index >= 0 && index < NUMBER_OF_TABLE_ENTRIES); Handle<CompilationCacheTable> result; - if (tables[entry]->IsUndefined()) { + if (tables[index]->IsUndefined()) { static const int kInitialCacheSize = 64; result = AllocateTable(kInitialCacheSize); - tables[entry] = *result; + tables[index] = *result; } else { - CompilationCacheTable* table = CompilationCacheTable::cast(tables[entry]); + CompilationCacheTable* table = CompilationCacheTable::cast(tables[index]); result = Handle<CompilationCacheTable>(table); } return result; @@ -121,47 +137,80 @@ static bool HasOrigin(Handle<JSFunction> boilerplate, } -static Handle<JSFunction> Lookup(Handle<String> source, - CompilationCache::Entry entry) { - // Make sure not to leak the table into the surrounding handle - // scope. Otherwise, we risk keeping old tables around even after - // having cleared the cache. - Object* result; - { HandleScope scope; - Handle<CompilationCacheTable> table = GetTable(entry); - result = table->Lookup(*source); - } - if (result->IsJSFunction()) { - return Handle<JSFunction>(JSFunction::cast(result)); - } else { - return Handle<JSFunction>::null(); - } -} - - -// TODO(245): Need to allow identical code from different contexts to be -// cached. Currently the first use will be cached, but subsequent code -// from different source / line won't. +// TODO(245): Need to allow identical code from different contexts to +// be cached in the same script generation. Currently the first use +// will be cached, but subsequent code from different source / line +// won't. Handle<JSFunction> CompilationCache::LookupScript(Handle<String> source, Handle<Object> name, int line_offset, int column_offset) { - Handle<JSFunction> result = Lookup(source, SCRIPT); - if (result.is_null()) { - Counters::compilation_cache_misses.Increment(); - } else if (HasOrigin(result, name, line_offset, column_offset)) { + if (!IsEnabled()) { + return Handle<JSFunction>::null(); + } + + // Use an int for the generation index, so value range propagation + // in gcc 4.3+ won't assume it can only go up to LAST_ENTRY when in + // fact it can go up to SCRIPT + NUMBER_OF_SCRIPT_GENERATIONS. + int generation = SCRIPT; + Object* result = NULL; + + // Probe the script generation tables. Make sure not to leak handles + // into the caller's handle scope. + { HandleScope scope; + while (generation < SCRIPT + NUMBER_OF_SCRIPT_GENERATIONS) { + Handle<CompilationCacheTable> table = GetTable(generation); + Handle<Object> probe(table->Lookup(*source)); + if (probe->IsJSFunction()) { + Handle<JSFunction> boilerplate = Handle<JSFunction>::cast(probe); + // Break when we've found a suitable boilerplate function that + // matches the origin. + if (HasOrigin(boilerplate, name, line_offset, column_offset)) { + result = *boilerplate; + break; + } + } + // Go to the next generation. + generation++; + } + } + + static void* script_histogram = StatsTable::CreateHistogram( + "V8.ScriptCache", + 0, + NUMBER_OF_SCRIPT_GENERATIONS, + NUMBER_OF_SCRIPT_GENERATIONS + 1); + + if (script_histogram != NULL) { + // The level NUMBER_OF_SCRIPT_GENERATIONS is equivalent to a cache miss. + StatsTable::AddHistogramSample(script_histogram, generation - SCRIPT); + } + + // Once outside the manacles of the handle scope, we need to recheck + // to see if we actually found a cached script. If so, we return a + // handle created in the caller's handle scope. + if (result != NULL) { + Handle<JSFunction> boilerplate(JSFunction::cast(result)); + ASSERT(HasOrigin(boilerplate, name, line_offset, column_offset)); + // If the script was found in a later generation, we promote it to + // the first generation to let it survive longer in the cache. + if (generation != SCRIPT) PutScript(source, boilerplate); Counters::compilation_cache_hits.Increment(); + return boilerplate; } else { - result = Handle<JSFunction>::null(); Counters::compilation_cache_misses.Increment(); + return Handle<JSFunction>::null(); } - return result; } Handle<JSFunction> CompilationCache::LookupEval(Handle<String> source, Handle<Context> context, Entry entry) { + if (!IsEnabled()) { + return Handle<JSFunction>::null(); + } + ASSERT(entry == EVAL_GLOBAL || entry == EVAL_CONTEXTUAL); Handle<JSFunction> result = Lookup(source, context, entry); if (result.is_null()) { @@ -175,6 +224,10 @@ Handle<JSFunction> CompilationCache::LookupEval(Handle<String> source, Handle<FixedArray> CompilationCache::LookupRegExp(Handle<String> source, JSRegExp::Flags flags) { + if (!IsEnabled()) { + return Handle<FixedArray>::null(); + } + Handle<FixedArray> result = Lookup(source, flags); if (result.is_null()) { Counters::compilation_cache_misses.Increment(); @@ -187,6 +240,10 @@ Handle<FixedArray> CompilationCache::LookupRegExp(Handle<String> source, void CompilationCache::PutScript(Handle<String> source, Handle<JSFunction> boilerplate) { + if (!IsEnabled()) { + return; + } + HandleScope scope; ASSERT(boilerplate->IsBoilerplate()); Handle<CompilationCacheTable> table = GetTable(SCRIPT); @@ -198,6 +255,10 @@ void CompilationCache::PutEval(Handle<String> source, Handle<Context> context, Entry entry, Handle<JSFunction> boilerplate) { + if (!IsEnabled()) { + return; + } + HandleScope scope; ASSERT(boilerplate->IsBoilerplate()); Handle<CompilationCacheTable> table = GetTable(entry); @@ -209,6 +270,10 @@ void CompilationCache::PutEval(Handle<String> source, void CompilationCache::PutRegExp(Handle<String> source, JSRegExp::Flags flags, Handle<FixedArray> data) { + if (!IsEnabled()) { + return; + } + HandleScope scope; Handle<CompilationCacheTable> table = GetTable(REGEXP); CALL_HEAP_FUNCTION_VOID(table->PutRegExp(*source, flags, *data)); @@ -216,14 +281,36 @@ void CompilationCache::PutRegExp(Handle<String> source, void CompilationCache::Clear() { - for (int i = 0; i < NUMBER_OF_ENTRY_KINDS; i++) { + for (int i = 0; i < NUMBER_OF_TABLE_ENTRIES; i++) { tables[i] = Heap::undefined_value(); } } void CompilationCache::Iterate(ObjectVisitor* v) { - v->VisitPointers(&tables[0], &tables[NUMBER_OF_ENTRY_KINDS]); + v->VisitPointers(&tables[0], &tables[NUMBER_OF_TABLE_ENTRIES]); +} + + +void CompilationCache::MarkCompactPrologue() { + ASSERT(LAST_ENTRY == SCRIPT); + for (int i = NUMBER_OF_TABLE_ENTRIES - 1; i > SCRIPT; i--) { + tables[i] = tables[i - 1]; + } + for (int j = 0; j <= LAST_ENTRY; j++) { + tables[j] = Heap::undefined_value(); + } +} + + +void CompilationCache::Enable() { + enabled = true; +} + + +void CompilationCache::Disable() { + enabled = false; + Clear(); } diff --git a/deps/v8/src/compilation-cache.h b/deps/v8/src/compilation-cache.h index 38a9e3a3a8..4545defc5d 100644 --- a/deps/v8/src/compilation-cache.h +++ b/deps/v8/src/compilation-cache.h @@ -28,7 +28,8 @@ #ifndef V8_COMPILATION_CACHE_H_ #define V8_COMPILATION_CACHE_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // The compilation cache keeps function boilerplates for compiled @@ -40,11 +41,11 @@ class CompilationCache { // scripts and evals. Internally, we use separate caches to avoid // getting the wrong kind of entry when looking up. enum Entry { - SCRIPT, EVAL_GLOBAL, EVAL_CONTEXTUAL, REGEXP, - LAST_ENTRY = REGEXP + SCRIPT, + LAST_ENTRY = SCRIPT }; // Finds the script function boilerplate for a source @@ -93,10 +94,13 @@ class CompilationCache { // Notify the cache that a mark-sweep garbage collection is about to // take place. This is used to retire entries from the cache to - // avoid keeping them alive too long without using them. For now, we - // just clear the cache but we should consider are more - // sophisticated LRU scheme. - static void MarkCompactPrologue() { Clear(); } + // avoid keeping them alive too long without using them. + static void MarkCompactPrologue(); + + // Enable/disable compilation cache. Used by debugger to disable compilation + // cache during debugging to make sure new scripts are always compiled. + static void Enable(); + static void Disable(); }; diff --git a/deps/v8/src/compiler.cc b/deps/v8/src/compiler.cc index 256c696963..ea7c134daa 100644 --- a/deps/v8/src/compiler.cc +++ b/deps/v8/src/compiler.cc @@ -37,7 +37,8 @@ #include "scopes.h" #include "usage-analyzer.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { static Handle<Code> MakeCode(FunctionLiteral* literal, Handle<Script> script, @@ -52,12 +53,15 @@ static Handle<Code> MakeCode(FunctionLiteral* literal, return Handle<Code>::null(); } - // Compute top scope and allocate variables. For lazy compilation - // the top scope only contains the single lazily compiled function, - // so this doesn't re-allocate variables repeatedly. - Scope* top = literal->scope(); - while (top->outer_scope() != NULL) top = top->outer_scope(); - top->AllocateVariables(context); + { + // Compute top scope and allocate variables. For lazy compilation + // the top scope only contains the single lazily compiled function, + // so this doesn't re-allocate variables repeatedly. + HistogramTimerScope timer(&Counters::variable_allocation); + Scope* top = literal->scope(); + while (top->outer_scope() != NULL) top = top->outer_scope(); + top->AllocateVariables(context); + } #ifdef DEBUG if (Bootstrapper::IsActive() ? @@ -86,7 +90,7 @@ static bool IsValidJSON(FunctionLiteral* lit) { Statement* stmt = lit->body()->at(0); if (stmt->AsExpressionStatement() == NULL) return false; - Expression *expr = stmt->AsExpressionStatement()->expression(); + Expression* expr = stmt->AsExpressionStatement()->expression(); return expr->IsValidJSON(); } @@ -98,7 +102,7 @@ static Handle<JSFunction> MakeFunction(bool is_global, Handle<Context> context, v8::Extension* extension, ScriptDataImpl* pre_data) { - ZoneScope zone_scope(DELETE_ON_EXIT); + CompilationZoneScope zone_scope(DELETE_ON_EXIT); // Make sure we have an initial stack limit. StackGuard guard; @@ -106,7 +110,22 @@ static Handle<JSFunction> MakeFunction(bool is_global, ASSERT(!i::Top::global_context().is_null()); script->set_context_data((*i::Top::global_context())->data()); + #ifdef ENABLE_DEBUGGER_SUPPORT + if (is_eval || is_json) { + script->set_compilation_type( + is_json ? Smi::FromInt(Script::COMPILATION_TYPE_JSON) : + Smi::FromInt(Script::COMPILATION_TYPE_EVAL)); + // For eval scripts add information on the function from which eval was + // called. + if (is_eval) { + JavaScriptFrameIterator it; + script->set_eval_from_function(it.frame()->function()); + int offset = it.frame()->pc() - it.frame()->code()->instruction_start(); + script->set_eval_from_instructions_offset(Smi::FromInt(offset)); + } + } + // Notify debugger Debugger::OnBeforeCompile(script); #endif @@ -156,7 +175,7 @@ static Handle<JSFunction> MakeFunction(bool is_global, #if defined ENABLE_LOGGING_AND_PROFILING || defined ENABLE_OPROFILE_AGENT // Log the code generation for the script. Check explicit whether logging is // to avoid allocating when not required. - if (Logger::is_enabled() || OProfileAgent::is_enabled()) { + if (Logger::IsEnabled() || OProfileAgent::is_enabled()) { if (script->name()->IsString()) { SmartPointer<char> data = String::cast(script->name())->ToCString(DISALLOW_NULLS); @@ -264,7 +283,6 @@ Handle<JSFunction> Compiler::Compile(Handle<String> source, Handle<JSFunction> Compiler::CompileEval(Handle<String> source, Handle<Context> context, - int line_offset, bool is_global, bool is_json) { int source_length = source->length(); @@ -284,7 +302,6 @@ Handle<JSFunction> Compiler::CompileEval(Handle<String> source, if (result.is_null()) { // Create a script object describing the script to be compiled. Handle<Script> script = Factory::NewScript(source); - script->set_line_offset(Smi::FromInt(line_offset)); result = MakeFunction(is_global, true, is_json, @@ -303,7 +320,7 @@ Handle<JSFunction> Compiler::CompileEval(Handle<String> source, bool Compiler::CompileLazy(Handle<SharedFunctionInfo> shared, int loop_nesting) { - ZoneScope zone_scope(DELETE_ON_EXIT); + CompilationZoneScope zone_scope(DELETE_ON_EXIT); // The VM is in the COMPILER state until exiting this function. VMState state(COMPILER); @@ -355,9 +372,9 @@ bool Compiler::CompileLazy(Handle<SharedFunctionInfo> shared, // Log the code generation. If source information is available include script // name and line number. Check explicit whether logging is enabled as finding // the line number is not for free. - if (Logger::is_enabled() || OProfileAgent::is_enabled()) { - Handle<String> func_name(lit->name()->length() > 0 ? - *lit->name() : shared->inferred_name()); + if (Logger::IsEnabled() || OProfileAgent::is_enabled()) { + Handle<String> func_name(name->length() > 0 ? + *name : shared->inferred_name()); if (script->name()->IsString()) { int line_num = GetScriptLineNumber(script, start_position); if (line_num > 0) { diff --git a/deps/v8/src/compiler.h b/deps/v8/src/compiler.h index 8abe130d83..9f02a8d3c1 100644 --- a/deps/v8/src/compiler.h +++ b/deps/v8/src/compiler.h @@ -28,9 +28,12 @@ #ifndef V8_COMPILER_H_ #define V8_COMPILER_H_ +#include "frame-element.h" #include "parser.h" +#include "zone.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // The V8 compiler // @@ -59,7 +62,6 @@ class Compiler : public AllStatic { // Compile a String source within a context for Eval. static Handle<JSFunction> CompileEval(Handle<String> source, Handle<Context> context, - int line_offset, bool is_global, bool is_json); @@ -69,6 +71,22 @@ class Compiler : public AllStatic { static bool CompileLazy(Handle<SharedFunctionInfo> shared, int loop_nesting); }; + +// During compilation we need a global list of handles to constants +// for frame elements. When the zone gets deleted, we make sure to +// clear this list of handles as well. +class CompilationZoneScope : public ZoneScope { + public: + explicit CompilationZoneScope(ZoneScopeMode mode) : ZoneScope(mode) { } + virtual ~CompilationZoneScope() { + if (ShouldDeleteOnExit()) { + FrameElement::ClearConstantList(); + Result::ClearConstantList(); + } + } +}; + + } } // namespace v8::internal #endif // V8_COMPILER_H_ diff --git a/deps/v8/src/contexts.cc b/deps/v8/src/contexts.cc index 36b54881a4..873c23ca54 100644 --- a/deps/v8/src/contexts.cc +++ b/deps/v8/src/contexts.cc @@ -31,7 +31,8 @@ #include "debug.h" #include "scopeinfo.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { JSBuiltinsObject* Context::builtins() { GlobalObject* object = global(); diff --git a/deps/v8/src/contexts.h b/deps/v8/src/contexts.h index f56143175e..bdfc40b040 100644 --- a/deps/v8/src/contexts.h +++ b/deps/v8/src/contexts.h @@ -28,7 +28,8 @@ #ifndef V8_CONTEXTS_H_ #define V8_CONTEXTS_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { enum ContextLookupFlags { @@ -90,6 +91,8 @@ enum ContextLookupFlags { V(FUNCTION_CACHE_INDEX, JSObject, function_cache) \ V(RUNTIME_CONTEXT_INDEX, Context, runtime_context) \ V(CALL_AS_FUNCTION_DELEGATE_INDEX, JSFunction, call_as_function_delegate) \ + V(CALL_AS_CONSTRUCTOR_DELEGATE_INDEX, JSFunction, \ + call_as_constructor_delegate) \ V(EMPTY_SCRIPT_INDEX, Script, empty_script) \ V(SCRIPT_FUNCTION_INDEX, JSFunction, script_function) \ V(CONTEXT_EXTENSION_FUNCTION_INDEX, JSFunction, context_extension_function) \ @@ -209,6 +212,7 @@ class Context: public FixedArray { FUNCTION_CACHE_INDEX, RUNTIME_CONTEXT_INDEX, CALL_AS_FUNCTION_DELEGATE_INDEX, + CALL_AS_CONSTRUCTOR_DELEGATE_INDEX, EMPTY_SCRIPT_INDEX, SCRIPT_FUNCTION_INDEX, CONTEXT_EXTENSION_FUNCTION_INDEX, diff --git a/deps/v8/src/conversions-inl.h b/deps/v8/src/conversions-inl.h index 64e4a796ee..8c875d75bf 100644 --- a/deps/v8/src/conversions-inl.h +++ b/deps/v8/src/conversions-inl.h @@ -38,7 +38,8 @@ #include "conversions.h" #include "platform.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // The fast double-to-int conversion routine does not guarantee // rounding towards zero. diff --git a/deps/v8/src/conversions.cc b/deps/v8/src/conversions.cc index 57a45688f9..7f63d9b337 100644 --- a/deps/v8/src/conversions.cc +++ b/deps/v8/src/conversions.cc @@ -33,7 +33,8 @@ #include "factory.h" #include "scanner.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { int HexValue(uc32 c) { if ('0' <= c && c <= '9') diff --git a/deps/v8/src/conversions.h b/deps/v8/src/conversions.h index 605327db7f..b6589cb5ca 100644 --- a/deps/v8/src/conversions.h +++ b/deps/v8/src/conversions.h @@ -28,7 +28,8 @@ #ifndef V8_CONVERSIONS_H_ #define V8_CONVERSIONS_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // The fast double-to-int conversion routine does not guarantee // rounding towards zero. diff --git a/deps/v8/src/counters.cc b/deps/v8/src/counters.cc index bf9b8d8b06..239a5f7a0c 100644 --- a/deps/v8/src/counters.cc +++ b/deps/v8/src/counters.cc @@ -30,7 +30,8 @@ #include "counters.h" #include "platform.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { CounterLookupCallback StatsTable::lookup_function_ = NULL; CreateHistogramCallback StatsTable::create_histogram_function_ = NULL; diff --git a/deps/v8/src/counters.h b/deps/v8/src/counters.h index df1c70a91b..5f4dca927e 100644 --- a/deps/v8/src/counters.h +++ b/deps/v8/src/counters.h @@ -28,7 +28,8 @@ #ifndef V8_COUNTERS_H_ #define V8_COUNTERS_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // StatsCounters is an interface for plugging into external // counters for monitoring. Counters can be looked up and @@ -74,7 +75,7 @@ class StatsTable : public AllStatic { // function. min and max define the expected minimum and maximum // sample values. buckets is the maximum number of buckets // that the samples will be grouped into. - static void *CreateHistogram(const char* name, + static void* CreateHistogram(const char* name, int min, int max, size_t buckets) { diff --git a/deps/v8/src/cpu.h b/deps/v8/src/cpu.h index d12c30c8ca..ddc402f7de 100644 --- a/deps/v8/src/cpu.h +++ b/deps/v8/src/cpu.h @@ -36,7 +36,8 @@ #ifndef V8_CPU_H_ #define V8_CPU_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // ---------------------------------------------------------------------------- // CPU diff --git a/deps/v8/src/d8-posix.cc b/deps/v8/src/d8-posix.cc index c2dc5311aa..3a091f93cc 100644 --- a/deps/v8/src/d8-posix.cc +++ b/deps/v8/src/d8-posix.cc @@ -280,7 +280,10 @@ static void ExecSubprocess(int* exec_error_fds, // Only get here if the exec failed. Write errno to the parent to tell // them it went wrong. If it went well the pipe is closed. int err = errno; - write(exec_error_fds[kWriteFD], &err, sizeof(err)); + int bytes_written; + do { + bytes_written = write(exec_error_fds[kWriteFD], &err, sizeof(err)); + } while (bytes_written == -1 && errno == EINTR); // Return (and exit child process). } diff --git a/deps/v8/src/d8.cc b/deps/v8/src/d8.cc index 70143c3c00..ee845ee2cd 100644 --- a/deps/v8/src/d8.cc +++ b/deps/v8/src/d8.cc @@ -451,7 +451,7 @@ void Shell::Initialize() { i::Handle<i::JSFunction> script_fun = Utils::OpenHandle(*script); i::Handle<i::Script> script_object = i::Handle<i::Script>(i::Script::cast(script_fun->shared()->script())); - script_object->set_type(i::Smi::FromInt(i::SCRIPT_TYPE_NATIVE)); + script_object->set_type(i::Smi::FromInt(i::Script::TYPE_NATIVE)); // Create the evaluation context evaluation_context_ = Context::New(NULL, global_template); @@ -487,7 +487,7 @@ void Shell::OnExit() { } -static char* ReadChars(const char *name, int* size_out) { +static char* ReadChars(const char* name, int* size_out) { v8::Unlocker unlocker; // Release the V8 lock while reading files. FILE* file = i::OS::FOpen(name, "rb"); if (file == NULL) return NULL; @@ -659,7 +659,7 @@ int Shell::Main(int argc, char* argv[]) { use_preemption = false; } else if (strcmp(str, "--preemption-interval") == 0) { if (i + 1 < argc) { - char *end = NULL; + char* end = NULL; preemption_interval = strtol(argv[++i], &end, 10); // NOLINT if (preemption_interval <= 0 || *end != '\0' || errno == ERANGE) { printf("Invalid value for --preemption-interval '%s'\n", argv[i]); @@ -687,9 +687,9 @@ int Shell::Main(int argc, char* argv[]) { i++; } else if (strcmp(str, "-p") == 0 && i + 1 < argc) { int size = 0; - const char *files = ReadChars(argv[++i], &size); + const char* files = ReadChars(argv[++i], &size); if (files == NULL) return 1; - ShellThread *thread = + ShellThread* thread = new ShellThread(threads.length(), i::Vector<const char>(files, size)); thread->Start(); @@ -736,7 +736,7 @@ int Shell::Main(int argc, char* argv[]) { if (run_shell) RunShell(); for (int i = 0; i < threads.length(); i++) { - i::Thread *thread = threads[i]; + i::Thread* thread = threads[i]; thread->Join(); delete thread; } diff --git a/deps/v8/src/d8.js b/deps/v8/src/d8.js index ea2fb44988..a8db9e1d9e 100644 --- a/deps/v8/src/d8.js +++ b/deps/v8/src/d8.js @@ -93,6 +93,13 @@ Debug.ScriptType = { Native: 0, Normal: 2 }; +// The different types of script compilations matching enum +// Script::CompilationType in objects.h. +Debug.ScriptCompilationType = { Host: 0, + Eval: 1, + JSON: 2 }; + + // Current debug state. const kNoFrame = -1; Debug.State = { @@ -498,9 +505,26 @@ DebugRequest.prototype.stepCommandToJSONRequest_ = function(args) { DebugRequest.prototype.backtraceCommandToJSONRequest_ = function(args) { // Build a backtrace request from the text command. var request = this.createRequest('backtrace'); + + // Default is to show top 10 frames. + request.arguments = {}; + request.arguments.fromFrame = 0; + request.arguments.toFrame = 10; + args = args.split(/\s*[ ]+\s*/g); - if (args.length == 2) { - request.arguments = {}; + if (args.length == 1 && args[0].length > 0) { + var frameCount = parseInt(args[0]); + if (frameCount > 0) { + // Show top frames. + request.arguments.fromFrame = 0; + request.arguments.toFrame = frameCount; + } else { + // Show bottom frames. + request.arguments.fromFrame = 0; + request.arguments.toFrame = -frameCount; + request.arguments.bottom = true; + } + } else if (args.length == 2) { var fromFrame = parseInt(args[0]); var toFrame = parseInt(args[1]); if (isNaN(fromFrame) || fromFrame < 0) { @@ -513,9 +537,13 @@ DebugRequest.prototype.backtraceCommandToJSONRequest_ = function(args) { throw new Error('Invalid arguments start frame cannot be larger ' + 'than end frame.'); } + // Show frame range. request.arguments.fromFrame = fromFrame; request.arguments.toFrame = toFrame + 1; + } else if (args.length > 2) { + throw new Error('Invalid backtrace arguments.'); } + return request.toJSONProtocol(); }; @@ -755,7 +783,7 @@ DebugRequest.prototype.helpCommand_ = function(args) { print(' break on function: location is #<id>#'); print(' break on script position: location is name:line[:column]'); print('clear <breakpoint #>'); - print('backtrace [from frame #] [to frame #]]'); + print('backtrace [n] | [-n] | [from to]'); print('frame <frame #>'); print('step [in | next | out| min [step count]]'); print('print <expression>'); @@ -942,7 +970,18 @@ function DebugResponseDetails(response) { if (body[i].name) { result += body[i].name; } else { - result += '[unnamed] '; + if (body[i].compilationType == Debug.ScriptCompilationType.Eval) { + result += 'eval from '; + var script_value = response.lookup(body[i].evalFromScript.ref); + result += ' ' + script_value.field('name'); + result += ':' + (body[i].evalFromLocation.line + 1); + result += ':' + body[i].evalFromLocation.column; + } else if (body[i].compilationType == + Debug.ScriptCompilationType.JSON) { + result += 'JSON '; + } else { // body[i].compilation == Debug.ScriptCompilationType.Host + result += '[unnamed] '; + } } result += ' (lines: '; result += body[i].lineCount; @@ -1105,6 +1144,15 @@ ProtocolValue.prototype.type = function() { /** + * Get a metadata field from a protocol value. + * @return {Object} the metadata field value + */ +ProtocolValue.prototype.field = function(name) { + return this.value_[name]; +} + + +/** * Check is the value is a primitive value. * @return {boolean} true if the value is primitive */ diff --git a/deps/v8/src/dateparser-inl.h b/deps/v8/src/dateparser-inl.h index 61a8c72e23..3d4161d19f 100644 --- a/deps/v8/src/dateparser-inl.h +++ b/deps/v8/src/dateparser-inl.h @@ -28,7 +28,8 @@ #ifndef V8_DATEPARSER_INL_H_ #define V8_DATEPARSER_INL_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { template <typename Char> bool DateParser::Parse(Vector<Char> str, FixedArray* out) { diff --git a/deps/v8/src/dateparser.cc b/deps/v8/src/dateparser.cc index a1ae55df04..1cc9aa169c 100644 --- a/deps/v8/src/dateparser.cc +++ b/deps/v8/src/dateparser.cc @@ -29,7 +29,8 @@ #include "dateparser.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { bool DateParser::DayComposer::Write(FixedArray* output) { int year = 0; // Default year is 0 (=> 2000) for KJS compatibility. diff --git a/deps/v8/src/dateparser.h b/deps/v8/src/dateparser.h index 04d7e8baf1..d339a4fb7f 100644 --- a/deps/v8/src/dateparser.h +++ b/deps/v8/src/dateparser.h @@ -30,7 +30,8 @@ #include "scanner.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { class DateParser : public AllStatic { public: diff --git a/deps/v8/src/debug-agent.cc b/deps/v8/src/debug-agent.cc index 63f143a790..62cc251ed1 100644 --- a/deps/v8/src/debug-agent.cc +++ b/deps/v8/src/debug-agent.cc @@ -30,7 +30,8 @@ #include "debug-agent.h" #ifdef ENABLE_DEBUGGER_SUPPORT -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Public V8 debugger API message handler function. This function just delegates // to the debugger agent through it's data parameter. diff --git a/deps/v8/src/debug-agent.h b/deps/v8/src/debug-agent.h index a3c6025cde..04f883f40e 100644 --- a/deps/v8/src/debug-agent.h +++ b/deps/v8/src/debug-agent.h @@ -32,7 +32,8 @@ #include "../include/v8-debug.h" #include "platform.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Forward decelrations. class DebuggerAgentSession; diff --git a/deps/v8/src/debug-delay.js b/deps/v8/src/debug-delay.js index ff7d6fbd98..0b0501fde6 100644 --- a/deps/v8/src/debug-delay.js +++ b/deps/v8/src/debug-delay.js @@ -43,7 +43,8 @@ Debug.DebugEvent = { Break: 1, Exception: 2, NewFunction: 3, BeforeCompile: 4, - AfterCompile: 5 }; + AfterCompile: 5, + ScriptCollected: 6 }; // Types of exceptions that can be broken upon. Debug.ExceptionBreak = { All : 0, @@ -61,6 +62,12 @@ Debug.ScriptType = { Native: 0, Extension: 1, Normal: 2 }; +// The different types of script compilations matching enum +// Script::CompilationType in objects.h. +Debug.ScriptCompilationType = { Host: 0, + Eval: 1, + JSON: 2 }; + // The different script break point types. Debug.ScriptBreakPointType = { ScriptId: 0, ScriptName: 1 }; @@ -833,7 +840,7 @@ BreakEvent.prototype.toJSONProtocol = function() { event: "break", body: { invocationText: this.exec_state_.frame(0).invocationText(), } - } + }; // Add script related information to the event if available. var script = this.func().script(); @@ -861,8 +868,7 @@ BreakEvent.prototype.toJSONProtocol = function() { o.body.breakpoints.push(number); } } - - return SimpleObjectToJSON_(o); + return JSON.stringify(ObjectToProtocolObject_(o)); }; @@ -923,7 +929,7 @@ ExceptionEvent.prototype.toJSONProtocol = function() { o.event = "exception"; o.body = { uncaught: this.uncaught_, exception: MakeMirror(this.exception_) - } + }; // Exceptions might happen whithout any JavaScript frames. if (this.exec_state_.frameCount() > 0) { @@ -984,7 +990,8 @@ CompileEvent.prototype.toJSONProtocol = function() { o.event = "afterCompile"; } o.body = {}; - o.body.script = MakeScriptObject_(this.script_, true); + o.body.script = this.script_; + o.setOption('includeSource', true); return o.toJSONProtocol(); } @@ -1015,6 +1022,37 @@ NewFunctionEvent.prototype.setBreakPoint = function(p) { }; +function MakeScriptCollectedEvent(exec_state, id) { + return new ScriptCollectedEvent(exec_state, id); +} + + +function ScriptCollectedEvent(exec_state, id) { + this.exec_state_ = exec_state; + this.id_ = id; +} + + +ScriptCollectedEvent.prototype.id = function() { + return this.id_; +}; + + +ScriptCollectedEvent.prototype.executionState = function() { + return this.exec_state_; +}; + + +ScriptCollectedEvent.prototype.toJSONProtocol = function() { + var o = new ProtocolMessage(); + o.running = true; + o.event = "scriptCollected"; + o.body = {}; + o.body.script = { id: this.id() }; + return o.toJSONProtocol(); +} + + function MakeScriptObject_(script, include_source) { var o = { id: script.id(), name: script.name(), @@ -1078,56 +1116,53 @@ ProtocolMessage.prototype.failed = function(message) { ProtocolMessage.prototype.toJSONProtocol = function() { // Encode the protocol header. - var json = '{'; - json += '"seq":' + this.seq; + var json = {}; + json.seq= this.seq; if (this.request_seq) { - json += ',"request_seq":' + this.request_seq; + json.request_seq = this.request_seq; } - json += ',"type":"' + this.type + '"'; + json.type = this.type; if (this.event) { - json += ',"event":' + StringToJSON_(this.event); + json.event = this.event; } if (this.command) { - json += ',"command":' + StringToJSON_(this.command); + json.command = this.command; } if (this.success) { - json += ',"success":' + this.success; + json.success = this.success; } else { - json += ',"success":false'; + json.success = false; } if (this.body) { - json += ',"body":'; // Encode the body part. + var bodyJson; var serializer = MakeMirrorSerializer(true, this.options_); if (this.body instanceof Mirror) { - json += serializer.serializeValue(this.body); + bodyJson = serializer.serializeValue(this.body); } else if (this.body instanceof Array) { - json += '['; + bodyJson = []; for (var i = 0; i < this.body.length; i++) { - if (i != 0) json += ','; if (this.body[i] instanceof Mirror) { - json += serializer.serializeValue(this.body[i]); + bodyJson.push(serializer.serializeValue(this.body[i])); } else { - json += SimpleObjectToJSON_(this.body[i], serializer); + bodyJson.push(ObjectToProtocolObject_(this.body[i], serializer)); } } - json += ']'; } else { - json += SimpleObjectToJSON_(this.body, serializer); + bodyJson = ObjectToProtocolObject_(this.body, serializer); } - json += ',"refs":'; - json += serializer.serializeReferencedObjects(); + json.body = bodyJson; + json.refs = serializer.serializeReferencedObjects(); } if (this.message) { - json += ',"message":' + StringToJSON_(this.message) ; + json.message = this.message; } if (this.running) { - json += ',"running":true'; + json.running = true; } else { - json += ',"running":false'; + json.running = false; } - json += '}'; - return json; + return JSON.stringify(json); } @@ -1142,7 +1177,7 @@ DebugCommandProcessor.prototype.processDebugJSONRequest = function(json_request) try { try { // Convert the JSON string to an object. - request = %CompileString('(' + json_request + ')', 0, false)(); + request = %CompileString('(' + json_request + ')', false)(); // Create an initial response. response = this.createResponse(request); @@ -1451,14 +1486,23 @@ DebugCommandProcessor.prototype.backtraceRequest_ = function(request, response) // Get the range from the arguments. if (request.arguments) { - from_index = request.arguments.fromFrame; - if (from_index < 0) { - return response.failed('Invalid frame number'); + if (request.arguments.fromFrame) { + from_index = request.arguments.fromFrame; + } + if (request.arguments.toFrame) { + to_index = request.arguments.toFrame; } - to_index = request.arguments.toFrame; - if (to_index < 0) { + if (request.arguments.bottom) { + var tmp_index = total_frames - from_index; + from_index = total_frames - to_index + to_index = tmp_index; + } + if (from_index < 0 || to_index < 0) { return response.failed('Invalid frame number'); } + if (request.arguments.compactFormat) { + response.setOption('compactFormat', true); + } } // Adjust the index. @@ -1581,6 +1625,16 @@ DebugCommandProcessor.prototype.lookupRequest_ = function(request, response) { return response.failed('Argument "handles" missing'); } + // Set 'includeSource' option for script lookup. + if (!IS_UNDEFINED(request.arguments.includeSource)) { + includeSource = %ToBoolean(request.arguments.includeSource); + response.setOption('includeSource', includeSource); + } + + if (request.arguments.compactFormat) { + response.setOption('compactFormat', true); + } + // Lookup handles. var mirrors = {}; for (var i = 0; i < handles.length; i++) { @@ -1677,6 +1731,7 @@ DebugCommandProcessor.prototype.sourceRequest_ = function(request, response) { DebugCommandProcessor.prototype.scriptsRequest_ = function(request, response) { var types = ScriptTypeFlag(Debug.ScriptType.Normal); var includeSource = false; + var idsToInclude = null; if (request.arguments) { // Pull out arguments. if (!IS_UNDEFINED(request.arguments.types)) { @@ -1690,6 +1745,14 @@ DebugCommandProcessor.prototype.scriptsRequest_ = function(request, response) { includeSource = %ToBoolean(request.arguments.includeSource); response.setOption('includeSource', includeSource); } + + if (IS_ARRAY(request.arguments.ids)) { + idsToInclude = {}; + var ids = request.arguments.ids; + for (var i = 0; i < ids.length; i++) { + idsToInclude[ids[i]] = true; + } + } } // Collect all scripts in the heap. @@ -1698,6 +1761,9 @@ DebugCommandProcessor.prototype.scriptsRequest_ = function(request, response) { response.body = []; for (var i = 0; i < scripts.length; i++) { + if (idsToInclude && !idsToInclude[scripts[i].id]) { + continue; + } if (types & ScriptTypeFlag(scripts[i].type)) { response.body.push(MakeMirror(scripts[i])); } @@ -1774,97 +1840,82 @@ DebugCommandProcessor.prototype.formatCFrame = function(cframe_value) { /** - * Convert an Object to its JSON representation (see http://www.json.org/). - * This implementation simply runs through all string property names and adds - * each property to the JSON representation for some predefined types. For type - * "object" the function calls itself recursively unless the object has the - * function property "toJSONProtocol" in which case that is used. This is not - * a general implementation but sufficient for the debugger. Note that circular - * structures will cause infinite recursion. - * @param {Object} object The object to format as JSON + * Convert an Object to its debugger protocol representation. The representation + * may be serilized to a JSON object using JSON.stringify(). + * This implementation simply runs through all string property names, converts + * each property value to a protocol value and adds the property to the result + * object. For type "object" the function will be called recursively. Note that + * circular structures will cause infinite recursion. + * @param {Object} object The object to format as protocol object. * @param {MirrorSerializer} mirror_serializer The serializer to use if any * mirror objects are encountered. - * @return {string} JSON formatted object value + * @return {Object} Protocol object value. */ -function SimpleObjectToJSON_(object, mirror_serializer) { - var content = []; +function ObjectToProtocolObject_(object, mirror_serializer) { + var content = {}; for (var key in object) { // Only consider string keys. if (typeof key == 'string') { - var property_value = object[key]; - // Format the value based on its type. - var property_value_json; - switch (typeof property_value) { - case 'object': - if (property_value instanceof Mirror) { - property_value_json = mirror_serializer.serializeValue(property_value); - } else if (typeof property_value.toJSONProtocol == 'function') { - property_value_json = property_value.toJSONProtocol(true) - } else if (IS_ARRAY(property_value)){ - property_value_json = SimpleArrayToJSON_(property_value, mirror_serializer); - } else { - property_value_json = SimpleObjectToJSON_(property_value, mirror_serializer); - } - break; - - case 'boolean': - property_value_json = BooleanToJSON_(property_value); - break; - - case 'number': - property_value_json = NumberToJSON_(property_value); - break; - - case 'string': - property_value_json = StringToJSON_(property_value); - break; - - default: - property_value_json = null; - } - + var property_value_json = ValueToProtocolValue_(object[key], + mirror_serializer); // Add the property if relevant. - if (property_value_json) { - content.push(StringToJSON_(key) + ':' + property_value_json); + if (!IS_UNDEFINED(property_value_json)) { + content[key] = property_value_json; } } } - - // Make JSON object representation. - return '{' + content.join(',') + '}'; + + return content; } + /** - * Convert an array to its JSON representation. This is a VERY simple - * implementation just to support what is needed for the debugger. - * @param {Array} array The array to format as JSON + * Convert an array to its debugger protocol representation. It will convert + * each array element to a protocol value. + * @param {Array} array The array to format as protocol array. * @param {MirrorSerializer} mirror_serializer The serializer to use if any * mirror objects are encountered. - * @return {string} JSON formatted array value + * @return {Array} Protocol array value. */ -function SimpleArrayToJSON_(array, mirror_serializer) { - // Make JSON array representation. - var json = '['; +function ArrayToProtocolArray_(array, mirror_serializer) { + var json = []; for (var i = 0; i < array.length; i++) { - if (i != 0) { - json += ','; - } - var elem = array[i]; - if (elem instanceof Mirror) { - json += mirror_serializer.serializeValue(elem); - } else if (IS_OBJECT(elem)) { - json += SimpleObjectToJSON_(elem); - } else if (IS_BOOLEAN(elem)) { - json += BooleanToJSON_(elem); - } else if (IS_NUMBER(elem)) { - json += NumberToJSON_(elem); - } else if (IS_STRING(elem)) { - json += StringToJSON_(elem); - } else { - json += elem; - } + json.push(ValueToProtocolValue_(array[i], mirror_serializer)); + } + return json; +} + + +/** + * Convert a value to its debugger protocol representation. + * @param {*} value The value to format as protocol value. + * @param {MirrorSerializer} mirror_serializer The serializer to use if any + * mirror objects are encountered. + * @return {*} Protocol value. + */ +function ValueToProtocolValue_(value, mirror_serializer) { + // Format the value based on its type. + var json; + switch (typeof value) { + case 'object': + if (value instanceof Mirror) { + json = mirror_serializer.serializeValue(value); + } else if (IS_ARRAY(value)){ + json = ArrayToProtocolArray_(value, mirror_serializer); + } else { + json = ObjectToProtocolObject_(value, mirror_serializer); + } + break; + + case 'boolean': + case 'string': + case 'number': + json = value; + break + + default: + json = null; } - json += ']'; return json; } diff --git a/deps/v8/src/debug.cc b/deps/v8/src/debug.cc index 8422a6710d..0daf5642de 100644 --- a/deps/v8/src/debug.cc +++ b/deps/v8/src/debug.cc @@ -31,6 +31,7 @@ #include "arguments.h" #include "bootstrapper.h" #include "code-stubs.h" +#include "compilation-cache.h" #include "compiler.h" #include "debug.h" #include "execution.h" @@ -43,7 +44,8 @@ #include "../include/v8-debug.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { #ifdef ENABLE_DEBUGGER_SUPPORT static void PrintLn(v8::Local<v8::Value> value) { @@ -425,6 +427,7 @@ void BreakLocationIterator::RinfoNext() { bool Debug::has_break_points_ = false; +ScriptCache* Debug::script_cache_ = NULL; DebugInfoListNode* Debug::debug_info_list_ = NULL; @@ -440,7 +443,7 @@ void Debug::ThreadInit() { thread_local_.step_into_fp_ = 0; thread_local_.after_break_target_ = 0; thread_local_.debugger_entry_ = NULL; - thread_local_.preemption_pending_ = false; + thread_local_.pending_interrupts_ = 0; } @@ -486,29 +489,77 @@ Code* Debug::debug_break_return_entry_ = NULL; Code* Debug::debug_break_return_ = NULL; -void Debug::HandleWeakDebugInfo(v8::Persistent<v8::Value> obj, void* data) { - DebugInfoListNode* node = reinterpret_cast<DebugInfoListNode*>(data); - RemoveDebugInfo(node->debug_info()); -#ifdef DEBUG - node = Debug::debug_info_list_; - while (node != NULL) { - ASSERT(node != reinterpret_cast<DebugInfoListNode*>(data)); - node = node->next(); +void ScriptCache::Add(Handle<Script> script) { + // Create an entry in the hash map for the script. + int id = Smi::cast(script->id())->value(); + HashMap::Entry* entry = + HashMap::Lookup(reinterpret_cast<void*>(id), Hash(id), true); + if (entry->value != NULL) { + ASSERT(*script == *reinterpret_cast<Script**>(entry->value)); + return; } -#endif + + // Globalize the script object, make it weak and use the location of the + // global handle as the value in the hash map. + Handle<Script> script_ = + Handle<Script>::cast((GlobalHandles::Create(*script))); + GlobalHandles::MakeWeak(reinterpret_cast<Object**>(script_.location()), + this, ScriptCache::HandleWeakScript); + entry->value = script_.location(); } -DebugInfoListNode::DebugInfoListNode(DebugInfo* debug_info): next_(NULL) { - // Globalize the request debug info object and make it weak. - debug_info_ = Handle<DebugInfo>::cast((GlobalHandles::Create(debug_info))); - GlobalHandles::MakeWeak(reinterpret_cast<Object**>(debug_info_.location()), - this, Debug::HandleWeakDebugInfo); +Handle<FixedArray> ScriptCache::GetScripts() { + Handle<FixedArray> instances = Factory::NewFixedArray(occupancy()); + int count = 0; + for (HashMap::Entry* entry = Start(); entry != NULL; entry = Next(entry)) { + ASSERT(entry->value != NULL); + if (entry->value != NULL) { + instances->set(count, *reinterpret_cast<Script**>(entry->value)); + count++; + } + } + return instances; } -DebugInfoListNode::~DebugInfoListNode() { - GlobalHandles::Destroy(reinterpret_cast<Object**>(debug_info_.location())); +void ScriptCache::ProcessCollectedScripts() { + for (int i = 0; i < collected_scripts_.length(); i++) { + Debugger::OnScriptCollected(collected_scripts_[i]); + } + collected_scripts_.Clear(); +} + + +void ScriptCache::Clear() { + // Iterate the script cache to get rid of all the weak handles. + for (HashMap::Entry* entry = Start(); entry != NULL; entry = Next(entry)) { + ASSERT(entry != NULL); + Object** location = reinterpret_cast<Object**>(entry->value); + ASSERT((*location)->IsScript()); + GlobalHandles::ClearWeakness(location); + GlobalHandles::Destroy(location); + } + // Clear the content of the hash map. + HashMap::Clear(); +} + + +void ScriptCache::HandleWeakScript(v8::Persistent<v8::Value> obj, void* data) { + ScriptCache* script_cache = reinterpret_cast<ScriptCache*>(data); + // Find the location of the global handle. + Script** location = + reinterpret_cast<Script**>(Utils::OpenHandle(*obj).location()); + ASSERT((*location)->IsScript()); + + // Remove the entry from the cache. + int id = Smi::cast((*location)->id())->value(); + script_cache->Remove(reinterpret_cast<void*>(id), Hash(id)); + script_cache->collected_scripts_.Add(id); + + // Clear the weak handle. + obj.Dispose(); + obj.Clear(); } @@ -528,6 +579,32 @@ void Debug::Setup(bool create_heap_objects) { } +void Debug::HandleWeakDebugInfo(v8::Persistent<v8::Value> obj, void* data) { + DebugInfoListNode* node = reinterpret_cast<DebugInfoListNode*>(data); + RemoveDebugInfo(node->debug_info()); +#ifdef DEBUG + node = Debug::debug_info_list_; + while (node != NULL) { + ASSERT(node != reinterpret_cast<DebugInfoListNode*>(data)); + node = node->next(); + } +#endif +} + + +DebugInfoListNode::DebugInfoListNode(DebugInfo* debug_info): next_(NULL) { + // Globalize the request debug info object and make it weak. + debug_info_ = Handle<DebugInfo>::cast((GlobalHandles::Create(debug_info))); + GlobalHandles::MakeWeak(reinterpret_cast<Object**>(debug_info_.location()), + this, Debug::HandleWeakDebugInfo); +} + + +DebugInfoListNode::~DebugInfoListNode() { + GlobalHandles::Destroy(reinterpret_cast<Object**>(debug_info_.location())); +} + + bool Debug::CompileDebuggerScript(int index) { HandleScope scope; @@ -575,7 +652,7 @@ bool Debug::CompileDebuggerScript(int index) { // Mark this script as native and return successfully. Handle<Script> script(Script::cast(function->shared()->script())); - script->set_type(Smi::FromInt(SCRIPT_TYPE_NATIVE)); + script->set_type(Smi::FromInt(Script::TYPE_NATIVE)); return true; } @@ -627,6 +704,7 @@ bool Debug::Load() { // Debugger loaded. debug_context_ = Handle<Context>::cast(GlobalHandles::Create(*context)); + return true; } @@ -637,6 +715,9 @@ void Debug::Unload() { return; } + // Clear the script cache. + DestroyScriptCache(); + // Clear debugger context global handle. GlobalHandles::Destroy(reinterpret_cast<Object**>(debug_context_.location())); debug_context_ = Handle<Context>(); @@ -646,7 +727,7 @@ void Debug::Unload() { // Set the flag indicating that preemption happened during debugging. void Debug::PreemptionWhileInDebugger() { ASSERT(InDebugger()); - Debug::set_preemption_pending(true); + Debug::set_interrupts_pending(PREEMPT); } @@ -1414,6 +1495,94 @@ void Debug::ClearMirrorCache() { } +// If an object given is an external string, check that the underlying +// resource is accessible. For other kinds of objects, always return true. +static bool IsExternalStringValid(Object* str) { + if (!str->IsString() || !StringShape(String::cast(str)).IsExternal()) { + return true; + } + if (String::cast(str)->IsAsciiRepresentation()) { + return ExternalAsciiString::cast(str)->resource() != NULL; + } else if (String::cast(str)->IsTwoByteRepresentation()) { + return ExternalTwoByteString::cast(str)->resource() != NULL; + } else { + return true; + } +} + + +void Debug::CreateScriptCache() { + HandleScope scope; + + // Perform two GCs to get rid of all unreferenced scripts. The first GC gets + // rid of all the cached script wrappers and the second gets rid of the + // scripts which is no longer referenced. + Heap::CollectAllGarbage(); + Heap::CollectAllGarbage(); + + ASSERT(script_cache_ == NULL); + script_cache_ = new ScriptCache(); + + // Scan heap for Script objects. + int count = 0; + HeapIterator iterator; + while (iterator.has_next()) { + HeapObject* obj = iterator.next(); + ASSERT(obj != NULL); + if (obj->IsScript() && IsExternalStringValid(Script::cast(obj)->source())) { + script_cache_->Add(Handle<Script>(Script::cast(obj))); + count++; + } + } +} + + +void Debug::DestroyScriptCache() { + // Get rid of the script cache if it was created. + if (script_cache_ != NULL) { + delete script_cache_; + script_cache_ = NULL; + } +} + + +void Debug::AddScriptToScriptCache(Handle<Script> script) { + if (script_cache_ != NULL) { + script_cache_->Add(script); + } +} + + +Handle<FixedArray> Debug::GetLoadedScripts() { + // Create and fill the script cache when the loaded scripts is requested for + // the first time. + if (script_cache_ == NULL) { + CreateScriptCache(); + } + + // If the script cache is not active just return an empty array. + ASSERT(script_cache_ != NULL); + if (script_cache_ == NULL) { + Factory::NewFixedArray(0); + } + + // Perform GC to get unreferenced scripts evicted from the cache before + // returning the content. + Heap::CollectAllGarbage(); + + // Get the scripts from the cache. + return script_cache_->GetScripts(); +} + + +void Debug::AfterGarbageCollection() { + // Generate events for collected scripts. + if (script_cache_ != NULL) { + script_cache_->ProcessCollectedScripts(); + } +} + + Mutex* Debugger::debugger_access_ = OS::CreateMutex(); Handle<Object> Debugger::event_listener_ = Handle<Object>(); Handle<Object> Debugger::event_listener_data_ = Handle<Object>(); @@ -1421,7 +1590,7 @@ bool Debugger::compiling_natives_ = false; bool Debugger::is_loading_debugger_ = false; bool Debugger::never_unload_debugger_ = false; v8::Debug::MessageHandler2 Debugger::message_handler_ = NULL; -bool Debugger::message_handler_cleared_ = false; +bool Debugger::debugger_unload_pending_ = false; v8::Debug::HostDispatchHandler Debugger::host_dispatch_handler_ = NULL; int Debugger::host_dispatch_micros_ = 100 * 1000; DebuggerAgent* Debugger::agent_ = NULL; @@ -1518,6 +1687,21 @@ Handle<Object> Debugger::MakeCompileEvent(Handle<Script> script, } +Handle<Object> Debugger::MakeScriptCollectedEvent(int id, + bool* caught_exception) { + // Create the script collected event object. + Handle<Object> exec_state = MakeExecutionState(caught_exception); + Handle<Object> id_object = Handle<Smi>(Smi::FromInt(id)); + const int argc = 2; + Object** argv[argc] = { exec_state.location(), id_object.location() }; + + return MakeJSObject(CStrVector("MakeScriptCollectedEvent"), + argc, + argv, + caught_exception); +} + + void Debugger::OnException(Handle<Object> exception, bool uncaught) { HandleScope scope; @@ -1624,12 +1808,15 @@ void Debugger::OnBeforeCompile(Handle<Script> script) { void Debugger::OnAfterCompile(Handle<Script> script, Handle<JSFunction> fun) { HandleScope scope; - // No compile events while compiling natives. - if (compiling_natives()) return; + // Add the newly compiled script to the script cache. + Debug::AddScriptToScriptCache(script); // No more to do if not debugging. if (!IsDebuggerActive()) return; + // No compile events while compiling natives. + if (compiling_natives()) return; + // Store whether in debugger before entering debugger. bool in_debugger = Debug::InDebugger(); @@ -1708,11 +1895,43 @@ void Debugger::OnNewFunction(Handle<JSFunction> function) { } +void Debugger::OnScriptCollected(int id) { + HandleScope scope; + + // No more to do if not debugging. + if (!IsDebuggerActive()) return; + if (!Debugger::EventActive(v8::ScriptCollected)) return; + + // Enter the debugger. + EnterDebugger debugger; + if (debugger.FailedToEnter()) return; + + // Create the script collected state object. + bool caught_exception = false; + Handle<Object> event_data = MakeScriptCollectedEvent(id, + &caught_exception); + // Bail out and don't call debugger if exception. + if (caught_exception) { + return; + } + + // Process debug event. + ProcessDebugEvent(v8::ScriptCollected, + Handle<JSObject>::cast(event_data), + true); +} + + void Debugger::ProcessDebugEvent(v8::DebugEvent event, Handle<JSObject> event_data, bool auto_continue) { HandleScope scope; + // Clear any pending debug break if this is a real break. + if (!auto_continue) { + Debug::clear_interrupt_pending(DEBUGBREAK); + } + // Create the execution state. bool caught_exception = false; Handle<Object> exec_state = MakeExecutionState(&caught_exception); @@ -1756,9 +1975,6 @@ void Debugger::ProcessDebugEvent(v8::DebugEvent event, } } } - - // Clear the mirror cache. - Debug::ClearMirrorCache(); } @@ -1771,8 +1987,8 @@ void Debugger::UnloadDebugger() { Debug::Unload(); } - // Clear the flag indicating that the message handler was recently cleared. - message_handler_cleared_ = false; + // Clear the flag indicating that the debugger should be unloaded. + debugger_unload_pending_ = false; } @@ -1798,6 +2014,9 @@ void Debugger::NotifyMessageHandler(v8::DebugEvent event, case v8::AfterCompile: sendEventMessage = true; break; + case v8::ScriptCollected: + sendEventMessage = true; + break; case v8::NewFunction: break; default: @@ -1820,7 +2039,12 @@ void Debugger::NotifyMessageHandler(v8::DebugEvent event, Handle<JSObject>::cast(event_data)); InvokeMessageHandler(message); } - if (auto_continue && !HasCommands()) { + + // If auto continue don't make the event cause a break, but process messages + // in the queue if any. For script collected events don't even process + // messages in the queue as the execution state might not be what is expected + // by the client. + if ((auto_continue && !HasCommands()) || event == v8::ScriptCollected) { return; } @@ -1956,10 +2180,7 @@ void Debugger::SetEventListener(Handle<Object> callback, event_listener_data_ = Handle<Object>::cast(GlobalHandles::Create(*data)); } - // Unload the debugger if event listener cleared. - if (callback->IsUndefined()) { - UnloadDebugger(); - } + ListenersChanged(); } @@ -1967,10 +2188,8 @@ void Debugger::SetMessageHandler(v8::Debug::MessageHandler2 handler) { ScopedLock with(debugger_access_); message_handler_ = handler; + ListenersChanged(); if (handler == NULL) { - // Indicate that the message handler was recently cleared. - message_handler_cleared_ = true; - // Send an empty command to the debugger if in a break to make JavaScript // run again if the debugger is closed. if (Debug::InDebugger()) { @@ -1980,6 +2199,25 @@ void Debugger::SetMessageHandler(v8::Debug::MessageHandler2 handler) { } +void Debugger::ListenersChanged() { + if (IsDebuggerActive()) { + // Disable the compilation cache when the debugger is active. + CompilationCache::Disable(); + } else { + CompilationCache::Enable(); + + // Unload the debugger if event listener and message handler cleared. + if (Debug::InDebugger()) { + // If we are in debugger set the flag to unload the debugger when last + // EnterDebugger on the current stack is destroyed. + debugger_unload_pending_ = true; + } else { + UnloadDebugger(); + } + } +} + + void Debugger::SetHostDispatchHandler(v8::Debug::HostDispatchHandler handler, int period) { host_dispatch_handler_ = handler; @@ -2172,7 +2410,14 @@ v8::Handle<v8::String> MessageImpl::GetJSON() const { v8::Handle<v8::Context> MessageImpl::GetEventContext() const { - return v8::Utils::ToLocal(Debug::debugger_entry()->GetContext()); + Handle<Context> context = Debug::debugger_entry()->GetContext(); + // Top::context() may have been NULL when "script collected" event occured. + if (*context == NULL) { + ASSERT(event_ == v8::ScriptCollected); + return v8::Local<v8::Context>(); + } + Handle<Context> global_context(context->global_context()); + return v8::Utils::ToLocal(global_context); } diff --git a/deps/v8/src/debug.h b/deps/v8/src/debug.h index 35336cb1d5..a1abceda84 100644 --- a/deps/v8/src/debug.h +++ b/deps/v8/src/debug.h @@ -33,6 +33,7 @@ #include "debug-agent.h" #include "execution.h" #include "factory.h" +#include "hashmap.h" #include "platform.h" #include "string-stream.h" #include "v8threads.h" @@ -40,7 +41,8 @@ #ifdef ENABLE_DEBUGGER_SUPPORT #include "../include/v8-debug.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Forward declarations. @@ -144,6 +146,42 @@ class BreakLocationIterator { }; +// Cache of all script objects in the heap. When a script is added a weak handle +// to it is created and that weak handle is stored in the cache. The weak handle +// callback takes care of removing the script from the cache. The key used in +// the cache is the script id. +class ScriptCache : private HashMap { + public: + ScriptCache() : HashMap(ScriptMatch), collected_scripts_(10) {} + virtual ~ScriptCache() { Clear(); } + + // Add script to the cache. + void Add(Handle<Script> script); + + // Return the scripts in the cache. + Handle<FixedArray> GetScripts(); + + // Generate debugger events for collected scripts. + void ProcessCollectedScripts(); + + private: + // Calculate the hash value from the key (script id). + static uint32_t Hash(int key) { return ComputeIntegerHash(key); } + + // Scripts match if their keys (script id) match. + static bool ScriptMatch(void* key1, void* key2) { return key1 == key2; } + + // Clear the cache releasing all the weak handles. + void Clear(); + + // Weak handle callback for scripts in the cache. + static void HandleWeakScript(v8::Persistent<v8::Value> obj, void* data); + + // List used during GC to temporarily store id's of collected scripts. + List<int> collected_scripts_; +}; + + // Linked list holding debug info objects. The debug info objects are kept as // weak handles to avoid a debug info object to keep a function alive. class DebugInfoListNode { @@ -230,9 +268,6 @@ class Debug { } static int break_id() { return thread_local_.break_id_; } - - - static bool StepInActive() { return thread_local_.step_into_fp_ != 0; } static void HandleStepIn(Handle<JSFunction> function, Address fp, @@ -247,11 +282,19 @@ class Debug { thread_local_.debugger_entry_ = entry; } - static bool preemption_pending() { - return thread_local_.preemption_pending_; + // Check whether any of the specified interrupts are pending. + static bool is_interrupt_pending(InterruptFlag what) { + return (thread_local_.pending_interrupts_ & what) != 0; } - static void set_preemption_pending(bool preemption_pending) { - thread_local_.preemption_pending_ = preemption_pending; + + // Set specified interrupts as pending. + static void set_interrupts_pending(InterruptFlag what) { + thread_local_.pending_interrupts_ |= what; + } + + // Clear specified interrupts from pending. + static void clear_interrupt_pending(InterruptFlag what) { + thread_local_.pending_interrupts_ &= ~static_cast<int>(what); } // Getter and setter for the disable break state. @@ -307,6 +350,15 @@ class Debug { // Mirror cache handling. static void ClearMirrorCache(); + // Script cache handling. + static void CreateScriptCache(); + static void DestroyScriptCache(); + static void AddScriptToScriptCache(Handle<Script> script); + static Handle<FixedArray> GetLoadedScripts(); + + // Garbage collection notifications. + static void AfterGarbageCollection(); + // Code generation assumptions. static const int kIa32CallInstructionLength = 5; static const int kIa32JSReturnSequenceLength = 6; @@ -343,6 +395,11 @@ class Debug { // Boolean state indicating whether any break points are set. static bool has_break_points_; + + // Cache of all scripts in the heap. + static ScriptCache* script_cache_; + + // List of active debug info objects. static DebugInfoListNode* debug_info_list_; static bool disable_break_; @@ -382,8 +439,8 @@ class Debug { // Top debugger entry. EnterDebugger* debugger_entry_; - // Preemption happened while debugging. - bool preemption_pending_; + // Pending interrupts scheduled while debugging. + int pending_interrupts_; }; // Storage location for registers when handling debug break calls @@ -532,12 +589,15 @@ class Debugger { static Handle<Object> MakeCompileEvent(Handle<Script> script, bool before, bool* caught_exception); + static Handle<Object> MakeScriptCollectedEvent(int id, + bool* caught_exception); static void OnDebugBreak(Handle<Object> break_points_hit, bool auto_continue); static void OnException(Handle<Object> exception, bool uncaught); static void OnBeforeCompile(Handle<Script> script); static void OnAfterCompile(Handle<Script> script, Handle<JSFunction> fun); static void OnNewFunction(Handle<JSFunction> fun); + static void OnScriptCollected(int id); static void ProcessDebugEvent(v8::DebugEvent event, Handle<JSObject> event_data, bool auto_continue); @@ -578,7 +638,7 @@ class Debugger { ScopedLock with(debugger_access_); // Check whether the message handler was been cleared. - if (message_handler_cleared_) { + if (debugger_unload_pending_) { UnloadDebugger(); } @@ -595,6 +655,7 @@ class Debugger { private: static bool IsDebuggerActive(); + static void ListenersChanged(); static Mutex* debugger_access_; // Mutex guarding debugger variables. static Handle<Object> event_listener_; // Global handle to listener. @@ -603,7 +664,7 @@ class Debugger { static bool is_loading_debugger_; // Are we loading the debugger? static bool never_unload_debugger_; // Can we unload the debugger? static v8::Debug::MessageHandler2 message_handler_; - static bool message_handler_cleared_; // Was message handler cleared? + static bool debugger_unload_pending_; // Was message handler cleared? static v8::Debug::HostDispatchHandler host_dispatch_handler_; static int host_dispatch_micros_; @@ -626,7 +687,8 @@ class EnterDebugger BASE_EMBEDDED { EnterDebugger() : prev_(Debug::debugger_entry()), has_js_frames_(!it_.done()) { - ASSERT(prev_ == NULL ? !Debug::preemption_pending() : true); + ASSERT(prev_ != NULL || !Debug::is_interrupt_pending(PREEMPT)); + ASSERT(prev_ != NULL || !Debug::is_interrupt_pending(DEBUGBREAK)); // Link recursive debugger entry. Debug::set_debugger_entry(this); @@ -656,22 +718,42 @@ class EnterDebugger BASE_EMBEDDED { // Restore to the previous break state. Debug::SetBreak(break_frame_id_, break_id_); - // Request preemption when leaving the last debugger entry and a preemption - // had been recorded while debugging. This is to avoid starvation in some - // debugging scenarios. - if (prev_ == NULL && Debug::preemption_pending()) { - StackGuard::Preempt(); - Debug::set_preemption_pending(false); - } + // Check for leaving the debugger. + if (prev_ == NULL) { + // Clear mirror cache when leaving the debugger. Skip this if there is a + // pending exception as clearing the mirror cache calls back into + // JavaScript. This can happen if the v8::Debug::Call is used in which + // case the exception should end up in the calling code. + if (!Top::has_pending_exception()) { + // Try to avoid any pending debug break breaking in the clear mirror + // cache JavaScript code. + if (StackGuard::IsDebugBreak()) { + Debug::set_interrupts_pending(DEBUGBREAK); + StackGuard::Continue(DEBUGBREAK); + } + Debug::ClearMirrorCache(); + } - // If there are commands in the queue when leaving the debugger request that - // these commands are processed. - if (prev_ == NULL && Debugger::HasCommands()) { - StackGuard::DebugCommand(); - } + // Request preemption and debug break when leaving the last debugger entry + // if any of these where recorded while debugging. + if (Debug::is_interrupt_pending(PREEMPT)) { + // This re-scheduling of preemption is to avoid starvation in some + // debugging scenarios. + Debug::clear_interrupt_pending(PREEMPT); + StackGuard::Preempt(); + } + if (Debug::is_interrupt_pending(DEBUGBREAK)) { + Debug::clear_interrupt_pending(DEBUGBREAK); + StackGuard::DebugBreak(); + } - // If leaving the debugger with the debugger no longer active unload it. - if (prev_ == NULL) { + // If there are commands in the queue when leaving the debugger request + // that these commands are processed. + if (Debugger::HasCommands()) { + StackGuard::DebugCommand(); + } + + // If leaving the debugger with the debugger no longer active unload it. if (!Debugger::IsDebuggerActive()) { Debugger::UnloadDebugger(); } diff --git a/deps/v8/src/disassembler.cc b/deps/v8/src/disassembler.cc index a838a08ffb..95022d052a 100644 --- a/deps/v8/src/disassembler.cc +++ b/deps/v8/src/disassembler.cc @@ -36,16 +36,18 @@ #include "serialize.h" #include "string-stream.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { #ifdef ENABLE_DISASSEMBLER void Disassembler::Dump(FILE* f, byte* begin, byte* end) { for (byte* pc = begin; pc < end; pc++) { if (f == NULL) { - PrintF("%p %4d %02x\n", pc, pc - begin, *pc); + PrintF("%" V8PRIxPTR " %4" V8PRIdPTR " %02x\n", pc, pc - begin, *pc); } else { - fprintf(f, "%p %4d %02x\n", pc, pc - begin, *pc); + fprintf(f, "%" V8PRIxPTR " %4" V8PRIdPTR " %02x\n", + reinterpret_cast<uintptr_t>(pc), pc - begin, *pc); } } } @@ -144,8 +146,8 @@ static int DecodeIt(FILE* f, // raw pointer embedded in code stream, e.g., jump table byte* ptr = *reinterpret_cast<byte**>(pc); OS::SNPrintF(decode_buffer, - "%08x jump table entry %4d", - reinterpret_cast<int32_t>(ptr), + "%08" V8PRIxPTR " jump table entry %4" V8PRIdPTR, + ptr, ptr - begin); pc += 4; } else { diff --git a/deps/v8/src/disassembler.h b/deps/v8/src/disassembler.h index 5003c00e21..68a338d18a 100644 --- a/deps/v8/src/disassembler.h +++ b/deps/v8/src/disassembler.h @@ -28,7 +28,8 @@ #ifndef V8_DISASSEMBLER_H_ #define V8_DISASSEMBLER_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { class Disassembler : public AllStatic { public: diff --git a/deps/v8/src/execution.cc b/deps/v8/src/execution.cc index 32dde9e4c9..fa3c2ecb25 100644 --- a/deps/v8/src/execution.cc +++ b/deps/v8/src/execution.cc @@ -43,7 +43,8 @@ #include "debug.h" #include "v8threads.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { static Handle<Object> Invoke(bool construct, @@ -188,6 +189,24 @@ Handle<Object> Execution::GetFunctionDelegate(Handle<Object> object) { } +Handle<Object> Execution::GetConstructorDelegate(Handle<Object> object) { + ASSERT(!object->IsJSFunction()); + + // If you return a function from here, it will be called when an + // attempt is made to call the given object as a constructor. + + // Objects created through the API can have an instance-call handler + // that should be used when calling the object as a function. + if (object->IsHeapObject() && + HeapObject::cast(*object)->map()->has_instance_call_handler()) { + return Handle<JSFunction>( + Top::global_context()->call_as_constructor_delegate()); + } + + return Factory::undefined_value(); +} + + // Static state for stack guards. StackGuard::ThreadLocal StackGuard::thread_local_; @@ -569,20 +588,7 @@ Object* Execution::DebugBreakHelper() { return Heap::undefined_value(); } - // Don't break in system functions. If the current function is - // either in the builtins object of some context or is in the debug - // context just return with the debug break stack guard active. - JavaScriptFrameIterator it; - JavaScriptFrame* frame = it.frame(); - Object* fun = frame->function(); - if (fun->IsJSFunction()) { - GlobalObject* global = JSFunction::cast(fun)->context()->global(); - if (global->IsJSBuiltinsObject() || Debug::IsDebugGlobal(global)) { - return Heap::undefined_value(); - } - } - - // Check for debug command break only. + // Collect the break state before clearing the flags. bool debug_command_only = StackGuard::IsDebugCommand() && !StackGuard::IsDebugBreak(); @@ -590,11 +596,6 @@ Object* Execution::DebugBreakHelper() { StackGuard::Continue(DEBUGBREAK); StackGuard::Continue(DEBUGCOMMAND); - // If debug command only and already in debugger ignore it. - if (debug_command_only && Debug::InDebugger()) { - return Heap::undefined_value(); - } - HandleScope scope; // Enter the debugger. Just continue if we fail to enter the debugger. EnterDebugger debugger; @@ -602,7 +603,8 @@ Object* Execution::DebugBreakHelper() { return Heap::undefined_value(); } - // Notify the debug event listeners. + // Notify the debug event listeners. Indicate auto continue if the break was + // a debug command break. Debugger::OnDebugBreak(Factory::undefined_value(), debug_command_only); // Return to continue execution. diff --git a/deps/v8/src/execution.h b/deps/v8/src/execution.h index 6f2f689226..8cfdec27f3 100644 --- a/deps/v8/src/execution.h +++ b/deps/v8/src/execution.h @@ -28,7 +28,8 @@ #ifndef V8_EXECUTION_H_ #define V8_EXECUTION_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Flag used to set the interrupt causes. @@ -129,6 +130,10 @@ class Execution : public AllStatic { // Get a function delegate (or undefined) for the given non-function // object. Used for support calling objects as functions. static Handle<Object> GetFunctionDelegate(Handle<Object> object); + + // Get a function delegate (or undefined) for the given non-function + // object. Used for support calling objects as constructors. + static Handle<Object> GetConstructorDelegate(Handle<Object> object); }; diff --git a/deps/v8/src/factory.cc b/deps/v8/src/factory.cc index 4b0b7f51f7..fad3e9c281 100644 --- a/deps/v8/src/factory.cc +++ b/deps/v8/src/factory.cc @@ -33,7 +33,8 @@ #include "factory.h" #include "macro-assembler.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { Handle<FixedArray> Factory::NewFixedArray(int size, PretenureFlag pretenure) { @@ -176,9 +177,12 @@ Handle<Script> Factory::NewScript(Handle<String> source) { script->set_column_offset(Smi::FromInt(0)); script->set_data(Heap::undefined_value()); script->set_context_data(Heap::undefined_value()); - script->set_type(Smi::FromInt(SCRIPT_TYPE_NORMAL)); + script->set_type(Smi::FromInt(Script::TYPE_NORMAL)); + script->set_compilation_type(Smi::FromInt(Script::COMPILATION_TYPE_HOST)); script->set_wrapper(*wrapper); script->set_line_ends(Heap::undefined_value()); + script->set_eval_from_function(Heap::undefined_value()); + script->set_eval_from_instructions_offset(Smi::FromInt(0)); return script; } @@ -509,8 +513,10 @@ Handle<JSFunction> Factory::NewFunctionWithPrototype(Handle<String> name, } -Handle<Code> Factory::NewCode(const CodeDesc& desc, ScopeInfo<>* sinfo, - Code::Flags flags, Handle<Object> self_ref) { +Handle<Code> Factory::NewCode(const CodeDesc& desc, + ZoneScopeInfo* sinfo, + Code::Flags flags, + Handle<Object> self_ref) { CALL_HEAP_FUNCTION(Heap::CreateCode(desc, sinfo, flags, self_ref), Code); } diff --git a/deps/v8/src/factory.h b/deps/v8/src/factory.h index 6ac2706ec1..95dbee9091 100644 --- a/deps/v8/src/factory.h +++ b/deps/v8/src/factory.h @@ -29,8 +29,10 @@ #define V8_FACTORY_H_ #include "heap.h" +#include "zone-inl.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Interface for handle based allocation. @@ -202,8 +204,10 @@ class Factory : public AllStatic { Handle<JSFunction> boilerplate, Handle<Context> context); - static Handle<Code> NewCode(const CodeDesc& desc, ScopeInfo<>* sinfo, - Code::Flags flags, Handle<Object> self_reference); + static Handle<Code> NewCode(const CodeDesc& desc, + ZoneScopeInfo* sinfo, + Code::Flags flags, + Handle<Object> self_reference); static Handle<Code> CopyCode(Handle<Code> code); diff --git a/deps/v8/src/flag-definitions.h b/deps/v8/src/flag-definitions.h index db494532b7..13e41e34fe 100644 --- a/deps/v8/src/flag-definitions.h +++ b/deps/v8/src/flag-definitions.h @@ -133,6 +133,9 @@ DEFINE_bool(strict, false, "strict error checking") DEFINE_int(min_preparse_length, 1024, "Minimum length for automatic enable preparsing") +// compilation-cache.cc +DEFINE_bool(compilation_cache, true, "enable compilation cache") + // debug.cc DEFINE_bool(remote_debugging, false, "enable remote debugging") DEFINE_bool(trace_debug_json, false, "trace debugging JSON request/response") @@ -333,6 +336,9 @@ DEFINE_bool(prof, false, "Log statistical profiling information (implies --log-code).") DEFINE_bool(prof_auto, true, "Used with --prof, starts profiling automatically") +DEFINE_bool(prof_lazy, false, + "Used with --prof, only does sampling and logging" + " when profiler is active (implies --noprof_auto).") DEFINE_bool(log_regexp, false, "Log regular expression execution.") DEFINE_bool(sliding_state_window, false, "Update sliding state window counters.") diff --git a/deps/v8/src/flags.cc b/deps/v8/src/flags.cc index 733d31a23e..5df3afd5f9 100644 --- a/deps/v8/src/flags.cc +++ b/deps/v8/src/flags.cc @@ -35,7 +35,8 @@ #include "string-stream.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Define all of our flags. #define FLAG_MODE_DEFINE @@ -86,9 +87,9 @@ struct Flag { return *reinterpret_cast<const char**>(valptr_); } - void set_string_value(const char *value, bool owns_ptr) { + void set_string_value(const char* value, bool owns_ptr) { ASSERT(type_ == TYPE_STRING); - const char **ptr = reinterpret_cast<const char **>(valptr_); + const char** ptr = reinterpret_cast<const char**>(valptr_); if (owns_ptr_ && *ptr != NULL) DeleteArray(*ptr); *ptr = value; owns_ptr_ = owns_ptr; diff --git a/deps/v8/src/flags.h b/deps/v8/src/flags.h index e6cbe3c233..a8eca95c2c 100644 --- a/deps/v8/src/flags.h +++ b/deps/v8/src/flags.h @@ -29,7 +29,8 @@ #include "checks.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Declare all of our flags. #define FLAG_MODE_DECLARE diff --git a/deps/v8/src/frame-element.h b/deps/v8/src/frame-element.h new file mode 100644 index 0000000000..d16eb481a3 --- /dev/null +++ b/deps/v8/src/frame-element.h @@ -0,0 +1,265 @@ +// 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_FRAME_ELEMENT_H_ +#define V8_FRAME_ELEMENT_H_ + +#include "register-allocator-inl.h" + +namespace v8 { +namespace internal { + +// ------------------------------------------------------------------------- +// Virtual frame elements +// +// The internal elements of the virtual frames. There are several kinds of +// elements: +// * Invalid: elements that are uninitialized or not actually part +// of the virtual frame. They should not be read. +// * 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 { + NOT_SYNCED, + SYNCED + }; + + // The default constructor creates an invalid frame element. + FrameElement() { + value_ = StaticTypeField::encode(StaticType::UNKNOWN_TYPE) + | TypeField::encode(INVALID) + | CopiedField::encode(false) + | SyncedField::encode(false) + | DataField::encode(0); + } + + // Factory function to construct an invalid frame element. + static FrameElement InvalidElement() { + FrameElement result; + return result; + } + + // Factory function to construct an in-memory frame element. + static FrameElement MemoryElement() { + FrameElement result(MEMORY, no_reg, SYNCED); + return result; + } + + // Factory function to construct an in-register frame element. + static FrameElement RegisterElement(Register reg, + SyncFlag is_synced, + StaticType static_type = StaticType()) { + return FrameElement(REGISTER, reg, is_synced, static_type); + } + + // Factory function to construct a frame element whose value is known at + // compile time. + static FrameElement ConstantElement(Handle<Object> value, + SyncFlag is_synced) { + FrameElement result(value, is_synced); + return result; + } + + // Static indirection table for handles to constants. If a frame + // element represents a constant, the data contains an index into + // this table of handles to the actual constants. + typedef ZoneList<Handle<Object> > ZoneObjectList; + + static ZoneObjectList* ConstantList() { + static ZoneObjectList list(10); + return &list; + } + + // Clear the constants indirection table. + static void ClearConstantList() { + ConstantList()->Clear(); + } + + bool is_synced() const { return SyncedField::decode(value_); } + + void set_sync() { + ASSERT(type() != MEMORY); + value_ = value_ | SyncedField::encode(true); + } + + void clear_sync() { + ASSERT(type() != MEMORY); + value_ = value_ & ~SyncedField::mask(); + } + + bool is_valid() const { return type() != INVALID; } + bool is_memory() const { return type() == MEMORY; } + bool is_register() const { return type() == REGISTER; } + bool is_constant() const { return type() == CONSTANT; } + bool is_copy() const { return type() == COPY; } + + bool is_copied() const { return CopiedField::decode(value_); } + void set_copied() { value_ = value_ | CopiedField::encode(true); } + void clear_copied() { value_ = value_ & ~CopiedField::mask(); } + + Register reg() const { + ASSERT(is_register()); + uint32_t reg = DataField::decode(value_); + Register result; + result.code_ = reg; + return result; + } + + Handle<Object> handle() const { + ASSERT(is_constant()); + return ConstantList()->at(DataField::decode(value_)); + } + + int index() const { + ASSERT(is_copy()); + return DataField::decode(value_); + } + + StaticType static_type() { + return StaticType(StaticTypeField::decode(value_)); + } + + void set_static_type(StaticType static_type) { + value_ = value_ & ~StaticTypeField::mask(); + value_ = value_ | StaticTypeField::encode(static_type.static_type_); + } + + bool Equals(FrameElement other) { + uint32_t masked_difference = (value_ ^ other.value_) & ~CopiedField::mask(); + if (!masked_difference) { + // The elements are equal if they agree exactly except on copied field. + return true; + } else { + // If two constants have the same value, and agree otherwise, return true. + return !(masked_difference & ~DataField::mask()) && + is_constant() && + handle().is_identical_to(other.handle()); + } + } + + // Test if two FrameElements refer to the same memory or register location. + bool SameLocation(FrameElement* other) { + if (type() == other->type()) { + if (value_ == other->value_) return true; + if (is_constant() && handle().is_identical_to(other->handle())) { + return true; + } + } + return false; + } + + // Given a pair of non-null frame element pointers, return one of them + // as an entry frame candidate or null if they are incompatible. + FrameElement* Combine(FrameElement* other) { + // If either is invalid, the result is. + if (!is_valid()) return this; + if (!other->is_valid()) return other; + + if (!SameLocation(other)) return NULL; + // If either is unsynced, the result is. The result static type is + // the merge of the static types. It's safe to set it on one of the + // frame elements, and harmless too (because we are only going to + // merge the reaching frames and will ensure that the types are + // coherent, and changing the static type does not emit code). + FrameElement* result = is_synced() ? other : this; + result->set_static_type(static_type().merge(other->static_type())); + return result; + } + + private: + enum Type { + INVALID, + MEMORY, + REGISTER, + CONSTANT, + COPY + }; + + // Used to construct memory and register elements. + FrameElement(Type type, Register reg, SyncFlag is_synced) { + value_ = StaticTypeField::encode(StaticType::UNKNOWN_TYPE) + | TypeField::encode(type) + | CopiedField::encode(false) + | SyncedField::encode(is_synced != NOT_SYNCED) + | DataField::encode(reg.code_ > 0 ? reg.code_ : 0); + } + + FrameElement(Type type, Register reg, SyncFlag is_synced, StaticType stype) { + value_ = StaticTypeField::encode(stype.static_type_) + | TypeField::encode(type) + | CopiedField::encode(false) + | SyncedField::encode(is_synced != NOT_SYNCED) + | DataField::encode(reg.code_ > 0 ? reg.code_ : 0); + } + + // Used to construct constant elements. + FrameElement(Handle<Object> value, SyncFlag is_synced) { + value_ = StaticTypeField::encode(StaticType::TypeOf(*value).static_type_) + | TypeField::encode(CONSTANT) + | CopiedField::encode(false) + | SyncedField::encode(is_synced != NOT_SYNCED) + | DataField::encode(ConstantList()->length()); + ConstantList()->Add(value); + } + + Type type() const { return TypeField::decode(value_); } + void set_type(Type type) { + value_ = value_ & ~TypeField::mask(); + value_ = value_ | TypeField::encode(type); + } + + void set_index(int new_index) { + ASSERT(is_copy()); + value_ = value_ & ~DataField::mask(); + value_ = value_ | DataField::encode(new_index); + } + + void set_reg(Register new_reg) { + ASSERT(is_register()); + value_ = value_ & ~DataField::mask(); + value_ = value_ | DataField::encode(new_reg.code_); + } + + // Encode static type, type, copied, synced and data in one 32 bit integer. + uint32_t value_; + + class StaticTypeField: public BitField<StaticType::StaticTypeEnum, 0, 3> {}; + class TypeField: public BitField<Type, 3, 3> {}; + class CopiedField: public BitField<uint32_t, 6, 1> {}; + class SyncedField: public BitField<uint32_t, 7, 1> {}; + class DataField: public BitField<uint32_t, 8, 32 - 9> {}; + + friend class VirtualFrame; +}; + +} } // namespace v8::internal + +#endif // V8_FRAME_ELEMENT_H_ diff --git a/deps/v8/src/frames-inl.h b/deps/v8/src/frames-inl.h index bf46f6bf7b..28be430661 100644 --- a/deps/v8/src/frames-inl.h +++ b/deps/v8/src/frames-inl.h @@ -38,7 +38,8 @@ #include "arm/frames-arm.h" #endif -namespace v8 { namespace internal { +namespace v8 { +namespace internal { inline Address StackHandler::address() const { diff --git a/deps/v8/src/frames.cc b/deps/v8/src/frames.cc index 1eedbf6401..dd0ea00c0e 100644 --- a/deps/v8/src/frames.cc +++ b/deps/v8/src/frames.cc @@ -34,7 +34,8 @@ #include "top.h" #include "zone-inl.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Iterator that supports traversing the stack handlers of a // particular frame. Needs to know the top of the handler chain. diff --git a/deps/v8/src/frames.h b/deps/v8/src/frames.h index 8ab4be9051..e250609fd9 100644 --- a/deps/v8/src/frames.h +++ b/deps/v8/src/frames.h @@ -28,7 +28,8 @@ #ifndef V8_FRAMES_H_ #define V8_FRAMES_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { typedef uint32_t RegList; @@ -442,7 +443,8 @@ class ArgumentsAdaptorFrame: public JavaScriptFrame { // the sentinel as its context, it is an arguments adaptor frame. It // must be tagged as a small integer to avoid GC issues. Crud. enum { - SENTINEL = (1 << kSmiTagSize) | kSmiTag + SENTINEL = (1 << kSmiTagSize) | kSmiTag, + NON_SENTINEL = ~SENTINEL }; virtual Type type() const { return ARGUMENTS_ADAPTOR; } diff --git a/deps/v8/src/func-name-inferrer.cc b/deps/v8/src/func-name-inferrer.cc index 75f7a99372..2d6a86a6f7 100644 --- a/deps/v8/src/func-name-inferrer.cc +++ b/deps/v8/src/func-name-inferrer.cc @@ -30,7 +30,8 @@ #include "ast.h" #include "func-name-inferrer.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { void FuncNameInferrer::PushEnclosingName(Handle<String> name) { diff --git a/deps/v8/src/func-name-inferrer.h b/deps/v8/src/func-name-inferrer.h index d8270c3642..e88586a445 100644 --- a/deps/v8/src/func-name-inferrer.h +++ b/deps/v8/src/func-name-inferrer.h @@ -28,47 +28,53 @@ #ifndef V8_FUNC_NAME_INFERRER_H_ #define V8_FUNC_NAME_INFERRER_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // FuncNameInferrer is a stateful class that is used to perform name // inference for anonymous functions during static analysis of source code. // Inference is performed in cases when an anonymous function is assigned // to a variable or a property (see test-func-name-inference.cc for examples.) - +// // The basic idea is that during AST traversal LHSs of expressions are // always visited before RHSs. Thus, during visiting the LHS, a name can be // collected, and during visiting the RHS, a function literal can be collected. // Inference is performed while leaving the assignment node. - class FuncNameInferrer BASE_EMBEDDED { public: - FuncNameInferrer() : - entries_stack_(10), - names_stack_(5), - funcs_to_infer_(4), - dot_(Factory::NewStringFromAscii(CStrVector("."))) { + FuncNameInferrer() + : entries_stack_(10), + names_stack_(5), + funcs_to_infer_(4), + dot_(Factory::NewStringFromAscii(CStrVector("."))) { } + // Returns whether we have entered name collection state. bool IsOpen() const { return !entries_stack_.is_empty(); } + // Pushes an enclosing the name of enclosing function onto names stack. void PushEnclosingName(Handle<String> name); + // Enters name collection state. void Enter() { entries_stack_.Add(names_stack_.length()); } + // Pushes an encountered name onto names stack when in collection state. void PushName(Handle<String> name) { if (IsOpen()) { names_stack_.Add(name); } } + // Adds a function to infer name for. void AddFunction(FunctionLiteral* func_to_infer) { if (IsOpen()) { funcs_to_infer_.Add(func_to_infer); } } + // Infers a function name and leaves names collection state. void InferAndLeave() { ASSERT(IsOpen()); if (!funcs_to_infer_.is_empty()) { @@ -78,13 +84,18 @@ class FuncNameInferrer BASE_EMBEDDED { } private: + // Constructs a full name in dotted notation from gathered names. Handle<String> MakeNameFromStack(); + + // A helper function for MakeNameFromStack. Handle<String> MakeNameFromStackHelper(int pos, Handle<String> prev); + + // Performs name inferring for added functions. void InferFunctionsNames(); - List<int> entries_stack_; - List<Handle<String> > names_stack_; - List<FunctionLiteral*> funcs_to_infer_; + ZoneList<int> entries_stack_; + ZoneList<Handle<String> > names_stack_; + ZoneList<FunctionLiteral*> funcs_to_infer_; Handle<String> dot_; DISALLOW_COPY_AND_ASSIGN(FuncNameInferrer); @@ -95,15 +106,17 @@ class FuncNameInferrer BASE_EMBEDDED { // leaving scope. class ScopedFuncNameInferrer BASE_EMBEDDED { public: - explicit ScopedFuncNameInferrer(FuncNameInferrer* inferrer) : - inferrer_(inferrer), - is_entered_(false) {} + explicit ScopedFuncNameInferrer(FuncNameInferrer* inferrer) + : inferrer_(inferrer), + is_entered_(false) {} + ~ScopedFuncNameInferrer() { if (is_entered_) { inferrer_->InferAndLeave(); } } + // Triggers the wrapped inferrer into name collection state. void Enter() { inferrer_->Enter(); is_entered_ = true; diff --git a/deps/v8/src/global-handles.cc b/deps/v8/src/global-handles.cc index 46b7db322a..ed4e262e62 100644 --- a/deps/v8/src/global-handles.cc +++ b/deps/v8/src/global-handles.cc @@ -30,7 +30,8 @@ #include "api.h" #include "global-handles.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { class GlobalHandles::Node : public Malloced { public: diff --git a/deps/v8/src/global-handles.h b/deps/v8/src/global-handles.h index e6e9de1d13..9e63ba7a97 100644 --- a/deps/v8/src/global-handles.h +++ b/deps/v8/src/global-handles.h @@ -30,7 +30,8 @@ #include "list-inl.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Structure for tracking global handles. // A single list keeps all the allocated global handles. diff --git a/deps/v8/src/globals.h b/deps/v8/src/globals.h index a0b5ac3639..2b0fe15d13 100644 --- a/deps/v8/src/globals.h +++ b/deps/v8/src/globals.h @@ -1,4 +1,4 @@ -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2006-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: @@ -28,7 +28,8 @@ #ifndef V8_GLOBALS_H_ #define V8_GLOBALS_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Processor architecture detection. For more info on what's defined, see: // http://msdn.microsoft.com/en-us/library/b0084kay.aspx @@ -77,16 +78,23 @@ typedef byte* Address; #define V8_UINT64_C(x) (x ## UI64) #define V8_INT64_C(x) (x ## I64) #define V8_PTR_PREFIX "ll" -#else +#else // _MSC_VER #define V8_UINT64_C(x) (x ## UL) #define V8_INT64_C(x) (x ## L) #define V8_PTR_PREFIX "l" -#endif +#endif // _MSC_VER #else // V8_HOST_ARCH_64_BIT #define V8_PTR_PREFIX "" -#endif +#endif // V8_HOST_ARCH_64_BIT -#define V8PRIp V8_PTR_PREFIX "x" +#define V8PRIxPTR V8_PTR_PREFIX "x" +#define V8PRIdPTR V8_PTR_PREFIX "d" + +// Fix for Mac OS X defining uintptr_t as "unsigned long": +#if defined(__APPLE__) && defined(__MACH__) +#undef V8PRIxPTR +#define V8PRIxPTR "lx" +#endif // Code-point values in Unicode 4.0 are 21 bits wide. typedef uint16_t uc16; @@ -103,11 +111,12 @@ const int kMinInt = -kMaxInt - 1; const uint32_t kMaxUInt32 = 0xFFFFFFFFu; -const int kCharSize = sizeof(char); // NOLINT -const int kShortSize = sizeof(short); // NOLINT -const int kIntSize = sizeof(int); // NOLINT -const int kDoubleSize = sizeof(double); // NOLINT -const int kPointerSize = sizeof(void*); // NOLINT +const int kCharSize = sizeof(char); // NOLINT +const int kShortSize = sizeof(short); // NOLINT +const int kIntSize = sizeof(int); // NOLINT +const int kDoubleSize = sizeof(double); // NOLINT +const int kPointerSize = sizeof(void*); // NOLINT +const int kIntptrSize = sizeof(intptr_t); // NOLINT #if V8_HOST_ARCH_64_BIT const int kPointerSizeLog2 = 3; @@ -116,9 +125,12 @@ const int kPointerSizeLog2 = 2; #endif const int kObjectAlignmentBits = kPointerSizeLog2; -const intptr_t kObjectAlignmentMask = (1 << kObjectAlignmentBits) - 1; const intptr_t kObjectAlignment = 1 << kObjectAlignmentBits; +const intptr_t kObjectAlignmentMask = kObjectAlignment - 1; +// Desired alignment for pointers. +const intptr_t kPointerAlignment = (1 << kPointerSizeLog2); +const intptr_t kPointerAlignmentMask = kPointerAlignment - 1; // Tag information for HeapObject. const int kHeapObjectTag = 1; @@ -232,6 +244,7 @@ class ObjectGroup; class TickSample; class VirtualMemory; class Mutex; +class ZoneScopeInfo; typedef bool (*WeakSlotCallback)(Object** pointer); @@ -314,8 +327,6 @@ typedef void (*InlineCacheCallback)(Code* code, Address ic); enum InlineCacheState { // Has never been executed. UNINITIALIZED, - // Has never been executed, but is in a loop. - UNINITIALIZED_IN_LOOP, // Has been executed but monomorhic state has been delayed. PREMONOMORPHIC, // Has been executed and only one receiver type has been seen. @@ -330,6 +341,12 @@ enum InlineCacheState { }; +enum InLoopFlag { + NOT_IN_LOOP, + IN_LOOP +}; + + // Type of properties. // Order of properties is significant. // Must fit in the BitField PropertyDetails::TypeField. @@ -411,7 +428,11 @@ enum StateTag { // OBJECT_SIZE_ALIGN returns the value aligned HeapObject size #define OBJECT_SIZE_ALIGN(value) \ - ((value + kObjectAlignmentMask) & ~kObjectAlignmentMask) + (((value) + kObjectAlignmentMask) & ~kObjectAlignmentMask) + +// POINTER_SIZE_ALIGN returns the value aligned as a pointer. +#define POINTER_SIZE_ALIGN(value) \ + (((value) + kPointerAlignmentMask) & ~kPointerAlignmentMask) // The expression OFFSET_OF(type, field) computes the byte-offset // of the specified field relative to the containing type. This diff --git a/deps/v8/src/handles-inl.h b/deps/v8/src/handles-inl.h index e5899e3ac9..6013c5b51e 100644 --- a/deps/v8/src/handles-inl.h +++ b/deps/v8/src/handles-inl.h @@ -33,7 +33,8 @@ #include "handles.h" #include "api.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { template<class T> Handle<T>::Handle(T* obj) { diff --git a/deps/v8/src/handles.cc b/deps/v8/src/handles.cc index 773483d609..44ca60221c 100644 --- a/deps/v8/src/handles.cc +++ b/deps/v8/src/handles.cc @@ -37,7 +37,8 @@ #include "natives.h" #include "runtime.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { v8::ImplementationUtilities::HandleScopeData HandleScope::current_ = @@ -221,6 +222,12 @@ Handle<Object> ForceSetProperty(Handle<JSObject> object, } +Handle<Object> ForceDeleteProperty(Handle<JSObject> object, + Handle<Object> key) { + CALL_HEAP_FUNCTION(Runtime::ForceDeleteObjectProperty(object, key), Object); +} + + Handle<Object> IgnoreAttributesAndSetLocalProperty( Handle<JSObject> object, Handle<String> key, @@ -230,6 +237,7 @@ Handle<Object> IgnoreAttributesAndSetLocalProperty( IgnoreAttributesAndSetLocalProperty(*key, *value, attributes), Object); } + Handle<Object> SetPropertyWithInterceptor(Handle<JSObject> object, Handle<String> key, Handle<Object> value, @@ -273,19 +281,49 @@ Handle<Object> GetPrototype(Handle<Object> obj) { Handle<Object> GetHiddenProperties(Handle<JSObject> obj, bool create_if_needed) { - CALL_HEAP_FUNCTION(obj->GetHiddenProperties(create_if_needed), Object); + Handle<String> key = Factory::hidden_symbol(); + + if (obj->HasFastProperties()) { + // If the object has fast properties, check whether the first slot + // in the descriptor array matches the hidden symbol. Since the + // hidden symbols hash code is zero (and no other string has hash + // code zero) it will always occupy the first entry if present. + DescriptorArray* descriptors = obj->map()->instance_descriptors(); + DescriptorReader r(descriptors, 0); // Explicitly position reader at zero. + if (!r.eos() && (r.GetKey() == *key) && r.IsProperty()) { + ASSERT(r.type() == FIELD); + return Handle<Object>(obj->FastPropertyAt(r.GetFieldIndex())); + } + } + + // Only attempt to find the hidden properties in the local object and not + // in the prototype chain. Note that HasLocalProperty() can cause a GC in + // the general case in the presence of interceptors. + if (!obj->HasLocalProperty(*key)) { + // Hidden properties object not found. Allocate a new hidden properties + // object if requested. Otherwise return the undefined value. + if (create_if_needed) { + Handle<Object> hidden_obj = Factory::NewJSObject(Top::object_function()); + return SetProperty(obj, key, hidden_obj, DONT_ENUM); + } else { + return Factory::undefined_value(); + } + } + return GetProperty(obj, key); } Handle<Object> DeleteElement(Handle<JSObject> obj, uint32_t index) { - CALL_HEAP_FUNCTION(obj->DeleteElement(index), Object); + CALL_HEAP_FUNCTION(obj->DeleteElement(index, JSObject::NORMAL_DELETION), + Object); } Handle<Object> DeleteProperty(Handle<JSObject> obj, Handle<String> prop) { - CALL_HEAP_FUNCTION(obj->DeleteProperty(*prop), Object); + CALL_HEAP_FUNCTION(obj->DeleteProperty(*prop, JSObject::NORMAL_DELETION), + Object); } diff --git a/deps/v8/src/handles.h b/deps/v8/src/handles.h index 652d6c70e5..af638b804b 100644 --- a/deps/v8/src/handles.h +++ b/deps/v8/src/handles.h @@ -30,7 +30,8 @@ #include "apiutils.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // ---------------------------------------------------------------------------- // A Handle provides a reference to an object that survives relocation by @@ -118,15 +119,15 @@ class HandleScope { static int NumberOfHandles(); // Creates a new handle with the given value. - static inline void** CreateHandle(void* value) { + static inline Object** CreateHandle(Object* value) { void** result = current_.next; if (result == current_.limit) result = Extend(); // Update the current next field, set the value in the created // handle, and return the result. ASSERT(result < current_.limit); current_.next = result + 1; - *result = value; - return result; + *reinterpret_cast<Object**>(result) = value; + return reinterpret_cast<Object**>(result); } private: @@ -201,6 +202,9 @@ Handle<Object> ForceSetProperty(Handle<JSObject> object, Handle<Object> value, PropertyAttributes attributes); +Handle<Object> ForceDeleteProperty(Handle<JSObject> object, + Handle<Object> key); + Handle<Object> IgnoreAttributesAndSetLocalProperty(Handle<JSObject> object, Handle<String> key, Handle<Object> value, @@ -228,6 +232,9 @@ Handle<Object> GetPropertyWithInterceptor(Handle<JSObject> receiver, Handle<Object> GetPrototype(Handle<Object> obj); +// Return the object's hidden properties object. If the object has no hidden +// properties and create_if_needed is true, then a new hidden property object +// will be allocated. Otherwise the Heap::undefined_value is returned. Handle<Object> GetHiddenProperties(Handle<JSObject> obj, bool create_if_needed); Handle<Object> DeleteElement(Handle<JSObject> obj, uint32_t index); diff --git a/deps/v8/src/hashmap.cc b/deps/v8/src/hashmap.cc index 126f739241..b7173127ec 100644 --- a/deps/v8/src/hashmap.cc +++ b/deps/v8/src/hashmap.cc @@ -29,14 +29,8 @@ #include "hashmap.h" -namespace v8 { namespace internal { - - -static inline bool IsPowerOf2(uint32_t x) { - ASSERT(x != 0); - return (x & (x - 1)) == 0; -} - +namespace v8 { +namespace internal { Allocator HashMap::DefaultAllocator; @@ -66,7 +60,7 @@ HashMap::~HashMap() { HashMap::Entry* HashMap::Lookup(void* key, uint32_t hash, bool insert) { // Find a matching entry. Entry* p = Probe(key, hash); - if (p->key != NULL) { + if (p->key != NULL) { return p; } @@ -91,6 +85,65 @@ HashMap::Entry* HashMap::Lookup(void* key, uint32_t hash, bool insert) { } +void HashMap::Remove(void* key, uint32_t hash) { + // Lookup the entry for the key to remove. + Entry* p = Probe(key, hash); + if (p->key == NULL) { + // Key not found nothing to remove. + return; + } + + // To remove an entry we need to ensure that it does not create an empty + // entry that will cause the search for another entry to stop too soon. If all + // the entries between the entry to remove and the next empty slot have their + // initial position inside this interval, clearing the entry to remove will + // not break the search. If, while searching for the next empty entry, an + // entry is encountered which does not have its initial position between the + // entry to remove and the position looked at, then this entry can be moved to + // the place of the entry to remove without breaking the search for it. The + // entry made vacant by this move is now the entry to remove and the process + // starts over. + // Algorithm from http://en.wikipedia.org/wiki/Open_addressing. + + // This guarantees loop termination as there is at least one empty entry so + // eventually the removed entry will have an empty entry after it. + ASSERT(occupancy_ < capacity_); + + // p is the candidate entry to clear. q is used to scan forwards. + Entry* q = p; // Start at the entry to remove. + while (true) { + // Move q to the next entry. + q = q + 1; + if (q == map_end()) { + q = map_; + } + + // All entries between p and q have their initial position between p and q + // and the entry p can be cleared without breaking the search for these + // entries. + if (q->key == NULL) { + break; + } + + // Find the initial position for the entry at position q. + Entry* r = map_ + (q->hash & (capacity_ - 1)); + + // If the entry at position q has its initial position outside the range + // between p and q it can be moved forward to position p and will still be + // found. There is now a new candidate entry for clearing. + if ((q > p && (r <= p || r > q)) || + (q < p && (r <= p && r > q))) { + *p = *q; + p = q; + } + } + + // Clear the entry which is allowed to en emptied. + p->key = NULL; + occupancy_--; +} + + void HashMap::Clear() { // Mark all entries as empty. const Entry* end = map_end(); @@ -126,7 +179,7 @@ HashMap::Entry* HashMap::Probe(void* key, uint32_t hash) { const Entry* end = map_end(); ASSERT(map_ <= p && p < end); - ASSERT(occupancy_ < capacity_); // guarantees loop termination + ASSERT(occupancy_ < capacity_); // Guarantees loop termination. while (p->key != NULL && (hash != p->hash || !match_(key, p->key))) { p++; if (p >= end) { diff --git a/deps/v8/src/hashmap.h b/deps/v8/src/hashmap.h index fabf3dc3ec..b92c71573f 100644 --- a/deps/v8/src/hashmap.h +++ b/deps/v8/src/hashmap.h @@ -28,7 +28,8 @@ #ifndef V8_HASHMAP_H_ #define V8_HASHMAP_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Allocator defines the memory allocator interface @@ -75,6 +76,9 @@ class HashMap { // Otherwise, NULL is returned. Entry* Lookup(void* key, uint32_t hash, bool insert); + // Removes the entry with matching key. + void Remove(void* key, uint32_t hash); + // Empties the hash map (occupancy() == 0). void Clear(); diff --git a/deps/v8/src/heap-inl.h b/deps/v8/src/heap-inl.h index 86165ee1bb..8dd09d77d6 100644 --- a/deps/v8/src/heap-inl.h +++ b/deps/v8/src/heap-inl.h @@ -31,7 +31,8 @@ #include "log.h" #include "v8-counters.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { int Heap::MaxHeapObjectSize() { return Page::kMaxHeapObjectSize; @@ -145,7 +146,9 @@ void Heap::RecordWrite(Address address, int offset) { if (new_space_.Contains(address)) return; ASSERT(!new_space_.FromSpaceContains(address)); SLOW_ASSERT(Contains(address + offset)); +#ifndef V8_HOST_ARCH_64_BIT Page::SetRSet(address, offset); +#endif // V8_HOST_ARCH_64_BIT } @@ -191,6 +194,27 @@ void Heap::CopyBlock(Object** dst, Object** src, int byte_size) { } +void Heap::ScavengeObject(HeapObject** p, HeapObject* object) { + ASSERT(InFromSpace(object)); + + // We use the first word (where the map pointer usually is) of a heap + // object to record the forwarding pointer. A forwarding pointer can + // point to an old space, the code space, or the to space of the new + // generation. + MapWord first_word = object->map_word(); + + // If the first word is a forwarding address, the object has already been + // copied. + if (first_word.IsForwardingAddress()) { + *p = first_word.ToForwardingAddress(); + return; + } + + // Call the slow part of scavenge object. + return ScavengeObjectSlow(p, object); +} + + Object* Heap::GetKeyedLookupCache() { if (keyed_lookup_cache()->IsUndefined()) { Object* obj = LookupCache::Allocate(4); diff --git a/deps/v8/src/heap.cc b/deps/v8/src/heap.cc index 6d600152e5..772cf329c7 100644 --- a/deps/v8/src/heap.cc +++ b/deps/v8/src/heap.cc @@ -40,7 +40,8 @@ #include "scopeinfo.h" #include "v8threads.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { #define ROOT_ALLOCATION(type, name) type* Heap::name##_; ROOT_LIST(ROOT_ALLOCATION) @@ -283,6 +284,9 @@ void Heap::GarbageCollectionEpilogue() { #if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING) ReportStatisticsAfterGC(); #endif +#ifdef ENABLE_DEBUGGER_SUPPORT + Debug::AfterGarbageCollection(); +#endif } @@ -537,8 +541,39 @@ class ScavengeVisitor: public ObjectVisitor { }; +// A queue of pointers and maps of to-be-promoted objects during a +// scavenge collection. +class PromotionQueue { + public: + void Initialize(Address start_address) { + front_ = rear_ = reinterpret_cast<HeapObject**>(start_address); + } + + bool is_empty() { return front_ <= rear_; } + + void insert(HeapObject* object, Map* map) { + *(--rear_) = object; + *(--rear_) = map; + // Assert no overflow into live objects. + ASSERT(reinterpret_cast<Address>(rear_) >= Heap::new_space()->top()); + } + + void remove(HeapObject** object, Map** map) { + *object = *(--front_); + *map = Map::cast(*(--front_)); + // Assert no underflow. + ASSERT(front_ >= rear_); + } + + private: + // The front of the queue is higher in memory than the rear. + HeapObject** front_; + HeapObject** rear_; +}; + + // Shared state read by the scavenge collector and set by ScavengeObject. -static Address promoted_rear = NULL; +static PromotionQueue promotion_queue; #ifdef DEBUG @@ -624,8 +659,7 @@ void Heap::Scavenge() { // frees up its size in bytes from the top of the new space, and // objects are at least one pointer in size. Address new_space_front = new_space_.ToSpaceLow(); - Address promoted_front = new_space_.ToSpaceHigh(); - promoted_rear = new_space_.ToSpaceHigh(); + promotion_queue.Initialize(new_space_.ToSpaceHigh()); ScavengeVisitor scavenge_visitor; // Copy roots. @@ -634,15 +668,36 @@ void Heap::Scavenge() { // Copy objects reachable from weak pointers. GlobalHandles::IterateWeakRoots(&scavenge_visitor); +#if V8_HOST_ARCH_64_BIT + // TODO(X64): Make this go away again. We currently disable RSets for + // 64-bit-mode. + HeapObjectIterator old_pointer_iterator(old_pointer_space_); + while (old_pointer_iterator.has_next()) { + HeapObject* heap_object = old_pointer_iterator.next(); + heap_object->Iterate(&scavenge_visitor); + } + HeapObjectIterator map_iterator(map_space_); + while (map_iterator.has_next()) { + HeapObject* heap_object = map_iterator.next(); + heap_object->Iterate(&scavenge_visitor); + } + LargeObjectIterator lo_iterator(lo_space_); + while (lo_iterator.has_next()) { + HeapObject* heap_object = lo_iterator.next(); + if (heap_object->IsFixedArray()) { + heap_object->Iterate(&scavenge_visitor); + } + } +#else // V8_HOST_ARCH_64_BIT // Copy objects reachable from the old generation. By definition, // there are no intergenerational pointers in code or data spaces. IterateRSet(old_pointer_space_, &ScavengePointer); IterateRSet(map_space_, &ScavengePointer); lo_space_->IterateRSet(&ScavengePointer); +#endif // V8_HOST_ARCH_64_BIT do { ASSERT(new_space_front <= new_space_.top()); - ASSERT(promoted_front >= promoted_rear); // The addresses new_space_front and new_space_.top() define a // queue of unprocessed copied objects. Process them until the @@ -653,15 +708,26 @@ void Heap::Scavenge() { new_space_front += object->Size(); } - // The addresses promoted_front and promoted_rear define a queue - // of unprocessed addresses of promoted objects. Process them - // until the queue is empty. - while (promoted_front > promoted_rear) { - promoted_front -= kPointerSize; - HeapObject* object = - HeapObject::cast(Memory::Object_at(promoted_front)); - object->Iterate(&scavenge_visitor); - UpdateRSet(object); + // Promote and process all the to-be-promoted objects. + while (!promotion_queue.is_empty()) { + HeapObject* source; + Map* map; + promotion_queue.remove(&source, &map); + // Copy the from-space object to its new location (given by the + // forwarding address) and fix its map. + HeapObject* target = source->map_word().ToForwardingAddress(); + CopyBlock(reinterpret_cast<Object**>(target->address()), + reinterpret_cast<Object**>(source->address()), + source->SizeFromMap(map)); + target->set_map(map); + +#if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING) + // Update NewSpace stats if necessary. + RecordCopiedObject(target); +#endif + // Visit the newly copied object for pointers to new space. + target->Iterate(&scavenge_visitor); + UpdateRSet(target); } // Take another spin if there are now unswept objects in new space @@ -735,6 +801,8 @@ class UpdateRSetVisitor: public ObjectVisitor { int Heap::UpdateRSet(HeapObject* obj) { +#ifndef V8_HOST_ARCH_64_BIT + // TODO(X64) Reenable RSet when we have a working 64-bit layout of Page. ASSERT(!InNewSpace(obj)); // Special handling of fixed arrays to iterate the body based on the start // address and offset. Just iterating the pointers as in UpdateRSetVisitor @@ -756,6 +824,7 @@ int Heap::UpdateRSet(HeapObject* obj) { UpdateRSetVisitor v; obj->Iterate(&v); } +#endif // V8_HOST_ARCH_64_BIT return obj->Size(); } @@ -818,8 +887,8 @@ HeapObject* Heap::MigrateObject(HeapObject* source, // Set the forwarding address. source->set_map_word(MapWord::FromForwardingAddress(target)); - // Update NewSpace stats if necessary. #if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING) + // Update NewSpace stats if necessary. RecordCopiedObject(target); #endif @@ -827,28 +896,6 @@ HeapObject* Heap::MigrateObject(HeapObject* source, } -// Inlined function. -void Heap::ScavengeObject(HeapObject** p, HeapObject* object) { - ASSERT(InFromSpace(object)); - - // We use the first word (where the map pointer usually is) of a heap - // object to record the forwarding pointer. A forwarding pointer can - // point to an old space, the code space, or the to space of the new - // generation. - MapWord first_word = object->map_word(); - - // If the first word is a forwarding address, the object has already been - // copied. - if (first_word.IsForwardingAddress()) { - *p = first_word.ToForwardingAddress(); - return; - } - - // Call the slow part of scavenge object. - return ScavengeObjectSlow(p, object); -} - - static inline bool IsShortcutCandidate(HeapObject* object, Map* map) { STATIC_ASSERT(kNotStringTag != 0 && kSymbolTag != 0); ASSERT(object->map() == map); @@ -879,6 +926,11 @@ void Heap::ScavengeObjectSlow(HeapObject** p, HeapObject* object) { } int object_size = object->SizeFromMap(first_word.ToMap()); + // We rely on live objects in new space to be at least two pointers, + // so we can store the from-space address and map pointer of promoted + // objects in the to space. + ASSERT(object_size >= 2 * kPointerSize); + // If the object should be promoted, we try to copy it to old space. if (ShouldBePromoted(object->address(), object_size)) { OldSpace* target_space = Heap::TargetSpace(object); @@ -886,16 +938,29 @@ void Heap::ScavengeObjectSlow(HeapObject** p, HeapObject* object) { target_space == Heap::old_data_space_); Object* result = target_space->AllocateRaw(object_size); if (!result->IsFailure()) { - *p = MigrateObject(object, HeapObject::cast(result), object_size); + HeapObject* target = HeapObject::cast(result); if (target_space == Heap::old_pointer_space_) { - // Record the object's address at the top of the to space, to allow - // it to be swept by the scavenger. - promoted_rear -= kPointerSize; - Memory::Object_at(promoted_rear) = *p; + // Save the from-space object pointer and its map pointer at the + // top of the to space to be swept and copied later. Write the + // forwarding address over the map word of the from-space + // object. + promotion_queue.insert(object, first_word.ToMap()); + object->set_map_word(MapWord::FromForwardingAddress(target)); + + // Give the space allocated for the result a proper map by + // treating it as a free list node (not linked into the free + // list). + FreeListNode* node = FreeListNode::FromAddress(target->address()); + node->set_size(object_size); + + *p = target; } else { + // Objects promoted to the data space can be copied immediately + // and not revisited---we will never sweep that space for + // pointers and the copied objects do not contain pointers to + // new space objects. + *p = MigrateObject(object, target, object_size); #ifdef DEBUG - // Objects promoted to the data space should not have pointers to - // new space. VerifyNonPointerSpacePointersVisitor v; (*p)->Iterate(&v); #endif @@ -960,7 +1025,7 @@ bool Heap::CreateInitialMaps() { meta_map_ = reinterpret_cast<Map*>(obj); meta_map()->set_map(meta_map()); - obj = AllocatePartialMap(FIXED_ARRAY_TYPE, Array::kHeaderSize); + obj = AllocatePartialMap(FIXED_ARRAY_TYPE, FixedArray::kHeaderSize); if (obj->IsFailure()) return false; fixed_array_map_ = Map::cast(obj); @@ -1017,37 +1082,37 @@ bool Heap::CreateInitialMaps() { STRING_TYPE_LIST(ALLOCATE_STRING_MAP); #undef ALLOCATE_STRING_MAP - obj = AllocateMap(SHORT_STRING_TYPE, SeqTwoByteString::kHeaderSize); + obj = AllocateMap(SHORT_STRING_TYPE, SeqTwoByteString::kAlignedSize); if (obj->IsFailure()) return false; undetectable_short_string_map_ = Map::cast(obj); undetectable_short_string_map_->set_is_undetectable(); - obj = AllocateMap(MEDIUM_STRING_TYPE, SeqTwoByteString::kHeaderSize); + obj = AllocateMap(MEDIUM_STRING_TYPE, SeqTwoByteString::kAlignedSize); if (obj->IsFailure()) return false; undetectable_medium_string_map_ = Map::cast(obj); undetectable_medium_string_map_->set_is_undetectable(); - obj = AllocateMap(LONG_STRING_TYPE, SeqTwoByteString::kHeaderSize); + obj = AllocateMap(LONG_STRING_TYPE, SeqTwoByteString::kAlignedSize); if (obj->IsFailure()) return false; undetectable_long_string_map_ = Map::cast(obj); undetectable_long_string_map_->set_is_undetectable(); - obj = AllocateMap(SHORT_ASCII_STRING_TYPE, SeqAsciiString::kHeaderSize); + obj = AllocateMap(SHORT_ASCII_STRING_TYPE, SeqAsciiString::kAlignedSize); if (obj->IsFailure()) return false; undetectable_short_ascii_string_map_ = Map::cast(obj); undetectable_short_ascii_string_map_->set_is_undetectable(); - obj = AllocateMap(MEDIUM_ASCII_STRING_TYPE, SeqAsciiString::kHeaderSize); + obj = AllocateMap(MEDIUM_ASCII_STRING_TYPE, SeqAsciiString::kAlignedSize); if (obj->IsFailure()) return false; undetectable_medium_ascii_string_map_ = Map::cast(obj); undetectable_medium_ascii_string_map_->set_is_undetectable(); - obj = AllocateMap(LONG_ASCII_STRING_TYPE, SeqAsciiString::kHeaderSize); + obj = AllocateMap(LONG_ASCII_STRING_TYPE, SeqAsciiString::kAlignedSize); if (obj->IsFailure()) return false; undetectable_long_ascii_string_map_ = Map::cast(obj); undetectable_long_ascii_string_map_->set_is_undetectable(); - obj = AllocateMap(BYTE_ARRAY_TYPE, Array::kHeaderSize); + obj = AllocateMap(BYTE_ARRAY_TYPE, Array::kAlignedSize); if (obj->IsFailure()) return false; byte_array_map_ = Map::cast(obj); @@ -1663,7 +1728,7 @@ void Heap::CreateFillerObjectAt(Address addr, int size) { Object* Heap::CreateCode(const CodeDesc& desc, - ScopeInfo<>* sinfo, + ZoneScopeInfo* sinfo, Code::Flags flags, Handle<Object> self_reference) { // Compute size @@ -2599,12 +2664,13 @@ void Heap::ZapFromSpace() { #endif // DEBUG -void Heap::IterateRSetRange(Address object_start, - Address object_end, - Address rset_start, - ObjectSlotCallback copy_object_func) { +int Heap::IterateRSetRange(Address object_start, + Address object_end, + Address rset_start, + ObjectSlotCallback copy_object_func) { Address object_address = object_start; Address rset_address = rset_start; + int set_bits_count = 0; // Loop over all the pointers in [object_start, object_end). while (object_address < object_end) { @@ -2621,6 +2687,7 @@ void Heap::IterateRSetRange(Address object_start, // If this pointer does not need to be remembered anymore, clear // the remembered set bit. if (!Heap::InNewSpace(*object_p)) result_rset &= ~bitmask; + set_bits_count++; } object_address += kPointerSize; } @@ -2634,6 +2701,7 @@ void Heap::IterateRSetRange(Address object_start, } rset_address += kIntSize; } + return set_bits_count; } @@ -2641,11 +2709,20 @@ void Heap::IterateRSet(PagedSpace* space, ObjectSlotCallback copy_object_func) { ASSERT(Page::is_rset_in_use()); ASSERT(space == old_pointer_space_ || space == map_space_); + static void* paged_rset_histogram = StatsTable::CreateHistogram( + "V8.RSetPaged", + 0, + Page::kObjectAreaSize / kPointerSize, + 30); + PageIterator it(space, PageIterator::PAGES_IN_USE); while (it.has_next()) { Page* page = it.next(); - IterateRSetRange(page->ObjectAreaStart(), page->AllocationTop(), - page->RSetStart(), copy_object_func); + int count = IterateRSetRange(page->ObjectAreaStart(), page->AllocationTop(), + page->RSetStart(), copy_object_func); + if (paged_rset_histogram != NULL) { + StatsTable::AddHistogramSample(paged_rset_histogram, count); + } } } diff --git a/deps/v8/src/heap.h b/deps/v8/src/heap.h index ccc552f8d0..d8080b6a8a 100644 --- a/deps/v8/src/heap.h +++ b/deps/v8/src/heap.h @@ -28,7 +28,10 @@ #ifndef V8_HEAP_H_ #define V8_HEAP_H_ -namespace v8 { namespace internal { +#include "zone-inl.h" + +namespace v8 { +namespace internal { // Defines all the roots in Heap. #define STRONG_ROOT_LIST(V) \ @@ -570,7 +573,7 @@ class Heap : public AllStatic { // object by containing this pointer. // Please note this function does not perform a garbage collection. static Object* CreateCode(const CodeDesc& desc, - ScopeInfo<>* sinfo, + ZoneScopeInfo* sinfo, Code::Flags flags, Handle<Object> self_reference); @@ -664,10 +667,11 @@ class Heap : public AllStatic { // Iterates a range of remembered set addresses starting with rset_start // corresponding to the range of allocated pointers // [object_start, object_end). - static void IterateRSetRange(Address object_start, - Address object_end, - Address rset_start, - ObjectSlotCallback copy_object_func); + // Returns the number of bits that were set. + static int IterateRSetRange(Address object_start, + Address object_end, + Address rset_start, + ObjectSlotCallback copy_object_func); // Returns whether the object resides in new space. static inline bool InNewSpace(Object* object); diff --git a/deps/v8/src/ia32/assembler-ia32-inl.h b/deps/v8/src/ia32/assembler-ia32-inl.h index a3d24b276b..045f17682f 100644 --- a/deps/v8/src/ia32/assembler-ia32-inl.h +++ b/deps/v8/src/ia32/assembler-ia32-inl.h @@ -39,7 +39,8 @@ #include "cpu.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { Condition NegateCondition(Condition cc) { return static_cast<Condition>(cc ^ 1); @@ -158,7 +159,7 @@ Immediate::Immediate(const char* s) { } -Immediate::Immediate(Label *internal_offset) { +Immediate::Immediate(Label* internal_offset) { x_ = reinterpret_cast<int32_t>(internal_offset); rmode_ = RelocInfo::INTERNAL_REFERENCE; } @@ -277,6 +278,22 @@ void Operand::set_modrm(int mod, Register rm) { } +void Operand::set_sib(ScaleFactor scale, Register index, Register base) { + ASSERT(len_ == 1); + ASSERT((scale & -4) == 0); + // Use SIB with no index register only for base esp. + ASSERT(!index.is(esp) || base.is(esp)); + buf_[1] = scale << 6 | index.code() << 3 | base.code(); + len_ = 2; +} + + +void Operand::set_disp8(int8_t disp) { + ASSERT(len_ == 1 || len_ == 2); + *reinterpret_cast<int8_t*>(&buf_[len_++]) = disp; +} + + void Operand::set_dispr(int32_t disp, RelocInfo::Mode rmode) { ASSERT(len_ == 1 || len_ == 2); int32_t* p = reinterpret_cast<int32_t*>(&buf_[len_]); diff --git a/deps/v8/src/ia32/assembler-ia32.cc b/deps/v8/src/ia32/assembler-ia32.cc index 3a2d3f82dd..434bf070fd 100644 --- a/deps/v8/src/ia32/assembler-ia32.cc +++ b/deps/v8/src/ia32/assembler-ia32.cc @@ -40,30 +40,8 @@ #include "macro-assembler.h" #include "serialize.h" -namespace v8 { namespace internal { - -// ----------------------------------------------------------------------------- -// Implementation of Register - -Register eax = { 0 }; -Register ecx = { 1 }; -Register edx = { 2 }; -Register ebx = { 3 }; -Register esp = { 4 }; -Register ebp = { 5 }; -Register esi = { 6 }; -Register edi = { 7 }; -Register no_reg = { -1 }; - -XMMRegister xmm0 = { 0 }; -XMMRegister xmm1 = { 1 }; -XMMRegister xmm2 = { 2 }; -XMMRegister xmm3 = { 3 }; -XMMRegister xmm4 = { 4 }; -XMMRegister xmm5 = { 5 }; -XMMRegister xmm6 = { 6 }; -XMMRegister xmm7 = { 7 }; - +namespace v8 { +namespace internal { // ----------------------------------------------------------------------------- // Implementation of CpuFeatures @@ -256,20 +234,6 @@ Operand::Operand(Register index, } -void Operand::set_sib(ScaleFactor scale, Register index, Register base) { - ASSERT(len_ == 1); - ASSERT((scale & -4) == 0); - buf_[1] = scale << 6 | index.code() << 3 | base.code(); - len_ = 2; -} - - -void Operand::set_disp8(int8_t disp) { - ASSERT(len_ == 1 || len_ == 2); - *reinterpret_cast<int8_t*>(&buf_[len_++]) = disp; -} - - bool Operand::is_reg(Register reg) const { return ((buf_[0] & 0xF8) == 0xC0) // addressing mode is register only. && ((buf_[0] & 0x07) == reg.code()); // register codes match. @@ -288,7 +252,7 @@ static void InitCoverageLog(); #endif // spare_buffer_ -static byte* spare_buffer_ = NULL; +byte* Assembler::spare_buffer_ = NULL; Assembler::Assembler(void* buffer, int buffer_size) { if (buffer == NULL) { diff --git a/deps/v8/src/ia32/assembler-ia32.h b/deps/v8/src/ia32/assembler-ia32.h index 4c995882e1..79f239d7ca 100644 --- a/deps/v8/src/ia32/assembler-ia32.h +++ b/deps/v8/src/ia32/assembler-ia32.h @@ -37,7 +37,8 @@ #ifndef V8_IA32_ASSEMBLER_IA32_H_ #define V8_IA32_ASSEMBLER_IA32_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // CPU Registers. // @@ -78,17 +79,15 @@ struct Register { int code_; }; -const int kNumRegisters = 8; - -extern Register eax; -extern Register ecx; -extern Register edx; -extern Register ebx; -extern Register esp; -extern Register ebp; -extern Register esi; -extern Register edi; -extern Register no_reg; +const Register eax = { 0 }; +const Register ecx = { 1 }; +const Register edx = { 2 }; +const Register ebx = { 3 }; +const Register esp = { 4 }; +const Register ebp = { 5 }; +const Register esi = { 6 }; +const Register edi = { 7 }; +const Register no_reg = { -1 }; struct XMMRegister { @@ -101,14 +100,14 @@ struct XMMRegister { int code_; }; -extern XMMRegister xmm0; -extern XMMRegister xmm1; -extern XMMRegister xmm2; -extern XMMRegister xmm3; -extern XMMRegister xmm4; -extern XMMRegister xmm5; -extern XMMRegister xmm6; -extern XMMRegister xmm7; +const XMMRegister xmm0 = { 0 }; +const XMMRegister xmm1 = { 1 }; +const XMMRegister xmm2 = { 2 }; +const XMMRegister xmm3 = { 3 }; +const XMMRegister xmm4 = { 4 }; +const XMMRegister xmm5 = { 5 }; +const XMMRegister xmm6 = { 6 }; +const XMMRegister xmm7 = { 7 }; enum Condition { // any value < 0 is considered no_condition @@ -815,6 +814,8 @@ class Assembler : public Malloced { int buffer_size_; // True if the assembler owns the buffer, false if buffer is external. bool own_buffer_; + // A previously allocated buffer of kMinimalBufferSize bytes, or NULL. + static byte* spare_buffer_; // code generation byte* pc_; // the program counter; moves forward diff --git a/deps/v8/src/ia32/builtins-ia32.cc b/deps/v8/src/ia32/builtins-ia32.cc index 5c7ba8e1ba..f65074bd49 100644 --- a/deps/v8/src/ia32/builtins-ia32.cc +++ b/deps/v8/src/ia32/builtins-ia32.cc @@ -29,7 +29,8 @@ #include "codegen-inl.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { #define __ ACCESS_MASM(masm) @@ -311,7 +312,7 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) { // Set expected number of arguments to zero (not changing eax). __ Set(ebx, Immediate(0)); - __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION); + __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR); __ jmp(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)), RelocInfo::CODE_TARGET); } diff --git a/deps/v8/src/ia32/codegen-ia32-inl.h b/deps/v8/src/ia32/codegen-ia32-inl.h new file mode 100644 index 0000000000..49c706d13b --- /dev/null +++ b/deps/v8/src/ia32/codegen-ia32-inl.h @@ -0,0 +1,46 @@ +// 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_CODEGEN_IA32_INL_H_ +#define V8_IA32_CODEGEN_IA32_INL_H_ + +namespace v8 { +namespace internal { + +#define __ ACCESS_MASM(masm_) + +// Platform-specific inline functions. + +void DeferredCode::Jump() { __ jmp(&entry_label_); } +void DeferredCode::Branch(Condition cc) { __ j(cc, &entry_label_); } + +#undef __ + +} } // namespace v8::internal + +#endif // V8_IA32_CODEGEN_IA32_INL_H_ diff --git a/deps/v8/src/ia32/codegen-ia32.cc b/deps/v8/src/ia32/codegen-ia32.cc index e260ab2d35..e9e40619d4 100644 --- a/deps/v8/src/ia32/codegen-ia32.cc +++ b/deps/v8/src/ia32/codegen-ia32.cc @@ -36,11 +36,41 @@ #include "runtime.h" #include "scopes.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { #define __ ACCESS_MASM(masm_) // ------------------------------------------------------------------------- +// Platform-specific DeferredCode functions. + +void DeferredCode::SaveRegisters() { + for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) { + int action = registers_[i]; + if (action == kPush) { + __ push(RegisterAllocator::ToRegister(i)); + } else if (action != kIgnore && (action & kSyncedFlag) == 0) { + __ mov(Operand(ebp, action), RegisterAllocator::ToRegister(i)); + } + } +} + + +void DeferredCode::RestoreRegisters() { + // Restore registers in reverse order due to the stack. + for (int i = RegisterAllocator::kNumRegisters - 1; i >= 0; i--) { + int action = registers_[i]; + if (action == kPush) { + __ pop(RegisterAllocator::ToRegister(i)); + } else if (action != kIgnore) { + action &= ~kSyncedFlag; + __ mov(RegisterAllocator::ToRegister(i), Operand(ebp, action)); + } + } +} + + +// ------------------------------------------------------------------------- // CodeGenState implementation. CodeGenState::CodeGenState(CodeGenerator* owner) @@ -72,7 +102,8 @@ CodeGenState::~CodeGenState() { // ------------------------------------------------------------------------- // CodeGenerator implementation -CodeGenerator::CodeGenerator(int buffer_size, Handle<Script> script, +CodeGenerator::CodeGenerator(int buffer_size, + Handle<Script> script, bool is_eval) : is_eval_(is_eval), script_(script), @@ -107,13 +138,25 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) { RegisterAllocator register_allocator(this); allocator_ = ®ister_allocator; ASSERT(frame_ == NULL); - frame_ = new VirtualFrame(this); + frame_ = new VirtualFrame(); set_in_spilled_code(false); // Adjust for function-level loop nesting. loop_nesting_ += fun->loop_nesting(); - { + JumpTarget::set_compiling_deferred_code(false); + +#ifdef DEBUG + if (strlen(FLAG_stop_at) > 0 && + fun->name()->IsEqualTo(CStrVector(FLAG_stop_at))) { + frame_->SpillAll(); + __ int3(); + } +#endif + + // New scope to get automatic timing calculation. + { // NOLINT + HistogramTimerScope codegen_timer(&Counters::code_generation); CodeGenState state(this); // Entry: @@ -125,19 +168,11 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) { allocator_->Initialize(); frame_->Enter(); -#ifdef DEBUG - if (strlen(FLAG_stop_at) > 0 && - fun->name()->IsEqualTo(CStrVector(FLAG_stop_at))) { - frame_->SpillAll(); - __ int3(); - } -#endif - // Allocate space for locals and initialize them. - frame_->AllocateStackSlots(scope_->num_stack_slots()); + frame_->AllocateStackSlots(); // Initialize the function return target after the locals are set // up, because it needs the expected frame height from the frame. - function_return_.Initialize(this, JumpTarget::BIDIRECTIONAL); + function_return_.set_direction(JumpTarget::BIDIRECTIONAL); function_return_is_shadowed_ = false; // Allocate the arguments object and copy the parameters into it. @@ -278,7 +313,7 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) { ASSERT(!function_return_is_shadowed_); CodeForReturnPosition(fun); frame_->PrepareForReturn(); - Result undefined(Factory::undefined_value(), this); + Result undefined(Factory::undefined_value()); if (function_return_.is_bound()) { function_return_.Jump(&undefined); } else { @@ -293,7 +328,7 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) { // control does not flow off the end of the body so we did not // compile an artificial return statement just above, and (b) there // are return statements in the body but (c) they are all shadowed. - Result return_value(this); + Result return_value; // Though this is a (possibly) backward block, the frames can // only differ on their top element. function_return_.Bind(&return_value, 1); @@ -313,10 +348,11 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) { DeleteFrame(); // Process any deferred code using the register allocator. - if (HasStackOverflow()) { - ClearDeferred(); - } else { + if (!HasStackOverflow()) { + HistogramTimerScope deferred_timer(&Counters::deferred_code_generation); + JumpTarget::set_compiling_deferred_code(true); ProcessDeferred(); + JumpTarget::set_compiling_deferred_code(false); } // There is no need to delete the register allocator, it is a @@ -382,26 +418,25 @@ Operand CodeGenerator::ContextSlotOperandCheckExtensions(Slot* slot, JumpTarget* slow) { ASSERT(slot->type() == Slot::CONTEXT); ASSERT(tmp.is_register()); - Result context(esi, this); + Register context = esi; for (Scope* s = scope(); s != slot->var()->scope(); s = s->outer_scope()) { if (s->num_heap_slots() > 0) { if (s->calls_eval()) { // Check that extension is NULL. - __ cmp(ContextOperand(context.reg(), Context::EXTENSION_INDEX), + __ cmp(ContextOperand(context, Context::EXTENSION_INDEX), Immediate(0)); slow->Branch(not_equal, not_taken); } - __ mov(tmp.reg(), ContextOperand(context.reg(), Context::CLOSURE_INDEX)); + __ mov(tmp.reg(), ContextOperand(context, Context::CLOSURE_INDEX)); __ mov(tmp.reg(), FieldOperand(tmp.reg(), JSFunction::kContextOffset)); - context = tmp; + context = tmp.reg(); } } // Check that last extension is NULL. - __ cmp(ContextOperand(context.reg(), Context::EXTENSION_INDEX), - Immediate(0)); + __ cmp(ContextOperand(context, Context::EXTENSION_INDEX), Immediate(0)); slow->Branch(not_equal, not_taken); - __ mov(tmp.reg(), ContextOperand(context.reg(), Context::FCONTEXT_INDEX)); + __ mov(tmp.reg(), ContextOperand(context, Context::FCONTEXT_INDEX)); return ContextOperand(tmp.reg(), slot->index()); } @@ -461,14 +496,14 @@ void CodeGenerator::Load(Expression* x, TypeofState typeof_state) { int original_height = frame_->height(); #endif ASSERT(!in_spilled_code()); - JumpTarget true_target(this); - JumpTarget false_target(this); + JumpTarget true_target; + JumpTarget false_target; ControlDestination dest(&true_target, &false_target, true); LoadCondition(x, typeof_state, &dest, false); if (dest.false_was_fall_through()) { // The false target was just bound. - JumpTarget loaded(this); + JumpTarget loaded; frame_->Push(Factory::false_value()); // There may be dangling jumps to the true target. if (true_target.is_linked()) { @@ -481,7 +516,7 @@ void CodeGenerator::Load(Expression* x, TypeofState typeof_state) { } else if (dest.is_used()) { // There is true, and possibly false, control flow (with true as // the fall through). - JumpTarget loaded(this); + JumpTarget loaded; frame_->Push(Factory::true_value()); if (false_target.is_linked()) { loaded.Jump(); @@ -497,7 +532,7 @@ void CodeGenerator::Load(Expression* x, TypeofState typeof_state) { // short-circuited boolean operators). ASSERT(has_valid_frame()); if (true_target.is_linked() || false_target.is_linked()) { - JumpTarget loaded(this); + JumpTarget loaded; loaded.Jump(); // Don't lose the current TOS. if (true_target.is_linked()) { true_target.Bind(); @@ -771,39 +806,35 @@ const char* GenericBinaryOpStub::GetName() { } -// A deferred code class implementing binary operations on likely smis. -// This class generates both inline code and deferred code. -// The fastest path is implemented inline. Deferred code calls -// the GenericBinaryOpStub stub for slow cases. +// Call the specialized stub for a binary operation. class DeferredInlineBinaryOperation: public DeferredCode { public: - DeferredInlineBinaryOperation(CodeGenerator* generator, - Token::Value op, - OverwriteMode mode, - GenericBinaryFlags flags) - : DeferredCode(generator), stub_(op, mode, flags), op_(op) { + DeferredInlineBinaryOperation(Token::Value op, + Register dst, + Register left, + Register right, + OverwriteMode mode) + : op_(op), dst_(dst), left_(left), right_(right), mode_(mode) { set_comment("[ DeferredInlineBinaryOperation"); } - // Consumes its arguments, left and right, leaving them invalid. - Result GenerateInlineCode(Result* left, Result* right); - virtual void Generate(); private: - GenericBinaryOpStub stub_; Token::Value op_; + Register dst_; + Register left_; + Register right_; + OverwriteMode mode_; }; void DeferredInlineBinaryOperation::Generate() { - Result left(generator()); - Result right(generator()); - enter()->Bind(&left, &right); - generator()->frame()->Push(&left); - generator()->frame()->Push(&right); - Result answer = generator()->frame()->CallStub(&stub_, 2); - exit_.Jump(&answer); + __ push(left_); + __ push(right_); + GenericBinaryOpStub stub(op_, mode_, SMI_CODE_INLINED); + __ CallStub(&stub); + if (!dst_.is(eax)) __ mov(dst_, eax); } @@ -853,7 +884,7 @@ void CodeGenerator::GenericBinaryOperation(Token::Value op, if (left_is_string || right_is_string) { frame_->Push(&left); frame_->Push(&right); - Result answer(this); + Result answer; if (left_is_string) { if (right_is_string) { // TODO(lrn): if (left.is_constant() && right.is_constant()) @@ -999,31 +1030,342 @@ bool CodeGenerator::FoldConstantSmis(Token::Value op, int left, int right) { } +// Implements a binary operation using a deferred code object and some +// inline code to operate on smis quickly. void CodeGenerator::LikelySmiBinaryOperation(Token::Value op, Result* left, Result* right, OverwriteMode overwrite_mode) { - // Implements a binary operation using a deferred code object - // and some inline code to operate on smis quickly. + // Special handling of div and mod because they use fixed registers. + if (op == Token::DIV || op == Token::MOD) { + // We need eax as the quotient register, edx as the remainder + // register, neither left nor right in eax or edx, and left copied + // to eax. + Result quotient; + Result remainder; + bool left_is_in_eax = false; + // Step 1: get eax for quotient. + if ((left->is_register() && left->reg().is(eax)) || + (right->is_register() && right->reg().is(eax))) { + // One or both is in eax. Use a fresh non-edx register for + // them. + Result fresh = allocator_->Allocate(); + ASSERT(fresh.is_valid()); + if (fresh.reg().is(edx)) { + remainder = fresh; + fresh = allocator_->Allocate(); + ASSERT(fresh.is_valid()); + } + if (left->is_register() && left->reg().is(eax)) { + quotient = *left; + *left = fresh; + left_is_in_eax = true; + } + if (right->is_register() && right->reg().is(eax)) { + quotient = *right; + *right = fresh; + } + __ mov(fresh.reg(), eax); + } else { + // Neither left nor right is in eax. + quotient = allocator_->Allocate(eax); + } + ASSERT(quotient.is_register() && quotient.reg().is(eax)); + ASSERT(!(left->is_register() && left->reg().is(eax))); + ASSERT(!(right->is_register() && right->reg().is(eax))); + + // Step 2: get edx for remainder if necessary. + if (!remainder.is_valid()) { + if ((left->is_register() && left->reg().is(edx)) || + (right->is_register() && right->reg().is(edx))) { + Result fresh = allocator_->Allocate(); + ASSERT(fresh.is_valid()); + if (left->is_register() && left->reg().is(edx)) { + remainder = *left; + *left = fresh; + } + if (right->is_register() && right->reg().is(edx)) { + remainder = *right; + *right = fresh; + } + __ mov(fresh.reg(), edx); + } else { + // Neither left nor right is in edx. + remainder = allocator_->Allocate(edx); + } + } + ASSERT(remainder.is_register() && remainder.reg().is(edx)); + ASSERT(!(left->is_register() && left->reg().is(edx))); + ASSERT(!(right->is_register() && right->reg().is(edx))); + + left->ToRegister(); + right->ToRegister(); + frame_->Spill(eax); + frame_->Spill(edx); + + // Check that left and right are smi tagged. + DeferredInlineBinaryOperation* deferred = + new DeferredInlineBinaryOperation(op, + (op == Token::DIV) ? eax : edx, + left->reg(), + right->reg(), + overwrite_mode); + if (left->reg().is(right->reg())) { + __ test(left->reg(), Immediate(kSmiTagMask)); + } else { + // Use the quotient register as a scratch for the tag check. + if (!left_is_in_eax) __ mov(eax, left->reg()); + left_is_in_eax = false; // About to destroy the value in eax. + __ or_(eax, Operand(right->reg())); + ASSERT(kSmiTag == 0); // Adjust test if not the case. + __ test(eax, Immediate(kSmiTagMask)); + } + deferred->Branch(not_zero); + + if (!left_is_in_eax) __ mov(eax, left->reg()); + // Sign extend eax into edx:eax. + __ cdq(); + // Check for 0 divisor. + __ test(right->reg(), Operand(right->reg())); + deferred->Branch(zero); + // Divide edx:eax by the right operand. + __ idiv(right->reg()); + + // Complete the operation. + if (op == Token::DIV) { + // Check for negative zero result. If result is zero, and divisor + // is negative, return a floating point negative zero. The + // virtual frame is unchanged in this block, so local control flow + // can use a Label rather than a JumpTarget. + Label non_zero_result; + __ test(left->reg(), Operand(left->reg())); + __ j(not_zero, &non_zero_result); + __ test(right->reg(), Operand(right->reg())); + deferred->Branch(negative); + __ bind(&non_zero_result); + // Check for the corner case of dividing the most negative smi by + // -1. We cannot use the overflow flag, since it is not set by + // idiv instruction. + ASSERT(kSmiTag == 0 && kSmiTagSize == 1); + __ cmp(eax, 0x40000000); + deferred->Branch(equal); + // Check that the remainder is zero. + __ test(edx, Operand(edx)); + deferred->Branch(not_zero); + // Tag the result and store it in the quotient register. + ASSERT(kSmiTagSize == times_2); // adjust code if not the case + __ lea(eax, Operand(eax, eax, times_1, kSmiTag)); + deferred->BindExit(); + left->Unuse(); + right->Unuse(); + frame_->Push("ient); + } else { + ASSERT(op == Token::MOD); + // Check for a negative zero result. If the result is zero, and + // the dividend is negative, return a floating point negative + // zero. The frame is unchanged in this block, so local control + // flow can use a Label rather than a JumpTarget. + Label non_zero_result; + __ test(edx, Operand(edx)); + __ j(not_zero, &non_zero_result, taken); + __ test(left->reg(), Operand(left->reg())); + deferred->Branch(negative); + __ bind(&non_zero_result); + deferred->BindExit(); + left->Unuse(); + right->Unuse(); + frame_->Push(&remainder); + } + return; + } + + // Special handling of shift operations because they use fixed + // registers. + if (op == Token::SHL || op == Token::SHR || op == Token::SAR) { + // Move left out of ecx if necessary. + if (left->is_register() && left->reg().is(ecx)) { + *left = allocator_->Allocate(); + ASSERT(left->is_valid()); + __ mov(left->reg(), ecx); + } + right->ToRegister(ecx); + left->ToRegister(); + ASSERT(left->is_register() && !left->reg().is(ecx)); + ASSERT(right->is_register() && right->reg().is(ecx)); + + // We will modify right, it must be spilled. + frame_->Spill(ecx); + + // Use a fresh answer register to avoid spilling the left operand. + Result answer = allocator_->Allocate(); + ASSERT(answer.is_valid()); + // Check that both operands are smis using the answer register as a + // temporary. + DeferredInlineBinaryOperation* deferred = + new DeferredInlineBinaryOperation(op, + answer.reg(), + left->reg(), + ecx, + overwrite_mode); + __ mov(answer.reg(), left->reg()); + __ or_(answer.reg(), Operand(ecx)); + __ test(answer.reg(), Immediate(kSmiTagMask)); + deferred->Branch(not_zero); + + // Untag both operands. + __ mov(answer.reg(), left->reg()); + __ sar(answer.reg(), kSmiTagSize); + __ sar(ecx, kSmiTagSize); + // Perform the operation. + switch (op) { + case Token::SAR: + __ sar(answer.reg()); + // No checks of result necessary + break; + case Token::SHR: { + Label result_ok; + __ shr(answer.reg()); + // Check that the *unsigned* result fits in a smi. Neither of + // the two high-order bits can be set: + // * 0x80000000: high bit would be lost when smi tagging. + // * 0x40000000: this number would convert to negative when smi + // tagging. + // These two cases can only happen with shifts by 0 or 1 when + // handed a valid smi. If the answer cannot be represented by a + // smi, restore the left and right arguments, and jump to slow + // case. The low bit of the left argument may be lost, but only + // in a case where it is dropped anyway. + __ test(answer.reg(), Immediate(0xc0000000)); + __ j(zero, &result_ok); + ASSERT(kSmiTag == 0); + __ shl(ecx, kSmiTagSize); + deferred->Jump(); + __ bind(&result_ok); + break; + } + case Token::SHL: { + Label result_ok; + __ shl(answer.reg()); + // Check that the *signed* result fits in a smi. + __ cmp(answer.reg(), 0xc0000000); + __ j(positive, &result_ok); + ASSERT(kSmiTag == 0); + __ shl(ecx, kSmiTagSize); + deferred->Jump(); + __ bind(&result_ok); + break; + } + default: + UNREACHABLE(); + } + // Smi-tag the result in answer. + ASSERT(kSmiTagSize == 1); // Adjust code if not the case. + __ lea(answer.reg(), + Operand(answer.reg(), answer.reg(), times_1, kSmiTag)); + deferred->BindExit(); + left->Unuse(); + right->Unuse(); + frame_->Push(&answer); + return; + } + + // Handle the other binary operations. + left->ToRegister(); + right->ToRegister(); + // A newly allocated register answer is used to hold the answer. The + // registers containing left and right are not modified so they don't + // need to be spilled in the fast case. + Result answer = allocator_->Allocate(); + ASSERT(answer.is_valid()); + + // Perform the smi tag check. DeferredInlineBinaryOperation* deferred = - new DeferredInlineBinaryOperation(this, op, overwrite_mode, - SMI_CODE_INLINED); - // Generate the inline code that handles some smi operations, - // and jumps to the deferred code for everything else. - Result answer = deferred->GenerateInlineCode(left, right); - deferred->BindExit(&answer); + new DeferredInlineBinaryOperation(op, + answer.reg(), + left->reg(), + right->reg(), + overwrite_mode); + if (left->reg().is(right->reg())) { + __ test(left->reg(), Immediate(kSmiTagMask)); + } else { + __ mov(answer.reg(), left->reg()); + __ or_(answer.reg(), Operand(right->reg())); + ASSERT(kSmiTag == 0); // Adjust test if not the case. + __ test(answer.reg(), Immediate(kSmiTagMask)); + } + deferred->Branch(not_zero); + __ mov(answer.reg(), left->reg()); + switch (op) { + case Token::ADD: + __ add(answer.reg(), Operand(right->reg())); // Add optimistically. + deferred->Branch(overflow); + break; + + case Token::SUB: + __ sub(answer.reg(), Operand(right->reg())); // Subtract optimistically. + deferred->Branch(overflow); + break; + + case Token::MUL: { + // If the smi tag is 0 we can just leave the tag on one operand. + ASSERT(kSmiTag == 0); // Adjust code below if not the case. + // Remove smi tag from the left operand (but keep sign). + // Left-hand operand has been copied into answer. + __ sar(answer.reg(), kSmiTagSize); + // Do multiplication of smis, leaving result in answer. + __ imul(answer.reg(), Operand(right->reg())); + // Go slow on overflows. + deferred->Branch(overflow); + // Check for negative zero result. If product is zero, and one + // argument is negative, go to slow case. The frame is unchanged + // in this block, so local control flow can use a Label rather + // than a JumpTarget. + Label non_zero_result; + __ test(answer.reg(), Operand(answer.reg())); + __ j(not_zero, &non_zero_result, taken); + __ mov(answer.reg(), left->reg()); + __ or_(answer.reg(), Operand(right->reg())); + deferred->Branch(negative); + __ xor_(answer.reg(), Operand(answer.reg())); // Positive 0 is correct. + __ bind(&non_zero_result); + break; + } + + case Token::BIT_OR: + __ or_(answer.reg(), Operand(right->reg())); + break; + + case Token::BIT_AND: + __ and_(answer.reg(), Operand(right->reg())); + break; + + case Token::BIT_XOR: + __ xor_(answer.reg(), Operand(right->reg())); + break; + + default: + UNREACHABLE(); + break; + } + deferred->BindExit(); + left->Unuse(); + right->Unuse(); frame_->Push(&answer); } +// Call the appropriate binary operation stub to compute src op value +// and leave the result in dst. class DeferredInlineSmiOperation: public DeferredCode { public: - DeferredInlineSmiOperation(CodeGenerator* generator, - Token::Value op, + DeferredInlineSmiOperation(Token::Value op, + Register dst, + Register src, Smi* value, OverwriteMode overwrite_mode) - : DeferredCode(generator), - op_(op), + : op_(op), + dst_(dst), + src_(src), value_(value), overwrite_mode_(overwrite_mode) { set_comment("[ DeferredInlineSmiOperation"); @@ -1033,31 +1375,35 @@ class DeferredInlineSmiOperation: public DeferredCode { private: Token::Value op_; + Register dst_; + Register src_; Smi* value_; OverwriteMode overwrite_mode_; }; void DeferredInlineSmiOperation::Generate() { - Result left(generator()); - enter()->Bind(&left); - generator()->frame()->Push(&left); - generator()->frame()->Push(value_); - GenericBinaryOpStub igostub(op_, overwrite_mode_, SMI_CODE_INLINED); - Result answer = generator()->frame()->CallStub(&igostub, 2); - exit_.Jump(&answer); + __ push(src_); + __ push(Immediate(value_)); + GenericBinaryOpStub stub(op_, overwrite_mode_, SMI_CODE_INLINED); + __ CallStub(&stub); + if (!dst_.is(eax)) __ mov(dst_, eax); } +// Call the appropriate binary operation stub to compute value op src +// and leave the result in dst. class DeferredInlineSmiOperationReversed: public DeferredCode { public: - DeferredInlineSmiOperationReversed(CodeGenerator* generator, - Token::Value op, + DeferredInlineSmiOperationReversed(Token::Value op, + Register dst, Smi* value, + Register src, OverwriteMode overwrite_mode) - : DeferredCode(generator), - op_(op), + : op_(op), + dst_(dst), value_(value), + src_(src), overwrite_mode_(overwrite_mode) { set_comment("[ DeferredInlineSmiOperationReversed"); } @@ -1066,36 +1412,38 @@ class DeferredInlineSmiOperationReversed: public DeferredCode { private: Token::Value op_; + Register dst_; Smi* value_; + Register src_; OverwriteMode overwrite_mode_; }; void DeferredInlineSmiOperationReversed::Generate() { - Result right(generator()); - enter()->Bind(&right); - generator()->frame()->Push(value_); - generator()->frame()->Push(&right); + __ push(Immediate(value_)); + __ push(src_); GenericBinaryOpStub igostub(op_, overwrite_mode_, SMI_CODE_INLINED); - Result answer = generator()->frame()->CallStub(&igostub, 2); - exit_.Jump(&answer); + __ CallStub(&igostub); + if (!dst_.is(eax)) __ mov(dst_, eax); } +// The result of src + value is in dst. It either overflowed or was not +// smi tagged. Undo the speculative addition and call the appropriate +// specialized stub for add. The result is left in dst. class DeferredInlineSmiAdd: public DeferredCode { public: - DeferredInlineSmiAdd(CodeGenerator* generator, + DeferredInlineSmiAdd(Register dst, Smi* value, OverwriteMode overwrite_mode) - : DeferredCode(generator), - value_(value), - overwrite_mode_(overwrite_mode) { + : dst_(dst), value_(value), overwrite_mode_(overwrite_mode) { set_comment("[ DeferredInlineSmiAdd"); } virtual void Generate(); private: + Register dst_; Smi* value_; OverwriteMode overwrite_mode_; }; @@ -1103,33 +1451,31 @@ class DeferredInlineSmiAdd: public DeferredCode { void DeferredInlineSmiAdd::Generate() { // Undo the optimistic add operation and call the shared stub. - Result left(generator()); // Initially left + value_. - enter()->Bind(&left); - left.ToRegister(); - generator()->frame()->Spill(left.reg()); - __ sub(Operand(left.reg()), Immediate(value_)); - generator()->frame()->Push(&left); - generator()->frame()->Push(value_); + __ sub(Operand(dst_), Immediate(value_)); + __ push(dst_); + __ push(Immediate(value_)); GenericBinaryOpStub igostub(Token::ADD, overwrite_mode_, SMI_CODE_INLINED); - Result answer = generator()->frame()->CallStub(&igostub, 2); - exit_.Jump(&answer); + __ CallStub(&igostub); + if (!dst_.is(eax)) __ mov(dst_, eax); } +// The result of value + src is in dst. It either overflowed or was not +// smi tagged. Undo the speculative addition and call the appropriate +// specialized stub for add. The result is left in dst. class DeferredInlineSmiAddReversed: public DeferredCode { public: - DeferredInlineSmiAddReversed(CodeGenerator* generator, + DeferredInlineSmiAddReversed(Register dst, Smi* value, OverwriteMode overwrite_mode) - : DeferredCode(generator), - value_(value), - overwrite_mode_(overwrite_mode) { + : dst_(dst), value_(value), overwrite_mode_(overwrite_mode) { set_comment("[ DeferredInlineSmiAddReversed"); } virtual void Generate(); private: + Register dst_; Smi* value_; OverwriteMode overwrite_mode_; }; @@ -1137,33 +1483,32 @@ class DeferredInlineSmiAddReversed: public DeferredCode { void DeferredInlineSmiAddReversed::Generate() { // Undo the optimistic add operation and call the shared stub. - Result right(generator()); // Initially value_ + right. - enter()->Bind(&right); - right.ToRegister(); - generator()->frame()->Spill(right.reg()); - __ sub(Operand(right.reg()), Immediate(value_)); - generator()->frame()->Push(value_); - generator()->frame()->Push(&right); + __ sub(Operand(dst_), Immediate(value_)); + __ push(Immediate(value_)); + __ push(dst_); GenericBinaryOpStub igostub(Token::ADD, overwrite_mode_, SMI_CODE_INLINED); - Result answer = generator()->frame()->CallStub(&igostub, 2); - exit_.Jump(&answer); + __ CallStub(&igostub); + if (!dst_.is(eax)) __ mov(dst_, eax); } +// The result of src - value is in dst. It either overflowed or was not +// smi tagged. Undo the speculative subtraction and call the +// appropriate specialized stub for subtract. The result is left in +// dst. class DeferredInlineSmiSub: public DeferredCode { public: - DeferredInlineSmiSub(CodeGenerator* generator, + DeferredInlineSmiSub(Register dst, Smi* value, OverwriteMode overwrite_mode) - : DeferredCode(generator), - value_(value), - overwrite_mode_(overwrite_mode) { + : dst_(dst), value_(value), overwrite_mode_(overwrite_mode) { set_comment("[ DeferredInlineSmiSub"); } virtual void Generate(); private: + Register dst_; Smi* value_; OverwriteMode overwrite_mode_; }; @@ -1171,47 +1516,12 @@ class DeferredInlineSmiSub: public DeferredCode { void DeferredInlineSmiSub::Generate() { // Undo the optimistic sub operation and call the shared stub. - Result left(generator()); // Initially left - value_. - enter()->Bind(&left); - left.ToRegister(); - generator()->frame()->Spill(left.reg()); - __ add(Operand(left.reg()), Immediate(value_)); - generator()->frame()->Push(&left); - generator()->frame()->Push(value_); + __ add(Operand(dst_), Immediate(value_)); + __ push(dst_); + __ push(Immediate(value_)); GenericBinaryOpStub igostub(Token::SUB, overwrite_mode_, SMI_CODE_INLINED); - Result answer = generator()->frame()->CallStub(&igostub, 2); - exit_.Jump(&answer); -} - - -class DeferredInlineSmiSubReversed: public DeferredCode { - public: - DeferredInlineSmiSubReversed(CodeGenerator* generator, - Smi* value, - OverwriteMode overwrite_mode) - : DeferredCode(generator), - value_(value), - overwrite_mode_(overwrite_mode) { - set_comment("[ DeferredInlineSmiSubReversed"); - } - - virtual void Generate(); - - private: - Smi* value_; - OverwriteMode overwrite_mode_; -}; - - -void DeferredInlineSmiSubReversed::Generate() { - // Call the shared stub. - Result right(generator()); - enter()->Bind(&right); - generator()->frame()->Push(value_); - generator()->frame()->Push(&right); - GenericBinaryOpStub igostub(Token::SUB, overwrite_mode_, SMI_CODE_INLINED); - Result answer = generator()->frame()->CallStub(&igostub, 2); - exit_.Jump(&answer); + __ CallStub(&igostub); + if (!dst_.is(eax)) __ mov(dst_, eax); } @@ -1229,7 +1539,7 @@ void CodeGenerator::ConstantSmiBinaryOperation(Token::Value op, // TODO(199): Optimize some special cases of operations involving a // smi literal (multiply by 2, shift by 0, etc.). if (IsUnsafeSmi(value)) { - Result unsafe_operand(value, this); + Result unsafe_operand(value); if (reversed) { LikelySmiBinaryOperation(op, &unsafe_operand, operand, overwrite_mode); @@ -1247,134 +1557,162 @@ void CodeGenerator::ConstantSmiBinaryOperation(Token::Value op, switch (op) { case Token::ADD: { + operand->ToRegister(); + frame_->Spill(operand->reg()); + + // Optimistically add. Call the specialized add stub if the + // result is not a smi or overflows. DeferredCode* deferred = NULL; if (reversed) { - deferred = new DeferredInlineSmiAddReversed(this, smi_value, + deferred = new DeferredInlineSmiAddReversed(operand->reg(), + smi_value, overwrite_mode); } else { - deferred = new DeferredInlineSmiAdd(this, smi_value, overwrite_mode); + deferred = new DeferredInlineSmiAdd(operand->reg(), + smi_value, + overwrite_mode); } - operand->ToRegister(); - frame_->Spill(operand->reg()); __ add(Operand(operand->reg()), Immediate(value)); - deferred->enter()->Branch(overflow, operand, not_taken); + deferred->Branch(overflow); __ test(operand->reg(), Immediate(kSmiTagMask)); - deferred->enter()->Branch(not_zero, operand, not_taken); - deferred->BindExit(operand); + deferred->Branch(not_zero); + deferred->BindExit(); frame_->Push(operand); break; } case Token::SUB: { DeferredCode* deferred = NULL; - Result answer(this); // Only allocate a new register if reversed. + Result answer; // Only allocate a new register if reversed. if (reversed) { + // The reversed case is only hit when the right operand is not a + // constant. + ASSERT(operand->is_register()); answer = allocator()->Allocate(); ASSERT(answer.is_valid()); - deferred = new DeferredInlineSmiSubReversed(this, - smi_value, - overwrite_mode); __ Set(answer.reg(), Immediate(value)); - // We are in the reversed case so they can't both be Smi constants. - ASSERT(operand->is_register()); + deferred = new DeferredInlineSmiOperationReversed(op, + answer.reg(), + smi_value, + operand->reg(), + overwrite_mode); __ sub(answer.reg(), Operand(operand->reg())); } else { operand->ToRegister(); frame_->Spill(operand->reg()); - deferred = new DeferredInlineSmiSub(this, + answer = *operand; + deferred = new DeferredInlineSmiSub(operand->reg(), smi_value, overwrite_mode); __ sub(Operand(operand->reg()), Immediate(value)); - answer = *operand; } - deferred->enter()->Branch(overflow, operand, not_taken); + deferred->Branch(overflow); __ test(answer.reg(), Immediate(kSmiTagMask)); - deferred->enter()->Branch(not_zero, operand, not_taken); + deferred->Branch(not_zero); + deferred->BindExit(); operand->Unuse(); - deferred->BindExit(&answer); frame_->Push(&answer); break; } - case Token::SAR: { + case Token::SAR: if (reversed) { - Result constant_operand(value, this); + Result constant_operand(value); LikelySmiBinaryOperation(op, &constant_operand, operand, overwrite_mode); } else { // Only the least significant 5 bits of the shift value are used. // In the slow case, this masking is done inside the runtime call. int shift_value = int_value & 0x1f; - DeferredCode* deferred = - new DeferredInlineSmiOperation(this, Token::SAR, smi_value, - overwrite_mode); operand->ToRegister(); + frame_->Spill(operand->reg()); + DeferredInlineSmiOperation* deferred = + new DeferredInlineSmiOperation(op, + operand->reg(), + operand->reg(), + smi_value, + overwrite_mode); __ test(operand->reg(), Immediate(kSmiTagMask)); - deferred->enter()->Branch(not_zero, operand, not_taken); + deferred->Branch(not_zero); if (shift_value > 0) { - frame_->Spill(operand->reg()); __ sar(operand->reg(), shift_value); __ and_(operand->reg(), ~kSmiTagMask); } - deferred->BindExit(operand); + deferred->BindExit(); frame_->Push(operand); } break; - } - case Token::SHR: { + case Token::SHR: if (reversed) { - Result constant_operand(value, this); + Result constant_operand(value); LikelySmiBinaryOperation(op, &constant_operand, operand, overwrite_mode); } else { // Only the least significant 5 bits of the shift value are used. // In the slow case, this masking is done inside the runtime call. int shift_value = int_value & 0x1f; - DeferredCode* deferred = - new DeferredInlineSmiOperation(this, Token::SHR, smi_value, - overwrite_mode); operand->ToRegister(); - __ test(operand->reg(), Immediate(kSmiTagMask)); - deferred->enter()->Branch(not_zero, operand, not_taken); Result answer = allocator()->Allocate(); ASSERT(answer.is_valid()); + DeferredInlineSmiOperation* deferred = + new DeferredInlineSmiOperation(op, + answer.reg(), + operand->reg(), + smi_value, + overwrite_mode); + __ test(operand->reg(), Immediate(kSmiTagMask)); + deferred->Branch(not_zero); __ mov(answer.reg(), operand->reg()); __ sar(answer.reg(), kSmiTagSize); __ shr(answer.reg(), shift_value); // A negative Smi shifted right two is in the positive Smi range. if (shift_value < 2) { __ test(answer.reg(), Immediate(0xc0000000)); - deferred->enter()->Branch(not_zero, operand, not_taken); + deferred->Branch(not_zero); } operand->Unuse(); ASSERT(kSmiTagSize == times_2); // Adjust the code if not true. __ lea(answer.reg(), Operand(answer.reg(), answer.reg(), times_1, kSmiTag)); - deferred->BindExit(&answer); + deferred->BindExit(); frame_->Push(&answer); } break; - } - case Token::SHL: { + case Token::SHL: if (reversed) { - Result constant_operand(value, this); + Result constant_operand(value); LikelySmiBinaryOperation(op, &constant_operand, operand, overwrite_mode); } else { // Only the least significant 5 bits of the shift value are used. // In the slow case, this masking is done inside the runtime call. int shift_value = int_value & 0x1f; - DeferredCode* deferred = - new DeferredInlineSmiOperation(this, Token::SHL, smi_value, - overwrite_mode); operand->ToRegister(); - __ test(operand->reg(), Immediate(kSmiTagMask)); - deferred->enter()->Branch(not_zero, operand, not_taken); - if (shift_value != 0) { + if (shift_value == 0) { + DeferredInlineSmiOperation* deferred = + new DeferredInlineSmiOperation(op, + operand->reg(), + operand->reg(), + smi_value, + overwrite_mode); + __ test(operand->reg(), Immediate(kSmiTagMask)); + deferred->Branch(not_zero); + deferred->BindExit(); + frame_->Push(operand); + } else { + // Use a fresh temporary for nonzero shift values. Result answer = allocator()->Allocate(); ASSERT(answer.is_valid()); + DeferredInlineSmiOperation* deferred = + new DeferredInlineSmiOperation(op, + answer.reg(), + operand->reg(), + smi_value, + overwrite_mode); + __ test(operand->reg(), Immediate(kSmiTagMask)); + deferred->Branch(not_zero); __ mov(answer.reg(), operand->reg()); ASSERT(kSmiTag == 0); // adjust code if not the case // We do no shifts, only the Smi conversion, if shift_value is 1. @@ -1382,35 +1720,37 @@ void CodeGenerator::ConstantSmiBinaryOperation(Token::Value op, __ shl(answer.reg(), shift_value - 1); } // Convert int result to Smi, checking that it is in int range. - ASSERT(kSmiTagSize == times_2); // adjust code if not the case + ASSERT(kSmiTagSize == 1); // adjust code if not the case __ add(answer.reg(), Operand(answer.reg())); - deferred->enter()->Branch(overflow, operand, not_taken); + deferred->Branch(overflow); + deferred->BindExit(); operand->Unuse(); - deferred->BindExit(&answer); frame_->Push(&answer); - } else { - deferred->BindExit(operand); - frame_->Push(operand); } } break; - } case Token::BIT_OR: case Token::BIT_XOR: case Token::BIT_AND: { + operand->ToRegister(); + frame_->Spill(operand->reg()); DeferredCode* deferred = NULL; if (reversed) { - deferred = new DeferredInlineSmiOperationReversed(this, op, smi_value, + deferred = new DeferredInlineSmiOperationReversed(op, + operand->reg(), + smi_value, + operand->reg(), overwrite_mode); } else { - deferred = new DeferredInlineSmiOperation(this, op, smi_value, + deferred = new DeferredInlineSmiOperation(op, + operand->reg(), + operand->reg(), + smi_value, overwrite_mode); } - operand->ToRegister(); __ test(operand->reg(), Immediate(kSmiTagMask)); - deferred->enter()->Branch(not_zero, operand, not_taken); - frame_->Spill(operand->reg()); + deferred->Branch(not_zero); if (op == Token::BIT_AND) { __ and_(Operand(operand->reg()), Immediate(value)); } else if (op == Token::BIT_XOR) { @@ -1423,13 +1763,13 @@ void CodeGenerator::ConstantSmiBinaryOperation(Token::Value op, __ or_(Operand(operand->reg()), Immediate(value)); } } - deferred->BindExit(operand); + deferred->BindExit(); frame_->Push(operand); break; } default: { - Result constant_operand(value, this); + Result constant_operand(value); if (reversed) { LikelySmiBinaryOperation(op, &constant_operand, operand, overwrite_mode); @@ -1478,8 +1818,8 @@ void CodeGenerator::Comparison(Condition cc, // Strict only makes sense for equality comparisons. ASSERT(!strict || cc == equal); - Result left_side(this); - Result right_side(this); + Result left_side; + Result right_side; // Implement '>' and '<=' by reversal to obtain ECMA-262 conversion order. if (cc == greater || cc == less_equal) { cc = ReverseCondition(cc); @@ -1534,7 +1874,7 @@ void CodeGenerator::Comparison(Condition cc, // where both sides are Smis. left_side.ToRegister(); ASSERT(left_side.is_valid()); - JumpTarget is_smi(this); + JumpTarget is_smi; __ test(left_side.reg(), Immediate(kSmiTagMask)); is_smi.Branch(zero, &left_side, &right_side, taken); @@ -1605,7 +1945,7 @@ void CodeGenerator::Comparison(Condition cc, (right_side.is_constant() && !right_side.handle()->IsSmi()); left_side.ToRegister(); right_side.ToRegister(); - JumpTarget is_smi(this); + JumpTarget is_smi; if (!known_non_smi) { // Check for the smi case. Result temp = allocator_->Allocate(); @@ -1645,12 +1985,14 @@ void CodeGenerator::Comparison(Condition cc, class CallFunctionStub: public CodeStub { public: - explicit CallFunctionStub(int argc) : argc_(argc) { } + CallFunctionStub(int argc, InLoopFlag in_loop) + : argc_(argc), in_loop_(in_loop) { } void Generate(MacroAssembler* masm); private: int argc_; + InLoopFlag in_loop_; #ifdef DEBUG void Print() { PrintF("CallFunctionStub (args %d)\n", argc_); } @@ -1658,6 +2000,7 @@ class CallFunctionStub: public CodeStub { Major MajorKey() { return CallFunction; } int MinorKey() { return argc_; } + InLoopFlag InLoop() { return in_loop_; } }; @@ -1675,7 +2018,8 @@ void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args, CodeForSourcePosition(position); // Use the shared code stub to call the function. - CallFunctionStub call_function(arg_count); + InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP; + CallFunctionStub call_function(arg_count, in_loop); Result answer = frame_->CallStub(&call_function, arg_count + 1); // Restore context and replace function on the stack with the // result of the stub invocation. @@ -1686,8 +2030,7 @@ void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args, class DeferredStackCheck: public DeferredCode { public: - explicit DeferredStackCheck(CodeGenerator* generator) - : DeferredCode(generator) { + DeferredStackCheck() { set_comment("[ DeferredStackCheck"); } @@ -1696,21 +2039,18 @@ class DeferredStackCheck: public DeferredCode { void DeferredStackCheck::Generate() { - enter()->Bind(); StackCheckStub stub; - Result ignored = generator()->frame()->CallStub(&stub, 0); - ignored.Unuse(); - exit_.Jump(); + __ CallStub(&stub); } void CodeGenerator::CheckStack() { if (FLAG_check_stack) { - DeferredStackCheck* deferred = new DeferredStackCheck(this); + DeferredStackCheck* deferred = new DeferredStackCheck; ExternalReference stack_guard_limit = ExternalReference::address_of_stack_guard_limit(); __ cmp(esp, Operand::StaticVariable(stack_guard_limit)); - deferred->enter()->Branch(below, not_taken); + deferred->Branch(below); deferred->BindExit(); } } @@ -1750,7 +2090,7 @@ void CodeGenerator::VisitBlock(Block* node) { ASSERT(!in_spilled_code()); Comment cmnt(masm_, "[ Block"); CodeForStatementPosition(node); - node->break_target()->Initialize(this); + node->break_target()->set_direction(JumpTarget::FORWARD_ONLY); VisitStatements(node->statements()); if (node->break_target()->is_linked()) { node->break_target()->Bind(); @@ -1760,13 +2100,14 @@ void CodeGenerator::VisitBlock(Block* node) { void CodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) { - frame_->Push(pairs); - - // Duplicate the context register. - Result context(esi, this); - frame_->Push(&context); - - frame_->Push(Smi::FromInt(is_eval() ? 1 : 0)); + // Call the runtime to declare the globals. The inevitable call + // will sync frame elements to memory anyway, so we do it eagerly to + // allow us to push the arguments directly into place. + frame_->SyncRange(0, frame_->element_count() - 1); + + frame_->EmitPush(Immediate(pairs)); + frame_->EmitPush(esi); // The context is the second argument. + frame_->EmitPush(Immediate(Smi::FromInt(is_eval() ? 1 : 0))); Result ignored = frame_->CallRuntime(Runtime::kDeclareGlobals, 3); // Return value is ignored. } @@ -1786,24 +2127,25 @@ void CodeGenerator::VisitDeclaration(Declaration* node) { // Variables with a "LOOKUP" slot were introduced as non-locals // during variable resolution and must have mode DYNAMIC. ASSERT(var->is_dynamic()); - // For now, just do a runtime call. Duplicate the context register. - Result context(esi, this); - frame_->Push(&context); - frame_->Push(var->name()); + // For now, just do a runtime call. Sync the virtual frame eagerly + // so we can simply push the arguments into place. + frame_->SyncRange(0, frame_->element_count() - 1); + frame_->EmitPush(esi); + frame_->EmitPush(Immediate(var->name())); // Declaration nodes are always introduced in one of two modes. ASSERT(node->mode() == Variable::VAR || node->mode() == Variable::CONST); PropertyAttributes attr = node->mode() == Variable::VAR ? NONE : READ_ONLY; - frame_->Push(Smi::FromInt(attr)); + frame_->EmitPush(Immediate(Smi::FromInt(attr))); // Push initial value, if any. // Note: For variables we must not push an initial value (such as // 'undefined') because we may have a (legal) redeclaration and we // must not destroy the current value. if (node->mode() == Variable::CONST) { - frame_->Push(Factory::the_hole_value()); + frame_->EmitPush(Immediate(Factory::the_hole_value())); } else if (node->fun() != NULL) { Load(node->fun()); } else { - frame_->Push(Smi::FromInt(0)); // no initial value! + frame_->EmitPush(Immediate(Smi::FromInt(0))); // no initial value! } Result ignored = frame_->CallRuntime(Runtime::kDeclareContextSlot, 4); // Ignore the return value (declarations are statements). @@ -1864,10 +2206,10 @@ void CodeGenerator::VisitIfStatement(IfStatement* node) { bool has_else_stm = node->HasElseStatement(); CodeForStatementPosition(node); - JumpTarget exit(this); + JumpTarget exit; if (has_then_stm && has_else_stm) { - JumpTarget then(this); - JumpTarget else_(this); + JumpTarget then; + JumpTarget else_; ControlDestination dest(&then, &else_, true); LoadCondition(node->condition(), NOT_INSIDE_TYPEOF, &dest, true); @@ -1894,7 +2236,7 @@ void CodeGenerator::VisitIfStatement(IfStatement* node) { } else if (has_then_stm) { ASSERT(!has_else_stm); - JumpTarget then(this); + JumpTarget then; ControlDestination dest(&then, &exit, true); LoadCondition(node->condition(), NOT_INSIDE_TYPEOF, &dest, true); @@ -1914,7 +2256,7 @@ void CodeGenerator::VisitIfStatement(IfStatement* node) { } else if (has_else_stm) { ASSERT(!has_then_stm); - JumpTarget else_(this); + JumpTarget else_; ControlDestination dest(&exit, &else_, false); LoadCondition(node->condition(), NOT_INSIDE_TYPEOF, &dest, true); @@ -2026,7 +2368,7 @@ void CodeGenerator::VisitWithEnterStatement(WithEnterStatement* node) { Comment cmnt(masm_, "[ WithEnterStatement"); CodeForStatementPosition(node); Load(node->expression()); - Result context(this); + Result context; if (node->is_catch_block()) { context = frame_->CallRuntime(Runtime::kPushCatchContext, 1); } else { @@ -2081,8 +2423,8 @@ void CodeGenerator::GenerateFastCaseSwitchJumpTable( // placeholders, and fill in the addresses after the labels have been // bound. - JumpTarget setup_default(this); - JumpTarget is_smi(this); + JumpTarget setup_default; + JumpTarget is_smi; // A non-null default label pointer indicates a default case among // the case labels. Otherwise we use the break target as a @@ -2127,7 +2469,7 @@ void CodeGenerator::GenerateFastCaseSwitchJumpTable( // frame of the correct height can be merged to). Keep a copy to // restore at the start of every label. Create a jump target and // bind it to set its entry frame properly. - JumpTarget entry_target(this, JumpTarget::BIDIRECTIONAL); + JumpTarget entry_target(JumpTarget::BIDIRECTIONAL); entry_target.Bind(&smi_value); VirtualFrame* start_frame = new VirtualFrame(frame_); @@ -2177,8 +2519,6 @@ void CodeGenerator::GenerateFastCaseSwitchJumpTable( __ WriteInternalReference(entry_pos, *case_targets[i]); } } - - delete start_frame; } @@ -2186,7 +2526,7 @@ void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) { ASSERT(!in_spilled_code()); Comment cmnt(masm_, "[ SwitchStatement"); CodeForStatementPosition(node); - node->break_target()->Initialize(this); + node->break_target()->set_direction(JumpTarget::FORWARD_ONLY); // Compile the switch value. Load(node->tag()); @@ -2199,7 +2539,7 @@ void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) { int length = cases->length(); CaseClause* default_clause = NULL; - JumpTarget next_test(this); + JumpTarget next_test; // Compile the case label expressions and comparisons. Exit early // if a comparison is unconditionally true. The target next_test is // bound before the loop in order to indicate control flow to the @@ -2207,7 +2547,6 @@ void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) { next_test.Bind(); for (int i = 0; i < length && !next_test.is_unused(); i++) { CaseClause* clause = cases->at(i); - clause->body_target()->Initialize(this); // The default is not a test, but remember it for later. if (clause->is_default()) { default_clause = clause; @@ -2278,7 +2617,7 @@ void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) { if (clause->is_default()) { clause->body_target()->Bind(); } else { - JumpTarget body(this); + JumpTarget body; body.Jump(); clause->body_target()->Bind(); frame_->Drop(); @@ -2316,7 +2655,7 @@ void CodeGenerator::VisitLoopStatement(LoopStatement* node) { ASSERT(!in_spilled_code()); Comment cmnt(masm_, "[ LoopStatement"); CodeForStatementPosition(node); - node->break_target()->Initialize(this); + node->break_target()->set_direction(JumpTarget::FORWARD_ONLY); // Simple condition analysis. ALWAYS_TRUE and ALWAYS_FALSE represent a // known result for the test expression, with no side effects. @@ -2337,21 +2676,21 @@ void CodeGenerator::VisitLoopStatement(LoopStatement* node) { switch (node->type()) { case LoopStatement::DO_LOOP: { - JumpTarget body(this, JumpTarget::BIDIRECTIONAL); + JumpTarget body(JumpTarget::BIDIRECTIONAL); IncrementLoopNesting(); // Label the top of the loop for the backward jump if necessary. if (info == ALWAYS_TRUE) { // Use the continue target. - node->continue_target()->Initialize(this, JumpTarget::BIDIRECTIONAL); + node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL); node->continue_target()->Bind(); } else if (info == ALWAYS_FALSE) { // No need to label it. - node->continue_target()->Initialize(this); + node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY); } else { // Continue is the test, so use the backward body target. ASSERT(info == DONT_KNOW); - node->continue_target()->Initialize(this); + node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY); body.Bind(); } @@ -2410,27 +2749,25 @@ void CodeGenerator::VisitLoopStatement(LoopStatement* node) { JumpTarget body; if (test_at_bottom) { - body.Initialize(this, JumpTarget::BIDIRECTIONAL); - } else { - body.Initialize(this); + body.set_direction(JumpTarget::BIDIRECTIONAL); } // Based on the condition analysis, compile the test as necessary. if (info == ALWAYS_TRUE) { // We will not compile the test expression. Label the top of // the loop with the continue target. - node->continue_target()->Initialize(this, JumpTarget::BIDIRECTIONAL); + node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL); node->continue_target()->Bind(); } else { ASSERT(info == DONT_KNOW); // ALWAYS_FALSE cannot reach here. if (test_at_bottom) { // Continue is the test at the bottom, no need to label the // test at the top. The body is a backward target. - node->continue_target()->Initialize(this); + node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY); } else { // Label the test at the top as the continue target. The // body is a forward-only target. - node->continue_target()->Initialize(this, JumpTarget::BIDIRECTIONAL); + node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL); node->continue_target()->Bind(); } // Compile the test with the body as the true target and @@ -2513,15 +2850,13 @@ void CodeGenerator::VisitLoopStatement(LoopStatement* node) { // Target for backward edge if no test at the bottom, otherwise // unused. - JumpTarget loop(this, JumpTarget::BIDIRECTIONAL); + JumpTarget loop(JumpTarget::BIDIRECTIONAL); // Target for backward edge if there is a test at the bottom, // otherwise used as target for test at the top. JumpTarget body; if (test_at_bottom) { - body.Initialize(this, JumpTarget::BIDIRECTIONAL); - } else { - body.Initialize(this); + body.set_direction(JumpTarget::BIDIRECTIONAL); } // Based on the condition analysis, compile the test as necessary. @@ -2530,11 +2865,11 @@ void CodeGenerator::VisitLoopStatement(LoopStatement* node) { // the loop. if (node->next() == NULL) { // Use the continue target if there is no update expression. - node->continue_target()->Initialize(this, JumpTarget::BIDIRECTIONAL); + node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL); node->continue_target()->Bind(); } else { // Otherwise use the backward loop target. - node->continue_target()->Initialize(this); + node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY); loop.Bind(); } } else { @@ -2542,16 +2877,16 @@ void CodeGenerator::VisitLoopStatement(LoopStatement* node) { if (test_at_bottom) { // Continue is either the update expression or the test at // the bottom, no need to label the test at the top. - node->continue_target()->Initialize(this); + node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY); } else if (node->next() == NULL) { // We are not recompiling the test at the bottom and there // is no update expression. - node->continue_target()->Initialize(this, JumpTarget::BIDIRECTIONAL); + node->continue_target()->set_direction(JumpTarget::BIDIRECTIONAL); node->continue_target()->Bind(); } else { // We are not recompiling the test at the bottom and there // is an update expression. - node->continue_target()->Initialize(this); + node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY); loop.Bind(); } @@ -2651,16 +2986,16 @@ void CodeGenerator::VisitLoopStatement(LoopStatement* node) { void CodeGenerator::VisitForInStatement(ForInStatement* node) { ASSERT(!in_spilled_code()); - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ ForInStatement"); CodeForStatementPosition(node); - JumpTarget primitive(this); - JumpTarget jsobject(this); - JumpTarget fixed_array(this); - JumpTarget entry(this, JumpTarget::BIDIRECTIONAL); - JumpTarget end_del_check(this); - JumpTarget exit(this); + JumpTarget primitive; + JumpTarget jsobject; + JumpTarget fixed_array; + JumpTarget entry(JumpTarget::BIDIRECTIONAL); + JumpTarget end_del_check; + JumpTarget exit; // Get the object to enumerate over (converted to JSObject). LoadAndSpill(node->enumerable()); @@ -2745,8 +3080,8 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) { entry.Bind(); // Grab the current frame's height for the break and continue // targets only after all the state is pushed on the frame. - node->break_target()->Initialize(this); - node->continue_target()->Initialize(this); + node->break_target()->set_direction(JumpTarget::FORWARD_ONLY); + node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY); __ mov(eax, frame_->ElementAt(0)); // load the current count __ cmp(eax, frame_->ElementAt(1)); // compare to the array length @@ -2841,12 +3176,12 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) { void CodeGenerator::VisitTryCatch(TryCatch* node) { ASSERT(!in_spilled_code()); - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ TryCatch"); CodeForStatementPosition(node); - JumpTarget try_block(this); - JumpTarget exit(this); + JumpTarget try_block; + JumpTarget exit; try_block.Call(); // --- Catch block --- @@ -2937,7 +3272,7 @@ void CodeGenerator::VisitTryCatch(TryCatch* node) { // Generate unlink code for the (formerly) shadowing targets that // have been jumped to. Deallocate each shadow target. - Result return_value(this); + Result return_value; for (int i = 0; i < shadows.length(); i++) { if (shadows[i]->is_linked()) { // Unlink from try chain; be careful not to destroy the TOS if @@ -2972,7 +3307,6 @@ void CodeGenerator::VisitTryCatch(TryCatch* node) { shadows[i]->other_target()->Jump(); } } - delete shadows[i]; } exit.Bind(); @@ -2981,7 +3315,7 @@ void CodeGenerator::VisitTryCatch(TryCatch* node) { void CodeGenerator::VisitTryFinally(TryFinally* node) { ASSERT(!in_spilled_code()); - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ TryFinally"); CodeForStatementPosition(node); @@ -2990,8 +3324,8 @@ void CodeGenerator::VisitTryFinally(TryFinally* node) { // break/continue from within the try block. enum { FALLING, THROWING, JUMPING }; - JumpTarget try_block(this); - JumpTarget finally_block(this); + JumpTarget try_block; + JumpTarget finally_block; try_block.Call(); @@ -3070,7 +3404,7 @@ void CodeGenerator::VisitTryFinally(TryFinally* node) { // on the virtual frame. We must preserve it until it is // pushed. if (i == kReturnShadowIndex) { - Result return_value(this); + Result return_value; shadows[i]->Bind(&return_value); return_value.ToRegister(eax); } else { @@ -3143,7 +3477,7 @@ void CodeGenerator::VisitTryFinally(TryFinally* node) { } else { // Branch around the preparation for return which may emit // code. - JumpTarget skip(this); + JumpTarget skip; skip.Branch(not_equal); frame_->PrepareForReturn(); original->Jump(&return_value); @@ -3153,12 +3487,11 @@ void CodeGenerator::VisitTryFinally(TryFinally* node) { original->Branch(equal); } } - delete shadows[i]; } if (has_valid_frame()) { // Check if we need to rethrow the exception. - JumpTarget exit(this); + JumpTarget exit; __ cmp(Operand(ecx), Immediate(Smi::FromInt(THROWING))); exit.Branch(not_equal); @@ -3186,13 +3519,18 @@ void CodeGenerator::VisitDebuggerStatement(DebuggerStatement* node) { void CodeGenerator::InstantiateBoilerplate(Handle<JSFunction> boilerplate) { + // Call the runtime to instantiate the function boilerplate object. + // The inevitable call will sync frame elements to memory anyway, so + // we do it eagerly to allow us to push the arguments directly into + // place. ASSERT(boilerplate->IsBoilerplate()); + frame_->SyncRange(0, frame_->element_count() - 1); // Push the boilerplate on the stack. - frame_->Push(boilerplate); + frame_->EmitPush(Immediate(boilerplate)); // Create a new closure. - frame_->Push(esi); + frame_->EmitPush(esi); Result result = frame_->CallRuntime(Runtime::kNewClosure, 2); frame_->Push(&result); } @@ -3218,9 +3556,9 @@ void CodeGenerator::VisitFunctionBoilerplateLiteral( void CodeGenerator::VisitConditional(Conditional* node) { Comment cmnt(masm_, "[ Conditional"); - JumpTarget then(this); - JumpTarget else_(this); - JumpTarget exit(this); + JumpTarget then; + JumpTarget else_; + JumpTarget exit; ControlDestination dest(&then, &else_, true); LoadCondition(node->condition(), NOT_INSIDE_TYPEOF, &dest, true); @@ -3252,9 +3590,9 @@ void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) { if (slot->type() == Slot::LOOKUP) { ASSERT(slot->var()->is_dynamic()); - JumpTarget slow(this); - JumpTarget done(this); - Result value(this); + JumpTarget slow; + JumpTarget done; + Result value; // Generate fast-case code for variables that might be shadowed by // eval-introduced variables. Eval is used a lot without @@ -3298,8 +3636,12 @@ void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) { } slow.Bind(); - frame_->Push(esi); - frame_->Push(slot->var()->name()); + // A runtime call is inevitable. We eagerly sync frame elements + // to memory so that we can push the arguments directly into place + // on top of the frame. + frame_->SyncRange(0, frame_->element_count() - 1); + frame_->EmitPush(esi); + frame_->EmitPush(Immediate(slot->var()->name())); if (typeof_state == INSIDE_TYPEOF) { value = frame_->CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2); @@ -3317,9 +3659,9 @@ void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) { // // We currently spill the virtual frame because constants use the // potentially unsafe direct-frame access of SlotOperand. - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ Load const"); - JumpTarget exit(this); + JumpTarget exit; __ mov(ecx, SlotOperand(slot, ecx)); __ cmp(ecx, Factory::the_hole_value()); exit.Branch(not_equal); @@ -3354,7 +3696,7 @@ Result CodeGenerator::LoadFromGlobalSlotCheckExtensions( JumpTarget* slow) { // Check that no extension objects have been created by calls to // eval from the current scope to the global scope. - Result context(esi, this); + Register context = esi; Result tmp = allocator_->Allocate(); ASSERT(tmp.is_valid()); // All non-reserved registers were available. @@ -3363,14 +3705,14 @@ Result CodeGenerator::LoadFromGlobalSlotCheckExtensions( if (s->num_heap_slots() > 0) { if (s->calls_eval()) { // Check that extension is NULL. - __ cmp(ContextOperand(context.reg(), Context::EXTENSION_INDEX), + __ cmp(ContextOperand(context, Context::EXTENSION_INDEX), Immediate(0)); slow->Branch(not_equal, not_taken); } // Load next context in chain. - __ mov(tmp.reg(), ContextOperand(context.reg(), Context::CLOSURE_INDEX)); + __ mov(tmp.reg(), ContextOperand(context, Context::CLOSURE_INDEX)); __ mov(tmp.reg(), FieldOperand(tmp.reg(), JSFunction::kContextOffset)); - context = tmp; + context = tmp.reg(); } // If no outer scope calls eval, we do not need to check more // context extensions. If we have reached an eval scope, we check @@ -3383,8 +3725,8 @@ Result CodeGenerator::LoadFromGlobalSlotCheckExtensions( // Loop up the context chain. There is no frame effect so it is // safe to use raw labels here. Label next, fast; - if (!context.reg().is(tmp.reg())) { - __ mov(tmp.reg(), context.reg()); + if (!context.is(tmp.reg())) { + __ mov(tmp.reg(), context); } __ bind(&next); // Terminate at global context. @@ -3400,7 +3742,6 @@ Result CodeGenerator::LoadFromGlobalSlotCheckExtensions( __ jmp(&next); __ bind(&fast); } - context.Unuse(); tmp.Unuse(); // All extension objects were empty and it is safe to use a global @@ -3425,11 +3766,15 @@ void CodeGenerator::StoreToSlot(Slot* slot, InitState init_state) { if (slot->type() == Slot::LOOKUP) { ASSERT(slot->var()->is_dynamic()); - // For now, just do a runtime call. - frame_->Push(esi); - frame_->Push(slot->var()->name()); + // For now, just do a runtime call. Since the call is inevitable, + // we eagerly sync the virtual frame so we can directly push the + // arguments into place. + frame_->SyncRange(0, frame_->element_count() - 1); - Result value(this); + frame_->EmitPush(esi); + frame_->EmitPush(Immediate(slot->var()->name())); + + Result value; if (init_state == CONST_INIT) { // Same as the case for a normal store, but ignores attribute // (e.g. READ_ONLY) of context slot so that we can initialize const @@ -3457,7 +3802,7 @@ void CodeGenerator::StoreToSlot(Slot* slot, InitState init_state) { } else { ASSERT(!slot->var()->is_dynamic()); - JumpTarget exit(this); + JumpTarget exit; if (init_state == CONST_INIT) { ASSERT(slot->var()->mode() == Variable::CONST); // Only the first const initialization must be executed (the slot @@ -3467,7 +3812,7 @@ void CodeGenerator::StoreToSlot(Slot* slot, InitState init_state) { // We spill the frame in the code below because the direct-frame // access of SlotOperand is potentially unsafe with an unspilled // frame. - VirtualFrame::SpilledScope spilled_scope(this); + VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ Init const"); __ mov(ecx, SlotOperand(slot, ecx)); __ cmp(ecx, Factory::the_hole_value()); @@ -3557,44 +3902,45 @@ bool CodeGenerator::IsUnsafeSmi(Handle<Object> value) { } +// Materialize the regexp literal 'node' in the literals array +// 'literals' of the function. Leave the regexp boilerplate in +// 'boilerplate'. class DeferredRegExpLiteral: public DeferredCode { public: - DeferredRegExpLiteral(CodeGenerator* generator, RegExpLiteral* node) - : DeferredCode(generator), node_(node) { + DeferredRegExpLiteral(Register boilerplate, + Register literals, + RegExpLiteral* node) + : boilerplate_(boilerplate), literals_(literals), node_(node) { set_comment("[ DeferredRegExpLiteral"); } - virtual void Generate(); + void Generate(); private: + Register boilerplate_; + Register literals_; RegExpLiteral* node_; }; void DeferredRegExpLiteral::Generate() { - Result literals(generator()); - enter()->Bind(&literals); // Since the entry is undefined we call the runtime system to // compute the literal. - - VirtualFrame* frame = generator()->frame(); // Literal array (0). - frame->Push(&literals); + __ push(literals_); // Literal index (1). - frame->Push(Smi::FromInt(node_->literal_index())); + __ push(Immediate(Smi::FromInt(node_->literal_index()))); // RegExp pattern (2). - frame->Push(node_->pattern()); + __ push(Immediate(node_->pattern())); // RegExp flags (3). - frame->Push(node_->flags()); - Result boilerplate = - frame->CallRuntime(Runtime::kMaterializeRegExpLiteral, 4); - exit_.Jump(&boilerplate); + __ push(Immediate(node_->flags())); + __ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4); + if (!boilerplate_.is(eax)) __ mov(boilerplate_, eax); } void CodeGenerator::VisitRegExpLiteral(RegExpLiteral* node) { Comment cmnt(masm_, "[ RegExp Literal"); - DeferredRegExpLiteral* deferred = new DeferredRegExpLiteral(this, node); // Retrieve the literals array and check the allocated entry. Begin // with a writable copy of the function of this activation in a @@ -3609,67 +3955,63 @@ void CodeGenerator::VisitRegExpLiteral(RegExpLiteral* node) { FieldOperand(literals.reg(), JSFunction::kLiteralsOffset)); // Load the literal at the ast saved index. - int literal_offset = - FixedArray::kHeaderSize + node->literal_index() * kPointerSize; Result boilerplate = allocator_->Allocate(); ASSERT(boilerplate.is_valid()); + int literal_offset = + FixedArray::kHeaderSize + node->literal_index() * kPointerSize; __ mov(boilerplate.reg(), FieldOperand(literals.reg(), literal_offset)); // Check whether we need to materialize the RegExp object. If so, // jump to the deferred code passing the literals array. + DeferredRegExpLiteral* deferred = + new DeferredRegExpLiteral(boilerplate.reg(), literals.reg(), node); __ cmp(boilerplate.reg(), Factory::undefined_value()); - deferred->enter()->Branch(equal, &literals, not_taken); - + deferred->Branch(equal); + deferred->BindExit(); literals.Unuse(); - // The deferred code returns the boilerplate object. - deferred->BindExit(&boilerplate); // Push the boilerplate object. frame_->Push(&boilerplate); } -// This deferred code stub will be used for creating the boilerplate -// by calling Runtime_CreateObjectLiteral. -// Each created boilerplate is stored in the JSFunction and they are -// therefore context dependent. +// Materialize the object literal 'node' in the literals array +// 'literals' of the function. Leave the object boilerplate in +// 'boilerplate'. class DeferredObjectLiteral: public DeferredCode { public: - DeferredObjectLiteral(CodeGenerator* generator, + DeferredObjectLiteral(Register boilerplate, + Register literals, ObjectLiteral* node) - : DeferredCode(generator), node_(node) { + : boilerplate_(boilerplate), literals_(literals), node_(node) { set_comment("[ DeferredObjectLiteral"); } - virtual void Generate(); + void Generate(); private: + Register boilerplate_; + Register literals_; ObjectLiteral* node_; }; void DeferredObjectLiteral::Generate() { - Result literals(generator()); - enter()->Bind(&literals); // Since the entry is undefined we call the runtime system to // compute the literal. - - VirtualFrame* frame = generator()->frame(); // Literal array (0). - frame->Push(&literals); + __ push(literals_); // Literal index (1). - frame->Push(Smi::FromInt(node_->literal_index())); + __ push(Immediate(Smi::FromInt(node_->literal_index()))); // Constant properties (2). - frame->Push(node_->constant_properties()); - Result boilerplate = - frame->CallRuntime(Runtime::kCreateObjectLiteralBoilerplate, 3); - exit_.Jump(&boilerplate); + __ push(Immediate(node_->constant_properties())); + __ CallRuntime(Runtime::kCreateObjectLiteralBoilerplate, 3); + if (!boilerplate_.is(eax)) __ mov(boilerplate_, eax); } void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) { Comment cmnt(masm_, "[ ObjectLiteral"); - DeferredObjectLiteral* deferred = new DeferredObjectLiteral(this, node); // Retrieve the literals array and check the allocated entry. Begin // with a writable copy of the function of this activation in a @@ -3684,20 +4026,20 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) { FieldOperand(literals.reg(), JSFunction::kLiteralsOffset)); // Load the literal at the ast saved index. - int literal_offset = - FixedArray::kHeaderSize + node->literal_index() * kPointerSize; Result boilerplate = allocator_->Allocate(); ASSERT(boilerplate.is_valid()); + int literal_offset = + FixedArray::kHeaderSize + node->literal_index() * kPointerSize; __ mov(boilerplate.reg(), FieldOperand(literals.reg(), literal_offset)); // Check whether we need to materialize the object literal boilerplate. // If so, jump to the deferred code passing the literals array. + DeferredObjectLiteral* deferred = + new DeferredObjectLiteral(boilerplate.reg(), literals.reg(), node); __ cmp(boilerplate.reg(), Factory::undefined_value()); - deferred->enter()->Branch(equal, &literals, not_taken); - + deferred->Branch(equal); + deferred->BindExit(); literals.Unuse(); - // The deferred code returns the boilerplate object. - deferred->BindExit(&boilerplate); // Push the boilerplate object. frame_->Push(&boilerplate); @@ -3767,47 +4109,42 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) { } -// This deferred code stub will be used for creating the boilerplate -// by calling Runtime_CreateArrayLiteralBoilerplate. -// Each created boilerplate is stored in the JSFunction and they are -// therefore context dependent. +// Materialize the array literal 'node' in the literals array 'literals' +// of the function. Leave the array boilerplate in 'boilerplate'. class DeferredArrayLiteral: public DeferredCode { public: - DeferredArrayLiteral(CodeGenerator* generator, + DeferredArrayLiteral(Register boilerplate, + Register literals, ArrayLiteral* node) - : DeferredCode(generator), node_(node) { + : boilerplate_(boilerplate), literals_(literals), node_(node) { set_comment("[ DeferredArrayLiteral"); } - virtual void Generate(); + void Generate(); private: + Register boilerplate_; + Register literals_; ArrayLiteral* node_; }; void DeferredArrayLiteral::Generate() { - Result literals(generator()); - enter()->Bind(&literals); // Since the entry is undefined we call the runtime system to // compute the literal. - - VirtualFrame* frame = generator()->frame(); // Literal array (0). - frame->Push(&literals); + __ push(literals_); // Literal index (1). - frame->Push(Smi::FromInt(node_->literal_index())); + __ push(Immediate(Smi::FromInt(node_->literal_index()))); // Constant properties (2). - frame->Push(node_->literals()); - Result boilerplate = - frame->CallRuntime(Runtime::kCreateArrayLiteralBoilerplate, 3); - exit_.Jump(&boilerplate); + __ push(Immediate(node_->literals())); + __ CallRuntime(Runtime::kCreateArrayLiteralBoilerplate, 3); + if (!boilerplate_.is(eax)) __ mov(boilerplate_, eax); } void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) { Comment cmnt(masm_, "[ ArrayLiteral"); - DeferredArrayLiteral* deferred = new DeferredArrayLiteral(this, node); // Retrieve the literals array and check the allocated entry. Begin // with a writable copy of the function of this activation in a @@ -3822,24 +4159,23 @@ void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) { FieldOperand(literals.reg(), JSFunction::kLiteralsOffset)); // Load the literal at the ast saved index. - int literal_offset = - FixedArray::kHeaderSize + node->literal_index() * kPointerSize; Result boilerplate = allocator_->Allocate(); ASSERT(boilerplate.is_valid()); + int literal_offset = + FixedArray::kHeaderSize + node->literal_index() * kPointerSize; __ mov(boilerplate.reg(), FieldOperand(literals.reg(), literal_offset)); // Check whether we need to materialize the object literal boilerplate. // If so, jump to the deferred code passing the literals array. + DeferredArrayLiteral* deferred = + new DeferredArrayLiteral(boilerplate.reg(), literals.reg(), node); __ cmp(boilerplate.reg(), Factory::undefined_value()); - deferred->enter()->Branch(equal, &literals, not_taken); - + deferred->Branch(equal); + deferred->BindExit(); literals.Unuse(); - // The deferred code returns the boilerplate object. - deferred->BindExit(&boilerplate); - // Push the resulting array literal on the stack. + // Push the resulting array literal boilerplate on the stack. frame_->Push(&boilerplate); - // Clone the boilerplate object. Runtime::FunctionId clone_function_id = Runtime::kCloneLiteralBoilerplate; if (node->depth() == 1) { @@ -4058,15 +4394,23 @@ void CodeGenerator::VisitCall(Call* node) { // JavaScript example: 'with (obj) foo(1, 2, 3)' // foo is in obj // ---------------------------------- - // Load the function - frame_->Push(esi); - frame_->Push(var->name()); + // Load the function from the context. Sync the frame so we can + // push the arguments directly into place. + frame_->SyncRange(0, frame_->element_count() - 1); + frame_->EmitPush(esi); + frame_->EmitPush(Immediate(var->name())); frame_->CallRuntime(Runtime::kLoadContextSlot, 2); - // eax: slot value; edx: receiver + // The runtime call returns a pair of values in eax and edx. The + // looked-up function is in eax and the receiver is in edx. These + // register references are not ref counted here. We spill them + // eagerly since they are arguments to an inevitable call (and are + // not sharable by the arguments). + ASSERT(!allocator()->is_used(eax)); + frame_->EmitPush(eax); // Load the receiver. - frame_->Push(eax); - frame_->Push(edx); + ASSERT(!allocator()->is_used(edx)); + frame_->EmitPush(edx); // Call the function. CallWithArguments(args, node->position()); @@ -4218,7 +4562,8 @@ void CodeGenerator::VisitCallEval(CallEval* node) { // Call the function. CodeForSourcePosition(node->position()); - CallFunctionStub call_function(arg_count); + InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP; + CallFunctionStub call_function(arg_count, in_loop); result = frame_->CallStub(&call_function, arg_count + 1); // Restore the context and overwrite the function on the stack with @@ -4282,13 +4627,13 @@ void CodeGenerator::GenerateIsNonNegativeSmi(ZoneList<Expression*>* args) { void CodeGenerator::GenerateFastCharCodeAt(ZoneList<Expression*>* args) { ASSERT(args->length() == 2); - JumpTarget slow_case(this); - JumpTarget end(this); - JumpTarget not_a_flat_string(this); - JumpTarget a_cons_string(this); - JumpTarget try_again_with_new_string(this, JumpTarget::BIDIRECTIONAL); - JumpTarget ascii_string(this); - JumpTarget got_char_code(this); + JumpTarget slow_case; + JumpTarget end; + JumpTarget not_a_flat_string; + JumpTarget a_cons_string; + JumpTarget try_again_with_new_string(JumpTarget::BIDIRECTIONAL); + JumpTarget ascii_string; + JumpTarget got_char_code; Load(args->at(0)); Load(args->at(1)); @@ -4436,7 +4781,7 @@ void CodeGenerator::GenerateArgumentsLength(ZoneList<Expression*>* args) { ASSERT(args->length() == 0); // ArgumentsAccessStub takes the parameter count as an input argument // in register eax. Create a constant result for it. - Result count(Handle<Smi>(Smi::FromInt(scope_->num_parameters())), this); + Result count(Handle<Smi>(Smi::FromInt(scope_->num_parameters()))); // Call the shared stub to get to the arguments.length. ArgumentsAccessStub stub(ArgumentsAccessStub::READ_LENGTH); Result result = frame_->CallStub(&stub, &count); @@ -4446,7 +4791,7 @@ void CodeGenerator::GenerateArgumentsLength(ZoneList<Expression*>* args) { void CodeGenerator::GenerateValueOf(ZoneList<Expression*>* args) { ASSERT(args->length() == 1); - JumpTarget leave(this); + JumpTarget leave; Load(args->at(0)); // Load the object. frame_->Dup(); Result object = frame_->Pop(); @@ -4470,7 +4815,7 @@ void CodeGenerator::GenerateValueOf(ZoneList<Expression*>* args) { void CodeGenerator::GenerateSetValueOf(ZoneList<Expression*>* args) { ASSERT(args->length() == 2); - JumpTarget leave(this); + JumpTarget leave; Load(args->at(0)); // Load the object. Load(args->at(1)); // Load the value. Result value = frame_->Pop(); @@ -4519,7 +4864,7 @@ void CodeGenerator::GenerateArgumentsAccess(ZoneList<Expression*>* args) { Load(args->at(0)); Result key = frame_->Pop(); // Explicitly create a constant result. - Result count(Handle<Smi>(Smi::FromInt(scope_->num_parameters())), this); + Result count(Handle<Smi>(Smi::FromInt(scope_->num_parameters()))); // Call the shared stub to get to arguments[key]. ArgumentsAccessStub stub(ArgumentsAccessStub::READ_ELEMENT); Result result = frame_->CallStub(&stub, &key, &count); @@ -4582,9 +4927,10 @@ void CodeGenerator::VisitCallRuntime(CallRuntime* node) { } if (function == NULL) { - // Call the JS runtime function. Pass 0 as the loop nesting depth - // because we do not handle runtime calls specially in loops. - Result answer = frame_->CallCallIC(RelocInfo::CODE_TARGET, arg_count, 0); + // Call the JS runtime function. + Result answer = frame_->CallCallIC(RelocInfo::CODE_TARGET, + arg_count, + loop_nesting_); frame_->RestoreContextRegister(); frame_->SetElementAt(0, &answer); } else { @@ -4633,12 +4979,17 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) { return; } else if (slot != NULL && slot->type() == Slot::LOOKUP) { - // lookup the context holding the named variable - frame_->Push(esi); - frame_->Push(variable->name()); + // Call the runtime to look up the context holding the named + // variable. Sync the virtual frame eagerly so we can push the + // arguments directly into place. + frame_->SyncRange(0, frame_->element_count() - 1); + frame_->EmitPush(esi); + frame_->EmitPush(Immediate(variable->name())); Result context = frame_->CallRuntime(Runtime::kLookupContext, 2); - frame_->Push(&context); - frame_->Push(variable->name()); + ASSERT(context.is_register()); + frame_->EmitPush(context.reg()); + context.Unuse(); + frame_->EmitPush(Immediate(variable->name())); Result answer = frame_->InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION, 2); frame_->Push(&answer); @@ -4699,8 +5050,8 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) { case Token::BIT_NOT: { // Smi check. - JumpTarget smi_label(this); - JumpTarget continue_label(this); + JumpTarget smi_label; + JumpTarget continue_label; Result operand = frame_->Pop(); operand.ToRegister(); __ test(operand.reg(), Immediate(kSmiTagMask)); @@ -4723,7 +5074,7 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) { case Token::ADD: { // Smi check. - JumpTarget continue_label(this); + JumpTarget continue_label; Result operand = frame_->Pop(); operand.ToRegister(); __ test(operand.reg(), Immediate(kSmiTagMask)); @@ -4745,57 +5096,89 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) { } -class DeferredCountOperation: public DeferredCode { +// The value in dst was optimistically incremented or decremented. The +// result overflowed or was not smi tagged. Undo the operation, call +// into the runtime to convert the argument to a number, and call the +// specialized add or subtract stub. The result is left in dst. +class DeferredPrefixCountOperation: public DeferredCode { public: - DeferredCountOperation(CodeGenerator* generator, - bool is_postfix, - bool is_increment, - int target_size) - : DeferredCode(generator), - is_postfix_(is_postfix), - is_increment_(is_increment), - target_size_(target_size) { + DeferredPrefixCountOperation(Register dst, bool is_increment) + : dst_(dst), is_increment_(is_increment) { set_comment("[ DeferredCountOperation"); } virtual void Generate(); private: - bool is_postfix_; + Register dst_; bool is_increment_; - int target_size_; }; -void DeferredCountOperation::Generate() { - CodeGenerator* cgen = generator(); - Result value(cgen); - enter()->Bind(&value); - VirtualFrame* frame = cgen->frame(); +void DeferredPrefixCountOperation::Generate() { + // Undo the optimistic smi operation. + if (is_increment_) { + __ sub(Operand(dst_), Immediate(Smi::FromInt(1))); + } else { + __ add(Operand(dst_), Immediate(Smi::FromInt(1))); + } + __ push(dst_); + __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION); + __ push(eax); + __ push(Immediate(Smi::FromInt(1))); + if (is_increment_) { + __ CallRuntime(Runtime::kNumberAdd, 2); + } else { + __ CallRuntime(Runtime::kNumberSub, 2); + } + if (!dst_.is(eax)) __ mov(dst_, eax); +} + + +// The value in dst was optimistically incremented or decremented. The +// result overflowed or was not smi tagged. Undo the operation and call +// into the runtime to convert the argument to a number. Update the +// original value in old. Call the specialized add or subtract stub. +// The result is left in dst. +class DeferredPostfixCountOperation: public DeferredCode { + public: + DeferredPostfixCountOperation(Register dst, Register old, bool is_increment) + : dst_(dst), old_(old), is_increment_(is_increment) { + set_comment("[ DeferredCountOperation"); + } + + virtual void Generate(); + + private: + Register dst_; + Register old_; + bool is_increment_; +}; + + +void DeferredPostfixCountOperation::Generate() { // Undo the optimistic smi operation. - value.ToRegister(); - frame->Spill(value.reg()); if (is_increment_) { - __ sub(Operand(value.reg()), Immediate(Smi::FromInt(1))); + __ sub(Operand(dst_), Immediate(Smi::FromInt(1))); } else { - __ add(Operand(value.reg()), Immediate(Smi::FromInt(1))); - } - frame->Push(&value); - value = frame->InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION, 1); - frame->Push(&value); - if (is_postfix_) { // Fix up copy of old value with ToNumber(value). - // This is only safe because VisitCountOperation makes this frame slot - // beneath the reference a register, which is spilled at the above call. - // We cannot safely write to constants or copies below the water line. - frame->StoreToElementAt(target_size_ + 1); - } - frame->Push(Smi::FromInt(1)); + __ add(Operand(dst_), Immediate(Smi::FromInt(1))); + } + __ push(dst_); + __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION); + + // Save the result of ToNumber to use as the old value. + __ push(eax); + + // Call the runtime for the addition or subtraction. + __ push(eax); + __ push(Immediate(Smi::FromInt(1))); if (is_increment_) { - value = frame->CallRuntime(Runtime::kNumberAdd, 2); + __ CallRuntime(Runtime::kNumberAdd, 2); } else { - value = frame->CallRuntime(Runtime::kNumberSub, 2); + __ CallRuntime(Runtime::kNumberSub, 2); } - exit_.Jump(&value); + if (!dst_.is(eax)) __ mov(dst_, eax); + __ pop(old_); } @@ -4808,96 +5191,93 @@ void CodeGenerator::VisitCountOperation(CountOperation* node) { Variable* var = node->expression()->AsVariableProxy()->AsVariable(); bool is_const = (var != NULL && var->mode() == Variable::CONST); - // Postfix operators need a stack slot under the reference to hold - // the old value while the new one is being stored. - if (is_postfix) { - frame_->Push(Smi::FromInt(0)); - } + // Postfix operations need a stack slot under the reference to hold + // the old value while the new value is being stored. This is so that + // in the case that storing the new value requires a call, the old + // value will be in the frame to be spilled. + if (is_postfix) frame_->Push(Smi::FromInt(0)); { Reference target(this, node->expression()); if (target.is_illegal()) { // Spoof the virtual frame to have the expected height (one higher // than on entry). - if (!is_postfix) { - frame_->Push(Smi::FromInt(0)); - } + if (!is_postfix) frame_->Push(Smi::FromInt(0)); return; } target.TakeValue(NOT_INSIDE_TYPEOF); - DeferredCountOperation* deferred = - new DeferredCountOperation(this, is_postfix, - is_increment, target.size()); + Result new_value = frame_->Pop(); + new_value.ToRegister(); - Result value = frame_->Pop(); - value.ToRegister(); - - // Postfix: Store the old value as the result. + Result old_value; // Only allocated in the postfix case. if (is_postfix) { - // Explicitly back the slot for the old value with a new register. - // This improves performance in some cases. - Result old_value = allocator_->Allocate(); + // Allocate a temporary to preserve the old value. + old_value = allocator_->Allocate(); ASSERT(old_value.is_valid()); - __ mov(old_value.reg(), value.reg()); - // SetElement must not create a constant element or a copy in this slot, - // since we will write to it, below the waterline, in deferred code. - frame_->SetElementAt(target.size(), &old_value); + __ mov(old_value.reg(), new_value.reg()); } + // Ensure the new value is writable. + frame_->Spill(new_value.reg()); - // Perform optimistic increment/decrement. Ensure the value is - // writable. - frame_->Spill(value.reg()); - ASSERT(allocator_->count(value.reg()) == 1); - - // In order to combine the overflow and the smi check, we need to - // be able to allocate a byte register. We attempt to do so - // without spilling. If we fail, we will generate separate - // overflow and smi checks. + // In order to combine the overflow and the smi tag check, we need + // to be able to allocate a byte register. We attempt to do so + // without spilling. If we fail, we will generate separate overflow + // and smi tag checks. // - // We need to allocate and clear the temporary byte register - // before performing the count operation since clearing the - // register using xor will clear the overflow flag. + // We allocate and clear the temporary byte register before + // performing the count operation since clearing the register using + // xor will clear the overflow flag. Result tmp = allocator_->AllocateByteRegisterWithoutSpilling(); if (tmp.is_valid()) { __ Set(tmp.reg(), Immediate(0)); } + DeferredCode* deferred = NULL; + if (is_postfix) { + deferred = new DeferredPostfixCountOperation(new_value.reg(), + old_value.reg(), + is_increment); + } else { + deferred = new DeferredPrefixCountOperation(new_value.reg(), + is_increment); + } + if (is_increment) { - __ add(Operand(value.reg()), Immediate(Smi::FromInt(1))); + __ add(Operand(new_value.reg()), Immediate(Smi::FromInt(1))); } else { - __ sub(Operand(value.reg()), Immediate(Smi::FromInt(1))); + __ sub(Operand(new_value.reg()), Immediate(Smi::FromInt(1))); } - // If the count operation didn't overflow and the result is a - // valid smi, we're done. Otherwise, we jump to the deferred - // slow-case code. - // - // We combine the overflow and the smi check if we could - // successfully allocate a temporary byte register. + // If the count operation didn't overflow and the result is a valid + // smi, we're done. Otherwise, we jump to the deferred slow-case + // code. if (tmp.is_valid()) { + // We combine the overflow and the smi tag check if we could + // successfully allocate a temporary byte register. __ setcc(overflow, tmp.reg()); - __ or_(Operand(value.reg()), tmp.reg()); + __ or_(Operand(tmp.reg()), new_value.reg()); + __ test(tmp.reg(), Immediate(kSmiTagMask)); tmp.Unuse(); - __ test(value.reg(), Immediate(kSmiTagMask)); - deferred->enter()->Branch(not_zero, &value, not_taken); - } else { // Otherwise we test separately for overflow and smi check. - deferred->enter()->Branch(overflow, &value, not_taken); - __ test(value.reg(), Immediate(kSmiTagMask)); - deferred->enter()->Branch(not_zero, &value, not_taken); + deferred->Branch(not_zero); + } else { + // Otherwise we test separately for overflow and smi tag. + deferred->Branch(overflow); + __ test(new_value.reg(), Immediate(kSmiTagMask)); + deferred->Branch(not_zero); } + deferred->BindExit(); - // Store the new value in the target if not const. - deferred->BindExit(&value); - frame_->Push(&value); - if (!is_const) { - target.SetValue(NOT_CONST_INIT); - } - } + // Postfix: store the old value in the allocated slot under the + // reference. + if (is_postfix) frame_->SetElementAt(target.size(), &old_value); - // Postfix: Discard the new value and use the old. - if (is_postfix) { - frame_->Drop(); + frame_->Push(&new_value); + // Non-constant: update the reference. + if (!is_const) target.SetValue(NOT_CONST_INIT); } + + // Postfix: drop the new value and use the old. + if (is_postfix) frame_->Drop(); } @@ -4918,7 +5298,7 @@ void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) { // is necessary because we assume that if we get control flow on the // last path out of an expression we got it on all paths. if (op == Token::AND) { - JumpTarget is_true(this); + JumpTarget is_true; ControlDestination dest(&is_true, destination()->false_target(), true); LoadCondition(node->left(), NOT_INSIDE_TYPEOF, &dest, false); @@ -4956,8 +5336,8 @@ void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) { // We have a materialized value on the frame, so we exit with // one on all paths. There are possibly also jumps to is_true // from nested subexpressions. - JumpTarget pop_and_continue(this); - JumpTarget exit(this); + JumpTarget pop_and_continue; + JumpTarget exit; // Avoid popping the result if it converts to 'false' using the // standard ToBoolean() conversion as described in ECMA-262, @@ -4981,7 +5361,7 @@ void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) { } } else if (op == Token::OR) { - JumpTarget is_false(this); + JumpTarget is_false; ControlDestination dest(destination()->true_target(), &is_false, false); LoadCondition(node->left(), NOT_INSIDE_TYPEOF, &dest, false); @@ -5018,8 +5398,8 @@ void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) { // We have a materialized value on the frame, so we exit with // one on all paths. There are possibly also jumps to is_false // from nested subexpressions. - JumpTarget pop_and_continue(this); - JumpTarget exit(this); + JumpTarget pop_and_continue; + JumpTarget exit; // Avoid popping the result if it converts to 'true' using the // standard ToBoolean() conversion as described in ECMA-262, @@ -5248,10 +5628,14 @@ bool CodeGenerator::HasValidEntryRegisters() { #endif +// Emit a LoadIC call to get the value from receiver and leave it in +// dst. The receiver register is restored after the call. class DeferredReferenceGetNamedValue: public DeferredCode { public: - DeferredReferenceGetNamedValue(CodeGenerator* cgen, Handle<String> name) - : DeferredCode(cgen), name_(name) { + DeferredReferenceGetNamedValue(Register dst, + Register receiver, + Handle<String> name) + : dst_(dst), receiver_(receiver), name_(name) { set_comment("[ DeferredReferenceGetNamedValue"); } @@ -5261,39 +5645,41 @@ class DeferredReferenceGetNamedValue: public DeferredCode { private: Label patch_site_; + Register dst_; + Register receiver_; Handle<String> name_; }; void DeferredReferenceGetNamedValue::Generate() { - CodeGenerator* cgen = generator(); - Result receiver(cgen); - enter()->Bind(&receiver); - - cgen->frame()->Push(&receiver); - cgen->frame()->Push(name_); - Result answer = cgen->frame()->CallLoadIC(RelocInfo::CODE_TARGET); + __ push(receiver_); + __ Set(ecx, Immediate(name_)); + Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); + __ call(ic, RelocInfo::CODE_TARGET); // The call must be followed by a test eax instruction to indicate // that the inobject property case was inlined. - ASSERT(answer.is_register() && answer.reg().is(eax)); - // Store the delta to the map check instruction here in the test instruction. - // Use masm_-> instead of the double underscore macro since the latter can't - // return a value. + // + // Store the delta to the map check instruction here in the test + // instruction. Use masm_-> instead of the __ macro since the + // latter can't return a value. int delta_to_patch_site = masm_->SizeOfCodeGeneratedSince(patch_site()); - // Here we use masm_-> instead of the double underscore macro because - // this is the instruction that gets patched and coverage code gets in - // the way. - masm_->test(answer.reg(), Immediate(-delta_to_patch_site)); + // Here we use masm_-> instead of the __ macro because this is the + // instruction that gets patched and coverage code gets in the way. + masm_->test(eax, Immediate(-delta_to_patch_site)); __ IncrementCounter(&Counters::named_load_inline_miss, 1); - receiver = cgen->frame()->Pop(); - exit_.Jump(&receiver, &answer); + + if (!dst_.is(eax)) __ mov(dst_, eax); + __ pop(receiver_); } class DeferredReferenceGetKeyedValue: public DeferredCode { public: - DeferredReferenceGetKeyedValue(CodeGenerator* generator, bool is_global) - : DeferredCode(generator), is_global_(is_global) { + explicit DeferredReferenceGetKeyedValue(Register dst, + Register receiver, + Register key, + bool is_global) + : dst_(dst), receiver_(receiver), key_(key), is_global_(is_global) { set_comment("[ DeferredReferenceGetKeyedValue"); } @@ -5303,17 +5689,16 @@ class DeferredReferenceGetKeyedValue: public DeferredCode { private: Label patch_site_; + Register dst_; + Register receiver_; + Register key_; bool is_global_; }; void DeferredReferenceGetKeyedValue::Generate() { - CodeGenerator* cgen = generator(); - Result receiver(cgen); - Result key(cgen); - enter()->Bind(&receiver, &key); - cgen->frame()->Push(&receiver); // First IC argument. - cgen->frame()->Push(&key); // Second IC argument. + __ push(receiver_); // First IC argument. + __ push(key_); // Second IC argument. // Calculate the delta from the IC call instruction to the map check // cmp instruction in the inlined version. This delta is stored in @@ -5321,32 +5706,25 @@ void DeferredReferenceGetKeyedValue::Generate() { // it in the IC initialization code and patch the cmp instruction. // This means that we cannot allow test instructions after calls to // KeyedLoadIC stubs in other places. + Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); RelocInfo::Mode mode = is_global_ ? RelocInfo::CODE_TARGET_CONTEXT : RelocInfo::CODE_TARGET; - Result value = cgen->frame()->CallKeyedLoadIC(mode); - // The result needs to be specifically the eax register because the - // offset to the patch site will be expected in a test eax - // instruction. - ASSERT(value.is_register() && value.reg().is(eax)); + __ call(ic, mode); // The delta from the start of the map-compare instruction to the - // test instruction. We use masm_ directly here instead of the - // double underscore macro because the macro sometimes uses macro - // expansion to turn into something that can't return a value. This - // is encountered when doing generated code coverage tests. + // test instruction. We use masm_-> directly here instead of the __ + // macro because the macro sometimes uses macro expansion to turn + // into something that can't return a value. This is encountered + // when doing generated code coverage tests. int delta_to_patch_site = masm_->SizeOfCodeGeneratedSince(patch_site()); - // Here we use masm_-> instead of the double underscore macro because this - // is the instruction that gets patched and coverage code gets in the way. - masm_->test(value.reg(), Immediate(-delta_to_patch_site)); + // Here we use masm_-> instead of the __ macro because this is the + // instruction that gets patched and coverage code gets in the way. + masm_->test(eax, Immediate(-delta_to_patch_site)); __ IncrementCounter(&Counters::keyed_load_inline_miss, 1); - // The receiver and key were spilled by the call, so their state as - // constants or copies has been changed. Thus, they need to be - // "mergable" in the block at the exit label and are therefore - // passed as return results here. - key = cgen->frame()->Pop(); - receiver = cgen->frame()->Pop(); - exit_.Jump(&receiver, &key, &value); + if (!dst_.is(eax)) __ mov(dst_, eax); + __ pop(key_); + __ pop(receiver_); } @@ -5395,9 +5773,13 @@ void Reference::GetValue(TypeofState typeof_state) { bool is_global = var != NULL; ASSERT(!is_global || var->is_global()); - if (is_global || cgen_->scope()->is_global_scope()) { - // Do not inline the inobject property case for loads from the - // global object or loads in toplevel code. + // Do not inline the inobject property case for loads from the global + // object. Also do not inline for unoptimized code. This saves time + // in the code generator. Unoptimized code is toplevel code or code + // that is not in a loop. + if (is_global || + cgen_->scope()->is_global_scope() || + cgen_->loop_nesting() == 0) { Comment cmnt(masm, "[ Load from named Property"); cgen_->frame()->Push(GetName()); @@ -5413,19 +5795,20 @@ void Reference::GetValue(TypeofState typeof_state) { } else { // Inline the inobject property case. Comment cmnt(masm, "[ Inlined named property load"); - DeferredReferenceGetNamedValue* deferred = - new DeferredReferenceGetNamedValue(cgen_, GetName()); Result receiver = cgen_->frame()->Pop(); receiver.ToRegister(); - // Check that the receiver is a heap object. - __ test(receiver.reg(), Immediate(kSmiTagMask)); - deferred->enter()->Branch(zero, &receiver, not_taken); - // Preallocate the value register to ensure that there is no - // spill emitted between the patch site label and the offset in - // the load instruction. Result value = cgen_->allocator()->Allocate(); ASSERT(value.is_valid()); + DeferredReferenceGetNamedValue* deferred = + new DeferredReferenceGetNamedValue(value.reg(), + receiver.reg(), + GetName()); + + // Check that the receiver is a heap object. + __ test(receiver.reg(), Immediate(kSmiTagMask)); + deferred->Branch(zero); + __ bind(deferred->patch_site()); // This is the map check instruction that will be patched (so we can't // use the double underscore macro that may insert instructions). @@ -5434,7 +5817,7 @@ void Reference::GetValue(TypeofState typeof_state) { Immediate(Factory::null_value())); // This branch is always a forwards branch so it's always a fixed // size which allows the assert below to succeed and patching to work. - deferred->enter()->Branch(not_equal, &receiver, not_taken); + deferred->Branch(not_equal); // The delta from the patch label to the load offset must be // statically known. @@ -5447,7 +5830,7 @@ void Reference::GetValue(TypeofState typeof_state) { masm->mov(value.reg(), FieldOperand(receiver.reg(), offset)); __ IncrementCounter(&Counters::named_load_inline, 1); - deferred->BindExit(&receiver, &value); + deferred->BindExit(); cgen_->frame()->Push(&receiver); cgen_->frame()->Push(&value); } @@ -5467,20 +5850,34 @@ void Reference::GetValue(TypeofState typeof_state) { // patch the map check if appropriate. if (cgen_->loop_nesting() > 0) { Comment cmnt(masm, "[ Inlined array index load"); - DeferredReferenceGetKeyedValue* deferred = - new DeferredReferenceGetKeyedValue(cgen_, is_global); Result key = cgen_->frame()->Pop(); Result receiver = cgen_->frame()->Pop(); key.ToRegister(); receiver.ToRegister(); + // Use a fresh temporary to load the elements without destroying + // the receiver which is needed for the deferred slow case. + Result elements = cgen_->allocator()->Allocate(); + ASSERT(elements.is_valid()); + + // Use a fresh temporary for the index and later the loaded + // value. + Result index = cgen_->allocator()->Allocate(); + ASSERT(index.is_valid()); + + DeferredReferenceGetKeyedValue* deferred = + new DeferredReferenceGetKeyedValue(index.reg(), + receiver.reg(), + key.reg(), + is_global); + // Check that the receiver is not a smi (only needed if this // is not a load from the global context) and that it has the // expected map. if (!is_global) { __ test(receiver.reg(), Immediate(kSmiTagMask)); - deferred->enter()->Branch(zero, &receiver, &key, not_taken); + deferred->Branch(zero); } // Initially, use an invalid map. The map is patched in the IC @@ -5489,32 +5886,28 @@ void Reference::GetValue(TypeofState typeof_state) { // Use masm-> here instead of the double underscore macro since extra // coverage code can interfere with the patching. masm->cmp(FieldOperand(receiver.reg(), HeapObject::kMapOffset), - Immediate(Factory::null_value())); - deferred->enter()->Branch(not_equal, &receiver, &key, not_taken); + Immediate(Factory::null_value())); + deferred->Branch(not_equal); // Check that the key is a smi. __ test(key.reg(), Immediate(kSmiTagMask)); - deferred->enter()->Branch(not_zero, &receiver, &key, not_taken); + deferred->Branch(not_zero); // Get the elements array from the receiver and check that it // is not a dictionary. - Result elements = cgen_->allocator()->Allocate(); - ASSERT(elements.is_valid()); __ mov(elements.reg(), FieldOperand(receiver.reg(), JSObject::kElementsOffset)); __ cmp(FieldOperand(elements.reg(), HeapObject::kMapOffset), Immediate(Factory::hash_table_map())); - deferred->enter()->Branch(equal, &receiver, &key, not_taken); + deferred->Branch(equal); // Shift the key to get the actual index value and check that // it is within bounds. - Result index = cgen_->allocator()->Allocate(); - ASSERT(index.is_valid()); __ mov(index.reg(), key.reg()); __ sar(index.reg(), kSmiTagSize); __ cmp(index.reg(), FieldOperand(elements.reg(), Array::kLengthOffset)); - deferred->enter()->Branch(above_equal, &receiver, &key, not_taken); + deferred->Branch(above_equal); // Load and check that the result is not the hole. We could // reuse the index or elements register for the value. @@ -5531,12 +5924,12 @@ void Reference::GetValue(TypeofState typeof_state) { elements.Unuse(); index.Unuse(); __ cmp(Operand(value.reg()), Immediate(Factory::the_hole_value())); - deferred->enter()->Branch(equal, &receiver, &key, not_taken); + deferred->Branch(equal); __ IncrementCounter(&Counters::keyed_load_inline, 1); + deferred->BindExit(); // Restore the receiver and key to the frame and push the // result on top of it. - deferred->BindExit(&receiver, &key, &value); cgen_->frame()->Push(&receiver); cgen_->frame()->Push(&key); cgen_->frame()->Push(&value); @@ -5684,340 +6077,6 @@ void ToBooleanStub::Generate(MacroAssembler* masm) { } -#undef __ -#define __ ACCESS_MASM(masm_) - -Result DeferredInlineBinaryOperation::GenerateInlineCode(Result* left, - Result* right) { - // Perform fast-case smi code for the operation (left <op> right) and - // returns the result in a Result. - // If any fast-case tests fail, it jumps to the slow-case deferred code, - // which calls the binary operation stub, with the arguments (in registers) - // on top of the frame. - // Consumes its arguments (sets left and right to invalid and frees their - // registers). - - left->ToRegister(); - right->ToRegister(); - // A newly allocated register answer is used to hold the answer. - // The registers containing left and right are not modified in - // most cases, so they usually don't need to be spilled in the fast case. - Result answer = generator()->allocator()->Allocate(); - - ASSERT(answer.is_valid()); - // Perform the smi check. - if (left->reg().is(right->reg())) { - __ test(left->reg(), Immediate(kSmiTagMask)); - } else { - __ mov(answer.reg(), left->reg()); - __ or_(answer.reg(), Operand(right->reg())); - ASSERT(kSmiTag == 0); // adjust zero check if not the case - __ test(answer.reg(), Immediate(kSmiTagMask)); - } - enter()->Branch(not_zero, left, right, not_taken); - - // All operations start by copying the left argument into answer. - __ mov(answer.reg(), left->reg()); - switch (op_) { - case Token::ADD: - __ add(answer.reg(), Operand(right->reg())); // add optimistically - enter()->Branch(overflow, left, right, not_taken); - break; - - case Token::SUB: - __ sub(answer.reg(), Operand(right->reg())); // subtract optimistically - enter()->Branch(overflow, left, right, not_taken); - break; - - case Token::MUL: { - // If the smi tag is 0 we can just leave the tag on one operand. - ASSERT(kSmiTag == 0); // adjust code below if not the case - // Remove tag from the left operand (but keep sign). - // Left hand operand has been copied into answer. - __ sar(answer.reg(), kSmiTagSize); - // Do multiplication of smis, leaving result in answer. - __ imul(answer.reg(), Operand(right->reg())); - // Go slow on overflows. - enter()->Branch(overflow, left, right, not_taken); - // Check for negative zero result. If product is zero, - // and one argument is negative, go to slow case. - // The frame is unchanged in this block, so local control flow can - // use a Label rather than a JumpTarget. - Label non_zero_result; - __ test(answer.reg(), Operand(answer.reg())); - __ j(not_zero, &non_zero_result, taken); - __ mov(answer.reg(), left->reg()); - __ or_(answer.reg(), Operand(right->reg())); - enter()->Branch(negative, left, right, not_taken); - __ xor_(answer.reg(), Operand(answer.reg())); // Positive 0 is correct. - __ bind(&non_zero_result); - break; - } - - case Token::DIV: // Fall through. - case Token::MOD: { - // Div and mod use the registers eax and edx. Left and right must - // be preserved, because the original operands are needed if we switch - // to the slow case. Move them if either is in eax or edx. - // The Result answer should be changed into an alias for eax. - // Precondition: - // The Results left and right are valid. They may be the same register, - // and may be unspilled. The Result answer is valid and is distinct - // from left and right, and is spilled. - // The value in left is copied to answer. - - Result reg_eax = generator()->allocator()->Allocate(eax); - Result reg_edx = generator()->allocator()->Allocate(edx); - // These allocations may have failed, if one of left, right, or answer - // is in register eax or edx. - bool left_copied_to_eax = false; // We will make sure this becomes true. - - // Part 1: Get eax - if (answer.reg().is(eax)) { - reg_eax = answer; - left_copied_to_eax = true; - } else if (right->reg().is(eax) || left->reg().is(eax)) { - // We need a non-edx register to move one or both of left and right to. - // We use answer if it is not edx, otherwise we allocate one. - if (answer.reg().is(edx)) { - reg_edx = answer; - answer = generator()->allocator()->Allocate(); - ASSERT(answer.is_valid()); - } - - if (left->reg().is(eax)) { - reg_eax = *left; - left_copied_to_eax = true; - *left = answer; - } - if (right->reg().is(eax)) { - reg_eax = *right; - *right = answer; - } - __ mov(answer.reg(), eax); - } - // End of Part 1. - // reg_eax is valid, and neither left nor right is in eax. - ASSERT(reg_eax.is_valid()); - ASSERT(!left->reg().is(eax)); - ASSERT(!right->reg().is(eax)); - - // Part 2: Get edx - // reg_edx is invalid if and only if either left, right, - // or answer is in edx. If edx is valid, then either edx - // was free, or it was answer, but answer was reallocated. - if (answer.reg().is(edx)) { - reg_edx = answer; - } else if (right->reg().is(edx) || left->reg().is(edx)) { - // Is answer used? - if (answer.reg().is(eax) || answer.reg().is(left->reg()) || - answer.reg().is(right->reg())) { - answer = generator()->allocator()->Allocate(); - ASSERT(answer.is_valid()); // We cannot hit both Allocate() calls. - } - if (left->reg().is(edx)) { - reg_edx = *left; - *left = answer; - } - if (right->reg().is(edx)) { - reg_edx = *right; - *right = answer; - } - __ mov(answer.reg(), edx); - } - // End of Part 2 - ASSERT(reg_edx.is_valid()); - ASSERT(!left->reg().is(eax)); - ASSERT(!right->reg().is(eax)); - - answer = reg_eax; // May free answer, if it was never used. - generator()->frame()->Spill(eax); - if (!left_copied_to_eax) { - __ mov(eax, left->reg()); - left_copied_to_eax = true; - } - generator()->frame()->Spill(edx); - - // Postcondition: - // reg_eax, reg_edx are valid, correct, and spilled. - // reg_eax contains the value originally in left - // left and right are not eax or edx. They may or may not be - // spilled or distinct. - // answer is an alias for reg_eax. - - // Sign extend eax into edx:eax. - __ cdq(); - // Check for 0 divisor. - __ test(right->reg(), Operand(right->reg())); - enter()->Branch(zero, left, right, not_taken); - // Divide edx:eax by the right operand. - __ idiv(right->reg()); - if (op_ == Token::DIV) { - // Check for negative zero result. If result is zero, and divisor - // is negative, return a floating point negative zero. - // The frame is unchanged in this block, so local control flow can - // use a Label rather than a JumpTarget. - Label non_zero_result; - __ test(left->reg(), Operand(left->reg())); - __ j(not_zero, &non_zero_result, taken); - __ test(right->reg(), Operand(right->reg())); - enter()->Branch(negative, left, right, not_taken); - __ bind(&non_zero_result); - // Check for the corner case of dividing the most negative smi - // by -1. We cannot use the overflow flag, since it is not set - // by idiv instruction. - ASSERT(kSmiTag == 0 && kSmiTagSize == 1); - __ cmp(eax, 0x40000000); - enter()->Branch(equal, left, right, not_taken); - // Check that the remainder is zero. - __ test(edx, Operand(edx)); - enter()->Branch(not_zero, left, right, not_taken); - // Tag the result and store it in register temp. - ASSERT(kSmiTagSize == times_2); // adjust code if not the case - __ lea(answer.reg(), Operand(eax, eax, times_1, kSmiTag)); - } else { - ASSERT(op_ == Token::MOD); - // Check for a negative zero result. If the result is zero, and the - // dividend is negative, return a floating point negative zero. - // The frame is unchanged in this block, so local control flow can - // use a Label rather than a JumpTarget. - Label non_zero_result; - __ test(edx, Operand(edx)); - __ j(not_zero, &non_zero_result, taken); - __ test(left->reg(), Operand(left->reg())); - enter()->Branch(negative, left, right, not_taken); - __ bind(&non_zero_result); - // The answer is in edx. - answer = reg_edx; - } - break; - } - case Token::BIT_OR: - __ or_(answer.reg(), Operand(right->reg())); - break; - - case Token::BIT_AND: - __ and_(answer.reg(), Operand(right->reg())); - break; - - case Token::BIT_XOR: - __ xor_(answer.reg(), Operand(right->reg())); - break; - - case Token::SHL: - case Token::SHR: - case Token::SAR: - // Move right into ecx. - // Left is in two registers already, so even if left or answer is ecx, - // we can move right to it, and use the other one. - // Right operand must be in register cl because x86 likes it that way. - if (right->reg().is(ecx)) { - // Right is already in the right place. Left may be in the - // same register, which causes problems. Always use answer - // instead of left, even if left is not ecx, since this avoids - // spilling left. - *left = answer; - } else if (left->reg().is(ecx)) { - generator()->frame()->Spill(left->reg()); - __ mov(left->reg(), right->reg()); - *right = *left; - *left = answer; // Use copy of left in answer as left. - } else if (answer.reg().is(ecx)) { - __ mov(answer.reg(), right->reg()); - *right = answer; - } else { - Result reg_ecx = generator()->allocator()->Allocate(ecx); - ASSERT(reg_ecx.is_valid()); - __ mov(ecx, right->reg()); - *right = reg_ecx; - // Answer and left both contain the left operand. Use answer, so - // left is not spilled. - *left = answer; - } - ASSERT(left->reg().is_valid()); - ASSERT(!left->reg().is(ecx)); - ASSERT(right->reg().is(ecx)); - answer.Unuse(); // Answer may now be being used for left or right. - // We will modify left and right, which we do not do in any other - // binary operation. The exits to slow code need to restore the - // original values of left and right, or at least values that give - // the same answer. - - // We are modifying left and right. They must be spilled! - generator()->frame()->Spill(left->reg()); - generator()->frame()->Spill(right->reg()); - - // Remove tags from operands (but keep sign). - __ sar(left->reg(), kSmiTagSize); - __ sar(ecx, kSmiTagSize); - // Perform the operation. - switch (op_) { - case Token::SAR: - __ sar(left->reg()); - // No checks of result necessary - break; - case Token::SHR: { - __ shr(left->reg()); - // Check that the *unsigned* result fits in a smi. - // Neither of the two high-order bits can be set: - // - 0x80000000: high bit would be lost when smi tagging. - // - 0x40000000: this number would convert to negative when - // Smi tagging these two cases can only happen with shifts - // by 0 or 1 when handed a valid smi. - // If the answer cannot be represented by a SMI, restore - // the left and right arguments, and jump to slow case. - // The low bit of the left argument may be lost, but only - // in a case where it is dropped anyway. - JumpTarget result_ok(generator()); - __ test(left->reg(), Immediate(0xc0000000)); - result_ok.Branch(zero, left, taken); - __ shl(left->reg()); - ASSERT(kSmiTag == 0); - __ shl(left->reg(), kSmiTagSize); - __ shl(right->reg(), kSmiTagSize); - enter()->Jump(left, right); - result_ok.Bind(left); - break; - } - case Token::SHL: { - __ shl(left->reg()); - // Check that the *signed* result fits in a smi. - JumpTarget result_ok(generator()); - __ cmp(left->reg(), 0xc0000000); - result_ok.Branch(positive, left, taken); - - __ shr(left->reg()); - ASSERT(kSmiTag == 0); - __ shl(left->reg(), kSmiTagSize); - __ shl(right->reg(), kSmiTagSize); - enter()->Jump(left, right); - result_ok.Bind(left); - break; - } - default: - UNREACHABLE(); - } - // Smi-tag the result, in left, and make answer an alias for left-> - answer = *left; - answer.ToRegister(); - ASSERT(kSmiTagSize == times_2); // adjust code if not the case - __ lea(answer.reg(), - Operand(answer.reg(), answer.reg(), times_1, kSmiTag)); - break; - - default: - UNREACHABLE(); - break; - } - left->Unuse(); - right->Unuse(); - return answer; -} - - -#undef __ -#define __ ACCESS_MASM(masm) - void GenericBinaryOpStub::GenerateSmiCode(MacroAssembler* masm, Label* slow) { // Perform fast-case smi code for the operation (eax <op> ebx) and // leave result in register eax. @@ -7101,6 +7160,9 @@ void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { Label invoke, exit; +#ifdef ENABLE_LOGGING_AND_PROFILING + Label not_outermost_js, not_outermost_js_2; +#endif // Setup frame. __ push(ebp); @@ -7119,6 +7181,15 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { ExternalReference c_entry_fp(Top::k_c_entry_fp_address); __ push(Operand::StaticVariable(c_entry_fp)); +#ifdef ENABLE_LOGGING_AND_PROFILING + // If this is the outermost JS call, set js_entry_sp value. + ExternalReference js_entry_sp(Top::k_js_entry_sp_address); + __ cmp(Operand::StaticVariable(js_entry_sp), Immediate(0)); + __ j(NegateCondition(equal), ¬_outermost_js); + __ mov(Operand::StaticVariable(js_entry_sp), ebp); + __ bind(¬_outermost_js); +#endif + // Call a faked try-block that does the invoke. __ call(&invoke); @@ -7162,6 +7233,15 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { // Pop next_sp. __ add(Operand(esp), Immediate(StackHandlerConstants::kSize - kPointerSize)); +#ifdef ENABLE_LOGGING_AND_PROFILING + // If current EBP value is the same as js_entry_sp value, it means that + // the current function is the outermost. + __ cmp(ebp, Operand::StaticVariable(js_entry_sp)); + __ j(NegateCondition(equal), ¬_outermost_js_2); + __ mov(Operand::StaticVariable(js_entry_sp), Immediate(0)); + __ bind(¬_outermost_js_2); +#endif + // Restore the top frame descriptor from the stack. __ bind(&exit); __ pop(Operand::StaticVariable(ExternalReference(Top::k_c_entry_fp_address))); @@ -7186,7 +7266,7 @@ void InstanceofStub::Generate(MacroAssembler* masm) { __ j(zero, &slow, not_taken); // Check that the left hand is a JS object. - __ mov(eax, FieldOperand(eax, HeapObject::kMapOffset)); // ebx - object map + __ mov(eax, FieldOperand(eax, HeapObject::kMapOffset)); // eax - object map __ movzx_b(ecx, FieldOperand(eax, Map::kInstanceTypeOffset)); // ecx - type __ cmp(ecx, FIRST_JS_OBJECT_TYPE); __ j(less, &slow, not_taken); @@ -7198,6 +7278,8 @@ void InstanceofStub::Generate(MacroAssembler* masm) { __ TryGetFunctionPrototype(edx, ebx, ecx, &slow); // Check that the function prototype is a JS object. + __ test(ebx, Immediate(kSmiTagMask)); + __ j(zero, &slow, not_taken); __ mov(ecx, FieldOperand(ebx, HeapObject::kMapOffset)); __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset)); __ cmp(ecx, FIRST_JS_OBJECT_TYPE); diff --git a/deps/v8/src/ia32/codegen-ia32.h b/deps/v8/src/ia32/codegen-ia32.h index 0e019570e3..9b609a1562 100644 --- a/deps/v8/src/ia32/codegen-ia32.h +++ b/deps/v8/src/ia32/codegen-ia32.h @@ -28,7 +28,8 @@ #ifndef V8_IA32_CODEGEN_IA32_H_ #define V8_IA32_CODEGEN_IA32_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Forward declarations class DeferredCode; @@ -332,8 +333,7 @@ class CodeGenerator: public AstVisitor { // Accessors Scope* scope() const { return scope_; } - // Clearing and generating deferred code. - void ClearDeferred(); + // Generating deferred code. void ProcessDeferred(); bool is_eval() { return is_eval_; } @@ -347,7 +347,6 @@ class CodeGenerator: public AstVisitor { void IncrementLoopNesting() { loop_nesting_++; } void DecrementLoopNesting() { loop_nesting_--; } - // Node visitors. void VisitStatements(ZoneList<Statement*>* statements); @@ -487,8 +486,7 @@ class CodeGenerator: public AstVisitor { Handle<JSFunction> BuildBoilerplate(FunctionLiteral* node); void ProcessDeclarations(ZoneList<Declaration*>* declarations); - Handle<Code> ComputeCallInitialize(int argc); - Handle<Code> ComputeCallInitializeInLoop(int argc); + Handle<Code> ComputeCallInitialize(int argc, InLoopFlag in_loop); // Declare global variables and functions in the given array of // name/value pairs. @@ -581,14 +579,14 @@ class CodeGenerator: public AstVisitor { void CodeForSourcePosition(int pos); #ifdef DEBUG - // True if the registers are valid for entry to a block. There should be - // no frame-external references to eax, ebx, ecx, edx, or edi. + // True if the registers are valid for entry to a block. There should + // be no frame-external references to (non-reserved) registers. bool HasValidEntryRegisters(); #endif bool is_eval_; // Tells whether code is generated for eval. Handle<Script> script_; - List<DeferredCode*> deferred_; + ZoneList<DeferredCode*> deferred_; // Assembler MacroAssembler* masm_; // to generate code diff --git a/deps/v8/src/ia32/cpu-ia32.cc b/deps/v8/src/ia32/cpu-ia32.cc index 9cd6b10bd6..82a5565700 100644 --- a/deps/v8/src/ia32/cpu-ia32.cc +++ b/deps/v8/src/ia32/cpu-ia32.cc @@ -32,7 +32,8 @@ #include "cpu.h" #include "macro-assembler.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { void CPU::Setup() { CpuFeatures::Probe(); @@ -46,7 +47,7 @@ void CPU::FlushICache(void* start, size_t size) { // is patched on an intel CPU the core performing the patching will have its // own instruction cache updated automatically. - // If flushing of the instruction cache becomes necessary Windows have the + // If flushing of the instruction cache becomes necessary Windows has the // API function FlushInstructionCache. } diff --git a/deps/v8/src/ia32/debug-ia32.cc b/deps/v8/src/ia32/debug-ia32.cc index 9503cfca7c..9913a39ba9 100644 --- a/deps/v8/src/ia32/debug-ia32.cc +++ b/deps/v8/src/ia32/debug-ia32.cc @@ -31,7 +31,8 @@ #include "debug.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { #ifdef ENABLE_DEBUGGER_SUPPORT diff --git a/deps/v8/src/ia32/frames-ia32.cc b/deps/v8/src/ia32/frames-ia32.cc index 1bc62ec226..dea439f24b 100644 --- a/deps/v8/src/ia32/frames-ia32.cc +++ b/deps/v8/src/ia32/frames-ia32.cc @@ -29,7 +29,8 @@ #include "frames-inl.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { StackFrame::Type StackFrame::ComputeType(State* state) { diff --git a/deps/v8/src/ia32/frames-ia32.h b/deps/v8/src/ia32/frames-ia32.h index f86dbe4c14..aec1f48359 100644 --- a/deps/v8/src/ia32/frames-ia32.h +++ b/deps/v8/src/ia32/frames-ia32.h @@ -28,7 +28,8 @@ #ifndef V8_IA32_FRAMES_IA32_H_ #define V8_IA32_FRAMES_IA32_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Register lists diff --git a/deps/v8/src/ia32/ic-ia32.cc b/deps/v8/src/ia32/ic-ia32.cc index 4231bfae7d..d7f264d495 100644 --- a/deps/v8/src/ia32/ic-ia32.cc +++ b/deps/v8/src/ia32/ic-ia32.cc @@ -32,7 +32,8 @@ #include "runtime.h" #include "stub-cache.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // ---------------------------------------------------------------------------- // Static IC stub generators. @@ -426,7 +427,7 @@ void CallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) { // Probe the stub cache. Code::Flags flags = - Code::ComputeFlags(Code::CALL_IC, MONOMORPHIC, NORMAL, argc); + Code::ComputeFlags(Code::CALL_IC, NOT_IN_LOOP, MONOMORPHIC, NORMAL, argc); StubCache::GenerateProbe(masm, flags, edx, ecx, ebx); // If the stub cache probing failed, the receiver might be a value. @@ -635,7 +636,9 @@ void LoadIC::GenerateMegamorphic(MacroAssembler* masm) { __ mov(eax, Operand(esp, kPointerSize)); // Probe the stub cache. - Code::Flags flags = Code::ComputeFlags(Code::LOAD_IC, MONOMORPHIC); + Code::Flags flags = Code::ComputeFlags(Code::LOAD_IC, + NOT_IN_LOOP, + MONOMORPHIC); StubCache::GenerateProbe(masm, flags, eax, ecx, ebx); // Cache miss: Jump to runtime. @@ -838,7 +841,9 @@ void StoreIC::GenerateMegamorphic(MacroAssembler* masm) { // Get the receiver from the stack and probe the stub cache. __ mov(edx, Operand(esp, 4)); - Code::Flags flags = Code::ComputeFlags(Code::STORE_IC, MONOMORPHIC); + Code::Flags flags = Code::ComputeFlags(Code::STORE_IC, + NOT_IN_LOOP, + MONOMORPHIC); StubCache::GenerateProbe(masm, flags, edx, ecx, ebx); // Cache miss: Jump to runtime. diff --git a/deps/v8/src/ia32/jump-target-ia32.cc b/deps/v8/src/ia32/jump-target-ia32.cc index 6c7d6e35b8..9644a16aab 100644 --- a/deps/v8/src/ia32/jump-target-ia32.cc +++ b/deps/v8/src/ia32/jump-target-ia32.cc @@ -28,46 +28,51 @@ #include "v8.h" #include "codegen-inl.h" +#include "jump-target-inl.h" #include "register-allocator-inl.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // ------------------------------------------------------------------------- // JumpTarget implementation. -#define __ ACCESS_MASM(masm_) +#define __ ACCESS_MASM(cgen()->masm()) void JumpTarget::DoJump() { - ASSERT(cgen_ != NULL); - ASSERT(cgen_->has_valid_frame()); + ASSERT(cgen()->has_valid_frame()); // Live non-frame registers are not allowed at unconditional jumps // because we have no way of invalidating the corresponding results // which are still live in the C++ code. - ASSERT(cgen_->HasValidEntryRegisters()); + ASSERT(cgen()->HasValidEntryRegisters()); if (is_bound()) { // Backward jump. There is an expected frame to merge to. ASSERT(direction_ == BIDIRECTIONAL); - cgen_->frame()->MergeTo(entry_frame_); - cgen_->DeleteFrame(); + cgen()->frame()->PrepareMergeTo(entry_frame_); + cgen()->frame()->MergeTo(entry_frame_); + cgen()->DeleteFrame(); + __ jmp(&entry_label_); + } else if (entry_frame_ != NULL) { + // Forward jump with a preconfigured entry frame. Assert the + // current frame matches the expected one and jump to the block. + ASSERT(cgen()->frame()->Equals(entry_frame_)); + cgen()->DeleteFrame(); __ jmp(&entry_label_); } else { - // Forward jump. The current frame is added to the end of the list - // of frames reaching the target block and a jump to the merge code - // is emitted. - AddReachingFrame(cgen_->frame()); + // Forward jump. Remember the current frame and emit a jump to + // its merge code. + AddReachingFrame(cgen()->frame()); RegisterFile empty; - cgen_->SetFrame(NULL, &empty); + cgen()->SetFrame(NULL, &empty); __ jmp(&merge_labels_.last()); } - - is_linked_ = !is_bound_; } void JumpTarget::DoBranch(Condition cc, Hint hint) { - ASSERT(cgen_ != NULL); - ASSERT(cgen_->has_valid_frame()); + ASSERT(cgen() != NULL); + ASSERT(cgen()->has_valid_frame()); if (is_bound()) { ASSERT(direction_ == BIDIRECTIONAL); @@ -77,29 +82,29 @@ void JumpTarget::DoBranch(Condition cc, Hint hint) { // Swap the current frame for a copy (we do the swapping to get // the off-frame registers off the fall through) to use for the // branch. - VirtualFrame* fall_through_frame = cgen_->frame(); + VirtualFrame* fall_through_frame = cgen()->frame(); VirtualFrame* branch_frame = new VirtualFrame(fall_through_frame); - RegisterFile non_frame_registers = RegisterAllocator::Reserved(); - cgen_->SetFrame(branch_frame, &non_frame_registers); + RegisterFile non_frame_registers; + cgen()->SetFrame(branch_frame, &non_frame_registers); // Check if we can avoid merge code. - cgen_->frame()->PrepareMergeTo(entry_frame_); - if (cgen_->frame()->Equals(entry_frame_)) { + cgen()->frame()->PrepareMergeTo(entry_frame_); + if (cgen()->frame()->Equals(entry_frame_)) { // Branch right in to the block. - cgen_->DeleteFrame(); + cgen()->DeleteFrame(); __ j(cc, &entry_label_, hint); - cgen_->SetFrame(fall_through_frame, &non_frame_registers); + cgen()->SetFrame(fall_through_frame, &non_frame_registers); return; } // Check if we can reuse existing merge code. for (int i = 0; i < reaching_frames_.length(); i++) { if (reaching_frames_[i] != NULL && - cgen_->frame()->Equals(reaching_frames_[i])) { + cgen()->frame()->Equals(reaching_frames_[i])) { // Branch to the merge code. - cgen_->DeleteFrame(); + cgen()->DeleteFrame(); __ j(cc, &merge_labels_[i], hint); - cgen_->SetFrame(fall_through_frame, &non_frame_registers); + cgen()->SetFrame(fall_through_frame, &non_frame_registers); return; } } @@ -108,21 +113,30 @@ void JumpTarget::DoBranch(Condition cc, Hint hint) { // around the merge code on the fall through path. Label original_fall_through; __ j(NegateCondition(cc), &original_fall_through, NegateHint(hint)); - cgen_->frame()->MergeTo(entry_frame_); - cgen_->DeleteFrame(); + cgen()->frame()->MergeTo(entry_frame_); + cgen()->DeleteFrame(); __ jmp(&entry_label_); - cgen_->SetFrame(fall_through_frame, &non_frame_registers); + cgen()->SetFrame(fall_through_frame, &non_frame_registers); __ bind(&original_fall_through); + } else if (entry_frame_ != NULL) { + // Forward branch with a preconfigured entry frame. Assert the + // current frame matches the expected one and branch to the block. + ASSERT(cgen()->frame()->Equals(entry_frame_)); + // Explicitly use the macro assembler instead of __ as forward + // branches are expected to be a fixed size (no inserted + // coverage-checking instructions please). This is used in + // Reference::GetValue. + cgen()->masm()->j(cc, &entry_label_, hint); + } else { - // Forward branch. A copy of the current frame is added to the end of the - // list of frames reaching the target block and a branch to the merge code - // is emitted. Use masm_-> instead of __ as forward branches are expected - // to be a fixed size (no inserted coverage-checking instructions please). - // This is used in Reference::GetValue. - AddReachingFrame(new VirtualFrame(cgen_->frame())); - masm_->j(cc, &merge_labels_.last(), hint); - is_linked_ = true; + // Forward branch. A copy of the current frame is remembered and + // a branch to the merge code is emitted. Explicitly use the + // macro assembler instead of __ as forward branches are expected + // to be a fixed size (no inserted coverage-checking instructions + // please). This is used in Reference::GetValue. + AddReachingFrame(new VirtualFrame(cgen()->frame())); + cgen()->masm()->j(cc, &merge_labels_.last(), hint); } } @@ -134,82 +148,107 @@ void JumpTarget::Call() { // at the label (which should be the only one) is the spilled current // frame plus an in-memory return address. The "fall-through" frame // at the return site is the spilled current frame. - ASSERT(cgen_ != NULL); - ASSERT(cgen_->has_valid_frame()); + ASSERT(cgen() != NULL); + ASSERT(cgen()->has_valid_frame()); // There are no non-frame references across the call. - ASSERT(cgen_->HasValidEntryRegisters()); + ASSERT(cgen()->HasValidEntryRegisters()); ASSERT(!is_linked()); - cgen_->frame()->SpillAll(); - VirtualFrame* target_frame = new VirtualFrame(cgen_->frame()); + cgen()->frame()->SpillAll(); + VirtualFrame* target_frame = new VirtualFrame(cgen()->frame()); target_frame->Adjust(1); + // We do not expect a call with a preconfigured entry frame. + ASSERT(entry_frame_ == NULL); AddReachingFrame(target_frame); __ call(&merge_labels_.last()); - - is_linked_ = !is_bound_; } void JumpTarget::DoBind(int mergable_elements) { - ASSERT(cgen_ != NULL); + ASSERT(cgen() != NULL); ASSERT(!is_bound()); // Live non-frame registers are not allowed at the start of a basic // block. - ASSERT(!cgen_->has_valid_frame() || cgen_->HasValidEntryRegisters()); + ASSERT(!cgen()->has_valid_frame() || cgen()->HasValidEntryRegisters()); - if (direction_ == FORWARD_ONLY) { - // A simple case: no forward jumps and no possible backward jumps. - if (!is_linked()) { - // The stack pointer can be floating above the top of the - // virtual frame before the bind. Afterward, it should not. - ASSERT(cgen_->has_valid_frame()); - VirtualFrame* frame = cgen_->frame(); - int difference = - frame->stack_pointer_ - (frame->elements_.length() - 1); - if (difference > 0) { - frame->stack_pointer_ -= difference; - __ add(Operand(esp), Immediate(difference * kPointerSize)); - } + // Fast case: the jump target was manually configured with an entry + // frame to use. + if (entry_frame_ != NULL) { + // Assert no reaching frames to deal with. + ASSERT(reaching_frames_.is_empty()); + ASSERT(!cgen()->has_valid_frame()); - is_bound_ = true; - return; + RegisterFile empty; + if (direction_ == BIDIRECTIONAL) { + // Copy the entry frame so the original can be used for a + // possible backward jump. + cgen()->SetFrame(new VirtualFrame(entry_frame_), &empty); + } else { + // Take ownership of the entry frame. + cgen()->SetFrame(entry_frame_, &empty); + entry_frame_ = NULL; } + __ bind(&entry_label_); + return; + } - // Another simple case: no fall through, a single forward jump, - // and no possible backward jumps. - if (!cgen_->has_valid_frame() && reaching_frames_.length() == 1) { - // Pick up the only reaching frame, take ownership of it, and - // use it for the block about to be emitted. - VirtualFrame* frame = reaching_frames_[0]; - RegisterFile reserved = RegisterAllocator::Reserved(); - cgen_->SetFrame(frame, &reserved); - reaching_frames_[0] = NULL; - __ bind(&merge_labels_[0]); - + if (!is_linked()) { + ASSERT(cgen()->has_valid_frame()); + if (direction_ == FORWARD_ONLY) { + // Fast case: no forward jumps and no possible backward jumps. // The stack pointer can be floating above the top of the // virtual frame before the bind. Afterward, it should not. - int difference = - frame->stack_pointer_ - (frame->elements_.length() - 1); + VirtualFrame* frame = cgen()->frame(); + int difference = frame->stack_pointer_ - (frame->element_count() - 1); if (difference > 0) { frame->stack_pointer_ -= difference; __ add(Operand(esp), Immediate(difference * kPointerSize)); } + } else { + ASSERT(direction_ == BIDIRECTIONAL); + // Fast case: no forward jumps, possible backward ones. Remove + // constants and copies above the watermark on the fall-through + // frame and use it as the entry frame. + cgen()->frame()->MakeMergable(mergable_elements); + entry_frame_ = new VirtualFrame(cgen()->frame()); + } + __ bind(&entry_label_); + return; + } - is_linked_ = false; - is_bound_ = true; - return; + if (direction_ == FORWARD_ONLY && + !cgen()->has_valid_frame() && + reaching_frames_.length() == 1) { + // Fast case: no fall-through, a single forward jump, and no + // possible backward jumps. Pick up the only reaching frame, take + // ownership of it, and use it for the block about to be emitted. + VirtualFrame* frame = reaching_frames_[0]; + RegisterFile empty; + cgen()->SetFrame(frame, &empty); + reaching_frames_[0] = NULL; + __ bind(&merge_labels_[0]); + + // The stack pointer can be floating above the top of the + // virtual frame before the bind. Afterward, it should not. + int difference = frame->stack_pointer_ - (frame->element_count() - 1); + if (difference > 0) { + frame->stack_pointer_ -= difference; + __ add(Operand(esp), Immediate(difference * kPointerSize)); } + + __ bind(&entry_label_); + return; } // If there is a current frame, record it as the fall-through. It // is owned by the reaching frames for now. bool had_fall_through = false; - if (cgen_->has_valid_frame()) { + if (cgen()->has_valid_frame()) { had_fall_through = true; - AddReachingFrame(cgen_->frame()); + AddReachingFrame(cgen()->frame()); // Return value ignored. RegisterFile empty; - cgen_->SetFrame(NULL, &empty); + cgen()->SetFrame(NULL, &empty); } // Compute the frame to use for entry to the block. @@ -244,17 +283,17 @@ void JumpTarget::DoBind(int mergable_elements) { // binding site or as the fall through from a previous merge // code block. Jump around the code we are about to // generate. - if (cgen_->has_valid_frame()) { - cgen_->DeleteFrame(); + if (cgen()->has_valid_frame()) { + cgen()->DeleteFrame(); __ jmp(&entry_label_); } // Pick up the frame for this block. Assume ownership if // there cannot be backward jumps. - RegisterFile reserved = RegisterAllocator::Reserved(); + RegisterFile empty; if (direction_ == BIDIRECTIONAL) { - cgen_->SetFrame(new VirtualFrame(frame), &reserved); + cgen()->SetFrame(new VirtualFrame(frame), &empty); } else { - cgen_->SetFrame(frame, &reserved); + cgen()->SetFrame(frame, &empty); reaching_frames_[i] = NULL; } __ bind(&merge_labels_[i]); @@ -263,23 +302,22 @@ void JumpTarget::DoBind(int mergable_elements) { // looking for any that can share merge code with this one. for (int j = 0; j < i; j++) { VirtualFrame* other = reaching_frames_[j]; - if (other != NULL && other->Equals(cgen_->frame())) { + if (other != NULL && other->Equals(cgen()->frame())) { // Set the reaching frame element to null to avoid // processing it later, and then bind its entry label. - delete other; reaching_frames_[j] = NULL; __ bind(&merge_labels_[j]); } } // Emit the merge code. - cgen_->frame()->MergeTo(entry_frame_); + cgen()->frame()->MergeTo(entry_frame_); } else if (i == reaching_frames_.length() - 1 && had_fall_through) { // If this is the fall through frame, and it didn't need // merge code, we need to pick up the frame so we can jump // around subsequent merge blocks if necessary. - RegisterFile reserved = RegisterAllocator::Reserved(); - cgen_->SetFrame(frame, &reserved); + RegisterFile empty; + cgen()->SetFrame(frame, &empty); reaching_frames_[i] = NULL; } } @@ -288,22 +326,17 @@ void JumpTarget::DoBind(int mergable_elements) { // The code generator may not have a current frame if there was no // fall through and none of the reaching frames needed merging. // In that case, clone the entry frame as the current frame. - if (!cgen_->has_valid_frame()) { - RegisterFile reserved_registers = RegisterAllocator::Reserved(); - cgen_->SetFrame(new VirtualFrame(entry_frame_), &reserved_registers); + if (!cgen()->has_valid_frame()) { + RegisterFile empty; + cgen()->SetFrame(new VirtualFrame(entry_frame_), &empty); } - // There is certainly a current frame equal to the entry frame. - // Bind the entry frame label. - __ bind(&entry_label_); - // There may be unprocessed reaching frames that did not need // merge code. They will have unbound merge labels. Bind their // merge labels to be the same as the entry label and deallocate // them. for (int i = 0; i < reaching_frames_.length(); i++) { if (!merge_labels_[i].is_bound()) { - delete reaching_frames_[i]; reaching_frames_[i] = NULL; __ bind(&merge_labels_[i]); } @@ -320,15 +353,13 @@ void JumpTarget::DoBind(int mergable_elements) { // Use a copy of the reaching frame so the original can be saved // for possible reuse as a backward merge block. - RegisterFile reserved = RegisterAllocator::Reserved(); - cgen_->SetFrame(new VirtualFrame(reaching_frames_[0]), &reserved); + RegisterFile empty; + cgen()->SetFrame(new VirtualFrame(reaching_frames_[0]), &empty); __ bind(&merge_labels_[0]); - cgen_->frame()->MergeTo(entry_frame_); - __ bind(&entry_label_); + cgen()->frame()->MergeTo(entry_frame_); } - is_linked_ = false; - is_bound_ = true; + __ bind(&entry_label_); } #undef __ diff --git a/deps/v8/src/ia32/macro-assembler-ia32.cc b/deps/v8/src/ia32/macro-assembler-ia32.cc index d6d5800fe6..7636c4ed80 100644 --- a/deps/v8/src/ia32/macro-assembler-ia32.cc +++ b/deps/v8/src/ia32/macro-assembler-ia32.cc @@ -33,7 +33,8 @@ #include "runtime.h" #include "serialize.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // ------------------------------------------------------------------------- // MacroAssembler implementation. @@ -560,8 +561,8 @@ Register MacroAssembler::CheckMaps(JSObject* object, Register object_reg, void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg, - Register scratch, - Label* miss) { + Register scratch, + Label* miss) { Label same_contexts; ASSERT(!holder_reg.is(scratch)); @@ -631,7 +632,7 @@ void MacroAssembler::NegativeZeroTest(CodeGenerator* cgen, Register result, Register op, JumpTarget* then_target) { - JumpTarget ok(cgen); + JumpTarget ok; test(result, Operand(result)); ok.Branch(not_zero, taken); test(op, Operand(op)); diff --git a/deps/v8/src/ia32/macro-assembler-ia32.h b/deps/v8/src/ia32/macro-assembler-ia32.h index cd7a233916..940a8b4627 100644 --- a/deps/v8/src/ia32/macro-assembler-ia32.h +++ b/deps/v8/src/ia32/macro-assembler-ia32.h @@ -30,7 +30,8 @@ #include "assembler.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Forward declaration. class JumpTarget; diff --git a/deps/v8/src/ia32/regexp-macro-assembler-ia32.cc b/deps/v8/src/ia32/regexp-macro-assembler-ia32.cc index 2a0cefd776..04a5390a2d 100644 --- a/deps/v8/src/ia32/regexp-macro-assembler-ia32.cc +++ b/deps/v8/src/ia32/regexp-macro-assembler-ia32.cc @@ -35,7 +35,8 @@ #include "ia32/macro-assembler-ia32.h" #include "ia32/regexp-macro-assembler-ia32.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { /* * This assembler uses the following register assignment convention @@ -1194,10 +1195,11 @@ int RegExpMacroAssemblerIA32::CheckStackGuardState(Address* return_address, const byte* new_address = StringCharacterPosition(*subject, start_index); if (start_address != new_address) { - // If there is a difference, update start and end addresses in the - // RegExp stack frame to match the new value. + // If there is a difference, update the object pointer and start and end + // addresses in the RegExp stack frame to match the new value. const byte* end_address = frame_entry<const byte* >(re_frame, kInputEnd); int byte_length = end_address - start_address; + frame_entry<const String*>(re_frame, kInputString) = *subject; frame_entry<const byte*>(re_frame, kInputStart) = new_address; frame_entry<const byte*>(re_frame, kInputEnd) = new_address + byte_length; } diff --git a/deps/v8/src/ia32/regexp-macro-assembler-ia32.h b/deps/v8/src/ia32/regexp-macro-assembler-ia32.h index 8c5dd24cc9..a06700a547 100644 --- a/deps/v8/src/ia32/regexp-macro-assembler-ia32.h +++ b/deps/v8/src/ia32/regexp-macro-assembler-ia32.h @@ -28,7 +28,8 @@ #ifndef V8_IA32_REGEXP_MACRO_ASSEMBLER_IA32_H_ #define V8_IA32_REGEXP_MACRO_ASSEMBLER_IA32_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { class RegExpMacroAssemblerIA32: public RegExpMacroAssembler { public: diff --git a/deps/v8/src/ia32/register-allocator-ia32-inl.h b/deps/v8/src/ia32/register-allocator-ia32-inl.h new file mode 100644 index 0000000000..ddee472d2d --- /dev/null +++ b/deps/v8/src/ia32/register-allocator-ia32-inl.h @@ -0,0 +1,82 @@ +// 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_REGISTER_ALLOCATOR_IA32_INL_H_ +#define V8_IA32_REGISTER_ALLOCATOR_IA32_INL_H_ + +#include "v8.h" + +namespace v8 { +namespace internal { + +// ------------------------------------------------------------------------- +// RegisterAllocator implementation. + +bool RegisterAllocator::IsReserved(Register reg) { + // The code for this test relies on the order of register codes. + return reg.code() >= esp.code() && reg.code() <= esi.code(); +} + + +// The register allocator uses small integers to represent the +// non-reserved assembler registers. The mapping is: + +// eax <-> 0, ebx <-> 1, ecx <-> 2, edx <-> 3, edi <-> 4. + +int RegisterAllocator::ToNumber(Register reg) { + ASSERT(reg.is_valid() && !IsReserved(reg)); + static int numbers[] = { + 0, // eax + 2, // ecx + 3, // edx + 1, // ebx + -1, // esp + -1, // ebp + -1, // esi + 4 // edi + }; + return numbers[reg.code()]; +} + + +Register RegisterAllocator::ToRegister(int num) { + ASSERT(num >= 0 && num < kNumRegisters); + static Register registers[] = { eax, ebx, ecx, edx, edi }; + return registers[num]; +} + + +void RegisterAllocator::Initialize() { + Reset(); + // The non-reserved edi register is live on JS function entry. + Use(edi); // JS function. +} + + +} } // namespace v8::internal + +#endif // V8_IA32_REGISTER_ALLOCATOR_IA32_INL_H_ diff --git a/deps/v8/src/ia32/register-allocator-ia32.cc b/deps/v8/src/ia32/register-allocator-ia32.cc index b72d76556d..2914960eac 100644 --- a/deps/v8/src/ia32/register-allocator-ia32.cc +++ b/deps/v8/src/ia32/register-allocator-ia32.cc @@ -30,7 +30,8 @@ #include "codegen-inl.h" #include "register-allocator-inl.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // ------------------------------------------------------------------------- // Result implementation. @@ -38,12 +39,13 @@ namespace v8 { namespace internal { void Result::ToRegister() { ASSERT(is_valid()); if (is_constant()) { - Result fresh = cgen_->allocator()->Allocate(); + Result fresh = CodeGeneratorScope::Current()->allocator()->Allocate(); ASSERT(fresh.is_valid()); - if (cgen_->IsUnsafeSmi(handle())) { - cgen_->LoadUnsafeSmi(fresh.reg(), handle()); + if (CodeGeneratorScope::Current()->IsUnsafeSmi(handle())) { + CodeGeneratorScope::Current()->LoadUnsafeSmi(fresh.reg(), handle()); } else { - cgen_->masm()->Set(fresh.reg(), Immediate(handle())); + CodeGeneratorScope::Current()->masm()->Set(fresh.reg(), + Immediate(handle())); } // This result becomes a copy of the fresh one. *this = fresh; @@ -55,23 +57,24 @@ void Result::ToRegister() { void Result::ToRegister(Register target) { ASSERT(is_valid()); if (!is_register() || !reg().is(target)) { - Result fresh = cgen_->allocator()->Allocate(target); + Result fresh = CodeGeneratorScope::Current()->allocator()->Allocate(target); ASSERT(fresh.is_valid()); if (is_register()) { - cgen_->masm()->mov(fresh.reg(), reg()); + CodeGeneratorScope::Current()->masm()->mov(fresh.reg(), reg()); } else { ASSERT(is_constant()); - if (cgen_->IsUnsafeSmi(handle())) { - cgen_->LoadUnsafeSmi(fresh.reg(), handle()); + if (CodeGeneratorScope::Current()->IsUnsafeSmi(handle())) { + CodeGeneratorScope::Current()->LoadUnsafeSmi(fresh.reg(), handle()); } else { - cgen_->masm()->Set(fresh.reg(), Immediate(handle())); + CodeGeneratorScope::Current()->masm()->Set(fresh.reg(), + Immediate(handle())); } } *this = fresh; } else if (is_register() && reg().is(target)) { - ASSERT(cgen_->has_valid_frame()); - cgen_->frame()->Spill(target); - ASSERT(cgen_->allocator()->count(target) == 1); + ASSERT(CodeGeneratorScope::Current()->has_valid_frame()); + CodeGeneratorScope::Current()->frame()->Spill(target); + ASSERT(CodeGeneratorScope::Current()->allocator()->count(target) == 1); } ASSERT(is_register()); ASSERT(reg().is(target)); @@ -81,53 +84,13 @@ void Result::ToRegister(Register target) { // ------------------------------------------------------------------------- // RegisterAllocator implementation. -RegisterFile RegisterAllocator::Reserved() { - RegisterFile reserved; - reserved.Use(esp); - reserved.Use(ebp); - reserved.Use(esi); - return reserved; -} - - -void RegisterAllocator::UnuseReserved(RegisterFile* register_file) { - register_file->ref_counts_[esp.code()] = 0; - register_file->ref_counts_[ebp.code()] = 0; - register_file->ref_counts_[esi.code()] = 0; -} - - -bool RegisterAllocator::IsReserved(int reg_code) { - // Test below relies on the order of register codes. - return reg_code >= esp.code() && reg_code <= esi.code(); -} - - -void RegisterAllocator::Initialize() { - Reset(); - // The following register is live on function entry, saved in the - // frame, and available for allocation during execution. - Use(edi); // JS function. -} - - -void RegisterAllocator::Reset() { - registers_.Reset(); - // The following registers are live on function entry and reserved - // during execution. - Use(esp); // Stack pointer. - Use(ebp); // Frame pointer (caller's frame pointer on entry). - Use(esi); // Context (callee's context on entry). -} - - Result RegisterAllocator::AllocateByteRegisterWithoutSpilling() { Result result = AllocateWithoutSpilling(); // Check that the register is a byte register. If not, unuse the // register if valid and return an invalid result. if (result.is_valid() && !result.reg().is_byte_register()) { result.Unuse(); - return Result(cgen_); + return Result(); } return result; } diff --git a/deps/v8/src/ia32/register-allocator-ia32.h b/deps/v8/src/ia32/register-allocator-ia32.h new file mode 100644 index 0000000000..e7ce91f4c5 --- /dev/null +++ b/deps/v8/src/ia32/register-allocator-ia32.h @@ -0,0 +1,43 @@ +// 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_REGISTER_ALLOCATOR_IA32_H_ +#define V8_IA32_REGISTER_ALLOCATOR_IA32_H_ + +namespace v8 { +namespace internal { + +class RegisterAllocatorConstants : public AllStatic { + public: + static const int kNumRegisters = 5; + static const int kInvalidRegister = -1; +}; + + +} } // namespace v8::internal + +#endif // V8_IA32_REGISTER_ALLOCATOR_IA32_H_ diff --git a/deps/v8/src/ia32/stub-cache-ia32.cc b/deps/v8/src/ia32/stub-cache-ia32.cc index bdfc3d6c31..b31f7062c1 100644 --- a/deps/v8/src/ia32/stub-cache-ia32.cc +++ b/deps/v8/src/ia32/stub-cache-ia32.cc @@ -1,4 +1,4 @@ -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2006-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: @@ -31,7 +31,8 @@ #include "codegen-inl.h" #include "stub-cache.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { #define __ ACCESS_MASM(masm) @@ -58,7 +59,7 @@ static void ProbeTable(MacroAssembler* masm, // Check that the flags match what we're looking for. __ mov(offset, FieldOperand(offset, Code::kFlagsOffset)); - __ and_(offset, ~Code::kFlagsTypeMask); + __ and_(offset, ~Code::kFlagsNotUsedInLookup); __ cmp(offset, flags); __ j(not_equal, &miss); @@ -321,6 +322,7 @@ void StubCompiler::GenerateLoadConstant(MacroAssembler* masm, void StubCompiler::GenerateLoadInterceptor(MacroAssembler* masm, JSObject* object, JSObject* holder, + Smi* lookup_hint, Register receiver, Register name, Register scratch1, @@ -339,12 +341,15 @@ void StubCompiler::GenerateLoadInterceptor(MacroAssembler* masm, __ push(receiver); // receiver __ push(reg); // holder __ push(name); // name + // TODO(367): Maybe don't push lookup_hint for LOOKUP_IN_HOLDER and/or + // LOOKUP_IN_PROTOTYPE, but use a special version of lookup method? + __ push(Immediate(lookup_hint)); __ push(scratch2); // restore return address // Do tail-call to the runtime system. ExternalReference load_ic_property = ExternalReference(IC_Utility(IC::kLoadInterceptorProperty)); - __ TailCallRuntime(load_ic_property, 3); + __ TailCallRuntime(load_ic_property, 4); } @@ -470,7 +475,9 @@ Object* StubCompiler::CompileLazyCompile(Code::Flags flags) { Object* CallStubCompiler::CompileCallField(Object* object, JSObject* holder, int index, - String* name) { + String* name, + Code::Flags flags) { + ASSERT_EQ(FIELD, Code::ExtractTypeFromFlags(flags)); // ----------- S t a t e ------------- // ----------------------------------- Label miss; @@ -511,14 +518,16 @@ Object* CallStubCompiler::CompileCallField(Object* object, __ jmp(ic, RelocInfo::CODE_TARGET); // Return the generated code. - return GetCode(FIELD, name); + return GetCodeWithFlags(flags, name); } Object* CallStubCompiler::CompileCallConstant(Object* object, JSObject* holder, JSFunction* function, - CheckType check) { + CheckType check, + Code::Flags flags) { + ASSERT_EQ(CONSTANT_FUNCTION, Code::ExtractTypeFromFlags(flags)); // ----------- S t a t e ------------- // ----------------------------------- Label miss; @@ -633,7 +642,7 @@ Object* CallStubCompiler::CompileCallConstant(Object* object, if (function->shared()->name()->IsString()) { function_name = String::cast(function->shared()->name()); } - return GetCode(CONSTANT_FUNCTION, function_name); + return GetCodeWithFlags(flags, function_name); } @@ -665,11 +674,12 @@ Object* CallStubCompiler::CompileCallInterceptor(Object* object, __ push(edx); // receiver __ push(reg); // holder __ push(Operand(ebp, (argc + 3) * kPointerSize)); // name + __ push(Immediate(holder->InterceptorPropertyLookupHint(name))); // Perform call. ExternalReference load_interceptor = ExternalReference(IC_Utility(IC::kLoadInterceptorProperty)); - __ mov(eax, Immediate(3)); + __ mov(eax, Immediate(4)); __ mov(ebx, Immediate(load_interceptor)); CEntryStub stub; @@ -969,7 +979,18 @@ Object* LoadStubCompiler::CompileLoadInterceptor(JSObject* receiver, Label miss; __ mov(eax, (Operand(esp, kPointerSize))); - GenerateLoadInterceptor(masm(), receiver, holder, eax, ecx, edx, ebx, &miss); + // TODO(368): Compile in the whole chain: all the interceptors in + // prototypes and ultimate answer. + GenerateLoadInterceptor(masm(), + receiver, + holder, + holder->InterceptorPropertyLookupHint(name), + eax, + ecx, + edx, + ebx, + &miss); + __ bind(&miss); GenerateLoadMiss(masm(), Code::LOAD_IC); @@ -1084,7 +1105,15 @@ Object* KeyedLoadStubCompiler::CompileLoadInterceptor(JSObject* receiver, __ cmp(Operand(eax), Immediate(Handle<String>(name))); __ j(not_equal, &miss, not_taken); - GenerateLoadInterceptor(masm(), receiver, holder, ecx, eax, edx, ebx, &miss); + GenerateLoadInterceptor(masm(), + receiver, + holder, + Smi::FromInt(JSObject::kLookupInHolder), + ecx, + eax, + edx, + ebx, + &miss); __ bind(&miss); __ DecrementCounter(&Counters::keyed_load_interceptor, 1); GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); diff --git a/deps/v8/src/ia32/virtual-frame-ia32.cc b/deps/v8/src/ia32/virtual-frame-ia32.cc index ff9f60cdfe..5f85de70db 100644 --- a/deps/v8/src/ia32/virtual-frame-ia32.cc +++ b/deps/v8/src/ia32/virtual-frame-ia32.cc @@ -31,29 +31,23 @@ #include "register-allocator-inl.h" #include "scopes.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { -#define __ ACCESS_MASM(masm_) +#define __ ACCESS_MASM(masm()) // ------------------------------------------------------------------------- // VirtualFrame implementation. // 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) - : cgen_(cgen), - masm_(cgen->masm()), - elements_(cgen->scope()->num_parameters() - + cgen->scope()->num_stack_slots() - + kPreallocatedElements), - parameter_count_(cgen->scope()->num_parameters()), - local_count_(0), - stack_pointer_(parameter_count_ + 1), // 0-based index of TOS. - frame_pointer_(kIllegalIndex) { - for (int i = 0; i < parameter_count_ + 2; i++) { +VirtualFrame::VirtualFrame() + : elements_(parameter_count() + local_count() + kPreallocatedElements), + stack_pointer_(parameter_count() + 1) { // 0-based index of TOS. + for (int i = 0; i <= stack_pointer_; i++) { elements_.Add(FrameElement::MemoryElement()); } - for (int i = 0; i < kNumRegisters; i++) { + for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) { register_locations_[i] = kIllegalIndex; } } @@ -80,10 +74,10 @@ void VirtualFrame::SyncElementBelowStackPointer(int index) { break; case FrameElement::CONSTANT: - if (cgen_->IsUnsafeSmi(element.handle())) { - Result temp = cgen_->allocator()->Allocate(); + if (cgen()->IsUnsafeSmi(element.handle())) { + Result temp = cgen()->allocator()->Allocate(); ASSERT(temp.is_valid()); - cgen_->LoadUnsafeSmi(temp.reg(), element.handle()); + cgen()->LoadUnsafeSmi(temp.reg(), element.handle()); __ mov(Operand(ebp, fp_relative(index)), temp.reg()); } else { __ Set(Operand(ebp, fp_relative(index)), @@ -95,7 +89,7 @@ void VirtualFrame::SyncElementBelowStackPointer(int index) { int backing_index = element.index(); FrameElement backing_element = elements_[backing_index]; if (backing_element.is_memory()) { - Result temp = cgen_->allocator()->Allocate(); + Result temp = cgen()->allocator()->Allocate(); ASSERT(temp.is_valid()); __ mov(temp.reg(), Operand(ebp, fp_relative(backing_index))); __ mov(Operand(ebp, fp_relative(index)), temp.reg()); @@ -132,10 +126,10 @@ void VirtualFrame::SyncElementByPushing(int index) { break; case FrameElement::CONSTANT: - if (cgen_->IsUnsafeSmi(element.handle())) { - Result temp = cgen_->allocator()->Allocate(); + if (cgen()->IsUnsafeSmi(element.handle())) { + Result temp = cgen()->allocator()->Allocate(); ASSERT(temp.is_valid()); - cgen_->LoadUnsafeSmi(temp.reg(), element.handle()); + cgen()->LoadUnsafeSmi(temp.reg(), element.handle()); __ push(temp.reg()); } else { __ push(Immediate(element.handle())); @@ -162,7 +156,7 @@ void VirtualFrame::SyncElementByPushing(int index) { // [min(stack_pointer_ + 1,begin), end]. void VirtualFrame::SyncRange(int begin, int end) { ASSERT(begin >= 0); - ASSERT(end < elements_.length()); + ASSERT(end < element_count()); // Sync elements below the range if they have not been materialized // on the stack. int start = Min(begin, stack_pointer_ + 1); @@ -180,11 +174,75 @@ void VirtualFrame::SyncRange(int begin, int end) { } +void VirtualFrame::MakeMergable(int mergable_elements) { + if (mergable_elements == JumpTarget::kAllElements) { + mergable_elements = element_count(); + } + ASSERT(mergable_elements <= element_count()); + + int start_index = element_count() - mergable_elements; + for (int i = start_index; i < element_count(); i++) { + FrameElement element = elements_[i]; + + if (element.is_constant() || element.is_copy()) { + if (element.is_synced()) { + // Just spill. + elements_[i] = FrameElement::MemoryElement(); + } else { + // Allocate to a register. + FrameElement backing_element; // Invalid if not a copy. + if (element.is_copy()) { + backing_element = elements_[element.index()]; + } + Result fresh = cgen()->allocator()->Allocate(); + ASSERT(fresh.is_valid()); + elements_[i] = + FrameElement::RegisterElement(fresh.reg(), + FrameElement::NOT_SYNCED); + Use(fresh.reg(), i); + + // Emit a move. + if (element.is_constant()) { + if (cgen()->IsUnsafeSmi(element.handle())) { + cgen()->LoadUnsafeSmi(fresh.reg(), element.handle()); + } else { + __ Set(fresh.reg(), Immediate(element.handle())); + } + } else { + ASSERT(element.is_copy()); + // Copies are only backed by register or memory locations. + if (backing_element.is_register()) { + // The backing store may have been spilled by allocating, + // but that's OK. If it was, the value is right where we + // want it. + if (!fresh.reg().is(backing_element.reg())) { + __ mov(fresh.reg(), backing_element.reg()); + } + } else { + ASSERT(backing_element.is_memory()); + __ mov(fresh.reg(), Operand(ebp, fp_relative(element.index()))); + } + } + } + // No need to set the copied flag---there are no copies of + // copies or constants so the original was not copied. + elements_[i].set_static_type(element.static_type()); + } else { + // Clear the copy flag of non-constant, non-copy elements above + // the high water mark. They cannot be copied because copes are + // always higher than their backing store and copies are not + // allowed above the water mark. + elements_[i].clear_copied(); + } + } +} + + void VirtualFrame::MergeTo(VirtualFrame* expected) { - Comment cmnt(masm_, "[ Merge frame"); + Comment cmnt(masm(), "[ Merge frame"); // We should always be merging the code generator's current frame to an // expected frame. - ASSERT(cgen_->frame() == this); + ASSERT(cgen()->frame() == this); // Adjust the stack pointer upward (toward the top of the virtual // frame) if necessary. @@ -198,23 +256,6 @@ void VirtualFrame::MergeTo(VirtualFrame* expected) { MergeMoveRegistersToRegisters(expected); MergeMoveMemoryToRegisters(expected); - // Fix any sync flag problems from the bottom-up and make the copied - // flags exact. This assumes that the backing store of copies is - // always lower in the frame. - for (int i = 0; i < elements_.length(); i++) { - FrameElement source = elements_[i]; - FrameElement target = expected->elements_[i]; - if (source.is_synced() && !target.is_synced()) { - elements_[i].clear_sync(); - } else if (!source.is_synced() && target.is_synced()) { - SyncElementAt(i); - } - elements_[i].clear_copied(); - if (elements_[i].is_copy()) { - elements_[elements_[i].index()].set_copied(); - } - } - // Adjust the stack pointer downward if necessary. if (stack_pointer_ > expected->stack_pointer_) { int difference = stack_pointer_ - expected->stack_pointer_; @@ -240,13 +281,9 @@ void VirtualFrame::MergeMoveRegistersToMemory(VirtualFrame* expected) { // of the index of the frame element esi is caching or kIllegalIndex // if esi has not been disturbed. int esi_caches = kIllegalIndex; - // A "singleton" memory element. - FrameElement memory_element = FrameElement::MemoryElement(); - // Loop downward from the stack pointer or the top of the frame if - // the stack pointer is floating above the frame. - int start = Min(stack_pointer_, elements_.length() - 1); - for (int i = start; i >= 0; i--) { + for (int i = element_count() - 1; i >= 0; i--) { FrameElement target = expected->elements_[i]; + if (target.is_register()) continue; // Handle registers later. if (target.is_memory()) { FrameElement source = elements_[i]; switch (source.type()) { @@ -268,9 +305,9 @@ void VirtualFrame::MergeMoveRegistersToMemory(VirtualFrame* expected) { case FrameElement::CONSTANT: if (!source.is_synced()) { - if (cgen_->IsUnsafeSmi(source.handle())) { + if (cgen()->IsUnsafeSmi(source.handle())) { esi_caches = i; - cgen_->LoadUnsafeSmi(esi, source.handle()); + cgen()->LoadUnsafeSmi(esi, source.handle()); __ mov(Operand(ebp, fp_relative(i)), esi); } else { __ Set(Operand(ebp, fp_relative(i)), Immediate(source.handle())); @@ -296,8 +333,8 @@ void VirtualFrame::MergeMoveRegistersToMemory(VirtualFrame* expected) { } break; } - elements_[i] = memory_element; } + elements_[i] = target; } if (esi_caches != kIllegalIndex) { @@ -310,64 +347,77 @@ void VirtualFrame::MergeMoveRegistersToRegisters(VirtualFrame* expected) { // We have already done X-to-memory moves. ASSERT(stack_pointer_ >= expected->stack_pointer_); - for (int i = 0; i < kNumRegisters; i++) { + for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) { // Move the right value into register i if it is currently in a register. - int index = expected->register_locations_[i]; - int use_index = register_locations_[i]; - // Fast check if register is unused in target or already correct - if (index != kIllegalIndex - && index != use_index - && elements_[index].is_register()) { - Register source = elements_[index].reg(); - Register target = { i }; + int index = expected->register_location(i); + int use_index = register_location(i); + // Skip if register i is unused in the target or else if source is + // not a register (this is not a register-to-register move). + if (index == kIllegalIndex || !elements_[index].is_register()) continue; + + Register target = RegisterAllocator::ToRegister(i); + Register source = elements_[index].reg(); + if (index != use_index) { if (use_index == kIllegalIndex) { // Target is currently unused. // Copy contents of source from source to target. // Set frame element register to target. - elements_[index].set_reg(target); Use(target, index); Unuse(source); __ mov(target, source); } else { // Exchange contents of registers source and target. + // Nothing except the register backing use_index has changed. elements_[use_index].set_reg(source); - elements_[index].set_reg(target); - register_locations_[target.code()] = index; - register_locations_[source.code()] = use_index; + set_register_location(target, index); + set_register_location(source, use_index); __ xchg(source, target); } } + + if (!elements_[index].is_synced() && + expected->elements_[index].is_synced()) { + __ mov(Operand(ebp, fp_relative(index)), target); + } + elements_[index] = expected->elements_[index]; } } -void VirtualFrame::MergeMoveMemoryToRegisters(VirtualFrame *expected) { +void VirtualFrame::MergeMoveMemoryToRegisters(VirtualFrame* expected) { // Move memory, constants, and copies to registers. This is the - // final step and is done from the bottom up so that the backing + // final step and since it is not done from the bottom up, but in + // register code order, we have special code to ensure that the backing // elements of copies are in their correct locations when we // encounter the copies. - for (int i = 0; i < kNumRegisters; i++) { - int index = expected->register_locations_[i]; + for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) { + int index = expected->register_location(i); if (index != kIllegalIndex) { FrameElement source = elements_[index]; FrameElement target = expected->elements_[index]; + Register target_reg = RegisterAllocator::ToRegister(i); + ASSERT(target.reg().is(target_reg)); switch (source.type()) { case FrameElement::INVALID: // Fall through. UNREACHABLE(); break; case FrameElement::REGISTER: - ASSERT(source.reg().is(target.reg())); - continue; // Go to next iteration. Skips Use(target.reg()) below. + ASSERT(source.Equals(target)); + // Go to next iteration. Skips Use(target_reg) and syncing + // below. It is safe to skip syncing because a target + // register frame element would only be synced if all source + // elements were. + continue; break; case FrameElement::MEMORY: ASSERT(index <= stack_pointer_); - __ mov(target.reg(), Operand(ebp, fp_relative(index))); + __ mov(target_reg, Operand(ebp, fp_relative(index))); break; case FrameElement::CONSTANT: - if (cgen_->IsUnsafeSmi(source.handle())) { - cgen_->LoadUnsafeSmi(target.reg(), source.handle()); + if (cgen()->IsUnsafeSmi(source.handle())) { + cgen()->LoadUnsafeSmi(target_reg, source.handle()); } else { - __ Set(target.reg(), Immediate(source.handle())); + __ Set(target_reg, Immediate(source.handle())); } break; @@ -387,21 +437,20 @@ void VirtualFrame::MergeMoveMemoryToRegisters(VirtualFrame *expected) { Use(new_backing_reg, backing_index); __ mov(new_backing_reg, Operand(ebp, fp_relative(backing_index))); - __ mov(target.reg(), new_backing_reg); + __ mov(target_reg, new_backing_reg); } else { - __ mov(target.reg(), Operand(ebp, fp_relative(backing_index))); + __ mov(target_reg, Operand(ebp, fp_relative(backing_index))); } } else { - __ mov(target.reg(), backing.reg()); + __ mov(target_reg, backing.reg()); } } } - // Ensure the proper sync state. If the source was memory no - // code needs to be emitted. + // Ensure the proper sync state. if (target.is_synced() && !source.is_synced()) { - __ mov(Operand(ebp, fp_relative(index)), target.reg()); + __ mov(Operand(ebp, fp_relative(index)), target_reg); } - Use(target.reg(), index); + Use(target_reg, index); elements_[index] = target; } } @@ -410,7 +459,7 @@ void VirtualFrame::MergeMoveMemoryToRegisters(VirtualFrame *expected) { void VirtualFrame::Enter() { // Registers live on entry: esp, ebp, esi, edi. - Comment cmnt(masm_, "[ Enter JS frame"); + Comment cmnt(masm(), "[ Enter JS frame"); #ifdef DEBUG // Verify that edi contains a JS function. The following code @@ -425,7 +474,6 @@ void VirtualFrame::Enter() { EmitPush(ebp); - frame_pointer_ = stack_pointer_; __ mov(ebp, Operand(esp)); // Store the context in the frame. The context is kept in esi and a @@ -436,13 +484,13 @@ void VirtualFrame::Enter() { // Store the function in the frame. The frame owns the register // reference now (ie, it can keep it in edi or spill it later). Push(edi); - SyncElementAt(elements_.length() - 1); - cgen_->allocator()->Unuse(edi); + SyncElementAt(element_count() - 1); + cgen()->allocator()->Unuse(edi); } void VirtualFrame::Exit() { - Comment cmnt(masm_, "[ Exit JS frame"); + Comment cmnt(masm(), "[ Exit JS frame"); // Record the location of the JS exit code for patching when setting // break point. __ RecordJSReturn(); @@ -452,34 +500,31 @@ void VirtualFrame::Exit() { // call instruction to support patching the exit code in the // debugger. See VisitReturnStatement for the full return sequence. __ mov(esp, Operand(ebp)); - stack_pointer_ = frame_pointer_; - for (int i = elements_.length() - 1; i > stack_pointer_; i--) { + stack_pointer_ = frame_pointer(); + for (int i = element_count() - 1; i > stack_pointer_; i--) { FrameElement last = elements_.RemoveLast(); if (last.is_register()) { Unuse(last.reg()); } } - frame_pointer_ = kIllegalIndex; EmitPop(ebp); } -void VirtualFrame::AllocateStackSlots(int count) { - ASSERT(height() == 0); - local_count_ = count; - +void VirtualFrame::AllocateStackSlots() { + int count = local_count(); if (count > 0) { - Comment cmnt(masm_, "[ Allocate space for locals"); + Comment cmnt(masm(), "[ Allocate space for locals"); // The locals are initialized to a constant (the undefined value), but // we sync them with the actual frame to allocate space for spilling // them later. First sync everything above the stack pointer so we can // use pushes to allocate and initialize the locals. - SyncRange(stack_pointer_ + 1, elements_.length() - 1); + SyncRange(stack_pointer_ + 1, element_count() - 1); Handle<Object> undefined = Factory::undefined_value(); FrameElement initial_value = FrameElement::ConstantElement(undefined, FrameElement::SYNCED); - Result temp = cgen_->allocator()->Allocate(); + Result temp = cgen()->allocator()->Allocate(); ASSERT(temp.is_valid()); __ Set(temp.reg(), Immediate(undefined)); for (int i = 0; i < count; i++) { @@ -504,7 +549,7 @@ void VirtualFrame::RestoreContextRegister() { void VirtualFrame::PushReceiverSlotAddress() { - Result temp = cgen_->allocator()->Allocate(); + Result temp = cgen()->allocator()->Allocate(); ASSERT(temp.is_valid()); __ lea(temp.reg(), ParameterAt(-1)); Push(&temp); @@ -518,7 +563,7 @@ int VirtualFrame::InvalidateFrameSlotAt(int index) { int new_backing_index = kIllegalIndex; if (original.is_copied()) { // Verify it is copied, and find first copy. - for (int i = index + 1; i < elements_.length(); i++) { + for (int i = index + 1; i < element_count(); i++) { if (elements_[i].is_copy() && elements_[i].index() == index) { new_backing_index = i; break; @@ -538,7 +583,7 @@ int VirtualFrame::InvalidateFrameSlotAt(int index) { // This is the backing store of copies. Register backing_reg; if (original.is_memory()) { - Result fresh = cgen_->allocator()->Allocate(); + Result fresh = cgen()->allocator()->Allocate(); ASSERT(fresh.is_valid()); Use(fresh.reg(), new_backing_index); backing_reg = fresh.reg(); @@ -546,7 +591,7 @@ int VirtualFrame::InvalidateFrameSlotAt(int index) { } else { // The original was in a register. backing_reg = original.reg(); - register_locations_[backing_reg.code()] = new_backing_index; + set_register_location(backing_reg, new_backing_index); } // Invalidate the element at index. elements_[index] = FrameElement::InvalidElement(); @@ -559,7 +604,7 @@ int VirtualFrame::InvalidateFrameSlotAt(int index) { FrameElement::RegisterElement(backing_reg, FrameElement::NOT_SYNCED); } // Update the other copies. - for (int i = new_backing_index + 1; i < elements_.length(); i++) { + for (int i = new_backing_index + 1; i < element_count(); i++) { if (elements_[i].is_copy() && elements_[i].index() == index) { elements_[i].set_index(new_backing_index); elements_[new_backing_index].set_copied(); @@ -571,7 +616,7 @@ int VirtualFrame::InvalidateFrameSlotAt(int index) { void VirtualFrame::TakeFrameSlotAt(int index) { ASSERT(index >= 0); - ASSERT(index <= elements_.length()); + ASSERT(index <= element_count()); FrameElement original = elements_[index]; int new_backing_store_index = InvalidateFrameSlotAt(index); if (new_backing_store_index != kIllegalIndex) { @@ -583,18 +628,18 @@ void VirtualFrame::TakeFrameSlotAt(int index) { case FrameElement::MEMORY: { // Emit code to load the original element's data into a register. // Push that register as a FrameElement on top of the frame. - Result fresh = cgen_->allocator()->Allocate(); + Result fresh = cgen()->allocator()->Allocate(); ASSERT(fresh.is_valid()); FrameElement new_element = FrameElement::RegisterElement(fresh.reg(), FrameElement::NOT_SYNCED); - Use(fresh.reg(), elements_.length()); + Use(fresh.reg(), element_count()); elements_.Add(new_element); __ mov(fresh.reg(), Operand(ebp, fp_relative(index))); break; } case FrameElement::REGISTER: - Use(original.reg(), elements_.length()); + Use(original.reg(), element_count()); // Fall through. case FrameElement::CONSTANT: case FrameElement::COPY: @@ -613,9 +658,9 @@ void VirtualFrame::StoreToFrameSlotAt(int index) { // a given index. The value on top of the frame is left in place. // This is a duplicating operation, so it can create copies. ASSERT(index >= 0); - ASSERT(index < elements_.length()); + ASSERT(index < element_count()); - int top_index = elements_.length() - 1; + int top_index = element_count() - 1; FrameElement top = elements_[top_index]; FrameElement original = elements_[index]; if (top.is_copy() && top.index() == index) return; @@ -657,12 +702,12 @@ void VirtualFrame::StoreToFrameSlotAt(int index) { // temp register. Alternatively, allow copies to appear in // any order in the frame and lazily move the value down to // the slot. - Result temp = cgen_->allocator()->Allocate(); + Result temp = cgen()->allocator()->Allocate(); ASSERT(temp.is_valid()); __ mov(temp.reg(), Operand(ebp, fp_relative(backing_index))); __ mov(Operand(ebp, fp_relative(index)), temp.reg()); } else { - register_locations_[backing_element.reg().code()] = index; + set_register_location(backing_element.reg(), index); if (backing_element.is_synced()) { // If the element is a register, we will not actually move // anything on the stack but only update the virtual frame @@ -682,7 +727,7 @@ void VirtualFrame::StoreToFrameSlotAt(int index) { // All the copies of the old backing element (including the top // element) become copies of the new backing element. - for (int i = backing_index + 1; i < elements_.length(); i++) { + for (int i = backing_index + 1; i < element_count(); i++) { if (elements_[i].is_copy() && elements_[i].index() == backing_index) { elements_[i].set_index(index); } @@ -704,12 +749,12 @@ void VirtualFrame::StoreToFrameSlotAt(int index) { // The sync state of the former top element is correct (synced). // Emit code to move the value down in the frame. - Result temp = cgen_->allocator()->Allocate(); + Result temp = cgen()->allocator()->Allocate(); ASSERT(temp.is_valid()); __ mov(temp.reg(), Operand(esp, 0)); __ mov(Operand(ebp, fp_relative(index)), temp.reg()); } else if (top.is_register()) { - register_locations_[top.reg().code()] = index; + set_register_location(top.reg(), index); // The stored-to slot has the (unsynced) register reference and // the top element becomes a copy. The sync state of the top is // preserved. @@ -729,7 +774,7 @@ void VirtualFrame::StoreToFrameSlotAt(int index) { void VirtualFrame::PushTryHandler(HandlerType type) { - ASSERT(cgen_->HasValidEntryRegisters()); + ASSERT(cgen()->HasValidEntryRegisters()); // Grow the expression stack by handler size less two (the return address // is already pushed by a call instruction, and PushTryHandler from the // macro assembler will leave the top of stack in the eax register to be @@ -742,9 +787,9 @@ void VirtualFrame::PushTryHandler(HandlerType type) { Result VirtualFrame::RawCallStub(CodeStub* stub) { - ASSERT(cgen_->HasValidEntryRegisters()); + ASSERT(cgen()->HasValidEntryRegisters()); __ CallStub(stub); - Result result = cgen_->allocator()->Allocate(eax); + Result result = cgen()->allocator()->Allocate(eax); ASSERT(result.is_valid()); return result; } @@ -785,9 +830,9 @@ Result VirtualFrame::CallStub(CodeStub* stub, Result* arg0, Result* arg1) { Result VirtualFrame::CallRuntime(Runtime::Function* f, int arg_count) { PrepareForCall(arg_count, arg_count); - ASSERT(cgen_->HasValidEntryRegisters()); + ASSERT(cgen()->HasValidEntryRegisters()); __ CallRuntime(f, arg_count); - Result result = cgen_->allocator()->Allocate(eax); + Result result = cgen()->allocator()->Allocate(eax); ASSERT(result.is_valid()); return result; } @@ -795,9 +840,9 @@ Result VirtualFrame::CallRuntime(Runtime::Function* f, int arg_count) { Result VirtualFrame::CallRuntime(Runtime::FunctionId id, int arg_count) { PrepareForCall(arg_count, arg_count); - ASSERT(cgen_->HasValidEntryRegisters()); + ASSERT(cgen()->HasValidEntryRegisters()); __ CallRuntime(id, arg_count); - Result result = cgen_->allocator()->Allocate(eax); + Result result = cgen()->allocator()->Allocate(eax); ASSERT(result.is_valid()); return result; } @@ -807,9 +852,9 @@ Result VirtualFrame::InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag, int arg_count) { PrepareForCall(arg_count, arg_count); - ASSERT(cgen_->HasValidEntryRegisters()); + ASSERT(cgen()->HasValidEntryRegisters()); __ InvokeBuiltin(id, flag); - Result result = cgen_->allocator()->Allocate(eax); + Result result = cgen()->allocator()->Allocate(eax); ASSERT(result.is_valid()); return result; } @@ -817,9 +862,9 @@ Result VirtualFrame::InvokeBuiltin(Builtins::JavaScript id, Result VirtualFrame::RawCallCodeObject(Handle<Code> code, RelocInfo::Mode rmode) { - ASSERT(cgen_->HasValidEntryRegisters()); + ASSERT(cgen()->HasValidEntryRegisters()); __ call(code, rmode); - Result result = cgen_->allocator()->Allocate(eax); + Result result = cgen()->allocator()->Allocate(eax); ASSERT(result.is_valid()); return result; } @@ -898,9 +943,8 @@ Result VirtualFrame::CallCallIC(RelocInfo::Mode mode, // Arguments, receiver, and function name are on top of the frame. // The IC expects them on the stack. It does not drop the function // name slot (but it does drop the rest). - Handle<Code> ic = (loop_nesting > 0) - ? cgen_->ComputeCallInitializeInLoop(arg_count) - : cgen_->ComputeCallInitialize(arg_count); + InLoopFlag in_loop = loop_nesting > 0 ? IN_LOOP : NOT_IN_LOOP; + Handle<Code> ic = cgen()->ComputeCallInitialize(arg_count, in_loop); // Spill args, receiver, and function. The call will drop args and // receiver. PrepareForCall(arg_count + 2, arg_count + 1); @@ -922,7 +966,7 @@ Result VirtualFrame::CallConstructor(int arg_count) { // Constructors are called with the number of arguments in register // eax for now. Another option would be to have separate construct // call trampolines per different arguments counts encountered. - Result num_args = cgen_->allocator()->Allocate(eax); + Result num_args = cgen()->allocator()->Allocate(eax); ASSERT(num_args.is_valid()); __ Set(num_args.reg(), Immediate(arg_count)); @@ -934,7 +978,7 @@ Result VirtualFrame::CallConstructor(int arg_count) { void VirtualFrame::Drop(int count) { ASSERT(height() >= count); - int num_virtual_elements = (elements_.length() - 1) - stack_pointer_; + int num_virtual_elements = (element_count() - 1) - stack_pointer_; // Emit code to lower the stack pointer if necessary. if (num_virtual_elements < count) { @@ -955,14 +999,14 @@ void VirtualFrame::Drop(int count) { Result VirtualFrame::Pop() { FrameElement element = elements_.RemoveLast(); - int index = elements_.length(); + int index = element_count(); ASSERT(element.is_valid()); bool pop_needed = (stack_pointer_ == index); if (pop_needed) { stack_pointer_--; if (element.is_memory()) { - Result temp = cgen_->allocator()->Allocate(); + Result temp = cgen()->allocator()->Allocate(); ASSERT(temp.is_valid()); temp.set_static_type(element.static_type()); __ pop(temp.reg()); @@ -989,7 +1033,7 @@ Result VirtualFrame::Pop() { // Memory elements could only be the backing store of a copy. // Allocate the original to a register. ASSERT(index <= stack_pointer_); - Result temp = cgen_->allocator()->Allocate(); + Result temp = cgen()->allocator()->Allocate(); ASSERT(temp.is_valid()); Use(temp.reg(), index); FrameElement new_element = @@ -999,18 +1043,18 @@ Result VirtualFrame::Pop() { new_element.set_static_type(element.static_type()); elements_[index] = new_element; __ mov(temp.reg(), Operand(ebp, fp_relative(index))); - return Result(temp.reg(), cgen_, element.static_type()); + return Result(temp.reg(), element.static_type()); } else if (element.is_register()) { - return Result(element.reg(), cgen_, element.static_type()); + return Result(element.reg(), element.static_type()); } else { ASSERT(element.is_constant()); - return Result(element.handle(), cgen_); + return Result(element.handle()); } } void VirtualFrame::EmitPop(Register reg) { - ASSERT(stack_pointer_ == elements_.length() - 1); + ASSERT(stack_pointer_ == element_count() - 1); stack_pointer_--; elements_.RemoveLast(); __ pop(reg); @@ -1018,7 +1062,7 @@ void VirtualFrame::EmitPop(Register reg) { void VirtualFrame::EmitPop(Operand operand) { - ASSERT(stack_pointer_ == elements_.length() - 1); + ASSERT(stack_pointer_ == element_count() - 1); stack_pointer_--; elements_.RemoveLast(); __ pop(operand); @@ -1026,7 +1070,7 @@ void VirtualFrame::EmitPop(Operand operand) { void VirtualFrame::EmitPush(Register reg) { - ASSERT(stack_pointer_ == elements_.length() - 1); + ASSERT(stack_pointer_ == element_count() - 1); elements_.Add(FrameElement::MemoryElement()); stack_pointer_++; __ push(reg); @@ -1034,7 +1078,7 @@ void VirtualFrame::EmitPush(Register reg) { void VirtualFrame::EmitPush(Operand operand) { - ASSERT(stack_pointer_ == elements_.length() - 1); + ASSERT(stack_pointer_ == element_count() - 1); elements_.Add(FrameElement::MemoryElement()); stack_pointer_++; __ push(operand); @@ -1042,7 +1086,7 @@ void VirtualFrame::EmitPush(Operand operand) { void VirtualFrame::EmitPush(Immediate immediate) { - ASSERT(stack_pointer_ == elements_.length() - 1); + ASSERT(stack_pointer_ == element_count() - 1); elements_.Add(FrameElement::MemoryElement()); stack_pointer_++; __ push(immediate); diff --git a/deps/v8/src/ia32/virtual-frame-ia32.h b/deps/v8/src/ia32/virtual-frame-ia32.h index 298eda21d0..6e6ebd50a9 100644 --- a/deps/v8/src/ia32/virtual-frame-ia32.h +++ b/deps/v8/src/ia32/virtual-frame-ia32.h @@ -29,8 +29,10 @@ #define V8_IA32_VIRTUAL_FRAME_IA32_H_ #include "register-allocator.h" +#include "scopes.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // ------------------------------------------------------------------------- // Virtual frames @@ -41,7 +43,7 @@ namespace v8 { namespace internal { // as random access to the expression stack elements, locals, and // parameters. -class VirtualFrame : public Malloced { +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 @@ -50,42 +52,66 @@ class VirtualFrame : public Malloced { // generator is being transformed. class SpilledScope BASE_EMBEDDED { public: - explicit SpilledScope(CodeGenerator* cgen); + SpilledScope() : previous_state_(cgen()->in_spilled_code()) { + ASSERT(cgen()->has_valid_frame()); + cgen()->frame()->SpillAll(); + cgen()->set_in_spilled_code(true); + } - ~SpilledScope(); + ~SpilledScope() { + cgen()->set_in_spilled_code(previous_state_); + } private: - CodeGenerator* cgen_; bool previous_state_; + + CodeGenerator* cgen() { return CodeGeneratorScope::Current(); } }; // An illegal index into the virtual frame. static const int kIllegalIndex = -1; // Construct an initial virtual frame on entry to a JS function. - explicit VirtualFrame(CodeGenerator* cgen); + VirtualFrame(); // Construct a virtual frame as a clone of an existing one. explicit VirtualFrame(VirtualFrame* original); + CodeGenerator* cgen() { return CodeGeneratorScope::Current(); } + MacroAssembler* masm() { return cgen()->masm(); } + // Create a duplicate of an existing valid frame element. FrameElement CopyElementAt(int index); + // The number of elements on the virtual frame. + int element_count() { return elements_.length(); } + // The height of the virtual expression stack. - int height() const { - return elements_.length() - expression_base_index(); + int height() { + return element_count() - expression_base_index(); + } + + int register_location(int num) { + ASSERT(num >= 0 && num < RegisterAllocator::kNumRegisters); + return register_locations_[num]; } - int register_index(Register reg) { - return register_locations_[reg.code()]; + int register_location(Register reg) { + return register_locations_[RegisterAllocator::ToNumber(reg)]; } - bool is_used(int reg_code) { - return register_locations_[reg_code] != kIllegalIndex; + void set_register_location(Register reg, int index) { + register_locations_[RegisterAllocator::ToNumber(reg)] = index; + } + + bool is_used(int num) { + ASSERT(num >= 0 && num < RegisterAllocator::kNumRegisters); + return register_locations_[num] != kIllegalIndex; } bool is_used(Register reg) { - return is_used(reg.code()); + return register_locations_[RegisterAllocator::ToNumber(reg)] + != kIllegalIndex; } // Add extra in-memory elements to the top of the frame to match an actual @@ -98,7 +124,12 @@ class VirtualFrame : public Malloced { // 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); + 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 @@ -109,13 +140,25 @@ class VirtualFrame : public Malloced { void SpillAll(); // Spill all occurrences of a specific register from the frame. - void Spill(Register reg); + void Spill(Register reg) { + if (is_used(reg)) SpillElementAt(register_location(reg)); + } // 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(); + // 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 + // topmost mergable_elements elements of the frame. A + // mergable_elements of JumpTarget::kAllElements indicates constants + // and copies are should be removed from the entire frame. + void MakeMergable(int mergable_elements); + // 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. @@ -130,13 +173,23 @@ class VirtualFrame : public Malloced { // 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(); + 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(); + 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 @@ -151,7 +204,7 @@ class VirtualFrame : public Malloced { void PrepareForReturn(); // Allocate and initialize the frame-allocated locals. - void AllocateStackSlots(int count); + void AllocateStackSlots(); // An element of the expression stack as an assembly operand. Operand ElementAt(int index) const { @@ -164,22 +217,22 @@ class VirtualFrame : public Malloced { // Set a frame element to a constant. The index is frame-top relative. void SetElementAt(int index, Handle<Object> value) { - Result temp(value, cgen_); + Result temp(value); SetElementAt(index, &temp); } void PushElementAt(int index) { - PushFrameSlotAt(elements_.length() - index - 1); + PushFrameSlotAt(element_count() - index - 1); } void StoreToElementAt(int index) { - StoreToFrameSlotAt(elements_.length() - index - 1); + StoreToFrameSlotAt(element_count() - index - 1); } // A frame-allocated local as an assembly operand. - Operand LocalAt(int index) const { + Operand LocalAt(int index) { ASSERT(0 <= index); - ASSERT(index < local_count_); + ASSERT(index < local_count()); return Operand(ebp, kLocal0Offset - index * kPointerSize); } @@ -215,10 +268,10 @@ class VirtualFrame : public Malloced { void RestoreContextRegister(); // A parameter as an assembly operand. - Operand ParameterAt(int index) const { + Operand ParameterAt(int index) { ASSERT(-1 <= index); // -1 is the receiver. - ASSERT(index < parameter_count_); - return Operand(ebp, (1 + parameter_count_ - index) * kPointerSize); + 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. @@ -240,14 +293,17 @@ class VirtualFrame : public Malloced { } // The receiver frame slot. - Operand Receiver() const { return ParameterAt(-1); } + 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. - Result CallStub(CodeStub* stub, int arg_count); + Result CallStub(CodeStub* stub, int arg_count) { + PrepareForCall(arg_count, arg_count); + return RawCallStub(stub); + } // 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 @@ -307,7 +363,7 @@ class VirtualFrame : public Malloced { void Drop() { Drop(1); } // Duplicate the top element of the frame. - void Dup() { PushFrameSlotAt(elements_.length() - 1); } + 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. @@ -331,7 +387,15 @@ class VirtualFrame : public Malloced { // Pushing a result invalidates it (its contents become owned by the // frame). - void Push(Result* result); + void Push(Result* result) { + if (result->is_register()) { + Push(result->reg(), result->static_type()); + } else { + ASSERT(result->is_constant()); + Push(result->handle()); + } + result->Unuse(); + } // Nip removes zero or more elements from immediately below the top // of the frame, leaving the previous top-of-frame value on top of @@ -346,70 +410,69 @@ class VirtualFrame : public Malloced { static const int kHandlerSize = StackHandlerConstants::kSize / kPointerSize; static const int kPreallocatedElements = 5 + 8; // 8 expression stack slots. - CodeGenerator* cgen_; - MacroAssembler* masm_; - - List<FrameElement> elements_; - - // The number of frame-allocated locals and parameters respectively. - int parameter_count_; - int local_count_; + 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 element that is at the processor's frame pointer - // (the ebp register). - int frame_pointer_; - // The index of the register frame element using each register, or // kIllegalIndex if a register is not on the frame. - int register_locations_[kNumRegisters]; + int register_locations_[RegisterAllocator::kNumRegisters]; + + // The number of frame-allocated locals and parameters respectively. + int parameter_count() { return cgen()->scope()->num_parameters(); } + int local_count() { return cgen()->scope()->num_stack_slots(); } + + // 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() const { return 1; } + int param0_index() { 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 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 lies above the context - // slot. - int function_index() const { - ASSERT(frame_pointer_ != kIllegalIndex); - return frame_pointer_ + 2; - } + // 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 parameters and the locals - // lie the return address, the saved frame pointer, the context, and the - // function. - int local0_index() const { - ASSERT(frame_pointer_ != kIllegalIndex); - return frame_pointer_ + 3; - } + // 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() const { return local0_index() + local_count_; } + 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) const { - return (frame_pointer_ - index) * kPointerSize; + 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); + 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); + 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 @@ -421,9 +484,6 @@ class VirtualFrame : public Malloced { // Keep the element type as register or constant, and clear the dirty bit. void SyncElementAt(int index); - // Sync the range of elements in [begin, end). - void SyncRange(int begin, int end); - // Sync a single unsynced element that lies beneath or at the stack pointer. void SyncElementBelowStackPointer(int index); @@ -485,9 +545,12 @@ class VirtualFrame : public Malloced { bool Equals(VirtualFrame* other); + // Classes that need raw access to the elements_ array. + friend class DeferredCode; friend class JumpTarget; }; + } } // namespace v8::internal #endif // V8_IA32_VIRTUAL_FRAME_IA32_H_ diff --git a/deps/v8/src/ic-inl.h b/deps/v8/src/ic-inl.h index bb5696281f..08304d83e7 100644 --- a/deps/v8/src/ic-inl.h +++ b/deps/v8/src/ic-inl.h @@ -32,7 +32,8 @@ #include "debug.h" #include "macro-assembler.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { Address IC::address() { diff --git a/deps/v8/src/ic.cc b/deps/v8/src/ic.cc index ccdf3cab43..657614a39b 100644 --- a/deps/v8/src/ic.cc +++ b/deps/v8/src/ic.cc @@ -1,4 +1,4 @@ -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2006-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: @@ -35,13 +35,13 @@ #include "runtime.h" #include "stub-cache.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { #ifdef DEBUG static char TransitionMarkFromState(IC::State state) { switch (state) { case UNINITIALIZED: return '0'; - case UNINITIALIZED_IN_LOOP: return 'L'; case PREMONOMORPHIC: return 'P'; case MONOMORPHIC: return '1'; case MONOMORPHIC_PROTOTYPE_FAILURE: return '^'; @@ -60,12 +60,14 @@ static char TransitionMarkFromState(IC::State state) { void IC::TraceIC(const char* type, Handle<String> name, State old_state, - Code* new_target) { + Code* new_target, + const char* extra_info) { if (FLAG_trace_ic) { State new_state = StateFrom(new_target, Heap::undefined_value()); - PrintF("[%s (%c->%c) ", type, + PrintF("[%s (%c->%c)%s", type, TransitionMarkFromState(old_state), - TransitionMarkFromState(new_state)); + TransitionMarkFromState(new_state), + extra_info); name->Print(); PrintF("]\n"); } @@ -226,8 +228,10 @@ void IC::Clear(Address address) { void CallIC::Clear(Address address, Code* target) { State state = target->ic_state(); - if (state == UNINITIALIZED || state == UNINITIALIZED_IN_LOOP) return; - Code* code = StubCache::FindCallInitialize(target->arguments_count()); + InLoopFlag in_loop = target->ic_in_loop(); + if (state == UNINITIALIZED) return; + Code* code = + StubCache::FindCallInitialize(target->arguments_count(), in_loop); SetTargetAtAddress(address, code); } @@ -390,21 +394,22 @@ void CallIC::UpdateCaches(LookupResult* lookup, // Compute the number of arguments. int argc = target()->arguments_count(); + InLoopFlag in_loop = target()->ic_in_loop(); Object* code = NULL; if (state == UNINITIALIZED) { // This is the first time we execute this inline cache. // Set the target to the pre monomorphic stub to delay // setting the monomorphic state. - code = StubCache::ComputeCallPreMonomorphic(argc); + code = StubCache::ComputeCallPreMonomorphic(argc, in_loop); } else if (state == MONOMORPHIC) { - code = StubCache::ComputeCallMegamorphic(argc); + code = StubCache::ComputeCallMegamorphic(argc, in_loop); } else { // Compute monomorphic stub. switch (lookup->type()) { case FIELD: { int index = lookup->GetFieldIndex(); - code = StubCache::ComputeCallField(argc, *name, *object, + code = StubCache::ComputeCallField(argc, in_loop, *name, *object, lookup->holder(), index); break; } @@ -413,7 +418,7 @@ void CallIC::UpdateCaches(LookupResult* lookup, // call; used for rewriting to monomorphic state and making sure // that the code stub is in the stub cache. JSFunction* function = lookup->GetConstantFunction(); - code = StubCache::ComputeCallConstant(argc, *name, *object, + code = StubCache::ComputeCallConstant(argc, in_loop, *name, *object, lookup->holder(), function); break; } @@ -425,7 +430,7 @@ void CallIC::UpdateCaches(LookupResult* lookup, if (!object->IsJSObject()) return; Handle<JSObject> receiver = Handle<JSObject>::cast(object); if (lookup->holder() != *receiver) return; - code = StubCache::ComputeCallNormal(argc, *name, *receiver); + code = StubCache::ComputeCallNormal(argc, in_loop, *name, *receiver); break; } case INTERCEPTOR: { @@ -443,14 +448,15 @@ void CallIC::UpdateCaches(LookupResult* lookup, if (code->IsFailure()) return; // Patch the call site depending on the state of the cache. - if (state == UNINITIALIZED || state == UNINITIALIZED_IN_LOOP || - state == PREMONOMORPHIC || state == MONOMORPHIC || + if (state == UNINITIALIZED || + state == PREMONOMORPHIC || + state == MONOMORPHIC || state == MONOMORPHIC_PROTOTYPE_FAILURE) { set_target(Code::cast(code)); } #ifdef DEBUG - TraceIC("CallIC", name, state, target()); + TraceIC("CallIC", name, state, target(), in_loop ? " (in-loop)" : ""); #endif } @@ -1088,14 +1094,27 @@ Object* CallIC_Miss(Arguments args) { IC::State state = IC::StateFrom(ic.target(), args[0]); Object* result = ic.LoadFunction(state, args.at<Object>(0), args.at<String>(1)); - if (state != UNINITIALIZED_IN_LOOP || !result->IsJSFunction()) + + // The first time the inline cache is updated may be the first time the + // function it references gets called. If the function was lazily compiled + // then the first call will trigger a compilation. We check for this case + // and we do the compilation immediately, instead of waiting for the stub + // currently attached to the JSFunction object to trigger compilation. We + // do this in the case where we know that the inline cache is inside a loop, + // because then we know that we want to optimize the function. + if (!result->IsJSFunction() || JSFunction::cast(result)->is_compiled()) { return result; + } - // Compile the function with the knowledge that it's called from - // within a loop. This enables further optimization of the function. + // Compile now with optimization. HandleScope scope; Handle<JSFunction> function = Handle<JSFunction>(JSFunction::cast(result)); - if (!function->is_compiled()) CompileLazyInLoop(function, CLEAR_EXCEPTION); + InLoopFlag in_loop = ic.target()->ic_in_loop(); + if (in_loop == IN_LOOP) { + CompileLazyInLoop(function, CLEAR_EXCEPTION); + } else { + CompileLazy(function, CLEAR_EXCEPTION); + } return *function; } diff --git a/deps/v8/src/ic.h b/deps/v8/src/ic.h index 11fd60454b..bd94fd89e5 100644 --- a/deps/v8/src/ic.h +++ b/deps/v8/src/ic.h @@ -1,4 +1,4 @@ -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2006-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: @@ -30,7 +30,8 @@ #include "assembler.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // IC_UTIL_LIST defines all utility functions called from generated // inline caching code. The argument for the macro, ICU, is the function name. @@ -120,7 +121,8 @@ class IC { static void TraceIC(const char* type, Handle<String> name, State old_state, - Code* new_target); + Code* new_target, + const char* extra_info = ""); #endif static Failure* TypeError(const char* type, diff --git a/deps/v8/src/interpreter-irregexp.cc b/deps/v8/src/interpreter-irregexp.cc index 77bcc90743..355fae4674 100644 --- a/deps/v8/src/interpreter-irregexp.cc +++ b/deps/v8/src/interpreter-irregexp.cc @@ -36,7 +36,8 @@ #include "interpreter-irregexp.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { static unibrow::Mapping<unibrow::Ecma262Canonicalize> interp_canonicalize; diff --git a/deps/v8/src/interpreter-irregexp.h b/deps/v8/src/interpreter-irregexp.h index c65cb9ecd5..0ad8846d79 100644 --- a/deps/v8/src/interpreter-irregexp.h +++ b/deps/v8/src/interpreter-irregexp.h @@ -30,7 +30,8 @@ #ifndef V8_INTERPRETER_IRREGEXP_H_ #define V8_INTERPRETER_IRREGEXP_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { class IrregexpInterpreter { diff --git a/deps/v8/src/json-delay.js b/deps/v8/src/json-delay.js index 90150c6f17..1a6f0085c5 100644 --- a/deps/v8/src/json-delay.js +++ b/deps/v8/src/json-delay.js @@ -29,7 +29,7 @@ var $JSON = global.JSON; function ParseJSONUnfiltered(text) { var s = $String(text); - var f = %CompileString("(" + text + ")", -1, true); + var f = %CompileString("(" + text + ")", true); return f(); } diff --git a/deps/v8/src/jsregexp-inl.h b/deps/v8/src/jsregexp-inl.h index 09c4c8f91c..cc90bd172b 100644 --- a/deps/v8/src/jsregexp-inl.h +++ b/deps/v8/src/jsregexp-inl.h @@ -33,7 +33,8 @@ #include "regexp-macro-assembler.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { template <typename C> diff --git a/deps/v8/src/jsregexp.cc b/deps/v8/src/jsregexp.cc index ebaefc0d7e..6fce1f5c9b 100644 --- a/deps/v8/src/jsregexp.cc +++ b/deps/v8/src/jsregexp.cc @@ -28,6 +28,7 @@ #include "v8.h" #include "ast.h" +#include "compiler.h" #include "execution.h" #include "factory.h" #include "jsregexp-inl.h" @@ -42,7 +43,7 @@ #include "regexp-macro-assembler-irregexp.h" #include "regexp-stack.h" -#ifdef V8_TARGET_ARCH_IA32 +#if V8_TARGET_ARCH_IA32 #include "ia32/macro-assembler-ia32.h" #include "ia32/regexp-macro-assembler-ia32.h" #elif V8_TARGET_ARCH_X64 @@ -55,7 +56,8 @@ #include "interpreter-irregexp.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { Handle<Object> RegExpImpl::CreateRegExpLiteral(Handle<JSFunction> constructor, @@ -153,7 +155,7 @@ Handle<Object> RegExpImpl::Compile(Handle<JSRegExp> re, return re; } FlattenString(pattern); - ZoneScope zone_scope(DELETE_ON_EXIT); + CompilationZoneScope zone_scope(DELETE_ON_EXIT); RegExpCompileData parse_result; FlatStringReader reader(pattern); if (!ParseRegExp(&reader, flags.is_multiline(), &parse_result)) { @@ -285,7 +287,7 @@ bool RegExpImpl::EnsureCompiledIrregexp(Handle<JSRegExp> re, bool is_ascii) { } // Compile the RegExp. - ZoneScope zone_scope(DELETE_ON_EXIT); + CompilationZoneScope zone_scope(DELETE_ON_EXIT); JSRegExp::Flags flags = re->GetFlags(); @@ -4187,6 +4189,11 @@ OutSet* DispatchTable::Get(uc16 value) { void Analysis::EnsureAnalyzed(RegExpNode* that) { + StackLimitCheck check; + if (check.HasOverflowed()) { + fail("Stack overflow"); + return; + } if (that->info()->been_analyzed || that->info()->being_analyzed) return; that->info()->being_analyzed = true; @@ -4224,16 +4231,20 @@ void Analysis::VisitText(TextNode* that) { that->MakeCaseIndependent(); } EnsureAnalyzed(that->on_success()); - that->CalculateOffsets(); + if (!has_failed()) { + that->CalculateOffsets(); + } } void Analysis::VisitAction(ActionNode* that) { RegExpNode* target = that->on_success(); EnsureAnalyzed(target); - // If the next node is interested in what it follows then this node - // has to be interested too so it can pass the information on. - that->info()->AddFromFollowing(target->info()); + if (!has_failed()) { + // If the next node is interested in what it follows then this node + // has to be interested too so it can pass the information on. + that->info()->AddFromFollowing(target->info()); + } } @@ -4242,6 +4253,7 @@ void Analysis::VisitChoice(ChoiceNode* that) { for (int i = 0; i < that->alternatives()->length(); i++) { RegExpNode* node = that->alternatives()->at(i).node(); EnsureAnalyzed(node); + if (has_failed()) return; // Anything the following nodes need to know has to be known by // this node also, so it can pass it on. info->AddFromFollowing(node->info()); @@ -4255,13 +4267,16 @@ void Analysis::VisitLoopChoice(LoopChoiceNode* that) { RegExpNode* node = that->alternatives()->at(i).node(); if (node != that->loop_node()) { EnsureAnalyzed(node); + if (has_failed()) return; info->AddFromFollowing(node->info()); } } // Check the loop last since it may need the value of this node // to get a correct result. EnsureAnalyzed(that->loop_node()); - info->AddFromFollowing(that->loop_node()->info()); + if (!has_failed()) { + info->AddFromFollowing(that->loop_node()->info()); + } } @@ -4433,6 +4448,10 @@ RegExpEngine::CompilationResult RegExpEngine::Compile(RegExpCompileData* data, data->node = node; Analysis analysis(ignore_case); analysis.EnsureAnalyzed(node); + if (analysis.has_failed()) { + const char* error_message = analysis.error_message(); + return CompilationResult(error_message); + } NodeInfo info = *node->info(); diff --git a/deps/v8/src/jsregexp.h b/deps/v8/src/jsregexp.h index 9fa0ecef0f..a86f7e648a 100644 --- a/deps/v8/src/jsregexp.h +++ b/deps/v8/src/jsregexp.h @@ -28,7 +28,8 @@ #ifndef V8_JSREGEXP_H_ #define V8_JSREGEXP_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { class RegExpMacroAssembler; @@ -1309,7 +1310,7 @@ FOR_EACH_NODE_TYPE(DECLARE_VISIT) class Analysis: public NodeVisitor { public: explicit Analysis(bool ignore_case) - : ignore_case_(ignore_case) { } + : ignore_case_(ignore_case), error_message_(NULL) { } void EnsureAnalyzed(RegExpNode* node); #define DECLARE_VISIT(Type) \ @@ -1318,8 +1319,17 @@ FOR_EACH_NODE_TYPE(DECLARE_VISIT) #undef DECLARE_VISIT virtual void VisitLoopChoice(LoopChoiceNode* that); + bool has_failed() { return error_message_ != NULL; } + const char* error_message() { + ASSERT(error_message_ != NULL); + return error_message_; + } + void fail(const char* error_message) { + error_message_ = error_message; + } private: bool ignore_case_; + const char* error_message_; DISALLOW_IMPLICIT_CONSTRUCTORS(Analysis); }; diff --git a/deps/v8/src/jump-target-inl.h b/deps/v8/src/jump-target-inl.h new file mode 100644 index 0000000000..1f0676df00 --- /dev/null +++ b/deps/v8/src/jump-target-inl.h @@ -0,0 +1,49 @@ +// 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_JUMP_TARGET_INL_H_ +#define V8_JUMP_TARGET_INL_H_ + +namespace v8 { +namespace internal { + +CodeGenerator* JumpTarget::cgen() { + return CodeGeneratorScope::Current(); +} + +void JumpTarget::InitializeEntryElement(int index, FrameElement* target) { + entry_frame_->elements_[index].clear_copied(); + if (target->is_register()) { + entry_frame_->set_register_location(target->reg(), index); + } else if (target->is_copy()) { + entry_frame_->elements_[target->index()].set_copied(); + } +} + +} } // namespace v8::internal + +#endif // V8_JUMP_TARGET_INL_H_ diff --git a/deps/v8/src/jump-target.cc b/deps/v8/src/jump-target.cc index 6e41270ee0..a8eda6bd98 100644 --- a/deps/v8/src/jump-target.cc +++ b/deps/v8/src/jump-target.cc @@ -28,115 +28,23 @@ #include "v8.h" #include "codegen-inl.h" +#include "jump-target-inl.h" #include "register-allocator-inl.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // ------------------------------------------------------------------------- // JumpTarget implementation. -JumpTarget::JumpTarget(CodeGenerator* cgen, Directionality direction) - : cgen_(cgen), - direction_(direction), - reaching_frames_(0), - merge_labels_(0), - entry_frame_(NULL), - is_bound_(false), - is_linked_(false) { - ASSERT(cgen != NULL); - masm_ = cgen->masm(); -} - - -JumpTarget::JumpTarget() - : cgen_(NULL), - masm_(NULL), - direction_(FORWARD_ONLY), - reaching_frames_(0), - merge_labels_(0), - entry_frame_(NULL), - is_bound_(false), - is_linked_(false) { -} - - -void JumpTarget::Initialize(CodeGenerator* cgen, Directionality direction) { - ASSERT(cgen != NULL); - ASSERT(cgen_ == NULL); - cgen_ = cgen; - masm_ = cgen->masm(); - direction_ = direction; -} +bool JumpTarget::compiling_deferred_code_ = false; void JumpTarget::Unuse() { - // We should not deallocate jump targets that have unresolved jumps - // to them. In the event of a compile-time stack overflow or an - // uninitialized jump target, we don't care. - ASSERT(!is_linked() || cgen_ == NULL || cgen_->HasStackOverflow()); - for (int i = 0; i < reaching_frames_.length(); i++) { - delete reaching_frames_[i]; - } - delete entry_frame_; - Reset(); -} - - -void JumpTarget::Reset() { reaching_frames_.Clear(); merge_labels_.Clear(); entry_frame_ = NULL; entry_label_.Unuse(); - is_bound_ = false; - is_linked_ = false; -} - - -FrameElement* JumpTarget::Combine(FrameElement* left, FrameElement* right) { - // Given a pair of non-null frame element pointers, return one of - // them as an entry frame candidate or null if they are - // incompatible. - - // If either is invalid, the result is. - if (!left->is_valid()) return left; - if (!right->is_valid()) return right; - - // If they have the same value, the result is the same. If either - // is unsynced, the result is. - - if (left->is_memory() && right->is_memory()) return left; - - if (left->is_register() && right->is_register() && - left->reg().is(right->reg())) { - if (!left->is_synced()) { - return left; - } else { - return right; - } - } - - if (left->is_constant() && - right->is_constant() && - left->handle().is_identical_to(right->handle())) { - if (!left->is_synced()) { - return left; - } else { - return right; - } - } - - if (left->is_copy() && - right->is_copy() && - left->index() == right->index()) { - if (!left->is_synced()) { - return left; - } else { - return right; - } - } - - // Otherwise they are incompatible and we will reallocate them. - return NULL; } @@ -145,13 +53,29 @@ void JumpTarget::ComputeEntryFrame(int mergable_elements) { // the directionality of the block. Compute: an entry frame for the // block. + Counters::compute_entry_frame.Increment(); +#ifdef DEBUG + if (compiling_deferred_code_) { + ASSERT(reaching_frames_.length() > 1); + VirtualFrame* frame = reaching_frames_[0]; + bool all_identical = true; + for (int i = 1; i < reaching_frames_.length(); i++) { + if (!frame->Equals(reaching_frames_[i])) { + all_identical = false; + break; + } + } + ASSERT(!all_identical || all_identical); + } +#endif + // Choose an initial frame. VirtualFrame* initial_frame = reaching_frames_[0]; // A list of pointers to frame elements in the entry frame. NULL // indicates that the element has not yet been determined. - int length = initial_frame->elements_.length(); - List<FrameElement*> elements(length); + int length = initial_frame->element_count(); + ZoneList<FrameElement*> elements(length); // Convert the number of mergable elements (counted from the top // down) to a frame high-water mark (counted from the bottom up). @@ -165,63 +89,59 @@ void JumpTarget::ComputeEntryFrame(int mergable_elements) { // frame. for (int i = 0; i < length; i++) { FrameElement element = initial_frame->elements_[i]; - // We do not allow copies or constants in bidirectional frames. - if (direction_ == BIDIRECTIONAL && i > high_water_mark && - (element.is_constant() || element.is_copy())) { - elements.Add(NULL); - } else { - elements.Add(&initial_frame->elements_[i]); + // We do not allow copies or constants in bidirectional frames. All + // elements above the water mark on bidirectional frames have + // unknown static types. + if (direction_ == BIDIRECTIONAL && i > high_water_mark) { + if (element.is_constant() || element.is_copy()) { + elements.Add(NULL); + continue; + } + // It's safe to change the static type on the initial frame + // element, see comment in JumpTarget::Combine. + initial_frame->elements_[i].set_static_type(StaticType::unknown()); } + elements.Add(&initial_frame->elements_[i]); } // Compute elements based on the other reaching frames. if (reaching_frames_.length() > 1) { for (int i = 0; i < length; i++) { + FrameElement* element = elements[i]; for (int j = 1; j < reaching_frames_.length(); j++) { - FrameElement* element = elements[i]; - // Element computation is monotonic: new information will not // change our decision about undetermined or invalid elements. if (element == NULL || !element->is_valid()) break; - elements[i] = Combine(element, &reaching_frames_[j]->elements_[i]); + element = element->Combine(&reaching_frames_[j]->elements_[i]); } + elements[i] = element; } } // Build the new frame. A freshly allocated frame has memory elements // for the parameters and some platform-dependent elements (e.g., // return address). Replace those first. - entry_frame_ = new VirtualFrame(cgen_); + entry_frame_ = new VirtualFrame(); int index = 0; - for (; index < entry_frame_->elements_.length(); index++) { + for (; index < entry_frame_->element_count(); index++) { + FrameElement* target = elements[index]; // If the element is determined, set it now. Count registers. Mark // elements as copied exactly when they have a copy. Undetermined // elements are initially recorded as if in memory. - if (elements[index] != NULL) { - entry_frame_->elements_[index] = *elements[index]; - entry_frame_->elements_[index].clear_copied(); - if (elements[index]->is_register()) { - entry_frame_->register_locations_[elements[index]->reg().code()] = - index; - } else if (elements[index]->is_copy()) { - entry_frame_->elements_[elements[index]->index()].set_copied(); - } + if (target != NULL) { + entry_frame_->elements_[index] = *target; + InitializeEntryElement(index, target); } } // Then fill in the rest of the frame with new elements. for (; index < length; index++) { - if (elements[index] == NULL) { + FrameElement* target = elements[index]; + if (target == NULL) { entry_frame_->elements_.Add(FrameElement::MemoryElement()); } else { - entry_frame_->elements_.Add(*elements[index]); - entry_frame_->elements_[index].clear_copied(); - if (elements[index]->is_register()) { - entry_frame_->register_locations_[elements[index]->reg().code()] = - index; - } else if (elements[index]->is_copy()) { - entry_frame_->elements_[elements[index]->index()].set_copied(); - } + entry_frame_->elements_.Add(*target); + InitializeEntryElement(index, target); } } @@ -229,82 +149,74 @@ void JumpTarget::ComputeEntryFrame(int mergable_elements) { // memory, from the top down. for (int i = length - 1; i >= 0; i--) { if (elements[i] == NULL) { - // If the value is synced on all frames, put it in memory. This - // costs nothing at the merge code but will incur a - // memory-to-register move when the value is needed later. + // Loop over all the reaching frames to check whether the element + // is synced on all frames, to count the registers it occupies, + // and to compute a merged static type. bool is_synced = true; - for (int j = 0; is_synced && j < reaching_frames_.length(); j++) { - is_synced = reaching_frames_[j]->elements_[i].is_synced(); - } - - // There is nothing to be done if the elements are all synced. - // It is already recorded as a memory element. - if (is_synced) continue; - - // Choose an available register. Prefer ones that the element - // is already occupying on some reaching frame. RegisterFile candidate_registers; - int max_count = kMinInt; - int best_reg_code = no_reg.code_; + int best_count = kMinInt; + int best_reg_num = RegisterAllocator::kInvalidRegister; + + StaticType type; // Initially invalid. + if (direction_ != BIDIRECTIONAL || i < high_water_mark) { + type = reaching_frames_[0]->elements_[i].static_type(); + } for (int j = 0; j < reaching_frames_.length(); j++) { FrameElement element = reaching_frames_[j]->elements_[i]; - if (element.is_register() && - !entry_frame_->is_used(element.reg())) { - candidate_registers.Use(element.reg()); - if (candidate_registers.count(element.reg()) > max_count) { - max_count = candidate_registers.count(element.reg()); - best_reg_code = element.reg().code(); + is_synced = is_synced && element.is_synced(); + if (element.is_register() && !entry_frame_->is_used(element.reg())) { + // Count the register occurrence and remember it if better + // than the previous best. + int num = RegisterAllocator::ToNumber(element.reg()); + candidate_registers.Use(num); + if (candidate_registers.count(num) > best_count) { + best_count = candidate_registers.count(num); + best_reg_num = num; } } + type = type.merge(element.static_type()); } - // If there was no preferred choice consider any free register. - if (best_reg_code == no_reg.code_) { - for (int j = 0; j < kNumRegisters; j++) { - if (!entry_frame_->is_used(j) && !RegisterAllocator::IsReserved(j)) { - best_reg_code = j; + + // If the value is synced on all frames, put it in memory. This + // costs nothing at the merge code but will incur a + // memory-to-register move when the value is needed later. + if (is_synced) { + // Already recorded as a memory element. + entry_frame_->elements_[i].set_static_type(type); + continue; + } + + // Try to put it in a register. If there was no best choice + // consider any free register. + if (best_reg_num == RegisterAllocator::kInvalidRegister) { + for (int j = 0; j < RegisterAllocator::kNumRegisters; j++) { + if (!entry_frame_->is_used(j)) { + best_reg_num = j; break; } } } - if (best_reg_code != no_reg.code_) { + if (best_reg_num == RegisterAllocator::kInvalidRegister) { + // If there was no register found, the element is already + // recorded as in memory. + entry_frame_->elements_[i].set_static_type(type); + } else { // If there was a register choice, use it. Preserve the copied - // flag on the element. + // flag on the element. Set the static type as computed. bool is_copied = entry_frame_->elements_[i].is_copied(); - Register reg = { best_reg_code }; + Register reg = RegisterAllocator::ToRegister(best_reg_num); entry_frame_->elements_[i] = FrameElement::RegisterElement(reg, FrameElement::NOT_SYNCED); if (is_copied) entry_frame_->elements_[i].set_copied(); - entry_frame_->register_locations_[best_reg_code] = i; + entry_frame_->elements_[i].set_static_type(type); + entry_frame_->set_register_location(reg, i); } - // If there was no register found, the element is already - // recorded as in memory. } } - // Set the static type of frame elements. - for (int i = 0; i < length; i++) { - FrameElement* current = &entry_frame_->elements_[i]; - if (direction_ == BIDIRECTIONAL && i >= high_water_mark) { - current->set_static_type(StaticType::unknown()); - } else { - StaticType merged_type = reaching_frames_[0]->elements_[i].static_type(); - for (int j = 1, n = reaching_frames_.length(); - !merged_type.is_unknown() && j < n; - j++) { - merged_type = - merged_type.merge(reaching_frames_[j]->elements_[i].static_type()); - } - current->set_static_type(merged_type); - } - } - - // Fill in the other fields of the entry frame. - entry_frame_->local_count_ = initial_frame->local_count_; - entry_frame_->frame_pointer_ = initial_frame->frame_pointer_; - // The stack pointer is at the highest synced element or the base of // the expression stack. int stack_pointer = length - 1; @@ -322,31 +234,28 @@ void JumpTarget::Jump() { void JumpTarget::Jump(Result* arg) { - ASSERT(cgen_ != NULL); - ASSERT(cgen_->has_valid_frame()); + ASSERT(cgen()->has_valid_frame()); - cgen_->frame()->Push(arg); + cgen()->frame()->Push(arg); DoJump(); } void JumpTarget::Jump(Result* arg0, Result* arg1) { - ASSERT(cgen_ != NULL); - ASSERT(cgen_->has_valid_frame()); + ASSERT(cgen()->has_valid_frame()); - cgen_->frame()->Push(arg0); - cgen_->frame()->Push(arg1); + cgen()->frame()->Push(arg0); + cgen()->frame()->Push(arg1); DoJump(); } void JumpTarget::Jump(Result* arg0, Result* arg1, Result* arg2) { - ASSERT(cgen_ != NULL); - ASSERT(cgen_->has_valid_frame()); + ASSERT(cgen()->has_valid_frame()); - cgen_->frame()->Push(arg0); - cgen_->frame()->Push(arg1); - cgen_->frame()->Push(arg2); + cgen()->frame()->Push(arg0); + cgen()->frame()->Push(arg1); + cgen()->frame()->Push(arg2); DoJump(); } @@ -372,35 +281,33 @@ void JumpTarget::Branch(Condition cc, Hint hint) { #endif void JumpTarget::Branch(Condition cc, Result* arg, Hint hint) { - ASSERT(cgen_ != NULL); - ASSERT(cgen_->has_valid_frame()); + ASSERT(cgen()->has_valid_frame()); // We want to check that non-frame registers at the call site stay in // the same registers on the fall-through branch. DECLARE_ARGCHECK_VARS(arg); - cgen_->frame()->Push(arg); + cgen()->frame()->Push(arg); DoBranch(cc, hint); - *arg = cgen_->frame()->Pop(); + *arg = cgen()->frame()->Pop(); ASSERT_ARGCHECK(arg); } void JumpTarget::Branch(Condition cc, Result* arg0, Result* arg1, Hint hint) { - ASSERT(cgen_ != NULL); - ASSERT(cgen_->frame() != NULL); + ASSERT(cgen()->frame() != NULL); // We want to check that non-frame registers at the call site stay in // the same registers on the fall-through branch. DECLARE_ARGCHECK_VARS(arg0); DECLARE_ARGCHECK_VARS(arg1); - cgen_->frame()->Push(arg0); - cgen_->frame()->Push(arg1); + cgen()->frame()->Push(arg0); + cgen()->frame()->Push(arg1); DoBranch(cc, hint); - *arg1 = cgen_->frame()->Pop(); - *arg0 = cgen_->frame()->Pop(); + *arg1 = cgen()->frame()->Pop(); + *arg0 = cgen()->frame()->Pop(); ASSERT_ARGCHECK(arg0); ASSERT_ARGCHECK(arg1); @@ -412,8 +319,7 @@ void JumpTarget::Branch(Condition cc, Result* arg1, Result* arg2, Hint hint) { - ASSERT(cgen_ != NULL); - ASSERT(cgen_->frame() != NULL); + ASSERT(cgen()->frame() != NULL); // We want to check that non-frame registers at the call site stay in // the same registers on the fall-through branch. @@ -421,13 +327,13 @@ void JumpTarget::Branch(Condition cc, DECLARE_ARGCHECK_VARS(arg1); DECLARE_ARGCHECK_VARS(arg2); - cgen_->frame()->Push(arg0); - cgen_->frame()->Push(arg1); - cgen_->frame()->Push(arg2); + cgen()->frame()->Push(arg0); + cgen()->frame()->Push(arg1); + cgen()->frame()->Push(arg2); DoBranch(cc, hint); - *arg2 = cgen_->frame()->Pop(); - *arg1 = cgen_->frame()->Pop(); - *arg0 = cgen_->frame()->Pop(); + *arg2 = cgen()->frame()->Pop(); + *arg1 = cgen()->frame()->Pop(); + *arg0 = cgen()->frame()->Pop(); ASSERT_ARGCHECK(arg0); ASSERT_ARGCHECK(arg1); @@ -441,8 +347,7 @@ void JumpTarget::Branch(Condition cc, Result* arg2, Result* arg3, Hint hint) { - ASSERT(cgen_ != NULL); - ASSERT(cgen_->frame() != NULL); + ASSERT(cgen()->frame() != NULL); // We want to check that non-frame registers at the call site stay in // the same registers on the fall-through branch. @@ -451,15 +356,15 @@ void JumpTarget::Branch(Condition cc, DECLARE_ARGCHECK_VARS(arg2); DECLARE_ARGCHECK_VARS(arg3); - cgen_->frame()->Push(arg0); - cgen_->frame()->Push(arg1); - cgen_->frame()->Push(arg2); - cgen_->frame()->Push(arg3); + cgen()->frame()->Push(arg0); + cgen()->frame()->Push(arg1); + cgen()->frame()->Push(arg2); + cgen()->frame()->Push(arg3); DoBranch(cc, hint); - *arg3 = cgen_->frame()->Pop(); - *arg2 = cgen_->frame()->Pop(); - *arg1 = cgen_->frame()->Pop(); - *arg0 = cgen_->frame()->Pop(); + *arg3 = cgen()->frame()->Pop(); + *arg2 = cgen()->frame()->Pop(); + *arg1 = cgen()->frame()->Pop(); + *arg0 = cgen()->frame()->Pop(); ASSERT_ARGCHECK(arg0); ASSERT_ARGCHECK(arg1); @@ -469,15 +374,14 @@ void JumpTarget::Branch(Condition cc, void BreakTarget::Branch(Condition cc, Result* arg, Hint hint) { - ASSERT(cgen_ != NULL); - ASSERT(cgen_->has_valid_frame()); + ASSERT(cgen()->has_valid_frame()); - int count = cgen_->frame()->height() - expected_height_; + int count = cgen()->frame()->height() - expected_height_; if (count > 0) { // We negate and branch here rather than using DoBranch's negate // and branch. This gives us a hook to remove statement state // from the frame. - JumpTarget fall_through(cgen_); + JumpTarget fall_through; // Branch to fall through will not negate, because it is a // forward-only target. fall_through.Branch(NegateCondition(cc), NegateHint(hint)); @@ -485,9 +389,9 @@ void BreakTarget::Branch(Condition cc, Result* arg, Hint hint) { fall_through.Bind(); } else { DECLARE_ARGCHECK_VARS(arg); - cgen_->frame()->Push(arg); + cgen()->frame()->Push(arg); DoBranch(cc, hint); - *arg = cgen_->frame()->Pop(); + *arg = cgen()->frame()->Pop(); ASSERT_ARGCHECK(arg); } } @@ -502,26 +406,22 @@ void JumpTarget::Bind(int mergable_elements) { void JumpTarget::Bind(Result* arg, int mergable_elements) { - ASSERT(cgen_ != NULL); - - if (cgen_->has_valid_frame()) { - cgen_->frame()->Push(arg); + if (cgen()->has_valid_frame()) { + cgen()->frame()->Push(arg); } DoBind(mergable_elements); - *arg = cgen_->frame()->Pop(); + *arg = cgen()->frame()->Pop(); } void JumpTarget::Bind(Result* arg0, Result* arg1, int mergable_elements) { - ASSERT(cgen_ != NULL); - - if (cgen_->has_valid_frame()) { - cgen_->frame()->Push(arg0); - cgen_->frame()->Push(arg1); + if (cgen()->has_valid_frame()) { + cgen()->frame()->Push(arg0); + cgen()->frame()->Push(arg1); } DoBind(mergable_elements); - *arg1 = cgen_->frame()->Pop(); - *arg0 = cgen_->frame()->Pop(); + *arg1 = cgen()->frame()->Pop(); + *arg0 = cgen()->frame()->Pop(); } @@ -529,17 +429,15 @@ void JumpTarget::Bind(Result* arg0, Result* arg1, Result* arg2, int mergable_elements) { - ASSERT(cgen_ != NULL); - - if (cgen_->has_valid_frame()) { - cgen_->frame()->Push(arg0); - cgen_->frame()->Push(arg1); - cgen_->frame()->Push(arg2); + if (cgen()->has_valid_frame()) { + cgen()->frame()->Push(arg0); + cgen()->frame()->Push(arg1); + cgen()->frame()->Push(arg2); } DoBind(mergable_elements); - *arg2 = cgen_->frame()->Pop(); - *arg1 = cgen_->frame()->Pop(); - *arg0 = cgen_->frame()->Pop(); + *arg2 = cgen()->frame()->Pop(); + *arg1 = cgen()->frame()->Pop(); + *arg0 = cgen()->frame()->Pop(); } @@ -548,24 +446,23 @@ void JumpTarget::Bind(Result* arg0, Result* arg2, Result* arg3, int mergable_elements) { - ASSERT(cgen_ != NULL); - - if (cgen_->has_valid_frame()) { - cgen_->frame()->Push(arg0); - cgen_->frame()->Push(arg1); - cgen_->frame()->Push(arg2); - cgen_->frame()->Push(arg3); + if (cgen()->has_valid_frame()) { + cgen()->frame()->Push(arg0); + cgen()->frame()->Push(arg1); + cgen()->frame()->Push(arg2); + cgen()->frame()->Push(arg3); } DoBind(mergable_elements); - *arg3 = cgen_->frame()->Pop(); - *arg2 = cgen_->frame()->Pop(); - *arg1 = cgen_->frame()->Pop(); - *arg0 = cgen_->frame()->Pop(); + *arg3 = cgen()->frame()->Pop(); + *arg2 = cgen()->frame()->Pop(); + *arg1 = cgen()->frame()->Pop(); + *arg0 = cgen()->frame()->Pop(); } void JumpTarget::AddReachingFrame(VirtualFrame* frame) { ASSERT(reaching_frames_.length() == merge_labels_.length()); + ASSERT(entry_frame_ == NULL); Label fresh; merge_labels_.Add(fresh); reaching_frames_.Add(frame); @@ -575,64 +472,54 @@ void JumpTarget::AddReachingFrame(VirtualFrame* frame) { // ------------------------------------------------------------------------- // BreakTarget implementation. -void BreakTarget::Initialize(CodeGenerator* cgen, Directionality direction) { - JumpTarget::Initialize(cgen, direction); - ASSERT(cgen_->has_valid_frame()); - expected_height_ = cgen_->frame()->height(); +void BreakTarget::set_direction(Directionality direction) { + JumpTarget::set_direction(direction); + ASSERT(cgen()->has_valid_frame()); + expected_height_ = cgen()->frame()->height(); } void BreakTarget::CopyTo(BreakTarget* destination) { ASSERT(destination != NULL); - destination->cgen_ = cgen_; - destination->masm_ = masm_; destination->direction_ = direction_; - destination->reaching_frames_.Clear(); - destination->merge_labels_.Clear(); - ASSERT(reaching_frames_.length() == merge_labels_.length()); - for (int i = 0; i < reaching_frames_.length(); i++) { - destination->reaching_frames_.Add(reaching_frames_[i]); - destination->merge_labels_.Add(merge_labels_[i]); - } + destination->reaching_frames_.Rewind(0); + destination->reaching_frames_.AddAll(reaching_frames_); + destination->merge_labels_.Rewind(0); + destination->merge_labels_.AddAll(merge_labels_); destination->entry_frame_ = entry_frame_; destination->entry_label_ = entry_label_; - destination->is_bound_ = is_bound_; - destination->is_linked_ = is_linked_; destination->expected_height_ = expected_height_; } void BreakTarget::Jump() { - ASSERT(cgen_ != NULL); - ASSERT(cgen_->has_valid_frame()); + ASSERT(cgen()->has_valid_frame()); // Drop leftover statement state from the frame before merging. - cgen_->frame()->ForgetElements(cgen_->frame()->height() - expected_height_); + cgen()->frame()->ForgetElements(cgen()->frame()->height() - expected_height_); DoJump(); } void BreakTarget::Jump(Result* arg) { - ASSERT(cgen_ != NULL); - ASSERT(cgen_->has_valid_frame()); + ASSERT(cgen()->has_valid_frame()); // Drop leftover statement state from the frame before merging. - cgen_->frame()->ForgetElements(cgen_->frame()->height() - expected_height_); - cgen_->frame()->Push(arg); + cgen()->frame()->ForgetElements(cgen()->frame()->height() - expected_height_); + cgen()->frame()->Push(arg); DoJump(); } void BreakTarget::Branch(Condition cc, Hint hint) { - ASSERT(cgen_ != NULL); - ASSERT(cgen_->has_valid_frame()); + ASSERT(cgen()->has_valid_frame()); - int count = cgen_->frame()->height() - expected_height_; + int count = cgen()->frame()->height() - expected_height_; if (count > 0) { // We negate and branch here rather than using DoBranch's negate // and branch. This gives us a hook to remove statement state // from the frame. - JumpTarget fall_through(cgen_); + JumpTarget fall_through; // Branch to fall through will not negate, because it is a // forward-only target. fall_through.Branch(NegateCondition(cc), NegateHint(hint)); @@ -646,7 +533,6 @@ void BreakTarget::Branch(Condition cc, Hint hint) { void BreakTarget::Bind(int mergable_elements) { #ifdef DEBUG - ASSERT(cgen_ != NULL); // All the forward-reaching frames should have been adjusted at the // jumps to this target. for (int i = 0; i < reaching_frames_.length(); i++) { @@ -657,8 +543,9 @@ void BreakTarget::Bind(int mergable_elements) { // Drop leftover statement state from the frame before merging, even // on the fall through. This is so we can bind the return target // with state on the frame. - if (cgen_->has_valid_frame()) { - cgen_->frame()->ForgetElements(cgen_->frame()->height() - expected_height_); + if (cgen()->has_valid_frame()) { + int count = cgen()->frame()->height() - expected_height_; + cgen()->frame()->ForgetElements(count); } DoBind(mergable_elements); } @@ -666,7 +553,6 @@ void BreakTarget::Bind(int mergable_elements) { void BreakTarget::Bind(Result* arg, int mergable_elements) { #ifdef DEBUG - ASSERT(cgen_ != NULL); // All the forward-reaching frames should have been adjusted at the // jumps to this target. for (int i = 0; i < reaching_frames_.length(); i++) { @@ -677,12 +563,13 @@ void BreakTarget::Bind(Result* arg, int mergable_elements) { // Drop leftover statement state from the frame before merging, even // on the fall through. This is so we can bind the return target // with state on the frame. - if (cgen_->has_valid_frame()) { - cgen_->frame()->ForgetElements(cgen_->frame()->height() - expected_height_); - cgen_->frame()->Push(arg); + if (cgen()->has_valid_frame()) { + int count = cgen()->frame()->height() - expected_height_; + cgen()->frame()->ForgetElements(count); + cgen()->frame()->Push(arg); } DoBind(mergable_elements); - *arg = cgen_->frame()->Pop(); + *arg = cgen()->frame()->Pop(); } @@ -699,36 +586,23 @@ ShadowTarget::ShadowTarget(BreakTarget* shadowed) { // While shadowing this shadow target saves the state of the original. shadowed->CopyTo(this); - // The original's state is reset. We do not Unuse it because that - // would delete the expected frame and assert that the target is not - // linked. - shadowed->Reset(); - ASSERT(cgen_ != NULL); - ASSERT(cgen_->has_valid_frame()); - shadowed->set_expected_height(cgen_->frame()->height()); - - // Setting the code generator to null prevents the shadow target from - // being used until shadowing stops. - cgen_ = NULL; - masm_ = NULL; + // The original's state is reset. + shadowed->Unuse(); + ASSERT(cgen()->has_valid_frame()); + shadowed->set_expected_height(cgen()->frame()->height()); } void ShadowTarget::StopShadowing() { ASSERT(is_shadowing_); - // This target does not have a valid code generator yet. - cgen_ = other_target_->code_generator(); - ASSERT(cgen_ != NULL); - masm_ = cgen_->masm(); - // The states of this target, which was shadowed, and the original // target, which was shadowing, are swapped. BreakTarget temp; other_target_->CopyTo(&temp); CopyTo(other_target_); temp.CopyTo(this); - temp.Reset(); // So the destructor does not deallocate virtual frames. + temp.Unuse(); #ifdef DEBUG is_shadowing_ = false; diff --git a/deps/v8/src/jump-target.h b/deps/v8/src/jump-target.h index 1cfbe29dca..7585fafbfc 100644 --- a/deps/v8/src/jump-target.h +++ b/deps/v8/src/jump-target.h @@ -28,14 +28,14 @@ #ifndef V8_JUMP_TARGET_H_ #define V8_JUMP_TARGET_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Forward declarations. class FrameElement; class Result; class VirtualFrame; - // ------------------------------------------------------------------------- // Jump targets // @@ -52,43 +52,39 @@ class VirtualFrame; // In particular, this means that at least one of the control-flow // graph edges reaching the target must be a forward edge. -class JumpTarget : public Malloced { // Shadows are dynamically allocated. +class JumpTarget : public ZoneObject { // Shadows are dynamically allocated. public: // Forward-only jump targets can only be reached by forward CFG edges. enum Directionality { FORWARD_ONLY, BIDIRECTIONAL }; - // Construct a jump target with a given code generator used to generate - // code and to provide access to a current frame. - explicit JumpTarget(CodeGenerator* cgen, - Directionality direction = FORWARD_ONLY); - - // Construct a jump target without a code generator. A code - // generator must be supplied before using the jump target as a - // label. This is useful, eg, when break targets are embedded in - // AST nodes. - JumpTarget(); - - // Supply a code generator and directionality to an already - // constructed jump target. This function expects to be given a - // non-null code generator, and to be called only when the code - // generator is not yet set. - virtual void Initialize(CodeGenerator* cgen, - Directionality direction = FORWARD_ONLY); - - virtual ~JumpTarget() { Unuse(); } - - // Treat the jump target as a fresh one. The state is reset and - // pointed-to virtual frames are deallocated. There should be no - // dangling jumps to the target. - void Unuse(); + // Construct a jump target used to generate code and to provide + // access to a current frame. + explicit JumpTarget(Directionality direction) + : direction_(direction), + reaching_frames_(0), + merge_labels_(0), + entry_frame_(NULL) { + } + + // Construct a jump target. + JumpTarget() + : direction_(FORWARD_ONLY), + reaching_frames_(0), + merge_labels_(0), + entry_frame_(NULL) { + } - // Reset the internal state of this jump target. Pointed-to virtual - // frames are not deallocated and dangling jumps to the target are - // left dangling. - void Reset(); + virtual ~JumpTarget() {} - // Accessors. - CodeGenerator* code_generator() const { return cgen_; } + // Set the direction of the jump target. + virtual void set_direction(Directionality direction) { + direction_ = direction; + } + + // Treat the jump target as a fresh one. The state is reset. + void Unuse(); + + inline CodeGenerator* cgen(); Label* entry_label() { return &entry_label_; } @@ -98,9 +94,14 @@ class JumpTarget : public Malloced { // Shadows are dynamically allocated. } // Predicates testing the state of the encapsulated label. - bool is_bound() const { return is_bound_; } - bool is_linked() const { return is_linked_; } - bool is_unused() const { return !is_bound() && !is_linked(); } + bool is_bound() const { return entry_label_.is_bound(); } + bool is_linked() const { + return !is_bound() && !reaching_frames_.is_empty(); + } + bool is_unused() const { + // This is !is_bound() && !is_linked(). + return !is_bound() && reaching_frames_.is_empty(); + } // Emit a jump to the target. There must be a current frame at the // jump and there will be no current frame after the jump. @@ -161,21 +162,19 @@ class JumpTarget : public Malloced { // Shadows are dynamically allocated. static const int kAllElements = -1; // Not a valid number of elements. - protected: - // The code generator gives access to its current frame. - CodeGenerator* cgen_; - - // Used to emit code. - MacroAssembler* masm_; + static void set_compiling_deferred_code(bool flag) { + compiling_deferred_code_ = flag; + } + protected: // Directionality flag set at initialization time. Directionality direction_; // A list of frames reaching this block via forward jumps. - List<VirtualFrame*> reaching_frames_; + ZoneList<VirtualFrame*> reaching_frames_; // A parallel list of labels for merge code. - List<Label> merge_labels_; + ZoneList<Label> merge_labels_; // The frame used on entry to the block and expected at backward // jumps to the block. Set when the jump target is bound, but may @@ -185,12 +184,6 @@ class JumpTarget : public Malloced { // Shadows are dynamically allocated. // The actual entry label of the block. Label entry_label_; - // A target is bound if its Bind member function has been called. - // It is linked if it is not bound but its Jump, Branch, or Call - // member functions have been called. - bool is_bound_; - bool is_linked_; - // Implementations of Jump, Branch, and Bind with all arguments and // return values using the virtual frame. void DoJump(); @@ -198,13 +191,16 @@ class JumpTarget : public Malloced { // Shadows are dynamically allocated. void DoBind(int mergable_elements); private: - // Add a virtual frame reaching this labeled block via a forward - // jump, and a fresh label for its merge code. + static bool compiling_deferred_code_; + + // Add a virtual frame reaching this labeled block via a forward jump, + // and a corresponding merge code label. void AddReachingFrame(VirtualFrame* frame); - // Choose an element from a pair of frame elements to be in the - // expected frame. Return null if they are incompatible. - FrameElement* Combine(FrameElement* left, FrameElement* right); + // Perform initialization required during entry frame computation + // after setting the virtual frame element at index in frame to be + // target. + inline void InitializeEntryElement(int index, FrameElement* target); // Compute a frame to use for entry to this block. Mergable // elements is as described for the Bind function. @@ -226,18 +222,13 @@ class JumpTarget : public Malloced { // Shadows are dynamically allocated. class BreakTarget : public JumpTarget { public: - // Construct a break target without a code generator. A code - // generator must be supplied before using the break target as a - // label. This is useful, eg, when break targets are embedded in AST - // nodes. + // Construct a break target. BreakTarget() {} - // Supply a code generator, expected expression stack height, and - // directionality to an already constructed break target. This - // function expects to be given a non-null code generator, and to be - // called only when the code generator is not yet set. - virtual void Initialize(CodeGenerator* cgen, - Directionality direction = FORWARD_ONLY); + virtual ~BreakTarget() {} + + // Set the direction of the break target. + virtual void set_direction(Directionality direction); // Copy the state of this break target to the destination. The // lists of forward-reaching frames and merge-point labels are @@ -294,9 +285,7 @@ class ShadowTarget : public BreakTarget { // flow intended for the shadowed one. explicit ShadowTarget(BreakTarget* shadowed); - virtual ~ShadowTarget() { - ASSERT(!is_shadowing_); - } + virtual ~ShadowTarget() {} // End shadowing. After shadowing ends, the original jump target // again gives access to the formerly shadowed target and the shadow diff --git a/deps/v8/src/list-inl.h b/deps/v8/src/list-inl.h index e3d251fba5..e41db11fc5 100644 --- a/deps/v8/src/list-inl.h +++ b/deps/v8/src/list-inl.h @@ -30,7 +30,8 @@ #include "list.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { template<typename T, class P> @@ -43,6 +44,17 @@ void List<T, P>::Add(const T& element) { } +template<typename T, class P> +void List<T, P>::AddAll(const List<T, P>& other) { + int result_length = length_ + other.length_; + if (capacity_ < result_length) Resize(result_length); + for (int i = 0; i < other.length_; i++) { + data_[length_ + i] = other.data_[i]; + } + length_ = result_length; +} + + // Use two layers of inlining so that the non-inlined function can // use the same implementation as the inlined version. template<typename T, class P> @@ -57,11 +69,18 @@ void List<T, P>::ResizeAddInternal(const T& element) { // Grow the list capacity by 50%, but make sure to let it grow // even when the capacity is zero (possible initial case). int new_capacity = 1 + capacity_ + (capacity_ >> 1); + // Since the element reference could be an element of the list, copy + // it out of the old backing storage before resizing. + T temp = element; + Resize(new_capacity); + data_[length_++] = temp; +} + + +template<typename T, class P> +void List<T, P>::Resize(int new_capacity) { T* new_data = List<T, P>::NewData(new_capacity); memcpy(new_data, data_, capacity_ * sizeof(T)); - // Since the element reference could be an element of the list, - // assign it to the new backing store before deleting the old. - new_data[length_++] = element; List<T, P>::DeleteData(data_); data_ = new_data; capacity_ = new_capacity; diff --git a/deps/v8/src/list.h b/deps/v8/src/list.h index 92d23ea391..b6c06d8461 100644 --- a/deps/v8/src/list.h +++ b/deps/v8/src/list.h @@ -28,7 +28,8 @@ #ifndef V8_LIST_H_ #define V8_LIST_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // ---------------------------------------------------------------------------- @@ -77,6 +78,9 @@ class List { // expanding the list if necessary. void Add(const T& element); + // Add all the elements from the argument list to this list. + void AddAll(const List<T, P>& other); + // Added 'count' elements with the value 'value' and returns a // vector that allows access to the elements. The vector is valid // until the next change is made to this list. @@ -126,6 +130,9 @@ class List { // non-inlined versions of ResizeAdd. void ResizeAddInternal(const T& element); + // Resize the list. + void Resize(int new_capacity); + DISALLOW_COPY_AND_ASSIGN(List); }; diff --git a/deps/v8/src/log-utils.cc b/deps/v8/src/log-utils.cc new file mode 100644 index 0000000000..43610497ef --- /dev/null +++ b/deps/v8/src/log-utils.cc @@ -0,0 +1,302 @@ +// 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. + +#include "v8.h" + +#include "log-utils.h" + +namespace v8 { +namespace internal { + +#ifdef ENABLE_LOGGING_AND_PROFILING + +LogDynamicBuffer::LogDynamicBuffer( + int block_size, int max_size, const char* seal, int seal_size) + : block_size_(block_size), + max_size_(max_size - (max_size % block_size_)), + seal_(seal), + seal_size_(seal_size), + blocks_(max_size_ / block_size_ + 1), + write_pos_(0), block_index_(0), block_write_pos_(0), is_sealed_(false) { + ASSERT(BlocksCount() > 0); + AllocateBlock(0); + for (int i = 1; i < BlocksCount(); ++i) { + blocks_[i] = NULL; + } +} + + +LogDynamicBuffer::~LogDynamicBuffer() { + for (int i = 0; i < BlocksCount(); ++i) { + DeleteArray(blocks_[i]); + } +} + + +int LogDynamicBuffer::Read(int from_pos, char* dest_buf, int buf_size) { + if (buf_size == 0) return 0; + int read_pos = from_pos; + int block_read_index = BlockIndex(from_pos); + int block_read_pos = PosInBlock(from_pos); + int dest_buf_pos = 0; + // Read until dest_buf is filled, or write_pos_ encountered. + while (read_pos < write_pos_ && dest_buf_pos < buf_size) { + const int read_size = Min(write_pos_ - read_pos, + Min(buf_size - dest_buf_pos, block_size_ - block_read_pos)); + memcpy(dest_buf + dest_buf_pos, + blocks_[block_read_index] + block_read_pos, read_size); + block_read_pos += read_size; + dest_buf_pos += read_size; + read_pos += read_size; + if (block_read_pos == block_size_) { + block_read_pos = 0; + ++block_read_index; + } + } + return dest_buf_pos; +} + + +int LogDynamicBuffer::Seal() { + WriteInternal(seal_, seal_size_); + is_sealed_ = true; + return 0; +} + + +int LogDynamicBuffer::Write(const char* data, int data_size) { + if (is_sealed_) { + return 0; + } + if ((write_pos_ + data_size) <= (max_size_ - seal_size_)) { + return WriteInternal(data, data_size); + } else { + return Seal(); + } +} + + +int LogDynamicBuffer::WriteInternal(const char* data, int data_size) { + int data_pos = 0; + while (data_pos < data_size) { + const int write_size = + Min(data_size - data_pos, block_size_ - block_write_pos_); + memcpy(blocks_[block_index_] + block_write_pos_, data + data_pos, + write_size); + block_write_pos_ += write_size; + data_pos += write_size; + if (block_write_pos_ == block_size_) { + block_write_pos_ = 0; + AllocateBlock(++block_index_); + } + } + write_pos_ += data_size; + return data_size; +} + + +bool Log::is_stopped_ = false; +Log::WritePtr Log::Write = NULL; +FILE* Log::output_handle_ = NULL; +LogDynamicBuffer* Log::output_buffer_ = NULL; +// Must be the same message as in Logger::PauseProfiler +const char* Log::kDynamicBufferSeal = "profiler,\"pause\"\n"; +Mutex* Log::mutex_ = NULL; +char* Log::message_buffer_ = NULL; + + +void Log::Init() { + mutex_ = OS::CreateMutex(); + message_buffer_ = NewArray<char>(kMessageBufferSize); +} + + +void Log::OpenStdout() { + ASSERT(!IsEnabled()); + output_handle_ = stdout; + Write = WriteToFile; + Init(); +} + + +void Log::OpenFile(const char* name) { + ASSERT(!IsEnabled()); + output_handle_ = OS::FOpen(name, OS::LogFileOpenMode); + Write = WriteToFile; + Init(); +} + + +void Log::OpenMemoryBuffer() { + ASSERT(!IsEnabled()); + output_buffer_ = new LogDynamicBuffer( + kDynamicBufferBlockSize, kMaxDynamicBufferSize, + kDynamicBufferSeal, strlen(kDynamicBufferSeal)); + Write = WriteToMemory; + Init(); +} + + +void Log::Close() { + if (Write == WriteToFile) { + fclose(output_handle_); + output_handle_ = NULL; + } else if (Write == WriteToMemory) { + delete output_buffer_; + output_buffer_ = NULL; + } else { + ASSERT(Write == NULL); + } + Write = NULL; + + delete mutex_; + mutex_ = NULL; + + is_stopped_ = false; +} + + +int Log::GetLogLines(int from_pos, char* dest_buf, int max_size) { + if (Write != WriteToMemory) return 0; + ASSERT(output_buffer_ != NULL); + ASSERT(from_pos >= 0); + ASSERT(max_size >= 0); + int actual_size = output_buffer_->Read(from_pos, dest_buf, max_size); + ASSERT(actual_size <= max_size); + if (actual_size == 0) return 0; + + // Find previous log line boundary. + char* end_pos = dest_buf + actual_size - 1; + while (end_pos >= dest_buf && *end_pos != '\n') --end_pos; + actual_size = end_pos - dest_buf + 1; + ASSERT(actual_size <= max_size); + return actual_size; +} + + +LogMessageBuilder::WriteFailureHandler + LogMessageBuilder::write_failure_handler = NULL; + + +LogMessageBuilder::LogMessageBuilder(): sl(Log::mutex_), pos_(0) { + ASSERT(Log::message_buffer_ != NULL); +} + + +void LogMessageBuilder::Append(const char* format, ...) { + Vector<char> buf(Log::message_buffer_ + pos_, + Log::kMessageBufferSize - pos_); + va_list args; + va_start(args, format); + Append(format, args); + va_end(args); + ASSERT(pos_ <= Log::kMessageBufferSize); +} + + +void LogMessageBuilder::Append(const char* format, va_list args) { + Vector<char> buf(Log::message_buffer_ + pos_, + Log::kMessageBufferSize - pos_); + int result = v8::internal::OS::VSNPrintF(buf, format, args); + + // Result is -1 if output was truncated. + if (result >= 0) { + pos_ += result; + } else { + pos_ = Log::kMessageBufferSize; + } + ASSERT(pos_ <= Log::kMessageBufferSize); +} + + +void LogMessageBuilder::Append(const char c) { + if (pos_ < Log::kMessageBufferSize) { + Log::message_buffer_[pos_++] = c; + } + ASSERT(pos_ <= Log::kMessageBufferSize); +} + + +void LogMessageBuilder::Append(String* str) { + AssertNoAllocation no_heap_allocation; // Ensure string stay valid. + int length = str->length(); + for (int i = 0; i < length; i++) { + Append(static_cast<char>(str->Get(i))); + } +} + + +void LogMessageBuilder::AppendDetailed(String* str, bool show_impl_info) { + AssertNoAllocation no_heap_allocation; // Ensure string stay valid. + int len = str->length(); + if (len > 0x1000) + len = 0x1000; + if (show_impl_info) { + Append(str->IsAsciiRepresentation() ? 'a' : '2'); + if (StringShape(str).IsExternal()) + Append('e'); + if (StringShape(str).IsSymbol()) + Append('#'); + Append(":%i:", str->length()); + } + for (int i = 0; i < len; i++) { + uc32 c = str->Get(i); + if (c > 0xff) { + Append("\\u%04x", c); + } else if (c < 32 || c > 126) { + Append("\\x%02x", c); + } else if (c == ',') { + Append("\\,"); + } else if (c == '\\') { + Append("\\\\"); + } else { + Append("%lc", c); + } + } +} + + +void LogMessageBuilder::WriteToLogFile() { + ASSERT(pos_ <= Log::kMessageBufferSize); + const int written = Log::Write(Log::message_buffer_, pos_); + if (written != pos_ && write_failure_handler != NULL) { + write_failure_handler(); + } +} + + +void LogMessageBuilder::WriteCStringToLogFile(const char* str) { + const int len = strlen(str); + const int written = Log::Write(str, len); + if (written != len && write_failure_handler != NULL) { + write_failure_handler(); + } +} + +#endif // ENABLE_LOGGING_AND_PROFILING + +} } // namespace v8::internal diff --git a/deps/v8/src/log-utils.h b/deps/v8/src/log-utils.h new file mode 100644 index 0000000000..2e8b3a3647 --- /dev/null +++ b/deps/v8/src/log-utils.h @@ -0,0 +1,223 @@ +// Copyright 2006-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_LOG_UTILS_H_ +#define V8_LOG_UTILS_H_ + +namespace v8 { +namespace internal { + +#ifdef ENABLE_LOGGING_AND_PROFILING + +// A memory buffer that increments its size as you write in it. Size +// is incremented with 'block_size' steps, never exceeding 'max_size'. +// During growth, memory contents are never copied. At the end of the +// buffer an amount of memory specified in 'seal_size' is reserved. +// When writing position reaches max_size - seal_size, buffer auto-seals +// itself with 'seal' and allows no further writes. Data pointed by +// 'seal' must be available during entire LogDynamicBuffer lifetime. +// +// An instance of this class is created dynamically by Log. +class LogDynamicBuffer { + public: + LogDynamicBuffer( + int block_size, int max_size, const char* seal, int seal_size); + + ~LogDynamicBuffer(); + + // Reads contents of the buffer starting from 'from_pos'. Upon + // return, 'dest_buf' is filled with the data. Actual amount of data + // filled is returned, it is <= 'buf_size'. + int Read(int from_pos, char* dest_buf, int buf_size); + + // Writes 'data' to the buffer, making it larger if necessary. If + // data is too big to fit in the buffer, it doesn't get written at + // all. In that case, buffer auto-seals itself and stops to accept + // any incoming writes. Returns amount of data written (it is either + // 'data_size', or 0, if 'data' is too big). + int Write(const char* data, int data_size); + + private: + void AllocateBlock(int index) { + blocks_[index] = NewArray<char>(block_size_); + } + + int BlockIndex(int pos) const { return pos / block_size_; } + + int BlocksCount() const { return BlockIndex(max_size_) + 1; } + + int PosInBlock(int pos) const { return pos % block_size_; } + + int Seal(); + + int WriteInternal(const char* data, int data_size); + + const int block_size_; + const int max_size_; + const char* seal_; + const int seal_size_; + ScopedVector<char*> blocks_; + int write_pos_; + int block_index_; + int block_write_pos_; + bool is_sealed_; +}; + + +// Functions and data for performing output of log messages. +class Log : public AllStatic { + public: + // Opens stdout for logging. + static void OpenStdout(); + + // Opens file for logging. + static void OpenFile(const char* name); + + // Opens memory buffer for logging. + static void OpenMemoryBuffer(); + + // Disables logging, but preserves acquired resources. + static void stop() { is_stopped_ = true; } + + // Frees all resources acquired in Open... functions. + static void Close(); + + // See description in include/v8.h. + static int GetLogLines(int from_pos, char* dest_buf, int max_size); + + // Returns whether logging is enabled. + static bool IsEnabled() { + return !is_stopped_ && (output_handle_ != NULL || output_buffer_ != NULL); + } + + private: + typedef int (*WritePtr)(const char* msg, int length); + + // Initialization function called from Open... functions. + static void Init(); + + // Write functions assume that mutex_ is acquired by the caller. + static WritePtr Write; + + // Implementation of writing to a log file. + static int WriteToFile(const char* msg, int length) { + ASSERT(output_handle_ != NULL); + int rv = fwrite(msg, 1, length, output_handle_); + ASSERT(length == rv); + return rv; + } + + // Implementation of writing to a memory buffer. + static int WriteToMemory(const char* msg, int length) { + ASSERT(output_buffer_ != NULL); + return output_buffer_->Write(msg, length); + } + + // Whether logging is stopped (e.g. due to insufficient resources). + static bool is_stopped_; + + // When logging is active, either output_handle_ or output_buffer_ is used + // to store a pointer to log destination. If logging was opened via OpenStdout + // or OpenFile, then output_handle_ is used. If logging was opened + // via OpenMemoryBuffer, then output_buffer_ is used. + // mutex_ should be acquired before using output_handle_ or output_buffer_. + static FILE* output_handle_; + + static LogDynamicBuffer* output_buffer_; + + // Size of dynamic buffer block (and dynamic buffer initial size). + static const int kDynamicBufferBlockSize = 65536; + + // Maximum size of dynamic buffer. + static const int kMaxDynamicBufferSize = 50 * 1024 * 1024; + + // Message to "seal" dynamic buffer with. + static const char* kDynamicBufferSeal; + + // mutex_ is a Mutex used for enforcing exclusive + // access to the formatting buffer and the log file or log memory buffer. + static Mutex* mutex_; + + // Size of buffer used for formatting log messages. + static const int kMessageBufferSize = 2048; + + // Buffer used for formatting log messages. This is a singleton buffer and + // mutex_ should be acquired before using it. + static char* message_buffer_; + + friend class LogMessageBuilder; +}; + + +// Utility class for formatting log messages. It fills the message into the +// static buffer in Log. +class LogMessageBuilder BASE_EMBEDDED { + public: + // Create a message builder starting from position 0. This acquires the mutex + // in the log as well. + explicit LogMessageBuilder(); + ~LogMessageBuilder() { } + + // Append string data to the log message. + void Append(const char* format, ...); + + // Append string data to the log message. + void Append(const char* format, va_list args); + + // Append a character to the log message. + void Append(const char c); + + // Append a heap string. + void Append(String* str); + + void AppendDetailed(String* str, bool show_impl_info); + + // Write the log message to the log file currently opened. + void WriteToLogFile(); + + // Write a null-terminated string to to the log file currently opened. + void WriteCStringToLogFile(const char* str); + + // A handler that is called when Log::Write fails. + typedef void (*WriteFailureHandler)(); + + static void set_write_failure_handler(WriteFailureHandler handler) { + write_failure_handler = handler; + } + + private: + static WriteFailureHandler write_failure_handler; + + ScopedLock sl; + int pos_; +}; + +#endif // ENABLE_LOGGING_AND_PROFILING + +} } // namespace v8::internal + +#endif // V8_LOG_UTILS_H_ diff --git a/deps/v8/src/log.cc b/deps/v8/src/log.cc index 5297a302d8..c1edf4d185 100644 --- a/deps/v8/src/log.cc +++ b/deps/v8/src/log.cc @@ -31,12 +31,14 @@ #include "bootstrapper.h" #include "log.h" +#include "log-utils.h" #include "macro-assembler.h" #include "platform.h" #include "serialize.h" #include "string-stream.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { #ifdef ENABLE_LOGGING_AND_PROFILING @@ -144,11 +146,18 @@ void StackTracer::Trace(TickSample* sample) { return; } + const Address js_entry_sp = Top::js_entry_sp(Top::GetCurrentThread()); + if (js_entry_sp == 0) { + // Not executing JS now. + sample->frames_count = 0; + return; + } + SafeStackTraceFrameIterator it( reinterpret_cast<Address>(sample->fp), reinterpret_cast<Address>(sample->sp), reinterpret_cast<Address>(sample->sp), - reinterpret_cast<Address>(low_stack_bound_)); + js_entry_sp); int i = 0; while (!it.done() && i < TickSample::kMaxFramesCount) { sample->stack[i++] = it.frame()->pc(); @@ -164,14 +173,13 @@ void StackTracer::Trace(TickSample* sample) { // class Ticker: public Sampler { public: - explicit Ticker(int interval, uintptr_t low_stack_bound): - Sampler(interval, FLAG_prof), window_(NULL), profiler_(NULL), - stack_tracer_(low_stack_bound) {} + explicit Ticker(int interval): + Sampler(interval, FLAG_prof), window_(NULL), profiler_(NULL) {} ~Ticker() { if (IsActive()) Stop(); } void Tick(TickSample* sample) { - if (IsProfiling()) stack_tracer_.Trace(sample); + if (IsProfiling()) StackTracer::Trace(sample); if (profiler_) profiler_->Insert(sample); if (window_) window_->AddState(sample->state); } @@ -188,7 +196,7 @@ class Ticker: public Sampler { void SetProfiler(Profiler* profiler) { profiler_ = profiler; - if (!IsActive()) Start(); + if (!FLAG_prof_lazy && !IsActive()) Start(); } void ClearProfiler() { @@ -199,7 +207,6 @@ class Ticker: public Sampler { private: SlidingStateWindow* window_; Profiler* profiler_; - StackTracer stack_tracer_; }; @@ -254,7 +261,7 @@ void Profiler::Engage() { // Register to get ticks. Logger::ticker_->SetProfiler(this); - LOG(UncheckedStringEvent("profiler", "begin")); + Logger::ProfilerBeginEvent(); } @@ -267,6 +274,8 @@ void Profiler::Disengage() { // the thread to terminate. running_ = false; TickSample sample; + // Reset 'paused_' flag, otherwise semaphore may not be signalled. + resume(); Insert(&sample); Join(); @@ -284,296 +293,6 @@ void Profiler::Run() { } -#ifdef ENABLE_LOGGING_AND_PROFILING - -// Functions and data for performing output of log messages. -class Log : public AllStatic { - public: - // Opens stdout for logging. - static void OpenStdout(); - - // Opens file for logging. - static void OpenFile(const char* name); - - // Opens memory buffer for logging. - static void OpenMemoryBuffer(); - - // Frees all resources acquired in Open... functions. - static void Close(); - - // See description in v8.h. - static int GetLogLines(int from_pos, char* dest_buf, int max_size); - - static bool is_enabled() { return output_.handle != NULL; } - - typedef int (*WritePtr)(const char* msg, int length); - private: - static void Init(); - - // Write functions assume that mutex_ is acquired by the caller. - static WritePtr Write; - - static int WriteToFile(const char* msg, int length) { - ASSERT(output_.handle != NULL); - int rv = fwrite(msg, 1, length, output_.handle); - ASSERT(length == rv); - return rv; - } - - static int WriteToMemory(const char* msg, int length) { - ASSERT(output_.buffer != NULL); - ASSERT(output_buffer_write_pos_ >= output_.buffer); - if (output_buffer_write_pos_ + length - <= output_.buffer + kOutputBufferSize) { - memcpy(output_buffer_write_pos_, msg, length); - output_buffer_write_pos_ += length; - return length; - } else { - // Memory buffer is full, ignore write. - return 0; - } - } - - // When logging is active, output_ refers the file or memory buffer - // events are written to. - // mutex_ should be acquired before using output_. - union Output { - FILE* handle; - char* buffer; - }; - static Output output_; - - // mutex_ is a Mutex used for enforcing exclusive - // access to the formatting buffer and the log file or log memory buffer. - static Mutex* mutex_; - - // Size of buffer used for memory logging. - static const int kOutputBufferSize = 2 * 1024 * 1024; - - // Writing position in a memory buffer. - static char* output_buffer_write_pos_; - - // Size of buffer used for formatting log messages. - static const int kMessageBufferSize = 2048; - - // Buffer used for formatting log messages. This is a singleton buffer and - // mutex_ should be acquired before using it. - static char* message_buffer_; - - friend class LogMessageBuilder; -}; - - -Log::WritePtr Log::Write = NULL; -Log::Output Log::output_ = {NULL}; -Mutex* Log::mutex_ = NULL; -char* Log::output_buffer_write_pos_ = NULL; -char* Log::message_buffer_ = NULL; - - -void Log::Init() { - mutex_ = OS::CreateMutex(); - message_buffer_ = NewArray<char>(kMessageBufferSize); -} - - -void Log::OpenStdout() { - ASSERT(output_.handle == NULL); - output_.handle = stdout; - Write = WriteToFile; - Init(); -} - - -void Log::OpenFile(const char* name) { - ASSERT(output_.handle == NULL); - output_.handle = OS::FOpen(name, OS::LogFileOpenMode); - Write = WriteToFile; - Init(); -} - - -void Log::OpenMemoryBuffer() { - ASSERT(output_.buffer == NULL); - output_.buffer = NewArray<char>(kOutputBufferSize); - output_buffer_write_pos_ = output_.buffer; - Write = WriteToMemory; - Init(); -} - - -void Log::Close() { - if (Write == WriteToFile) { - fclose(output_.handle); - output_.handle = NULL; - } else if (Write == WriteToMemory) { - DeleteArray(output_.buffer); - output_.buffer = NULL; - } else { - ASSERT(Write == NULL); - } - Write = NULL; - - delete mutex_; - mutex_ = NULL; - - DeleteArray(message_buffer_); - message_buffer_ = NULL; -} - - -int Log::GetLogLines(int from_pos, char* dest_buf, int max_size) { - if (Write != WriteToMemory) return 0; - ASSERT(output_.buffer != NULL); - ASSERT(output_buffer_write_pos_ >= output_.buffer); - ASSERT(from_pos >= 0); - ASSERT(max_size >= 0); - int actual_size = max_size; - char* buffer_read_pos = output_.buffer + from_pos; - ScopedLock sl(mutex_); - if (actual_size == 0 - || output_buffer_write_pos_ == output_.buffer - || buffer_read_pos >= output_buffer_write_pos_) { - // No data requested or can be returned. - return 0; - } - if (buffer_read_pos + actual_size > output_buffer_write_pos_) { - // Requested size overlaps with current writing position and - // needs to be truncated. - actual_size = output_buffer_write_pos_ - buffer_read_pos; - ASSERT(actual_size == 0 || buffer_read_pos[actual_size - 1] == '\n'); - } else { - // Find previous log line boundary. - char* end_pos = buffer_read_pos + actual_size - 1; - while (end_pos >= buffer_read_pos && *end_pos != '\n') --end_pos; - actual_size = end_pos - buffer_read_pos + 1; - } - ASSERT(actual_size <= max_size); - if (actual_size > 0) { - memcpy(dest_buf, buffer_read_pos, actual_size); - } - return actual_size; -} - - -// Utility class for formatting log messages. It fills the message into the -// static buffer in Log. -class LogMessageBuilder BASE_EMBEDDED { - public: - explicit LogMessageBuilder(); - ~LogMessageBuilder() { } - - void Append(const char* format, ...); - void Append(const char* format, va_list args); - void Append(const char c); - void Append(String *str); - void AppendDetailed(String* str, bool show_impl_info); - - void WriteToLogFile(); - void WriteCStringToLogFile(const char* str); - - private: - ScopedLock sl; - int pos_; -}; - - -// Create a message builder starting from position 0. This acquires the mutex -// in the logger as well. -LogMessageBuilder::LogMessageBuilder(): sl(Log::mutex_), pos_(0) { - ASSERT(Log::message_buffer_ != NULL); -} - - -// Append string data to the log message. -void LogMessageBuilder::Append(const char* format, ...) { - Vector<char> buf(Log::message_buffer_ + pos_, - Log::kMessageBufferSize - pos_); - va_list args; - va_start(args, format); - Append(format, args); - va_end(args); - ASSERT(pos_ <= Log::kMessageBufferSize); -} - - -// Append string data to the log message. -void LogMessageBuilder::Append(const char* format, va_list args) { - Vector<char> buf(Log::message_buffer_ + pos_, - Log::kMessageBufferSize - pos_); - int result = v8::internal::OS::VSNPrintF(buf, format, args); - - // Result is -1 if output was truncated. - if (result >= 0) { - pos_ += result; - } else { - pos_ = Log::kMessageBufferSize; - } - ASSERT(pos_ <= Log::kMessageBufferSize); -} - - -// Append a character to the log message. -void LogMessageBuilder::Append(const char c) { - if (pos_ < Log::kMessageBufferSize) { - Log::message_buffer_[pos_++] = c; - } - ASSERT(pos_ <= Log::kMessageBufferSize); -} - - -// Append a heap string. -void LogMessageBuilder::Append(String* str) { - AssertNoAllocation no_heap_allocation; // Ensure string stay valid. - int length = str->length(); - for (int i = 0; i < length; i++) { - Append(static_cast<char>(str->Get(i))); - } -} - -void LogMessageBuilder::AppendDetailed(String* str, bool show_impl_info) { - AssertNoAllocation no_heap_allocation; // Ensure string stay valid. - int len = str->length(); - if (len > 0x1000) - len = 0x1000; - if (show_impl_info) { - Append(str->IsAsciiRepresentation() ? 'a' : '2'); - if (StringShape(str).IsExternal()) - Append('e'); - if (StringShape(str).IsSymbol()) - Append('#'); - Append(":%i:", str->length()); - } - for (int i = 0; i < len; i++) { - uc32 c = str->Get(i); - if (c > 0xff) { - Append("\\u%04x", c); - } else if (c < 32 || c > 126) { - Append("\\x%02x", c); - } else if (c == ',') { - Append("\\,"); - } else if (c == '\\') { - Append("\\\\"); - } else { - Append("%lc", c); - } - } -} - -// Write the log message to the log file currently opened. -void LogMessageBuilder::WriteToLogFile() { - ASSERT(pos_ <= Log::kMessageBufferSize); - Log::Write(Log::message_buffer_, pos_); -} - -// Write a null-terminated string to to the log file currently opened. -void LogMessageBuilder::WriteCStringToLogFile(const char* str) { - int len = strlen(str); - Log::Write(str, len); -} -#endif - - // // Logger class implementation. // @@ -584,8 +303,16 @@ VMState Logger::bottom_state_(EXTERNAL); SlidingStateWindow* Logger::sliding_state_window_ = NULL; -bool Logger::is_enabled() { - return Log::is_enabled(); +bool Logger::IsEnabled() { + return Log::IsEnabled(); +} + + +void Logger::ProfilerBeginEvent() { + if (!Log::IsEnabled()) return; + LogMessageBuilder msg; + msg.Append("profiler,\"begin\",%d\n", kSamplingIntervalMs); + msg.WriteToLogFile(); } #endif // ENABLE_LOGGING_AND_PROFILING @@ -593,7 +320,7 @@ bool Logger::is_enabled() { void Logger::Preamble(const char* content) { #ifdef ENABLE_LOGGING_AND_PROFILING - if (!Log::is_enabled() || !FLAG_log_code) return; + if (!Log::IsEnabled() || !FLAG_log_code) return; LogMessageBuilder msg; msg.WriteCStringToLogFile(content); #endif @@ -609,7 +336,7 @@ void Logger::StringEvent(const char* name, const char* value) { #ifdef ENABLE_LOGGING_AND_PROFILING void Logger::UncheckedStringEvent(const char* name, const char* value) { - if (!Log::is_enabled()) return; + if (!Log::IsEnabled()) return; LogMessageBuilder msg; msg.Append("%s,\"%s\"\n", name, value); msg.WriteToLogFile(); @@ -619,7 +346,7 @@ void Logger::UncheckedStringEvent(const char* name, const char* value) { void Logger::IntEvent(const char* name, int value) { #ifdef ENABLE_LOGGING_AND_PROFILING - if (!Log::is_enabled() || !FLAG_log) return; + if (!Log::IsEnabled() || !FLAG_log) return; LogMessageBuilder msg; msg.Append("%s,%d\n", name, value); msg.WriteToLogFile(); @@ -629,9 +356,9 @@ void Logger::IntEvent(const char* name, int value) { void Logger::HandleEvent(const char* name, Object** location) { #ifdef ENABLE_LOGGING_AND_PROFILING - if (!Log::is_enabled() || !FLAG_log_handles) return; + if (!Log::IsEnabled() || !FLAG_log_handles) return; LogMessageBuilder msg; - msg.Append("%s,0x%%"V8PRIp"\n", name, location); + msg.Append("%s,0x%" V8PRIxPTR "\n", name, location); msg.WriteToLogFile(); #endif } @@ -642,7 +369,7 @@ void Logger::HandleEvent(const char* name, Object** location) { // caller's responsibility to ensure that log is enabled and that // FLAG_log_api is true. void Logger::ApiEvent(const char* format, ...) { - ASSERT(Log::is_enabled() && FLAG_log_api); + ASSERT(Log::IsEnabled() && FLAG_log_api); LogMessageBuilder msg; va_list ap; va_start(ap, format); @@ -655,7 +382,7 @@ void Logger::ApiEvent(const char* format, ...) { void Logger::ApiNamedSecurityCheck(Object* key) { #ifdef ENABLE_LOGGING_AND_PROFILING - if (!Log::is_enabled() || !FLAG_log_api) return; + if (!Log::IsEnabled() || !FLAG_log_api) return; if (key->IsString()) { SmartPointer<char> str = String::cast(key)->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); @@ -673,7 +400,7 @@ void Logger::SharedLibraryEvent(const char* library_path, unsigned start, unsigned end) { #ifdef ENABLE_LOGGING_AND_PROFILING - if (!Log::is_enabled() || !FLAG_prof) return; + if (!Log::IsEnabled() || !FLAG_prof) return; LogMessageBuilder msg; msg.Append("shared-library,\"%s\",0x%08x,0x%08x\n", library_path, start, end); @@ -686,7 +413,7 @@ void Logger::SharedLibraryEvent(const wchar_t* library_path, unsigned start, unsigned end) { #ifdef ENABLE_LOGGING_AND_PROFILING - if (!Log::is_enabled() || !FLAG_prof) return; + if (!Log::IsEnabled() || !FLAG_prof) return; LogMessageBuilder msg; msg.Append("shared-library,\"%ls\",0x%08x,0x%08x\n", library_path, start, end); @@ -741,7 +468,7 @@ void Logger::LogRegExpSource(Handle<JSRegExp> regexp) { void Logger::RegExpCompileEvent(Handle<JSRegExp> regexp, bool in_cache) { #ifdef ENABLE_LOGGING_AND_PROFILING - if (!Log::is_enabled() || !FLAG_log_regexp) return; + if (!Log::IsEnabled() || !FLAG_log_regexp) return; LogMessageBuilder msg; msg.Append("regexp-compile,"); LogRegExpSource(regexp); @@ -753,7 +480,7 @@ void Logger::RegExpCompileEvent(Handle<JSRegExp> regexp, bool in_cache) { void Logger::LogRuntime(Vector<const char> format, JSArray* args) { #ifdef ENABLE_LOGGING_AND_PROFILING - if (!Log::is_enabled() || !FLAG_log_runtime) return; + if (!Log::IsEnabled() || !FLAG_log_runtime) return; HandleScope scope; LogMessageBuilder msg; for (int i = 0; i < format.length(); i++) { @@ -794,7 +521,7 @@ void Logger::LogRuntime(Vector<const char> format, JSArray* args) { void Logger::ApiIndexedSecurityCheck(uint32_t index) { #ifdef ENABLE_LOGGING_AND_PROFILING - if (!Log::is_enabled() || !FLAG_log_api) return; + if (!Log::IsEnabled() || !FLAG_log_api) return; ApiEvent("api,check-security,%u\n", index); #endif } @@ -805,7 +532,7 @@ void Logger::ApiNamedPropertyAccess(const char* tag, Object* name) { #ifdef ENABLE_LOGGING_AND_PROFILING ASSERT(name->IsString()); - if (!Log::is_enabled() || !FLAG_log_api) return; + if (!Log::IsEnabled() || !FLAG_log_api) return; String* class_name_obj = holder->class_name(); SmartPointer<char> class_name = class_name_obj->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); @@ -819,7 +546,7 @@ void Logger::ApiIndexedPropertyAccess(const char* tag, JSObject* holder, uint32_t index) { #ifdef ENABLE_LOGGING_AND_PROFILING - if (!Log::is_enabled() || !FLAG_log_api) return; + if (!Log::IsEnabled() || !FLAG_log_api) return; String* class_name_obj = holder->class_name(); SmartPointer<char> class_name = class_name_obj->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); @@ -829,7 +556,7 @@ void Logger::ApiIndexedPropertyAccess(const char* tag, void Logger::ApiObjectAccess(const char* tag, JSObject* object) { #ifdef ENABLE_LOGGING_AND_PROFILING - if (!Log::is_enabled() || !FLAG_log_api) return; + if (!Log::IsEnabled() || !FLAG_log_api) return; String* class_name_obj = object->class_name(); SmartPointer<char> class_name = class_name_obj->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); @@ -840,7 +567,7 @@ void Logger::ApiObjectAccess(const char* tag, JSObject* object) { void Logger::ApiEntryCall(const char* name) { #ifdef ENABLE_LOGGING_AND_PROFILING - if (!Log::is_enabled() || !FLAG_log_api) return; + if (!Log::IsEnabled() || !FLAG_log_api) return; Logger::ApiEvent("api,%s\n", name); #endif } @@ -848,9 +575,9 @@ void Logger::ApiEntryCall(const char* name) { void Logger::NewEvent(const char* name, void* object, size_t size) { #ifdef ENABLE_LOGGING_AND_PROFILING - if (!Log::is_enabled() || !FLAG_log) return; + if (!Log::IsEnabled() || !FLAG_log) return; LogMessageBuilder msg; - msg.Append("new,%s,0x%%"V8PRIp",%u\n", name, object, + msg.Append("new,%s,0x%" V8PRIxPTR ",%u\n", name, object, static_cast<unsigned int>(size)); msg.WriteToLogFile(); #endif @@ -859,9 +586,9 @@ void Logger::NewEvent(const char* name, void* object, size_t size) { void Logger::DeleteEvent(const char* name, void* object) { #ifdef ENABLE_LOGGING_AND_PROFILING - if (!Log::is_enabled() || !FLAG_log) return; + if (!Log::IsEnabled() || !FLAG_log) return; LogMessageBuilder msg; - msg.Append("delete,%s,0x%%"V8PRIp"\n", name, object); + msg.Append("delete,%s,0x%" V8PRIxPTR "\n", name, object); msg.WriteToLogFile(); #endif } @@ -869,9 +596,9 @@ void Logger::DeleteEvent(const char* name, void* object) { void Logger::CodeCreateEvent(const char* tag, Code* code, const char* comment) { #ifdef ENABLE_LOGGING_AND_PROFILING - if (!Log::is_enabled() || !FLAG_log_code) return; + if (!Log::IsEnabled() || !FLAG_log_code) return; LogMessageBuilder msg; - msg.Append("code-creation,%s,0x%"V8PRIp",%d,\"", tag, code->address(), + msg.Append("code-creation,%s,0x%" V8PRIxPTR ",%d,\"", tag, code->address(), code->ExecutableSize()); for (const char* p = comment; *p != '\0'; p++) { if (*p == '"') { @@ -888,12 +615,12 @@ void Logger::CodeCreateEvent(const char* tag, Code* code, const char* comment) { void Logger::CodeCreateEvent(const char* tag, Code* code, String* name) { #ifdef ENABLE_LOGGING_AND_PROFILING - if (!Log::is_enabled() || !FLAG_log_code) return; + if (!Log::IsEnabled() || !FLAG_log_code) return; LogMessageBuilder msg; SmartPointer<char> str = name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); - msg.Append("code-creation,%s,0x%"V8PRIp",%d,\"%s\"\n", tag, code->address(), - code->ExecutableSize(), *str); + msg.Append("code-creation,%s,0x%" V8PRIxPTR ",%d,\"%s\"\n", + tag, code->address(), code->ExecutableSize(), *str); msg.WriteToLogFile(); #endif } @@ -902,13 +629,13 @@ void Logger::CodeCreateEvent(const char* tag, Code* code, String* name) { void Logger::CodeCreateEvent(const char* tag, Code* code, String* name, String* source, int line) { #ifdef ENABLE_LOGGING_AND_PROFILING - if (!Log::is_enabled() || !FLAG_log_code) return; + if (!Log::IsEnabled() || !FLAG_log_code) return; LogMessageBuilder msg; SmartPointer<char> str = name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); SmartPointer<char> sourcestr = source->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); - msg.Append("code-creation,%s,0x%"V8PRIp",%d,\"%s %s:%d\"\n", + msg.Append("code-creation,%s,0x%" V8PRIxPTR ",%d,\"%s %s:%d\"\n", tag, code->address(), code->ExecutableSize(), *str, *sourcestr, line); @@ -919,9 +646,9 @@ void Logger::CodeCreateEvent(const char* tag, Code* code, String* name, void Logger::CodeCreateEvent(const char* tag, Code* code, int args_count) { #ifdef ENABLE_LOGGING_AND_PROFILING - if (!Log::is_enabled() || !FLAG_log_code) return; + if (!Log::IsEnabled() || !FLAG_log_code) return; LogMessageBuilder msg; - msg.Append("code-creation,%s,0x%"V8PRIp",%d,\"args_count: %d\"\n", tag, + msg.Append("code-creation,%s,0x%" V8PRIxPTR ",%d,\"args_count: %d\"\n", tag, code->address(), code->ExecutableSize(), args_count); @@ -932,9 +659,9 @@ void Logger::CodeCreateEvent(const char* tag, Code* code, int args_count) { void Logger::RegExpCodeCreateEvent(Code* code, String* source) { #ifdef ENABLE_LOGGING_AND_PROFILING - if (!Log::is_enabled() || !FLAG_log_code) return; + if (!Log::IsEnabled() || !FLAG_log_code) return; LogMessageBuilder msg; - msg.Append("code-creation,%s,0x%"V8PRIp",%d,\"", "RegExp", + msg.Append("code-creation,%s,0x%" V8PRIxPTR ",%d,\"", "RegExp", code->address(), code->ExecutableSize()); msg.AppendDetailed(source, false); @@ -946,9 +673,11 @@ void Logger::RegExpCodeCreateEvent(Code* code, String* source) { void Logger::CodeAllocateEvent(Code* code, Assembler* assem) { #ifdef ENABLE_LOGGING_AND_PROFILING - if (!Log::is_enabled() || !FLAG_log_code) return; + if (!Log::IsEnabled() || !FLAG_log_code) return; LogMessageBuilder msg; - msg.Append("code-allocate,0x%"V8PRIp",0x%"V8PRIp"\n", code->address(), assem); + msg.Append("code-allocate,0x%" V8PRIxPTR ",0x%" V8PRIxPTR "\n", + code->address(), + assem); msg.WriteToLogFile(); #endif } @@ -956,9 +685,9 @@ void Logger::CodeAllocateEvent(Code* code, Assembler* assem) { void Logger::CodeMoveEvent(Address from, Address to) { #ifdef ENABLE_LOGGING_AND_PROFILING - if (!Log::is_enabled() || !FLAG_log_code) return; + if (!Log::IsEnabled() || !FLAG_log_code) return; LogMessageBuilder msg; - msg.Append("code-move,0x%"V8PRIp",0x%"V8PRIp"\n", from, to); + msg.Append("code-move,0x%" V8PRIxPTR ",0x%" V8PRIxPTR "\n", from, to); msg.WriteToLogFile(); #endif } @@ -966,9 +695,9 @@ void Logger::CodeMoveEvent(Address from, Address to) { void Logger::CodeDeleteEvent(Address from) { #ifdef ENABLE_LOGGING_AND_PROFILING - if (!Log::is_enabled() || !FLAG_log_code) return; + if (!Log::IsEnabled() || !FLAG_log_code) return; LogMessageBuilder msg; - msg.Append("code-delete,0x%"V8PRIp"\n", from); + msg.Append("code-delete,0x%" V8PRIxPTR "\n", from); msg.WriteToLogFile(); #endif } @@ -976,7 +705,7 @@ void Logger::CodeDeleteEvent(Address from) { void Logger::ResourceEvent(const char* name, const char* tag) { #ifdef ENABLE_LOGGING_AND_PROFILING - if (!Log::is_enabled() || !FLAG_log) return; + if (!Log::IsEnabled() || !FLAG_log) return; LogMessageBuilder msg; msg.Append("%s,%s,", name, tag); @@ -994,7 +723,7 @@ void Logger::ResourceEvent(const char* name, const char* tag) { void Logger::SuspectReadEvent(String* name, Object* obj) { #ifdef ENABLE_LOGGING_AND_PROFILING - if (!Log::is_enabled() || !FLAG_log_suspect) return; + if (!Log::IsEnabled() || !FLAG_log_suspect) return; LogMessageBuilder msg; String* class_name = obj->IsJSObject() ? JSObject::cast(obj)->class_name() @@ -1013,7 +742,7 @@ void Logger::SuspectReadEvent(String* name, Object* obj) { void Logger::HeapSampleBeginEvent(const char* space, const char* kind) { #ifdef ENABLE_LOGGING_AND_PROFILING - if (!Log::is_enabled() || !FLAG_log_gc) return; + if (!Log::IsEnabled() || !FLAG_log_gc) return; LogMessageBuilder msg; msg.Append("heap-sample-begin,\"%s\",\"%s\"\n", space, kind); msg.WriteToLogFile(); @@ -1023,7 +752,7 @@ void Logger::HeapSampleBeginEvent(const char* space, const char* kind) { void Logger::HeapSampleEndEvent(const char* space, const char* kind) { #ifdef ENABLE_LOGGING_AND_PROFILING - if (!Log::is_enabled() || !FLAG_log_gc) return; + if (!Log::IsEnabled() || !FLAG_log_gc) return; LogMessageBuilder msg; msg.Append("heap-sample-end,\"%s\",\"%s\"\n", space, kind); msg.WriteToLogFile(); @@ -1033,7 +762,7 @@ void Logger::HeapSampleEndEvent(const char* space, const char* kind) { void Logger::HeapSampleItemEvent(const char* type, int number, int bytes) { #ifdef ENABLE_LOGGING_AND_PROFILING - if (!Log::is_enabled() || !FLAG_log_gc) return; + if (!Log::IsEnabled() || !FLAG_log_gc) return; LogMessageBuilder msg; msg.Append("heap-sample-item,%s,%d,%d\n", type, number, bytes); msg.WriteToLogFile(); @@ -1043,7 +772,7 @@ void Logger::HeapSampleItemEvent(const char* type, int number, int bytes) { void Logger::DebugTag(const char* call_site_tag) { #ifdef ENABLE_LOGGING_AND_PROFILING - if (!Log::is_enabled() || !FLAG_log) return; + if (!Log::IsEnabled() || !FLAG_log) return; LogMessageBuilder msg; msg.Append("debug-tag,%s\n", call_site_tag); msg.WriteToLogFile(); @@ -1053,7 +782,7 @@ void Logger::DebugTag(const char* call_site_tag) { void Logger::DebugEvent(const char* event_type, Vector<uint16_t> parameter) { #ifdef ENABLE_LOGGING_AND_PROFILING - if (!Log::is_enabled() || !FLAG_log) return; + if (!Log::IsEnabled() || !FLAG_log) return; StringBuilder s(parameter.length() + 1); for (int i = 0; i < parameter.length(); ++i) { s.AddCharacter(static_cast<char>(parameter[i])); @@ -1072,15 +801,15 @@ void Logger::DebugEvent(const char* event_type, Vector<uint16_t> parameter) { #ifdef ENABLE_LOGGING_AND_PROFILING void Logger::TickEvent(TickSample* sample, bool overflow) { - if (!Log::is_enabled() || !FLAG_prof) return; + if (!Log::IsEnabled() || !FLAG_prof) return; LogMessageBuilder msg; - msg.Append("tick,0x%"V8PRIp",0x%"V8PRIp",%d", sample->pc, sample->sp, - static_cast<int>(sample->state)); + msg.Append("tick,0x%" V8PRIxPTR ",0x%" V8PRIxPTR ",%d", + sample->pc, sample->sp, static_cast<int>(sample->state)); if (overflow) { msg.Append(",overflow"); } for (int i = 0; i < sample->frames_count; ++i) { - msg.Append(",0x%"V8PRIp, sample->stack[i]); + msg.Append(",0x%" V8PRIxPTR, sample->stack[i]); } msg.Append('\n'); msg.WriteToLogFile(); @@ -1093,19 +822,113 @@ bool Logger::IsProfilerPaused() { void Logger::PauseProfiler() { + if (profiler_->paused()) { + return; + } profiler_->pause(); + if (FLAG_prof_lazy) { + if (!FLAG_sliding_state_window) ticker_->Stop(); + FLAG_log_code = false; + // Must be the same message as Log::kDynamicBufferSeal. + LOG(UncheckedStringEvent("profiler", "pause")); + } } void Logger::ResumeProfiler() { + if (!profiler_->paused() || !Log::IsEnabled()) { + return; + } + if (FLAG_prof_lazy) { + LOG(UncheckedStringEvent("profiler", "resume")); + FLAG_log_code = true; + LogCompiledFunctions(); + if (!FLAG_sliding_state_window) ticker_->Start(); + } profiler_->resume(); } +// This function can be called when Log's mutex is acquired, +// either from main or Profiler's thread. +void Logger::StopLoggingAndProfiling() { + Log::stop(); + PauseProfiler(); +} + + +bool Logger::IsProfilerSamplerActive() { + return ticker_->IsActive(); +} + + int Logger::GetLogLines(int from_pos, char* dest_buf, int max_size) { return Log::GetLogLines(from_pos, dest_buf, max_size); } + +void Logger::LogCompiledFunctions() { + HandleScope scope; + Handle<SharedFunctionInfo>* sfis = NULL; + int compiled_funcs_count = 0; + + { + AssertNoAllocation no_alloc; + + HeapIterator iterator; + while (iterator.has_next()) { + HeapObject* obj = iterator.next(); + ASSERT(obj != NULL); + if (obj->IsSharedFunctionInfo() + && SharedFunctionInfo::cast(obj)->is_compiled()) { + ++compiled_funcs_count; + } + } + + sfis = NewArray< Handle<SharedFunctionInfo> >(compiled_funcs_count); + iterator.reset(); + + int i = 0; + while (iterator.has_next()) { + HeapObject* obj = iterator.next(); + ASSERT(obj != NULL); + if (obj->IsSharedFunctionInfo() + && SharedFunctionInfo::cast(obj)->is_compiled()) { + sfis[i++] = Handle<SharedFunctionInfo>(SharedFunctionInfo::cast(obj)); + } + } + } + + // During iteration, there can be heap allocation due to + // GetScriptLineNumber call. + for (int i = 0; i < compiled_funcs_count; ++i) { + Handle<SharedFunctionInfo> shared = sfis[i]; + Handle<String> name(String::cast(shared->name())); + Handle<String> func_name(name->length() > 0 ? + *name : shared->inferred_name()); + if (shared->script()->IsScript()) { + Handle<Script> script(Script::cast(shared->script())); + if (script->name()->IsString()) { + Handle<String> script_name(String::cast(script->name())); + int line_num = GetScriptLineNumber(script, shared->start_position()); + if (line_num > 0) { + line_num += script->line_offset()->value() + 1; + LOG(CodeCreateEvent("LazyCompile", shared->code(), *func_name, + *script_name, line_num)); + } else { + // Can't distinguish enum and script here, so always use Script. + LOG(CodeCreateEvent("Script", shared->code(), *script_name)); + } + continue; + } + } + // If no script or script has no name. + LOG(CodeCreateEvent("LazyCompile", shared->code(), *func_name)); + } + + DeleteArray(sfis); +} + #endif @@ -1125,9 +948,15 @@ bool Logger::Setup() { // --prof implies --log-code. if (FLAG_prof) FLAG_log_code = true; + // --prof_lazy controls --log-code, implies --noprof_auto. + if (FLAG_prof_lazy) { + FLAG_log_code = false; + FLAG_prof_auto = false; + } + bool open_log_file = FLAG_log || FLAG_log_runtime || FLAG_log_api || FLAG_log_code || FLAG_log_gc || FLAG_log_handles || FLAG_log_suspect - || FLAG_log_regexp || FLAG_log_state_changes; + || FLAG_log_regexp || FLAG_log_state_changes || FLAG_prof_lazy; // If we're logging anything, we need to open the log file. if (open_log_file) { @@ -1178,10 +1007,7 @@ bool Logger::Setup() { current_state_ = &bottom_state_; - // as log is initialized early with V8, we can assume that JS execution - // frames can never reach this point on stack - int stack_var; - ticker_ = new Ticker(1, reinterpret_cast<uintptr_t>(&stack_var)); + ticker_ = new Ticker(kSamplingIntervalMs); if (FLAG_sliding_state_window && sliding_state_window_ == NULL) { sliding_state_window_ = new SlidingStateWindow(); @@ -1194,6 +1020,8 @@ bool Logger::Setup() { profiler_->Engage(); } + LogMessageBuilder::set_write_failure_handler(StopLoggingAndProfiling); + return true; #else @@ -1204,6 +1032,8 @@ bool Logger::Setup() { void Logger::TearDown() { #ifdef ENABLE_LOGGING_AND_PROFILING + LogMessageBuilder::set_write_failure_handler(NULL); + // Stop the profiler before closing the file. if (profiler_ != NULL) { profiler_->Disengage(); @@ -1212,8 +1042,10 @@ void Logger::TearDown() { } delete sliding_state_window_; + sliding_state_window_ = NULL; delete ticker_; + ticker_ = NULL; Log::Close(); #endif @@ -1264,7 +1096,7 @@ static const char* StateToString(StateTag state) { VMState::VMState(StateTag state) { #if !defined(ENABLE_HEAP_PROTECTION) // When not protecting the heap, there is no difference between - // EXTERNAL and OTHER. As an optimizatin in that case, we will not + // EXTERNAL and OTHER. As an optimization in that case, we will not // perform EXTERNAL->OTHER transitions through the API. We thus // compress the two states into one. if (state == EXTERNAL) state = OTHER; diff --git a/deps/v8/src/log.h b/deps/v8/src/log.h index 5f3c188c53..2f8f81c95b 100644 --- a/deps/v8/src/log.h +++ b/deps/v8/src/log.h @@ -28,7 +28,8 @@ #ifndef V8_LOG_H_ #define V8_LOG_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Logger is used for collecting logging information from V8 during // execution. The result is dumped to a file. @@ -75,7 +76,7 @@ class LogMessageBuilder; #ifdef ENABLE_LOGGING_AND_PROFILING #define LOG(Call) \ do { \ - if (v8::internal::Logger::is_enabled()) \ + if (v8::internal::Logger::IsEnabled()) \ v8::internal::Logger::Call; \ } while (false) #else @@ -201,7 +202,7 @@ class Logger { return current_state_ ? current_state_->state() : OTHER; } - static bool is_enabled(); + static bool IsEnabled(); // Pause/Resume collection of profiling data. // When data collection is paused, Tick events are discarded until @@ -214,8 +215,17 @@ class Logger { // retrieve previously written messages. See v8.h. static int GetLogLines(int from_pos, char* dest_buf, int max_size); + // Logs all compiled functions found in the heap. + static void LogCompiledFunctions(); + private: + // Profiler's sampling interval (in milliseconds). + static const int kSamplingIntervalMs = 1; + + // Emits the profiler's first message. + static void ProfilerBeginEvent(); + // Emits the source code of a regexp. Used by regexp events. static void LogRegExpSource(Handle<JSRegExp> regexp); @@ -227,6 +237,12 @@ class Logger { // Logs a StringEvent regardless of whether FLAG_log is true. static void UncheckedStringEvent(const char* name, const char* value); + // Stops logging and profiling in case of insufficient resources. + static void StopLoggingAndProfiling(); + + // Returns whether profiler's sampler is active. + static bool IsProfilerSamplerActive(); + // The sampler used by the profiler and the sliding state window. static Ticker* ticker_; @@ -252,6 +268,8 @@ class Logger { friend class Profiler; friend class SlidingStateWindow; friend class VMState; + + friend class LoggerTestHelper; #else static bool is_enabled() { return false; } #endif @@ -259,14 +277,9 @@ class Logger { // Class that extracts stack trace, used for profiling. -class StackTracer BASE_EMBEDDED { +class StackTracer : public AllStatic { public: - explicit StackTracer(uintptr_t low_stack_bound) - : low_stack_bound_(low_stack_bound) { } - void Trace(TickSample* sample); - private: - - uintptr_t low_stack_bound_; + static void Trace(TickSample* sample); }; diff --git a/deps/v8/src/mark-compact.cc b/deps/v8/src/mark-compact.cc index 48774ec5b5..56e4ea6caf 100644 --- a/deps/v8/src/mark-compact.cc +++ b/deps/v8/src/mark-compact.cc @@ -33,7 +33,8 @@ #include "mark-compact.h" #include "stub-cache.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // ------------------------------------------------------------------------- // MarkCompactCollector @@ -471,7 +472,7 @@ void MarkCompactCollector::MarkMapContents(Map* map) { void MarkCompactCollector::MarkDescriptorArray( - DescriptorArray *descriptors) { + DescriptorArray* descriptors) { if (descriptors->IsMarked()) return; // Empty descriptor array is marked as a root before any maps are marked. ASSERT(descriptors != Heap::empty_descriptor_array()); @@ -871,7 +872,7 @@ void MarkCompactCollector::ClearNonLiveTransitions() { // clearing map transitions when necessary. current = map; bool on_dead_path = !current->IsMarked(); - Object *next; + Object* next; while (SafeIsMap(current)) { next = current->prototype(); // There should never be a dead map above a live map. diff --git a/deps/v8/src/mark-compact.h b/deps/v8/src/mark-compact.h index bfa2c3ce51..d7ad630136 100644 --- a/deps/v8/src/mark-compact.h +++ b/deps/v8/src/mark-compact.h @@ -28,7 +28,8 @@ #ifndef V8_MARK_COMPACT_H_ #define V8_MARK_COMPACT_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Callback function, returns whether an object is alive. The heap size // of the object is returned in size. It optionally updates the offset diff --git a/deps/v8/src/memory.h b/deps/v8/src/memory.h index 2397bc6c7a..c64699ee3b 100644 --- a/deps/v8/src/memory.h +++ b/deps/v8/src/memory.h @@ -28,13 +28,18 @@ #ifndef V8_MEMORY_H_ #define V8_MEMORY_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Memory provides an interface to 'raw' memory. It encapsulates the casts // that typically are needed when incompatible pointer types are used. class Memory { public: + static uint16_t& uint16_at(Address addr) { + return *reinterpret_cast<uint16_t*>(addr); + } + static uint32_t& uint32_at(Address addr) { return *reinterpret_cast<uint32_t*>(addr); } @@ -43,6 +48,10 @@ class Memory { return *reinterpret_cast<int32_t*>(addr); } + static uint64_t& uint64_at(Address addr) { + return *reinterpret_cast<uint64_t*>(addr); + } + static int& int_at(Address addr) { return *reinterpret_cast<int*>(addr); } diff --git a/deps/v8/src/messages.cc b/deps/v8/src/messages.cc index ca0ce2a542..a3fffcb50a 100644 --- a/deps/v8/src/messages.cc +++ b/deps/v8/src/messages.cc @@ -33,7 +33,8 @@ #include "spaces-inl.h" #include "top.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // If no message listeners have been registered this one is called diff --git a/deps/v8/src/messages.h b/deps/v8/src/messages.h index 1ff10aae89..80ce8eb9ca 100644 --- a/deps/v8/src/messages.h +++ b/deps/v8/src/messages.h @@ -36,7 +36,8 @@ #include "handles-inl.h" // Forward declaration of MessageLocation. -namespace v8 { namespace internal { +namespace v8 { +namespace internal { class MessageLocation; } } // namespace v8::internal @@ -57,7 +58,8 @@ class V8Message { }; -namespace v8 { namespace internal { +namespace v8 { +namespace internal { struct Language; class SourceInfo; diff --git a/deps/v8/src/mirror-delay.js b/deps/v8/src/mirror-delay.js index 30f19f0a0f..f5a12c793b 100644 --- a/deps/v8/src/mirror-delay.js +++ b/deps/v8/src/mirror-delay.js @@ -29,8 +29,7 @@ // Touch the RegExp and Date functions to make sure that date-delay.js and // regexp-delay.js has been loaded. This is required as the mirrors use -// functions within these files through the builtins object. See the -// function DateToISO8601_ as an example. +// functions within these files through the builtins object. RegExp; Date; @@ -935,7 +934,8 @@ inherits(DateMirror, ObjectMirror); DateMirror.prototype.toText = function() { - return DateToISO8601_(this.value_); + var s = JSON.stringify(this.value_); + return s.substring(1, s.length - 1); // cut quotes } @@ -1617,6 +1617,11 @@ ScriptMirror.prototype.scriptType = function() { }; +ScriptMirror.prototype.compilationType = function() { + return this.script_.compilation_type; +}; + + ScriptMirror.prototype.lineCount = function() { return this.script_.lineCount(); }; @@ -1638,6 +1643,20 @@ ScriptMirror.prototype.context = function() { }; +ScriptMirror.prototype.evalFromFunction = function() { + return MakeMirror(this.script_.eval_from_function); +}; + + +ScriptMirror.prototype.evalFromLocation = function() { + var eval_from_function = this.evalFromFunction(); + if (!eval_from_function.isUndefined()) { + var position = this.script_.eval_from_position; + return eval_from_function.script().locationFromPosition(position, true); + } +}; + + ScriptMirror.prototype.toText = function() { var result = ''; result += this.name(); @@ -1728,12 +1747,13 @@ JSONProtocolSerializer.prototype.serializeValue = function(mirror) { /** * Returns a serialization of all the objects referenced. * - * @param {Mirror} mirror The mirror to serialize - * @returns {String} JSON serialization + * @param {Mirror} mirror The mirror to serialize. + * @returns {Array.<Object>} Array of the referenced objects converted to + * protcol objects. */ JSONProtocolSerializer.prototype.serializeReferencedObjects = function() { - // Collect the JSON serialization of the referenced objects in an array. - var content = new Array(); + // Collect the protocol representation of the referenced objects in an array. + var content = []; // Get the number of referenced objects. var count = this.mirrors_.length; @@ -1742,8 +1762,7 @@ JSONProtocolSerializer.prototype.serializeReferencedObjects = function() { content.push(this.serialize_(this.mirrors_[i], false, false)); } - var json = ArrayToJSONArray_(content); - return json; + return content; } @@ -1752,6 +1771,11 @@ JSONProtocolSerializer.prototype.includeSource_ = function() { } +JSONProtocolSerializer.prototype.compactFormat_ = function() { + return this.options_ && this.options_.compactFormat; +} + + JSONProtocolSerializer.prototype.add_ = function(mirror) { // If this mirror is already in the list just return. for (var i = 0; i < this.mirrors_.length; i++) { @@ -1765,26 +1789,70 @@ JSONProtocolSerializer.prototype.add_ = function(mirror) { } +/** + * Formats mirror object to protocol reference object with some data that can + * be used to display the value in debugger. + * @param {Mirror} mirror Mirror to serialize. + * @return {Object} Protocol reference object. + */ +JSONProtocolSerializer.prototype.serializeReferenceWithDisplayData_ = + function(mirror) { + var o = {}; + o.ref = mirror.handle(); + o.type = mirror.type(); + switch (mirror.type()) { + case UNDEFINED_TYPE: + case NULL_TYPE: + case BOOLEAN_TYPE: + case NUMBER_TYPE: + o.value = mirror.value(); + break; + case STRING_TYPE: + // Limit string length. + o.value = mirror.toText(); + break; + case FUNCTION_TYPE: + o.name = mirror.name(); + o.inferredName = mirror.inferredName(); + if (mirror.script()) { + o.scriptId = mirror.script().id(); + } + break; + case ERROR_TYPE: + case REGEXP_TYPE: + o.value = mirror.toText(); + break; + case OBJECT_TYPE: + o.className = mirror.className(); + break; + } + return o; +}; + JSONProtocolSerializer.prototype.serialize_ = function(mirror, reference, details) { // If serializing a reference to a mirror just return the reference and add // the mirror to the referenced mirrors. if (reference && (mirror.isValue() || mirror.isScript() || mirror.isContext())) { - this.add_(mirror); - return '{"ref":' + mirror.handle() + '}'; + if (this.compactFormat_() && mirror.isValue()) { + return this.serializeReferenceWithDisplayData_(mirror); + } else { + this.add_(mirror); + return {'ref' : mirror.handle()}; + } } - // Collect the JSON property/value pairs in an array. - var content = new Array(); + // Collect the JSON property/value pairs. + var content = {}; // Add the mirror handle. if (mirror.isValue() || mirror.isScript() || mirror.isContext()) { - content.push(MakeJSONPair_('handle', NumberToJSON_(mirror.handle()))); + content.handle = mirror.handle(); } // Always add the type. - content.push(MakeJSONPair_('type', StringToJSON_(mirror.type()))); + content.type = mirror.type(); switch (mirror.type()) { case UNDEFINED_TYPE: @@ -1794,26 +1862,25 @@ JSONProtocolSerializer.prototype.serialize_ = function(mirror, reference, case BOOLEAN_TYPE: // Boolean values are simply represented by their value. - content.push(MakeJSONPair_('value', BooleanToJSON_(mirror.value()))); + content.value = mirror.value(); break; case NUMBER_TYPE: // Number values are simply represented by their value. - content.push(MakeJSONPair_('value', NumberToJSON_(mirror.value()))); + content.value = NumberToJSON_(mirror.value()); break; case STRING_TYPE: // String values might have their value cropped to keep down size. if (mirror.length() > kMaxProtocolStringLength) { var str = mirror.value().substring(0, kMaxProtocolStringLength); - content.push(MakeJSONPair_('value', StringToJSON_(str))); - content.push(MakeJSONPair_('fromIndex', NumberToJSON_(0))); - content.push(MakeJSONPair_('toIndex', - NumberToJSON_(kMaxProtocolStringLength))); + content.value = str; + content.fromIndex = 0; + content.toIndex = kMaxProtocolStringLength; } else { - content.push(MakeJSONPair_('value', StringToJSON_(mirror.value()))); + content.value = mirror.value(); } - content.push(MakeJSONPair_('length', NumberToJSON_(mirror.length()))); + content.length = mirror.length(); break; case OBJECT_TYPE: @@ -1836,46 +1903,46 @@ JSONProtocolSerializer.prototype.serialize_ = function(mirror, reference, case SCRIPT_TYPE: // Script is represented by id, name and source attributes. if (mirror.name()) { - content.push(MakeJSONPair_('name', StringToJSON_(mirror.name()))); + content.name = mirror.name(); } - content.push(MakeJSONPair_('id', NumberToJSON_(mirror.id()))); - content.push(MakeJSONPair_('lineOffset', - NumberToJSON_(mirror.lineOffset()))); - content.push(MakeJSONPair_('columnOffset', - NumberToJSON_(mirror.columnOffset()))); - content.push(MakeJSONPair_('lineCount', - NumberToJSON_(mirror.lineCount()))); + content.id = mirror.id(); + content.lineOffset = mirror.lineOffset(); + content.columnOffset = mirror.columnOffset(); + content.lineCount = mirror.lineCount(); if (mirror.data()) { - content.push(MakeJSONPair_('data', JSON.stringify(mirror.data()))); + content.data = mirror.data(); } if (this.includeSource_()) { - content.push(MakeJSONPair_('source', - StringToJSON_(mirror.source()))); + content.source = mirror.source(); } else { var sourceStart = mirror.source().substring(0, 80); - content.push(MakeJSONPair_('sourceStart', - StringToJSON_(sourceStart))); + content.sourceStart = sourceStart; + } + content.sourceLength = mirror.source().length; + content.scriptType = mirror.scriptType(); + content.compilationType = mirror.compilationType(); + if (mirror.compilationType() == 1) { // Compilation type eval. + content.evalFromScript = + this.serializeReference(mirror.evalFromFunction().script()); + var evalFromLocation = mirror.evalFromLocation() + content.evalFromLocation = { line: evalFromLocation.line, + column: evalFromLocation.column} } - content.push(MakeJSONPair_('sourceLength', - NumberToJSON_(mirror.source().length))); - content.push(MakeJSONPair_('scriptType', - NumberToJSON_(mirror.scriptType()))); if (mirror.context()) { - content.push(MakeJSONPair_('context', - this.serializeReference(mirror.context()))); + content.context = this.serializeReference(mirror.context()); } break; case CONTEXT_TYPE: - content.push(MakeJSONPair_('data', JSON.stringify(mirror.data()))); + content.data = mirror.data(); break; } // Always add the text representation. - content.push(MakeJSONPair_('text', StringToJSON_(mirror.toText()))); + content.text = mirror.toText(); // Create and return the JSON string. - return ArrayToJSONObject_(content); + return content; } @@ -1893,44 +1960,40 @@ JSONProtocolSerializer.prototype.serialize_ = function(mirror, reference, JSONProtocolSerializer.prototype.serializeObject_ = function(mirror, content, details) { // Add general object properties. - content.push(MakeJSONPair_('className', - StringToJSON_(mirror.className()))); - content.push(MakeJSONPair_('constructorFunction', - this.serializeReference(mirror.constructorFunction()))); - content.push(MakeJSONPair_('protoObject', - this.serializeReference(mirror.protoObject()))); - content.push(MakeJSONPair_('prototypeObject', - this.serializeReference(mirror.prototypeObject()))); + content.className = mirror.className(); + content.constructorFunction = + this.serializeReference(mirror.constructorFunction()); + content.protoObject = this.serializeReference(mirror.protoObject()); + content.prototypeObject = this.serializeReference(mirror.prototypeObject()); // Add flags to indicate whether there are interceptors. if (mirror.hasNamedInterceptor()) { - content.push(MakeJSONPair_('namedInterceptor', BooleanToJSON_(true))); + content.namedInterceptor = true; } if (mirror.hasIndexedInterceptor()) { - content.push(MakeJSONPair_('indexedInterceptor', BooleanToJSON_(true))); + content.indexedInterceptor = true; } // Add function specific properties. if (mirror.isFunction()) { // Add function specific properties. - content.push(MakeJSONPair_('name', StringToJSON_(mirror.name()))); + content.name = mirror.name(); if (!IS_UNDEFINED(mirror.inferredName())) { - content.push(MakeJSONPair_('inferredName', - StringToJSON_(mirror.inferredName()))); + content.inferredName = mirror.inferredName(); } - content.push(MakeJSONPair_('resolved', BooleanToJSON_(mirror.resolved()))); + content.resolved = mirror.resolved(); if (mirror.resolved()) { - content.push(MakeJSONPair_('source', StringToJSON_(mirror.source()))); + content.source = mirror.source(); } if (mirror.script()) { - content.push(MakeJSONPair_('script', this.serializeReference(mirror.script()))); + content.script = this.serializeReference(mirror.script()); } } // Add date specific properties. if (mirror.isDate()) { // Add date specific properties. - content.push(MakeJSONPair_('value', DateToJSON_(mirror.value()))); + content.value = mirror.value(); } // Add actual properties - named properties followed by indexed properties. @@ -1938,20 +2001,20 @@ JSONProtocolSerializer.prototype.serializeObject_ = function(mirror, content, var propertyIndexes = mirror.propertyNames(PropertyKind.Indexed); var p = new Array(propertyNames.length + propertyIndexes.length); for (var i = 0; i < propertyNames.length; i++) { - var property_mirror = mirror.property(propertyNames[i]); - p[i] = this.serializeProperty_(property_mirror); + var propertyMirror = mirror.property(propertyNames[i]); + p[i] = this.serializeProperty_(propertyMirror); if (details) { - this.add_(property_mirror.value()); + this.add_(propertyMirror.value()); } } for (var i = 0; i < propertyIndexes.length; i++) { - var property_mirror = mirror.property(propertyIndexes[i]); - p[propertyNames.length + i] = this.serializeProperty_(property_mirror); + var propertyMirror = mirror.property(propertyIndexes[i]); + p[propertyNames.length + i] = this.serializeProperty_(propertyMirror); if (details) { - this.add_(property_mirror.value()); + this.add_(propertyMirror.value()); } } - content.push(MakeJSONPair_('properties', ArrayToJSONArray_(p))); + content.properties = p; } @@ -1971,207 +2034,93 @@ JSONProtocolSerializer.prototype.serializeObject_ = function(mirror, content, * {"name":"hello","ref":1} * {"name":"length","attributes":7,"propertyType":3,"ref":2} * - * @param {PropertyMirror} property_mirror The property to serialize - * @returns {String} JSON serialization + * @param {PropertyMirror} propertyMirror The property to serialize. + * @returns {Object} Protocol object representing the property. */ -JSONProtocolSerializer.prototype.serializeProperty_ = function(property_mirror) { - var builder = new builtins.StringBuilder(); - builder.add('{"name":'); - builder.add(StringToJSON_(property_mirror.name())); - if (property_mirror.attributes() != PropertyAttribute.None) { - builder.add(',"attributes":'); - builder.add(NumberToJSON_(property_mirror.attributes())); - } - if (property_mirror.propertyType() != PropertyType.Normal) { - builder.add(',"propertyType":'); - builder.add(NumberToJSON_(property_mirror.propertyType())); +JSONProtocolSerializer.prototype.serializeProperty_ = function(propertyMirror) { + var result = {}; + + result.name = propertyMirror.name(); + var propertyValue = propertyMirror.value(); + if (this.compactFormat_() && propertyValue.isValue()) { + result.value = this.serializeReferenceWithDisplayData_(propertyValue); + } else { + if (propertyMirror.attributes() != PropertyAttribute.None) { + result.attributes = propertyMirror.attributes(); + } + if (propertyMirror.propertyType() != PropertyType.Normal) { + result.propertyType = propertyMirror.propertyType(); + } + result.ref = propertyValue.handle(); } - builder.add(',"ref":'); - builder.add(NumberToJSON_(property_mirror.value().handle())); - builder.add('}'); - return builder.generate(); + return result; } JSONProtocolSerializer.prototype.serializeFrame_ = function(mirror, content) { - content.push(MakeJSONPair_('index', NumberToJSON_(mirror.index()))); - content.push(MakeJSONPair_('receiver', - this.serializeReference(mirror.receiver()))); + content.index = mirror.index(); + content.receiver = this.serializeReference(mirror.receiver()); var func = mirror.func(); - content.push(MakeJSONPair_('func', this.serializeReference(func))); + content.func = this.serializeReference(func); if (func.script()) { - content.push(MakeJSONPair_('script', - this.serializeReference(func.script()))); + content.script = this.serializeReference(func.script()); } - content.push(MakeJSONPair_('constructCall', - BooleanToJSON_(mirror.isConstructCall()))); - content.push(MakeJSONPair_('debuggerFrame', - BooleanToJSON_(mirror.isDebuggerFrame()))); + content.constructCall = mirror.isConstructCall(); + content.debuggerFrame = mirror.isDebuggerFrame(); var x = new Array(mirror.argumentCount()); for (var i = 0; i < mirror.argumentCount(); i++) { - arg = new Array(); + var arg = {}; var argument_name = mirror.argumentName(i) if (argument_name) { - arg.push(MakeJSONPair_('name', StringToJSON_(argument_name))); + arg.name = argument_name; } - arg.push(MakeJSONPair_('value', - this.serializeReference(mirror.argumentValue(i)))); - x[i] = ArrayToJSONObject_(arg); + arg.value = this.serializeReference(mirror.argumentValue(i)); + x[i] = arg; } - content.push(MakeJSONPair_('arguments', ArrayToJSONArray_(x))); + content.arguments = x; var x = new Array(mirror.localCount()); for (var i = 0; i < mirror.localCount(); i++) { - var name = MakeJSONPair_('name', StringToJSON_(mirror.localName(i))); - var value = MakeJSONPair_('value', - this.serializeReference(mirror.localValue(i))); - x[i] = '{' + name + ',' + value + '}'; + var local = {}; + local.name = mirror.localName(i); + local.value = this.serializeReference(mirror.localValue(i)); + x[i] = local; } - content.push(MakeJSONPair_('locals', ArrayToJSONArray_(x))); - content.push(MakeJSONPair_('position', - NumberToJSON_(mirror.sourcePosition()))); + content.locals = x; + content.position = mirror.sourcePosition(); var line = mirror.sourceLine(); if (!IS_UNDEFINED(line)) { - content.push(MakeJSONPair_('line', NumberToJSON_(line))); + content.line = line; } var column = mirror.sourceColumn(); if (!IS_UNDEFINED(column)) { - content.push(MakeJSONPair_('column', NumberToJSON_(column))); + content.column = column; } var source_line_text = mirror.sourceLineText(); if (!IS_UNDEFINED(source_line_text)) { - content.push(MakeJSONPair_('sourceLineText', - StringToJSON_(source_line_text))); + content.sourceLineText = source_line_text; } } -function MakeJSONPair_(name, value) { - return '"' + name + '":' + value; -} - - -function ArrayToJSONObject_(content) { - return '{' + content.join(',') + '}'; -} - - -function ArrayToJSONArray_(content) { - return '[' + content.join(',') + ']'; -} - - -function BooleanToJSON_(value) { - return String(value); -} - - /** - * Convert a number to a JSON string value. For all finite numbers the number - * literal representation is used. For non finite numbers NaN, Infinite and + * Convert a number to a protocol value. For all finite numbers the number + * itself is returned. For non finite numbers NaN, Infinite and * -Infinite the string representation "NaN", "Infinite" or "-Infinite" - * (including the quotes) is returned. + * (not including the quotes) is returned. * - * @param {number} value The number value to convert to a JSON value - * @returns {String} JSON value + * @param {number} value The number value to convert to a protocol value. + * @returns {number|string} Protocol value. */ function NumberToJSON_(value) { if (isNaN(value)) { - return '"NaN"'; + return 'NaN'; } if (!isFinite(value)) { if (value > 0) { - return '"Infinity"'; + return 'Infinity'; } else { - return '"-Infinity"'; + return '-Infinity'; } } - return String(value); -} - - -// Mapping of some control characters to avoid the \uXXXX syntax for most -// commonly used control cahracters. -const ctrlCharMap_ = { - '\b': '\\b', - '\t': '\\t', - '\n': '\\n', - '\f': '\\f', - '\r': '\\r', - '"' : '\\"', - '\\': '\\\\' -}; - - -// Regular expression testing for ", \ and control characters (0x00 - 0x1F). -const ctrlCharTest_ = new RegExp('["\\\\\x00-\x1F]'); - - -// Regular expression matching ", \ and control characters (0x00 - 0x1F) -// globally. -const ctrlCharMatch_ = new RegExp('["\\\\\x00-\x1F]', 'g'); - - -/** - * Convert a String to its JSON representation (see http://www.json.org/). To - * avoid depending on the String object this method calls the functions in - * string.js directly and not through the value. - * @param {String} value The String value to format as JSON - * @return {string} JSON formatted String value - */ -function StringToJSON_(value) { - // Check for" , \ and control characters (0x00 - 0x1F). No need to call - // RegExpTest as ctrlchar is constructed using RegExp. - if (ctrlCharTest_.test(value)) { - // Replace ", \ and control characters (0x00 - 0x1F). - return '"' + - value.replace(ctrlCharMatch_, function (char) { - // Use charmap if possible. - var mapped = ctrlCharMap_[char]; - if (mapped) return mapped; - mapped = char.charCodeAt(); - // Convert control character to unicode escape sequence. - return '\\u00' + - %NumberToRadixString(Math.floor(mapped / 16), 16) + - %NumberToRadixString(mapped % 16, 16); - }) - + '"'; - } - - // Simple string with no special characters. - return '"' + value + '"'; -} - - -/** - * Convert a Date to ISO 8601 format. To avoid depending on the Date object - * this method calls the functions in date.js directly and not through the - * value. - * @param {Date} value The Date value to format as JSON - * @return {string} JSON formatted Date value - */ -function DateToISO8601_(value) { - function f(n) { - return n < 10 ? '0' + n : n; - } - function g(n) { - return n < 10 ? '00' + n : n < 100 ? '0' + n : n; - } - return builtins.GetUTCFullYearFrom(value) + '-' + - f(builtins.GetUTCMonthFrom(value) + 1) + '-' + - f(builtins.GetUTCDateFrom(value)) + 'T' + - f(builtins.GetUTCHoursFrom(value)) + ':' + - f(builtins.GetUTCMinutesFrom(value)) + ':' + - f(builtins.GetUTCSecondsFrom(value)) + '.' + - g(builtins.GetUTCMillisecondsFrom(value)) + 'Z'; -} - -/** - * Convert a Date to ISO 8601 format. To avoid depending on the Date object - * this method calls the functions in date.js directly and not through the - * value. - * @param {Date} value The Date value to format as JSON - * @return {string} JSON formatted Date value - */ -function DateToJSON_(value) { - return '"' + DateToISO8601_(value) + '"'; + return value; } diff --git a/deps/v8/src/natives.h b/deps/v8/src/natives.h index 3eb80909a1..fdfd213580 100644 --- a/deps/v8/src/natives.h +++ b/deps/v8/src/natives.h @@ -28,7 +28,8 @@ #ifndef V8_NATIVES_H_ #define V8_NATIVES_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { typedef bool (*NativeSourceCallback)(Vector<const char> name, Vector<const char> source, diff --git a/deps/v8/src/objects-debug.cc b/deps/v8/src/objects-debug.cc index e172014780..ba07af7e01 100644 --- a/deps/v8/src/objects-debug.cc +++ b/deps/v8/src/objects-debug.cc @@ -32,7 +32,8 @@ #include "macro-assembler.h" #include "jsregexp.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { #ifdef DEBUG @@ -941,6 +942,8 @@ void Script::ScriptPrint() { column_offset()->ShortPrint(); PrintF("\n - type: "); type()->ShortPrint(); + PrintF("\n - id: "); + id()->ShortPrint(); PrintF("\n"); } diff --git a/deps/v8/src/objects-inl.h b/deps/v8/src/objects-inl.h index 7821178088..d34e46539e 100644 --- a/deps/v8/src/objects-inl.h +++ b/deps/v8/src/objects-inl.h @@ -40,7 +40,8 @@ #include "conversions-inl.h" #include "property.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { PropertyDetails::PropertyDetails(Smi* smi) { value_ = smi->value(); @@ -764,9 +765,11 @@ Failure* Failure::RetryAfterGC(int requested_bytes) { Failure* Failure::Construct(Type type, int value) { int info = (value << kFailureTypeTagSize) | type; + // TODO(X64): Stop using Smi validation for non-smi checks, even if they + // happen to be identical at the moment. ASSERT(Smi::IsValid(info)); // Same validation check as in Smi return reinterpret_cast<Failure*>( - static_cast<intptr_t>((info << kFailureTagSize) | kFailureTag)); + (static_cast<intptr_t>(info) << kFailureTagSize) | kFailureTag); } @@ -1794,7 +1797,7 @@ int HeapObject::SizeFromMap(Map* map) { void Map::set_instance_size(int value) { - ASSERT((value & ~(kPointerSize - 1)) == value); + ASSERT_EQ(0, value & (kPointerSize - 1)); value >>= kPointerSizeLog2; ASSERT(0 <= value && value < 256); WRITE_BYTE_FIELD(this, kInstanceSizeOffset, static_cast<byte>(value)); @@ -1895,6 +1898,11 @@ Code::Kind Code::kind() { } +InLoopFlag Code::ic_in_loop() { + return ExtractICInLoopFromFlags(flags()); +} + + InlineCacheState Code::ic_state() { InlineCacheState result = ExtractICStateFromFlags(flags()); // Only allow uninitialized or debugger states for non-IC code @@ -1941,11 +1949,13 @@ bool Code::is_inline_cache_stub() { Code::Flags Code::ComputeFlags(Kind kind, + InLoopFlag in_loop, InlineCacheState ic_state, PropertyType type, int argc) { // Compute the bit mask. int bits = kind << kFlagsKindShift; + if (in_loop) bits |= kFlagsICInLoopMask; bits |= ic_state << kFlagsICStateShift; bits |= type << kFlagsTypeShift; bits |= argc << kFlagsArgumentsCountShift; @@ -1953,6 +1963,7 @@ Code::Flags Code::ComputeFlags(Kind kind, Flags result = static_cast<Flags>(bits); ASSERT(ExtractKindFromFlags(result) == kind); ASSERT(ExtractICStateFromFlags(result) == ic_state); + ASSERT(ExtractICInLoopFromFlags(result) == in_loop); ASSERT(ExtractTypeFromFlags(result) == type); ASSERT(ExtractArgumentsCountFromFlags(result) == argc); return result; @@ -1961,8 +1972,9 @@ Code::Flags Code::ComputeFlags(Kind kind, Code::Flags Code::ComputeMonomorphicFlags(Kind kind, PropertyType type, + InLoopFlag in_loop, int argc) { - return ComputeFlags(kind, MONOMORPHIC, type, argc); + return ComputeFlags(kind, in_loop, MONOMORPHIC, type, argc); } @@ -1978,6 +1990,12 @@ InlineCacheState Code::ExtractICStateFromFlags(Flags flags) { } +InLoopFlag Code::ExtractICInLoopFromFlags(Flags flags) { + int bits = (flags & kFlagsICInLoopMask); + return bits != 0 ? IN_LOOP : NOT_IN_LOOP; +} + + PropertyType Code::ExtractTypeFromFlags(Flags flags) { int bits = (flags & kFlagsTypeMask) >> kFlagsTypeShift; return static_cast<PropertyType>(bits); @@ -2094,7 +2112,11 @@ ACCESSORS(Script, data, Object, kDataOffset) ACCESSORS(Script, context_data, Object, kContextOffset) ACCESSORS(Script, wrapper, Proxy, kWrapperOffset) ACCESSORS(Script, type, Smi, kTypeOffset) +ACCESSORS(Script, compilation_type, Smi, kCompilationTypeOffset) ACCESSORS(Script, line_ends, Object, kLineEndsOffset) +ACCESSORS(Script, eval_from_function, Object, kEvalFromFunctionOffset) +ACCESSORS(Script, eval_from_instructions_offset, Smi, + kEvalFrominstructionsOffsetOffset) #ifdef ENABLE_DEBUGGER_SUPPORT ACCESSORS(DebugInfo, shared, SharedFunctionInfo, kSharedFunctionInfoIndex) @@ -2536,6 +2558,24 @@ bool JSObject::HasElement(uint32_t index) { } +Smi* JSObject::InterceptorPropertyLookupHint(String* name) { + // TODO(antonm): Do we want to do any shortcuts for global object? + if (HasFastProperties()) { + LookupResult lookup; + LocalLookupRealNamedProperty(name, &lookup); + if (lookup.IsValid()) { + if (lookup.type() == FIELD && lookup.IsCacheable()) { + return Smi::FromInt(lookup.GetFieldIndex()); + } + } else { + return Smi::FromInt(kLookupInPrototype); + } + } + + return Smi::FromInt(kLookupInHolder); +} + + bool AccessorInfo::all_can_read() { return BooleanBit::get(flag(), kAllCanReadBit); } diff --git a/deps/v8/src/objects.cc b/deps/v8/src/objects.cc index 9a7f7aa674..0546578ab1 100644 --- a/deps/v8/src/objects.cc +++ b/deps/v8/src/objects.cc @@ -41,7 +41,8 @@ #include "disassembler.h" #endif -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Getters and setters are stored in a fixed array property. These are // constants for their indices. @@ -384,7 +385,9 @@ Object* JSObject::SetLazyProperty(LookupResult* result, } -Object* JSObject::DeleteLazyProperty(LookupResult* result, String* name) { +Object* JSObject::DeleteLazyProperty(LookupResult* result, + String* name, + DeleteMode mode) { HandleScope scope; Handle<JSObject> this_handle(this); Handle<String> name_handle(name); @@ -392,7 +395,7 @@ Object* JSObject::DeleteLazyProperty(LookupResult* result, String* name) { LoadLazy(Handle<JSObject>(JSObject::cast(result->GetLazyValue())), &pending_exception); if (pending_exception) return Failure::Exception(); - return this_handle->DeleteProperty(*name_handle); + return this_handle->DeleteProperty(*name_handle, mode); } @@ -536,6 +539,9 @@ void Failure::FailurePrint() { Failure* Failure::RetryAfterGC(int requested_bytes, AllocationSpace space) { ASSERT((space & ~kSpaceTagMask) == 0); + // TODO(X64): Stop using Smi validation for non-smi checks, even if they + // happen to be identical at the moment. + int requested = requested_bytes >> kObjectAlignmentBits; int value = (requested << kSpaceTagSize) | space; // We can't very well allocate a heap number in this situation, and if the @@ -1736,8 +1742,10 @@ Object* JSObject::SetProperty(LookupResult* result, } return ConvertDescriptorToField(name, value, attributes); case CONSTANT_FUNCTION: - if (value == result->GetConstantFunction()) return value; // Only replace the function if necessary. + if (value == result->GetConstantFunction()) return value; + // Preserve the attributes of this existing property. + attributes = result->GetAttributes(); return ConvertDescriptorToFieldAndMapTransition(name, value, attributes); case CALLBACKS: return SetPropertyWithCallback(result->GetCallbackObject(), @@ -1817,8 +1825,10 @@ Object* JSObject::IgnoreAttributesAndSetLocalProperty( } return ConvertDescriptorToField(name, value, attributes); case CONSTANT_FUNCTION: - if (value == result->GetConstantFunction()) return value; // Only replace the function if necessary. + if (value == result->GetConstantFunction()) return value; + // Preserve the attributes of this existing property. + attributes = result->GetAttributes(); return ConvertDescriptorToFieldAndMapTransition(name, value, attributes); case CALLBACKS: case INTERCEPTOR: @@ -2112,7 +2122,7 @@ Object* JSObject::NormalizeElements() { } -Object* JSObject::DeletePropertyPostInterceptor(String* name) { +Object* JSObject::DeletePropertyPostInterceptor(String* name, DeleteMode mode) { // Check local property, ignore interceptor. LookupResult result; LocalLookupRealNamedProperty(name, &result); @@ -2126,7 +2136,7 @@ Object* JSObject::DeletePropertyPostInterceptor(String* name) { // Attempt to remove the property from the property dictionary. Dictionary* dictionary = property_dictionary(); int entry = dictionary->FindStringEntry(name); - if (entry != -1) return dictionary->DeleteProperty(entry); + if (entry != -1) return dictionary->DeleteProperty(entry, mode); return Heap::true_value(); } @@ -2156,13 +2166,15 @@ Object* JSObject::DeletePropertyWithInterceptor(String* name) { return *v8::Utils::OpenHandle(*result); } } - Object* raw_result = this_handle->DeletePropertyPostInterceptor(*name_handle); + Object* raw_result = + this_handle->DeletePropertyPostInterceptor(*name_handle, NORMAL_DELETION); RETURN_IF_SCHEDULED_EXCEPTION(); return raw_result; } -Object* JSObject::DeleteElementPostInterceptor(uint32_t index) { +Object* JSObject::DeleteElementPostInterceptor(uint32_t index, + DeleteMode mode) { if (HasFastElements()) { uint32_t length = IsJSArray() ? static_cast<uint32_t>(Smi::cast(JSArray::cast(this)->length())->value()) : @@ -2175,7 +2187,7 @@ Object* JSObject::DeleteElementPostInterceptor(uint32_t index) { ASSERT(!HasFastElements()); Dictionary* dictionary = element_dictionary(); int entry = dictionary->FindNumberEntry(index); - if (entry != -1) return dictionary->DeleteProperty(entry); + if (entry != -1) return dictionary->DeleteProperty(entry, mode); return Heap::true_value(); } @@ -2206,13 +2218,14 @@ Object* JSObject::DeleteElementWithInterceptor(uint32_t index) { ASSERT(result->IsBoolean()); return *v8::Utils::OpenHandle(*result); } - Object* raw_result = this_handle->DeleteElementPostInterceptor(index); + Object* raw_result = + this_handle->DeleteElementPostInterceptor(index, NORMAL_DELETION); RETURN_IF_SCHEDULED_EXCEPTION(); return raw_result; } -Object* JSObject::DeleteElement(uint32_t index) { +Object* JSObject::DeleteElement(uint32_t index, DeleteMode mode) { // Check access rights if needed. if (IsAccessCheckNeeded() && !Top::MayIndexedAccess(this, index, v8::ACCESS_DELETE)) { @@ -2224,10 +2237,14 @@ Object* JSObject::DeleteElement(uint32_t index) { Object* proto = GetPrototype(); if (proto->IsNull()) return Heap::false_value(); ASSERT(proto->IsJSGlobalObject()); - return JSGlobalObject::cast(proto)->DeleteElement(index); + return JSGlobalObject::cast(proto)->DeleteElement(index, mode); } if (HasIndexedInterceptor()) { + // Skip interceptor if forcing deletion. + if (mode == FORCE_DELETION) { + return DeleteElementPostInterceptor(index, mode); + } return DeleteElementWithInterceptor(index); } @@ -2242,13 +2259,13 @@ Object* JSObject::DeleteElement(uint32_t index) { } else { Dictionary* dictionary = element_dictionary(); int entry = dictionary->FindNumberEntry(index); - if (entry != -1) return dictionary->DeleteProperty(entry); + if (entry != -1) return dictionary->DeleteProperty(entry, mode); } return Heap::true_value(); } -Object* JSObject::DeleteProperty(String* name) { +Object* JSObject::DeleteProperty(String* name, DeleteMode mode) { // ECMA-262, 3rd, 8.6.2.5 ASSERT(name->IsString()); @@ -2263,23 +2280,32 @@ Object* JSObject::DeleteProperty(String* name) { Object* proto = GetPrototype(); if (proto->IsNull()) return Heap::false_value(); ASSERT(proto->IsJSGlobalObject()); - return JSGlobalObject::cast(proto)->DeleteProperty(name); + return JSGlobalObject::cast(proto)->DeleteProperty(name, mode); } uint32_t index = 0; if (name->AsArrayIndex(&index)) { - return DeleteElement(index); + return DeleteElement(index, mode); } else { LookupResult result; LocalLookup(name, &result); if (!result.IsValid()) return Heap::true_value(); - if (result.IsDontDelete()) return Heap::false_value(); + // Ignore attributes if forcing a deletion. + if (result.IsDontDelete() && mode != FORCE_DELETION) { + return Heap::false_value(); + } // Check for interceptor. if (result.type() == INTERCEPTOR) { + // Skip interceptor if forcing a deletion. + if (mode == FORCE_DELETION) { + return DeletePropertyPostInterceptor(name, mode); + } return DeletePropertyWithInterceptor(name); } if (!result.IsLoaded()) { - return JSObject::cast(this)->DeleteLazyProperty(&result, name); + return JSObject::cast(this)->DeleteLazyProperty(&result, + name, + mode); } // Normalize object if needed. Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES); @@ -2287,7 +2313,7 @@ Object* JSObject::DeleteProperty(String* name) { // Make sure the properties are normalized before removing the entry. Dictionary* dictionary = property_dictionary(); int entry = dictionary->FindStringEntry(name); - if (entry != -1) return dictionary->DeleteProperty(entry); + if (entry != -1) return dictionary->DeleteProperty(entry, mode); return Heap::true_value(); } } @@ -3433,8 +3459,8 @@ const uc16* SeqTwoByteString::SeqTwoByteStringGetData(unsigned start) { void SeqTwoByteString::SeqTwoByteStringReadBlockIntoBuffer(ReadBlockBuffer* rbb, - unsigned* offset_ptr, - unsigned max_chars) { + unsigned* offset_ptr, + unsigned max_chars) { unsigned chars_read = 0; unsigned offset = *offset_ptr; while (chars_read < max_chars) { @@ -4852,7 +4878,6 @@ const char* Code::Kind2String(Kind kind) { const char* Code::ICState2String(InlineCacheState state) { switch (state) { case UNINITIALIZED: return "UNINITIALIZED"; - case UNINITIALIZED_IN_LOOP: return "UNINITIALIZED_IN_LOOP"; case PREMONOMORPHIC: return "PREMONOMORPHIC"; case MONOMORPHIC: return "MONOMORPHIC"; case MONOMORPHIC_PROTOTYPE_FAILURE: return "MONOMORPHIC_PROTOTYPE_FAILURE"; @@ -5145,42 +5170,6 @@ bool JSObject::HasLocalElement(uint32_t index) { } -Object* JSObject::GetHiddenProperties(bool create_if_needed) { - String* key = Heap::hidden_symbol(); - if (this->HasFastProperties()) { - // If the object has fast properties, check whether the first slot - // in the descriptor array matches the hidden symbol. Since the - // hidden symbols hash code is zero (and no other string has hash - // code zero) it will always occupy the first entry if present. - DescriptorArray* descriptors = this->map()->instance_descriptors(); - DescriptorReader r(descriptors); - if (!r.eos() && (r.GetKey() == key) && r.IsProperty()) { - ASSERT(r.type() == FIELD); - return FastPropertyAt(r.GetFieldIndex()); - } - } - - // Only attempt to find the hidden properties in the local object and not - // in the prototype chain. Note that HasLocalProperty() can cause a GC in - // the general case, but in this case we know it won't hit an interceptor. - if (!this->HasLocalProperty(key)) { - // Hidden properties object not found. Allocate a new hidden properties - // object if requested. Otherwise return the undefined value. - if (create_if_needed) { - Object* obj = Heap::AllocateJSObject( - Top::context()->global_context()->object_function()); - if (obj->IsFailure()) { - return obj; - } - return this->SetProperty(key, obj, DONT_ENUM); - } else { - return Heap::undefined_value(); - } - } - return this->GetProperty(key); -} - - bool JSObject::HasElementWithReceiver(JSObject* receiver, uint32_t index) { // Check access rights if needed. if (IsAccessCheckNeeded() && @@ -5646,9 +5635,11 @@ Object* JSObject::GetPropertyPostInterceptor(JSObject* receiver, } -Object* JSObject::GetPropertyWithInterceptor(JSObject* receiver, - String* name, - PropertyAttributes* attributes) { +bool JSObject::GetPropertyWithInterceptorProper( + JSObject* receiver, + String* name, + PropertyAttributes* attributes, + Object** result_object) { HandleScope scope; Handle<InterceptorInfo> interceptor(GetNamedInterceptor()); Handle<JSObject> receiver_handle(receiver); @@ -5669,19 +5660,93 @@ Object* JSObject::GetPropertyWithInterceptor(JSObject* receiver, VMState state(EXTERNAL); result = getter(v8::Utils::ToLocal(name_handle), info); } - RETURN_IF_SCHEDULED_EXCEPTION(); + if (Top::has_scheduled_exception()) { + return false; + } if (!result.IsEmpty()) { *attributes = NONE; - return *v8::Utils::OpenHandle(*result); + *result_object = *v8::Utils::OpenHandle(*result); + return true; } } - Object* raw_result = holder_handle->GetPropertyPostInterceptor( + return false; +} + + +Object* JSObject::GetInterceptorPropertyWithLookupHint( + JSObject* receiver, + Smi* lookup_hint, + String* name, + PropertyAttributes* attributes) { + HandleScope scope; + Handle<JSObject> receiver_handle(receiver); + Handle<JSObject> holder_handle(this); + Handle<String> name_handle(name); + + Object* result = NULL; + if (GetPropertyWithInterceptorProper(receiver, name, attributes, &result)) { + return result; + } else { + RETURN_IF_SCHEDULED_EXCEPTION(); + } + + int property_index = lookup_hint->value(); + if (property_index >= 0) { + result = holder_handle->FastPropertyAt(property_index); + } else { + switch (property_index) { + case kLookupInPrototype: { + Object* pt = holder_handle->GetPrototype(); + *attributes = ABSENT; + if (pt == Heap::null_value()) return Heap::undefined_value(); + result = pt->GetPropertyWithReceiver( + *receiver_handle, + *name_handle, + attributes); + RETURN_IF_SCHEDULED_EXCEPTION(); + } + break; + + case kLookupInHolder: + result = holder_handle->GetPropertyPostInterceptor( + *receiver_handle, + *name_handle, + attributes); + RETURN_IF_SCHEDULED_EXCEPTION(); + break; + + default: + UNREACHABLE(); + } + } + + return result; +} + + +Object* JSObject::GetPropertyWithInterceptor( + JSObject* receiver, + String* name, + PropertyAttributes* attributes) { + HandleScope scope; + Handle<JSObject> receiver_handle(receiver); + Handle<JSObject> holder_handle(this); + Handle<String> name_handle(name); + + Object* result = NULL; + if (GetPropertyWithInterceptorProper(receiver, name, attributes, &result)) { + return result; + } else { + RETURN_IF_SCHEDULED_EXCEPTION(); + } + + result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attributes); RETURN_IF_SCHEDULED_EXCEPTION(); - return raw_result; + return result; } @@ -5968,20 +6033,6 @@ int JSObject::GetEnumElementKeys(FixedArray* storage) { } -// Thomas Wang, Integer Hash Functions. -// http://www.concentric.net/~Ttwang/tech/inthash.htm -static uint32_t ComputeIntegerHash(uint32_t key) { - uint32_t hash = key; - hash = ~hash + (hash << 15); // hash = (hash << 15) - hash - 1; - hash = hash ^ (hash >> 12); - hash = hash + (hash << 2); - hash = hash ^ (hash >> 4); - hash = hash * 2057; // hash = (hash + (hash << 3)) + (hash << 11); - hash = hash ^ (hash >> 16); - return hash; -} - - // The NumberKey uses carries the uint32_t as key. // This avoids allocation in HasProperty. class NumberKey : public HashTableKey { @@ -6725,7 +6776,10 @@ class MapNameKey : public HashTableKey { virtual HashFunction GetHashFunction() { return MapNameHash; } static uint32_t MapNameHashHelper(Map* map, String* name) { - return reinterpret_cast<uint32_t>(map) ^ name->Hash(); + // Uses only lower 32 bits if pointers are larger. + uintptr_t addr_hash = + static_cast<uint32_t>(reinterpret_cast<uintptr_t>(map)); + return addr_hash ^ name->Hash(); } static uint32_t MapNameHash(Object* obj) { @@ -6907,9 +6961,12 @@ void Dictionary::RemoveNumberEntries(uint32_t from, uint32_t to) { } -Object* Dictionary::DeleteProperty(int entry) { +Object* Dictionary::DeleteProperty(int entry, JSObject::DeleteMode mode) { PropertyDetails details = DetailsAt(entry); - if (details.IsDontDelete()) return Heap::false_value(); + // Ignore attributes if forcing a deletion. + if (details.IsDontDelete() && mode == JSObject::NORMAL_DELETION) { + return Heap::false_value(); + } SetEntry(entry, Heap::null_value(), Heap::null_value(), Smi::FromInt(0)); ElementRemoved(); return Heap::true_value(); diff --git a/deps/v8/src/objects.h b/deps/v8/src/objects.h index 3e132ff19e..493d22b416 100644 --- a/deps/v8/src/objects.h +++ b/deps/v8/src/objects.h @@ -1,4 +1,4 @@ -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2006-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: @@ -50,7 +50,6 @@ // - JSBuiltinsObject // - JSGlobalProxy // - JSValue -// - Script // - Array // - ByteArray // - FixedArray @@ -83,8 +82,10 @@ // - AccessCheckInfo // - InterceptorInfo // - CallHandlerInfo -// - FunctionTemplateInfo -// - ObjectTemplateInfo +// - TemplateInfo +// - FunctionTemplateInfo +// - ObjectTemplateInfo +// - Script // - SignatureInfo // - TypeSwitchInfo // - DebugInfo @@ -108,7 +109,8 @@ enum PropertyAttributes { // a non-existent property. }; -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // PropertyDetails captures type and attributes for a property. @@ -295,12 +297,14 @@ enum PropertyNormalizationMode { // Since string types are not consecutive, this macro is used to // iterate over them. #define STRING_TYPE_LIST(V) \ - V(SHORT_SYMBOL_TYPE, SeqTwoByteString::kHeaderSize, short_symbol) \ - V(MEDIUM_SYMBOL_TYPE, SeqTwoByteString::kHeaderSize, medium_symbol) \ - V(LONG_SYMBOL_TYPE, SeqTwoByteString::kHeaderSize, long_symbol) \ - V(SHORT_ASCII_SYMBOL_TYPE, SeqAsciiString::kHeaderSize, short_ascii_symbol) \ - V(MEDIUM_ASCII_SYMBOL_TYPE, SeqAsciiString::kHeaderSize, medium_ascii_symbol)\ - V(LONG_ASCII_SYMBOL_TYPE, SeqAsciiString::kHeaderSize, long_ascii_symbol) \ + V(SHORT_SYMBOL_TYPE, SeqTwoByteString::kAlignedSize, short_symbol) \ + V(MEDIUM_SYMBOL_TYPE, SeqTwoByteString::kAlignedSize, medium_symbol) \ + V(LONG_SYMBOL_TYPE, SeqTwoByteString::kAlignedSize, long_symbol) \ + V(SHORT_ASCII_SYMBOL_TYPE, SeqAsciiString::kAlignedSize, short_ascii_symbol) \ + V(MEDIUM_ASCII_SYMBOL_TYPE, \ + SeqAsciiString::kAlignedSize, \ + medium_ascii_symbol) \ + V(LONG_ASCII_SYMBOL_TYPE, SeqAsciiString::kAlignedSize, long_ascii_symbol) \ V(SHORT_CONS_SYMBOL_TYPE, ConsString::kSize, short_cons_symbol) \ V(MEDIUM_CONS_SYMBOL_TYPE, ConsString::kSize, medium_cons_symbol) \ V(LONG_CONS_SYMBOL_TYPE, ConsString::kSize, long_cons_symbol) \ @@ -337,12 +341,14 @@ enum PropertyNormalizationMode { V(LONG_EXTERNAL_ASCII_SYMBOL_TYPE, \ ExternalAsciiString::kSize, \ long_external_ascii_symbol) \ - V(SHORT_STRING_TYPE, SeqTwoByteString::kHeaderSize, short_string) \ - V(MEDIUM_STRING_TYPE, SeqTwoByteString::kHeaderSize, medium_string) \ - V(LONG_STRING_TYPE, SeqTwoByteString::kHeaderSize, long_string) \ - V(SHORT_ASCII_STRING_TYPE, SeqAsciiString::kHeaderSize, short_ascii_string) \ - V(MEDIUM_ASCII_STRING_TYPE, SeqAsciiString::kHeaderSize, medium_ascii_string)\ - V(LONG_ASCII_STRING_TYPE, SeqAsciiString::kHeaderSize, long_ascii_string) \ + V(SHORT_STRING_TYPE, SeqTwoByteString::kAlignedSize, short_string) \ + V(MEDIUM_STRING_TYPE, SeqTwoByteString::kAlignedSize, medium_string) \ + V(LONG_STRING_TYPE, SeqTwoByteString::kAlignedSize, long_string) \ + V(SHORT_ASCII_STRING_TYPE, SeqAsciiString::kAlignedSize, short_ascii_string) \ + V(MEDIUM_ASCII_STRING_TYPE, \ + SeqAsciiString::kAlignedSize, \ + medium_ascii_string) \ + V(LONG_ASCII_STRING_TYPE, SeqAsciiString::kAlignedSize, long_ascii_string) \ V(SHORT_CONS_STRING_TYPE, ConsString::kSize, short_cons_string) \ V(MEDIUM_CONS_STRING_TYPE, ConsString::kSize, medium_cons_string) \ V(LONG_CONS_STRING_TYPE, ConsString::kSize, long_cons_string) \ @@ -771,8 +777,10 @@ class Object BASE_EMBEDDED { // Smi represents integer Numbers that can be stored in 31 bits. +// TODO(X64) Increase to 53 bits? // Smis are immediate which means they are NOT allocated in the heap. // The this pointer has the following format: [31 bit signed int] 0 +// TODO(X64): 31 bits signed int sign-extended to 63 bits. // Smi stands for small integer. class Smi: public Object { public: @@ -1267,9 +1275,12 @@ class JSObject: public HeapObject { return GetLocalPropertyAttribute(name) != ABSENT; } - Object* DeleteProperty(String* name); - Object* DeleteElement(uint32_t index); - Object* DeleteLazyProperty(LookupResult* result, String* name); + enum DeleteMode { NORMAL_DELETION, FORCE_DELETION }; + Object* DeleteProperty(String* name, DeleteMode mode); + Object* DeleteElement(uint32_t index, DeleteMode mode); + Object* DeleteLazyProperty(LookupResult* result, + String* name, + DeleteMode mode); // Tests for the fast common case for property enumeration. bool IsSimpleEnum(); @@ -1286,11 +1297,6 @@ class JSObject: public HeapObject { // Return the object's prototype (might be Heap::null_value()). inline Object* GetPrototype(); - // Return the object's hidden properties object. If the object has no hidden - // properties and create_if_needed is true, then a new hidden property object - // will be allocated. Otherwise the Heap::undefined_value is returned. - Object* GetHiddenProperties(bool create_if_needed); - // Tells whether the index'th element is present. inline bool HasElement(uint32_t index); bool HasElementWithReceiver(JSObject* receiver, uint32_t index); @@ -1346,6 +1352,14 @@ class JSObject: public HeapObject { Object* LookupCallbackSetterInPrototypes(uint32_t index); void LookupCallback(String* name, LookupResult* result); + inline Smi* InterceptorPropertyLookupHint(String* name); + Object* GetInterceptorPropertyWithLookupHint(JSObject* receiver, + Smi* lookup_hint, + String* name, + PropertyAttributes* attributes); + static const int kLookupInHolder = -1; + static const int kLookupInPrototype = -2; + // Returns the number of properties on this object filtering out properties // with the specified attributes (ignoring interceptors). int NumberOfLocalProperties(PropertyAttributes filter); @@ -1508,10 +1522,10 @@ class JSObject: public HeapObject { Object* GetElementPostInterceptor(JSObject* receiver, uint32_t index); - Object* DeletePropertyPostInterceptor(String* name); + Object* DeletePropertyPostInterceptor(String* name, DeleteMode mode); Object* DeletePropertyWithInterceptor(String* name); - Object* DeleteElementPostInterceptor(uint32_t index); + Object* DeleteElementPostInterceptor(uint32_t index, DeleteMode mode); Object* DeleteElementWithInterceptor(uint32_t index); PropertyAttributes GetPropertyAttributePostInterceptor(JSObject* receiver, @@ -1537,6 +1551,14 @@ class JSObject: public HeapObject { void LookupInDescriptor(String* name, LookupResult* result); + // Attempts to get property with a named interceptor getter. Returns + // |true| and stores result into |result| if succesful, otherwise + // returns |false| + bool GetPropertyWithInterceptorProper(JSObject* receiver, + String* name, + PropertyAttributes* attributes, + Object** result); + DISALLOW_IMPLICIT_CONSTRUCTORS(JSObject); }; @@ -1555,6 +1577,7 @@ class Array: public HeapObject { // Layout descriptor. static const int kLengthOffset = HeapObject::kHeaderSize; static const int kHeaderSize = kLengthOffset + kIntSize; + static const int kAlignedSize = POINTER_SIZE_ALIGN(kHeaderSize); private: DISALLOW_IMPLICIT_CONSTRUCTORS(Array); @@ -1601,6 +1624,9 @@ class FixedArray: public Array { // Casting. static inline FixedArray* cast(Object* obj); + // Align data at kPointerSize, even if Array.kHeaderSize isn't aligned. + static const int kHeaderSize = POINTER_SIZE_ALIGN(Array::kHeaderSize); + // Dispatched behavior. int FixedArraySize() { return SizeFor(length()); } void FixedArrayIterateBody(ObjectVisitor* v); @@ -2034,7 +2060,7 @@ class Dictionary: public DictionaryBase { int FindNumberEntry(uint32_t index); // Delete a property from the dictionary. - Object* DeleteProperty(int entry); + Object* DeleteProperty(int entry, JSObject::DeleteMode mode); // Type specific at put (default NONE attributes is used when adding). Object* AtStringPut(String* key, Object* value); @@ -2152,7 +2178,7 @@ class ByteArray: public Array { inline int get_int(int index); static int SizeFor(int length) { - return kHeaderSize + OBJECT_SIZE_ALIGN(length); + return OBJECT_SIZE_ALIGN(kHeaderSize + length); } // We use byte arrays for free blocks in the heap. Given a desired size in // bytes that is a multiple of the word size and big enough to hold a byte @@ -2246,9 +2272,10 @@ class Code: public HeapObject { // [flags]: Access to specific code flags. inline Kind kind(); - inline InlineCacheState ic_state(); // only valid for IC stubs - inline PropertyType type(); // only valid for monomorphic IC stubs - inline int arguments_count(); // only valid for call IC stubs + inline InlineCacheState ic_state(); // Only valid for IC stubs. + inline InLoopFlag ic_in_loop(); // Only valid for IC stubs.. + inline PropertyType type(); // Only valid for monomorphic IC stubs. + inline int arguments_count(); // Only valid for call IC stubs. // Testers for IC stub kinds. inline bool is_inline_cache_stub(); @@ -2270,16 +2297,20 @@ class Code: public HeapObject { // Flags operations. static inline Flags ComputeFlags(Kind kind, + InLoopFlag in_loop = NOT_IN_LOOP, InlineCacheState ic_state = UNINITIALIZED, PropertyType type = NORMAL, int argc = -1); - static inline Flags ComputeMonomorphicFlags(Kind kind, - PropertyType type, - int argc = -1); + static inline Flags ComputeMonomorphicFlags( + Kind kind, + PropertyType type, + InLoopFlag in_loop = NOT_IN_LOOP, + int argc = -1); static inline Kind ExtractKindFromFlags(Flags flags); static inline InlineCacheState ExtractICStateFromFlags(Flags flags); + static inline InLoopFlag ExtractICInLoopFromFlags(Flags flags); static inline PropertyType ExtractTypeFromFlags(Flags flags); static inline int ExtractArgumentsCountFromFlags(Flags flags); static inline Flags RemoveTypeFromFlags(Flags flags); @@ -2349,6 +2380,9 @@ class Code: public HeapObject { void CodePrint(); void CodeVerify(); #endif + // Code entry points are aligned to 32 bytes. + static const int kCodeAlignment = 32; + static const int kCodeAlignmentMask = kCodeAlignment - 1; // Layout description. static const int kInstructionSizeOffset = HeapObject::kHeaderSize; @@ -2356,14 +2390,11 @@ class Code: public HeapObject { static const int kSInfoSizeOffset = kRelocationSizeOffset + kIntSize; static const int kFlagsOffset = kSInfoSizeOffset + kIntSize; static const int kKindSpecificFlagsOffset = kFlagsOffset + kIntSize; - // Add filler objects to align the instruction start following right after + // Add padding to align the instruction start following right after // the Code object header. - static const int kFiller6Offset = kKindSpecificFlagsOffset + kIntSize; - static const int kFiller7Offset = kFiller6Offset + kIntSize; - static const int kHeaderSize = kFiller7Offset + kIntSize; - - // Code entry points are aligned to 32 bytes. - static const int kCodeAlignment = 32; + static const int kHeaderSize = + (kKindSpecificFlagsOffset + kIntSize + kCodeAlignmentMask) & + ~kCodeAlignmentMask; // Byte offsets within kKindSpecificFlagsOffset. static const int kICFlagOffset = kKindSpecificFlagsOffset + 0; @@ -2371,14 +2402,19 @@ class Code: public HeapObject { // Flags layout. static const int kFlagsICStateShift = 0; - static const int kFlagsKindShift = 3; - static const int kFlagsTypeShift = 6; - static const int kFlagsArgumentsCountShift = 9; + static const int kFlagsICInLoopShift = 3; + static const int kFlagsKindShift = 4; + static const int kFlagsTypeShift = 7; + static const int kFlagsArgumentsCountShift = 10; + + static const int kFlagsICStateMask = 0x00000007; // 0000000111 + static const int kFlagsICInLoopMask = 0x00000008; // 0000001000 + static const int kFlagsKindMask = 0x00000070; // 0001110000 + static const int kFlagsTypeMask = 0x00000380; // 1110000000 + static const int kFlagsArgumentsCountMask = 0xFFFFFC00; - static const int kFlagsICStateMask = 0x00000007; // 000000111 - static const int kFlagsKindMask = 0x00000038; // 000111000 - static const int kFlagsTypeMask = 0x000001C0; // 111000000 - static const int kFlagsArgumentsCountMask = 0xFFFFFE00; + static const int kFlagsNotUsedInLookup = + (kFlagsICInLoopMask | kFlagsTypeMask); private: DISALLOW_IMPLICIT_CONSTRUCTORS(Code); @@ -2572,7 +2608,7 @@ class Map: public HeapObject { static const int kInstanceDescriptorsOffset = kConstructorOffset + kPointerSize; static const int kCodeCacheOffset = kInstanceDescriptorsOffset + kPointerSize; - static const int kSize = kCodeCacheOffset + kIntSize; + static const int kSize = kCodeCacheOffset + kPointerSize; // Byte offsets within kInstanceSizesOffset. static const int kInstanceSizeOffset = kInstanceSizesOffset + 0; @@ -2595,7 +2631,7 @@ class Map: public HeapObject { static const int kHasInstanceCallHandler = 6; static const int kIsAccessCheckNeeded = 7; - // Bit positions for but field 2 + // Bit positions for bit field 2 static const int kNeedsLoading = 0; private: @@ -2613,17 +2649,23 @@ class Struct: public HeapObject { }; -// Script types. -enum ScriptType { - SCRIPT_TYPE_NATIVE, - SCRIPT_TYPE_EXTENSION, - SCRIPT_TYPE_NORMAL -}; - - // Script describes a script which has been added to the VM. class Script: public Struct { public: + // Script types. + enum Type { + TYPE_NATIVE, + TYPE_EXTENSION, + TYPE_NORMAL + }; + + // Script compilation types. + enum CompilationType { + COMPILATION_TYPE_HOST, + COMPILATION_TYPE_EVAL, + COMPILATION_TYPE_JSON + }; + // [source]: the script source. DECL_ACCESSORS(source, Object) @@ -2652,9 +2694,20 @@ class Script: public Struct { // [type]: the script type. DECL_ACCESSORS(type, Smi) - // [line_ends]: array of line ends positions + // [compilation]: how the the script was compiled. + DECL_ACCESSORS(compilation_type, Smi) + + // [line_ends]: array of line ends positions. DECL_ACCESSORS(line_ends, Object) + // [eval_from_function]: for eval scripts the funcion from which eval was + // called. + DECL_ACCESSORS(eval_from_function, Object) + + // [eval_from_instructions_offset]: the instruction offset in the code for the + // function from which eval was called where eval was called. + DECL_ACCESSORS(eval_from_instructions_offset, Smi) + static inline Script* cast(Object* obj); #ifdef DEBUG @@ -2670,9 +2723,13 @@ class Script: public Struct { static const int kContextOffset = kDataOffset + kPointerSize; static const int kWrapperOffset = kContextOffset + kPointerSize; static const int kTypeOffset = kWrapperOffset + kPointerSize; - static const int kLineEndsOffset = kTypeOffset + kPointerSize; + static const int kCompilationTypeOffset = kTypeOffset + kPointerSize; + static const int kLineEndsOffset = kCompilationTypeOffset + kPointerSize; static const int kIdOffset = kLineEndsOffset + kPointerSize; - static const int kSize = kIdOffset + kPointerSize; + static const int kEvalFromFunctionOffset = kIdOffset + kPointerSize; + static const int kEvalFrominstructionsOffsetOffset = + kEvalFromFunctionOffset + kPointerSize; + static const int kSize = kEvalFrominstructionsOffsetOffset + kPointerSize; private: DISALLOW_IMPLICIT_CONSTRUCTORS(Script); @@ -2781,21 +2838,23 @@ class SharedFunctionInfo: public HeapObject { static const int kDontAdaptArgumentsSentinel = -1; // Layout description. + // (An even number of integers has a size that is a multiple of a pointer.) static const int kNameOffset = HeapObject::kHeaderSize; static const int kCodeOffset = kNameOffset + kPointerSize; static const int kLengthOffset = kCodeOffset + kPointerSize; static const int kFormalParameterCountOffset = kLengthOffset + kIntSize; static const int kExpectedNofPropertiesOffset = kFormalParameterCountOffset + kIntSize; - static const int kInstanceClassNameOffset = + static const int kStartPositionAndTypeOffset = kExpectedNofPropertiesOffset + kIntSize; + static const int kEndPositionOffset = kStartPositionAndTypeOffset + kIntSize; + static const int kFunctionTokenPositionOffset = kEndPositionOffset + kIntSize; + static const int kInstanceClassNameOffset = + kFunctionTokenPositionOffset + kIntSize; static const int kExternalReferenceDataOffset = kInstanceClassNameOffset + kPointerSize; static const int kScriptOffset = kExternalReferenceDataOffset + kPointerSize; - static const int kStartPositionAndTypeOffset = kScriptOffset + kPointerSize; - static const int kEndPositionOffset = kStartPositionAndTypeOffset + kIntSize; - static const int kFunctionTokenPositionOffset = kEndPositionOffset + kIntSize; - static const int kDebugInfoOffset = kFunctionTokenPositionOffset + kIntSize; + static const int kDebugInfoOffset = kScriptOffset + kPointerSize; static const int kInferredNameOffset = kDebugInfoOffset + kPointerSize; static const int kSize = kInferredNameOffset + kPointerSize; @@ -3105,7 +3164,7 @@ class JSRegExp: public JSObject { #endif static const int kDataOffset = JSObject::kHeaderSize; - static const int kSize = kDataOffset + kIntSize; + static const int kSize = kDataOffset + kPointerSize; // Indices in the data array. static const int kTagIndex = 0; @@ -3370,6 +3429,7 @@ class String: public HeapObject { // Layout description. static const int kLengthOffset = HeapObject::kHeaderSize; static const int kSize = kLengthOffset + kIntSize; + // Notice: kSize is not pointer-size aligned if pointers are 64-bit. // Limits on sizes of different types of strings. static const int kMaxShortStringSize = 63; @@ -3518,11 +3578,12 @@ class SeqAsciiString: public SeqString { // Computes the size for an AsciiString instance of a given length. static int SizeFor(int length) { - return kHeaderSize + OBJECT_SIZE_ALIGN(length * kCharSize); + return OBJECT_SIZE_ALIGN(kHeaderSize + length * kCharSize); } // Layout description. static const int kHeaderSize = String::kSize; + static const int kAlignedSize = POINTER_SIZE_ALIGN(kHeaderSize); // Support for StringInputBuffer. inline void SeqAsciiStringReadBlockIntoBuffer(ReadBlockBuffer* buffer, @@ -3563,11 +3624,12 @@ class SeqTwoByteString: public SeqString { // Computes the size for a TwoByteString instance of a given length. static int SizeFor(int length) { - return kHeaderSize + OBJECT_SIZE_ALIGN(length * kShortSize); + return OBJECT_SIZE_ALIGN(kHeaderSize + length * kShortSize); } // Layout description. static const int kHeaderSize = String::kSize; + static const int kAlignedSize = POINTER_SIZE_ALIGN(kHeaderSize); // Support for StringInputBuffer. inline void SeqTwoByteStringReadBlockIntoBuffer(ReadBlockBuffer* buffer, @@ -3617,7 +3679,7 @@ class ConsString: public String { void ConsStringIterateBody(ObjectVisitor* v); // Layout description. - static const int kFirstOffset = String::kSize; + static const int kFirstOffset = POINTER_SIZE_ALIGN(String::kSize); static const int kSecondOffset = kFirstOffset + kPointerSize; static const int kSize = kSecondOffset + kPointerSize; @@ -3661,9 +3723,18 @@ class SlicedString: public String { void SlicedStringIterateBody(ObjectVisitor* v); // Layout description +#if V8_HOST_ARCH_64_BIT + // Optimizations expect buffer to be located at same offset as a ConsString's + // first substring. In 64 bit mode we have room for the size before the + // buffer. + static const int kStartOffset = String::kSize; + static const int kBufferOffset = kStartOffset + kIntSize; + static const int kSize = kBufferOffset + kPointerSize; +#else static const int kBufferOffset = String::kSize; static const int kStartOffset = kBufferOffset + kPointerSize; static const int kSize = kStartOffset + kIntSize; +#endif // Support for StringInputBuffer. inline const unibrow::byte* SlicedStringReadBlock(ReadBlockBuffer* buffer, @@ -3693,7 +3764,7 @@ class ExternalString: public String { static inline ExternalString* cast(Object* obj); // Layout description. - static const int kResourceOffset = String::kSize; + static const int kResourceOffset = POINTER_SIZE_ALIGN(String::kSize); static const int kSize = kResourceOffset + kPointerSize; private: @@ -4153,7 +4224,7 @@ class ObjectTemplateInfo: public TemplateInfo { static const int kConstructorOffset = TemplateInfo::kHeaderSize; static const int kInternalFieldCountOffset = kConstructorOffset + kPointerSize; - static const int kSize = kInternalFieldCountOffset + kHeaderSize; + static const int kSize = kInternalFieldCountOffset + kPointerSize; }; diff --git a/deps/v8/src/oprofile-agent.cc b/deps/v8/src/oprofile-agent.cc index e9f7d3e2fa..c4595b40a9 100644 --- a/deps/v8/src/oprofile-agent.cc +++ b/deps/v8/src/oprofile-agent.cc @@ -29,7 +29,8 @@ #include "oprofile-agent.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { #ifdef ENABLE_OPROFILE_AGENT op_agent_t OProfileAgent::handle_ = NULL; diff --git a/deps/v8/src/oprofile-agent.h b/deps/v8/src/oprofile-agent.h index 75cfe18a29..4c299bfbc6 100644 --- a/deps/v8/src/oprofile-agent.h +++ b/deps/v8/src/oprofile-agent.h @@ -39,7 +39,8 @@ #include <opagent.h> // NOLINT #endif -namespace v8 { namespace internal { +namespace v8 { +namespace internal { class OProfileAgent { public: diff --git a/deps/v8/src/parser.cc b/deps/v8/src/parser.cc index 9db10cf2ce..271c3fd167 100644 --- a/deps/v8/src/parser.cc +++ b/deps/v8/src/parser.cc @@ -30,20 +30,64 @@ #include "api.h" #include "ast.h" #include "bootstrapper.h" +#include "compiler.h" #include "platform.h" #include "runtime.h" #include "parser.h" #include "scopes.h" #include "string-stream.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { class ParserFactory; class ParserLog; class TemporaryScope; +class Target; + template <typename T> class ZoneListWrapper; +// PositionStack is used for on-stack allocation of token positions for +// new expressions. Please look at ParseNewExpression. + +class PositionStack { + public: + explicit PositionStack(bool* ok) : top_(NULL), ok_(ok) {} + ~PositionStack() { ASSERT(!*ok_ || is_empty()); } + + class Element { + public: + Element(PositionStack* stack, int value) { + previous_ = stack->top(); + value_ = value; + stack->set_top(this); + } + + private: + Element* previous() { return previous_; } + int value() { return value_; } + friend class PositionStack; + Element* previous_; + int value_; + }; + + bool is_empty() { return top_ == NULL; } + int pop() { + ASSERT(!is_empty()); + int result = top_->value(); + top_ = top_->previous(); + return result; + } + + private: + Element* top() { return top_; } + void set_top(Element* value) { top_ = value; } + Element* top_; + bool* ok_; +}; + + class Parser { public: Parser(Handle<Script> script, bool allow_natives_syntax, @@ -92,7 +136,8 @@ class Parser { TemporaryScope* temp_scope_; Mode mode_; - List<Node*>* target_stack_; // for break, continue statements + + Target* target_stack_; // for break, continue statements bool allow_natives_syntax_; v8::Extension* extension_; ParserFactory* factory_; @@ -149,7 +194,8 @@ class Parser { Expression* ParseLeftHandSideExpression(bool* ok); Expression* ParseNewExpression(bool* ok); Expression* ParseMemberExpression(bool* ok); - Expression* ParseMemberWithNewPrefixesExpression(List<int>* new_prefixes, + Expression* ParseNewPrefix(PositionStack* stack, bool* ok); + Expression* ParseMemberWithNewPrefixesExpression(PositionStack* stack, bool* ok); Expression* ParsePrimaryExpression(bool* ok); Expression* ParseArrayLiteral(bool* ok); @@ -207,7 +253,7 @@ class Parser { BreakableStatement* LookupBreakTarget(Handle<String> label, bool* ok); IterationStatement* LookupContinueTarget(Handle<String> label, bool* ok); - void RegisterTargetUse(BreakTarget* target, int index); + void RegisterTargetUse(BreakTarget* target, Target* stop); // Create a number literal. Literal* NewNumberLiteral(double value); @@ -970,35 +1016,39 @@ VariableProxy* PreParser::Declare(Handle<String> name, Variable::Mode mode, class Target BASE_EMBEDDED { public: - Target(Parser* parser, Node* node) : parser_(parser) { - parser_->target_stack_->Add(node); + Target(Parser* parser, Node* node) + : parser_(parser), node_(node), previous_(parser_->target_stack_) { + parser_->target_stack_ = this; } ~Target() { - parser_->target_stack_->RemoveLast(); + parser_->target_stack_ = previous_; } + Target* previous() { return previous_; } + Node* node() { return node_; } + private: Parser* parser_; + Node* node_; + Target* previous_; }; class TargetScope BASE_EMBEDDED { public: explicit TargetScope(Parser* parser) - : parser_(parser), previous_(parser->target_stack_), stack_(0) { - parser_->target_stack_ = &stack_; + : parser_(parser), previous_(parser->target_stack_) { + parser->target_stack_ = NULL; } ~TargetScope() { - ASSERT(stack_.is_empty()); parser_->target_stack_ = previous_; } private: Parser* parser_; - List<Node*>* previous_; - List<Node*> stack_; + Target* previous_; }; @@ -1096,7 +1146,7 @@ bool Parser::PreParseProgram(unibrow::CharacterStream* stream) { FunctionLiteral* Parser::ParseProgram(Handle<String> source, unibrow::CharacterStream* stream, bool in_global_context) { - ZoneScope zone_scope(DONT_DELETE_ON_EXIT); + CompilationZoneScope zone_scope(DONT_DELETE_ON_EXIT); HistogramTimerScope timer(&Counters::parse); Counters::total_parse_size.Increment(source->length()); @@ -1149,7 +1199,7 @@ FunctionLiteral* Parser::ParseLazy(Handle<String> source, Handle<String> name, int start_position, bool is_expression) { - ZoneScope zone_scope(DONT_DELETE_ON_EXIT); + CompilationZoneScope zone_scope(DONT_DELETE_ON_EXIT); HistogramTimerScope timer(&Counters::parse_lazy); source->TryFlattenIfNotFlat(); Counters::total_parse_size.Increment(source->length()); @@ -2791,7 +2841,8 @@ Expression* Parser::ParseLeftHandSideExpression(bool* ok) { } -Expression* Parser::ParseNewExpression(bool* ok) { + +Expression* Parser::ParseNewPrefix(PositionStack* stack, bool* ok) { // NewExpression :: // ('new')+ MemberExpression @@ -2803,32 +2854,37 @@ Expression* Parser::ParseNewExpression(bool* ok) { // many we have parsed. This information is then passed on to the // member expression parser, which is only allowed to match argument // lists as long as it has 'new' prefixes left - List<int> new_positions(4); - while (peek() == Token::NEW) { - Consume(Token::NEW); - new_positions.Add(scanner().location().beg_pos); + Expect(Token::NEW, CHECK_OK); + PositionStack::Element pos(stack, scanner().location().beg_pos); + + Expression* result; + if (peek() == Token::NEW) { + result = ParseNewPrefix(stack, CHECK_OK); + } else { + result = ParseMemberWithNewPrefixesExpression(stack, CHECK_OK); } - ASSERT(new_positions.length() > 0); - Expression* result = - ParseMemberWithNewPrefixesExpression(&new_positions, CHECK_OK); - while (!new_positions.is_empty()) { - int last = new_positions.RemoveLast(); + if (!stack->is_empty()) { + int last = stack->pop(); result = NEW(CallNew(result, new ZoneList<Expression*>(0), last)); } return result; } +Expression* Parser::ParseNewExpression(bool* ok) { + PositionStack stack(ok); + return ParseNewPrefix(&stack, ok); +} + + Expression* Parser::ParseMemberExpression(bool* ok) { - static List<int> new_positions(0); - return ParseMemberWithNewPrefixesExpression(&new_positions, ok); + return ParseMemberWithNewPrefixesExpression(NULL, ok); } -Expression* Parser::ParseMemberWithNewPrefixesExpression( - List<int>* new_positions, - bool* ok) { +Expression* Parser::ParseMemberWithNewPrefixesExpression(PositionStack* stack, + bool* ok) { // MemberExpression :: // (PrimaryExpression | FunctionLiteral) // ('[' Expression ']' | '.' Identifier | Arguments)* @@ -2864,10 +2920,10 @@ Expression* Parser::ParseMemberWithNewPrefixesExpression( break; } case Token::LPAREN: { - if (new_positions->is_empty()) return result; + if ((stack == NULL) || stack->is_empty()) return result; // Consume one of the new prefixes (already parsed). ZoneList<Expression*>* args = ParseArguments(CHECK_OK); - int last = new_positions->RemoveLast(); + int last = stack->pop(); result = NEW(CallNew(result, args, last)); break; } @@ -3547,8 +3603,8 @@ Handle<String> Parser::ParseIdentifierOrGetOrSet(bool* is_get, bool Parser::TargetStackContainsLabel(Handle<String> label) { - for (int i = target_stack_->length(); i-- > 0;) { - BreakableStatement* stat = target_stack_->at(i)->AsBreakableStatement(); + for (Target* t = target_stack_; t != NULL; t = t->previous()) { + BreakableStatement* stat = t->node()->AsBreakableStatement(); if (stat != NULL && ContainsLabel(stat->labels(), label)) return true; } @@ -3558,13 +3614,12 @@ bool Parser::TargetStackContainsLabel(Handle<String> label) { BreakableStatement* Parser::LookupBreakTarget(Handle<String> label, bool* ok) { bool anonymous = label.is_null(); - for (int i = target_stack_->length(); i-- > 0;) { - BreakableStatement* stat = target_stack_->at(i)->AsBreakableStatement(); + for (Target* t = target_stack_; t != NULL; t = t->previous()) { + BreakableStatement* stat = t->node()->AsBreakableStatement(); if (stat == NULL) continue; - if ((anonymous && stat->is_target_for_anonymous()) || (!anonymous && ContainsLabel(stat->labels(), label))) { - RegisterTargetUse(stat->break_target(), i); + RegisterTargetUse(stat->break_target(), t->previous()); return stat; } } @@ -3575,13 +3630,13 @@ BreakableStatement* Parser::LookupBreakTarget(Handle<String> label, bool* ok) { IterationStatement* Parser::LookupContinueTarget(Handle<String> label, bool* ok) { bool anonymous = label.is_null(); - for (int i = target_stack_->length(); i-- > 0;) { - IterationStatement* stat = target_stack_->at(i)->AsIterationStatement(); + for (Target* t = target_stack_; t != NULL; t = t->previous()) { + IterationStatement* stat = t->node()->AsIterationStatement(); if (stat == NULL) continue; ASSERT(stat->is_target_for_anonymous()); if (anonymous || ContainsLabel(stat->labels(), label)) { - RegisterTargetUse(stat->continue_target(), i); + RegisterTargetUse(stat->continue_target(), t->previous()); return stat; } } @@ -3589,12 +3644,12 @@ IterationStatement* Parser::LookupContinueTarget(Handle<String> label, } -void Parser::RegisterTargetUse(BreakTarget* target, int index) { - // Register that a break target found at the given index in the +void Parser::RegisterTargetUse(BreakTarget* target, Target* stop) { + // Register that a break target found at the given stop in the // target stack has been used from the top of the target stack. Add // the break target to any TargetCollectors passed on the stack. - for (int i = target_stack_->length(); i-- > index;) { - TargetCollector* collector = target_stack_->at(i)->AsTargetCollector(); + for (Target* t = target_stack_; t != stop; t = t->previous()) { + TargetCollector* collector = t->node()->AsTargetCollector(); if (collector != NULL) collector->AddTarget(target); } } diff --git a/deps/v8/src/parser.h b/deps/v8/src/parser.h index 4c1401cf47..c029c4b25d 100644 --- a/deps/v8/src/parser.h +++ b/deps/v8/src/parser.h @@ -31,7 +31,8 @@ #include "scanner.h" #include "allocation.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { class ParserMessage : public Malloced { diff --git a/deps/v8/src/platform-freebsd.cc b/deps/v8/src/platform-freebsd.cc index 82208f1a3d..acef74cc5c 100644 --- a/deps/v8/src/platform-freebsd.cc +++ b/deps/v8/src/platform-freebsd.cc @@ -55,7 +55,8 @@ #include "platform.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // 0 is never a valid thread id on FreeBSD since tids and pids share a // name space and pid 0 is used to kill the group (see man 2 kill). diff --git a/deps/v8/src/platform-linux.cc b/deps/v8/src/platform-linux.cc index c02eebc3b4..79ffe81497 100644 --- a/deps/v8/src/platform-linux.cc +++ b/deps/v8/src/platform-linux.cc @@ -58,7 +58,8 @@ #include "platform.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // 0 is never a valid thread id on Linux since tids and pids share a // name space and pid 0 is reserved (see man 2 kill). @@ -87,8 +88,15 @@ double OS::nan_value() { int OS::ActivationFrameAlignment() { - // Floating point code runs faster if the stack is 8-byte aligned. +#ifdef V8_TARGET_ARCH_ARM + // On EABI ARM targets this is required for fp correctness in the + // runtime system. return 8; +#else + // With gcc 4.4 the tree vectorization optimiser can generate code + // that requires 16 byte alignment such as movdqa on x86. + return 16; +#endif } @@ -609,9 +617,16 @@ static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) { sample.sp = mcontext.gregs[REG_RSP]; sample.fp = mcontext.gregs[REG_RBP]; #elif V8_HOST_ARCH_ARM +// An undefined macro evaluates to 0, so this applies to Android's Bionic also. +#if (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3)) sample.pc = mcontext.gregs[R15]; sample.sp = mcontext.gregs[R13]; sample.fp = mcontext.gregs[R11]; +#else + sample.pc = mcontext.arm_pc; + sample.sp = mcontext.arm_sp; + sample.fp = mcontext.arm_fp; +#endif #endif } diff --git a/deps/v8/src/platform-macos.cc b/deps/v8/src/platform-macos.cc index 79515434e6..3e0e2841bc 100644 --- a/deps/v8/src/platform-macos.cc +++ b/deps/v8/src/platform-macos.cc @@ -58,7 +58,8 @@ #include "platform.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // 0 is never a valid thread id on MacOSX since a ptread_t is // a pointer. @@ -481,6 +482,13 @@ static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) { // Extracting the sample from the context is extremely machine dependent. ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context); mcontext_t& mcontext = ucontext->uc_mcontext; +#if V8_HOST_ARCH_X64 + UNIMPLEMENTED(); + USE(mcontext); + sample.pc = 0; + sample.sp = 0; + sample.fp = 0; +#elif V8_HOST_ARCH_IA32 #if __DARWIN_UNIX03 sample.pc = mcontext->__ss.__eip; sample.sp = mcontext->__ss.__esp; @@ -490,6 +498,9 @@ static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) { sample.sp = mcontext->ss.esp; sample.fp = mcontext->ss.ebp; #endif // __DARWIN_UNIX03 +#else +#error Unsupported Mac OS X host architecture. +#endif // V8_TARGET_ARCH_IA32 } // We always sample the VM state. diff --git a/deps/v8/src/platform-nullos.cc b/deps/v8/src/platform-nullos.cc index 42583f17f6..60ae76d6bc 100644 --- a/deps/v8/src/platform-nullos.cc +++ b/deps/v8/src/platform-nullos.cc @@ -37,7 +37,8 @@ #include "platform.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Give V8 the opportunity to override the default ceil behaviour. double ceiling(double x) { diff --git a/deps/v8/src/platform-posix.cc b/deps/v8/src/platform-posix.cc index de16ef5146..d628a5148b 100644 --- a/deps/v8/src/platform-posix.cc +++ b/deps/v8/src/platform-posix.cc @@ -47,7 +47,8 @@ #include "platform.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // ---------------------------------------------------------------------------- diff --git a/deps/v8/src/platform-win32.cc b/deps/v8/src/platform-win32.cc index 6c4e67a7f2..1b0f9b24d5 100644 --- a/deps/v8/src/platform-win32.cc +++ b/deps/v8/src/platform-win32.cc @@ -58,6 +58,12 @@ #include <time.h> // For LocalOffset() implementation. #include <mmsystem.h> // For timeGetTime(). +#ifdef __MINGW32__ +// Require Windows XP or higher when compiling with MinGW. This is for MinGW +// header files to expose getaddrinfo. +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x501 +#endif // __MINGW32__ #ifndef __MINGW32__ #include <dbghelp.h> // For SymLoadModule64 and al. #endif // __MINGW32__ @@ -210,7 +216,8 @@ int random() { } -namespace v8 { namespace internal { +namespace v8 { +namespace internal { double ceiling(double x) { return ceil(x); diff --git a/deps/v8/src/platform.h b/deps/v8/src/platform.h index e23abfc375..4522c74031 100644 --- a/deps/v8/src/platform.h +++ b/deps/v8/src/platform.h @@ -60,7 +60,8 @@ enum { #define INFINITY HUGE_VAL -namespace v8 { namespace internal { +namespace v8 { +namespace internal { int isfinite(double x); } } int isnan(double x); @@ -105,7 +106,8 @@ int random(); #endif // __GNUC__ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { double ceiling(double x); @@ -491,7 +493,7 @@ class Socket { // TickSample captures the information collected for each sample. class TickSample { public: - TickSample() : pc(0), sp(0), fp(0), state(OTHER) {} + TickSample() : pc(0), sp(0), fp(0), state(OTHER), frames_count(0) {} uintptr_t pc; // Instruction pointer. uintptr_t sp; // Stack pointer. uintptr_t fp; // Frame pointer. @@ -518,10 +520,11 @@ class Sampler { // Is the sampler used for profiling. inline bool IsProfiling() { return profiling_; } - class PlatformData; - protected: + // Whether the sampler is running (that is, consumes resources). inline bool IsActive() { return active_; } + class PlatformData; + private: int interval_; bool profiling_; diff --git a/deps/v8/src/prettyprinter.cc b/deps/v8/src/prettyprinter.cc index b58000a8f9..79f1883ef4 100644 --- a/deps/v8/src/prettyprinter.cc +++ b/deps/v8/src/prettyprinter.cc @@ -33,7 +33,8 @@ #include "scopes.h" #include "platform.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { #ifdef DEBUG @@ -692,10 +693,10 @@ void AstPrinter::PrintLabelsIndented(const char* info, ZoneStringList* labels) { Print(" "); } PrintLabels(labels); - Print("\n"); } else if (info != NULL) { PrintIndented(info); } + Print("\n"); } @@ -917,9 +918,8 @@ void AstPrinter::VisitLiteral(Literal* node) { void AstPrinter::VisitRegExpLiteral(RegExpLiteral* node) { IndentedScope indent("REGEXP LITERAL"); - PrintLiteral(node->pattern(), false); - Print(","); - PrintLiteral(node->flags(), false); + PrintLiteralIndented("PATTERN", node->pattern(), false); + PrintLiteralIndented("FLAGS", node->flags(), false); } diff --git a/deps/v8/src/prettyprinter.h b/deps/v8/src/prettyprinter.h index 720fe7b4c9..bfce9b0332 100644 --- a/deps/v8/src/prettyprinter.h +++ b/deps/v8/src/prettyprinter.h @@ -30,7 +30,8 @@ #include "ast.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { #ifdef DEBUG diff --git a/deps/v8/src/property.cc b/deps/v8/src/property.cc index 6c2153008a..2915c4abc9 100644 --- a/deps/v8/src/property.cc +++ b/deps/v8/src/property.cc @@ -27,7 +27,8 @@ #include "v8.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { void DescriptorWriter::Write(Descriptor* desc) { diff --git a/deps/v8/src/property.h b/deps/v8/src/property.h index 60a9b544de..edab97ab02 100644 --- a/deps/v8/src/property.h +++ b/deps/v8/src/property.h @@ -28,7 +28,8 @@ #ifndef V8_PROPERTY_H_ #define V8_PROPERTY_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Abstraction for elements in instance-descriptor arrays. diff --git a/deps/v8/src/regexp-macro-assembler-irregexp-inl.h b/deps/v8/src/regexp-macro-assembler-irregexp-inl.h index fa4c3d16b4..5074f210a0 100644 --- a/deps/v8/src/regexp-macro-assembler-irregexp-inl.h +++ b/deps/v8/src/regexp-macro-assembler-irregexp-inl.h @@ -35,7 +35,8 @@ #ifndef V8_REGEXP_MACRO_ASSEMBLER_IRREGEXP_INL_H_ #define V8_REGEXP_MACRO_ASSEMBLER_IRREGEXP_INL_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { void RegExpMacroAssemblerIrregexp::Emit(uint32_t byte, diff --git a/deps/v8/src/regexp-macro-assembler-irregexp.cc b/deps/v8/src/regexp-macro-assembler-irregexp.cc index 436db35d8b..b87c51f900 100644 --- a/deps/v8/src/regexp-macro-assembler-irregexp.cc +++ b/deps/v8/src/regexp-macro-assembler-irregexp.cc @@ -33,7 +33,8 @@ #include "regexp-macro-assembler-irregexp-inl.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { RegExpMacroAssemblerIrregexp::RegExpMacroAssemblerIrregexp(Vector<byte> buffer) diff --git a/deps/v8/src/regexp-macro-assembler-irregexp.h b/deps/v8/src/regexp-macro-assembler-irregexp.h index 9ed82e396b..597046c4c3 100644 --- a/deps/v8/src/regexp-macro-assembler-irregexp.h +++ b/deps/v8/src/regexp-macro-assembler-irregexp.h @@ -28,7 +28,8 @@ #ifndef V8_REGEXP_MACRO_ASSEMBLER_IRREGEXP_H_ #define V8_REGEXP_MACRO_ASSEMBLER_IRREGEXP_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { class RegExpMacroAssemblerIrregexp: public RegExpMacroAssembler { diff --git a/deps/v8/src/regexp-macro-assembler-tracer.cc b/deps/v8/src/regexp-macro-assembler-tracer.cc index 74345d853e..30eb485e37 100644 --- a/deps/v8/src/regexp-macro-assembler-tracer.cc +++ b/deps/v8/src/regexp-macro-assembler-tracer.cc @@ -30,7 +30,8 @@ #include "regexp-macro-assembler.h" #include "regexp-macro-assembler-tracer.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { RegExpMacroAssemblerTracer::RegExpMacroAssemblerTracer( RegExpMacroAssembler* assembler) : diff --git a/deps/v8/src/regexp-macro-assembler-tracer.h b/deps/v8/src/regexp-macro-assembler-tracer.h index f25289e6e8..0fd73f3d6f 100644 --- a/deps/v8/src/regexp-macro-assembler-tracer.h +++ b/deps/v8/src/regexp-macro-assembler-tracer.h @@ -28,7 +28,8 @@ #ifndef V8_REGEXP_MACRO_ASSEMBLER_TRACER_H_ #define V8_REGEXP_MACRO_ASSEMBLER_TRACER_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Decorator on a RegExpMacroAssembler that write all calls. class RegExpMacroAssemblerTracer: public RegExpMacroAssembler { diff --git a/deps/v8/src/regexp-macro-assembler.cc b/deps/v8/src/regexp-macro-assembler.cc index 913ac6462a..8dede304eb 100644 --- a/deps/v8/src/regexp-macro-assembler.cc +++ b/deps/v8/src/regexp-macro-assembler.cc @@ -31,7 +31,8 @@ #include "assembler.h" #include "regexp-macro-assembler.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { RegExpMacroAssembler::RegExpMacroAssembler() { } diff --git a/deps/v8/src/regexp-macro-assembler.h b/deps/v8/src/regexp-macro-assembler.h index a3f398d8ae..484986428b 100644 --- a/deps/v8/src/regexp-macro-assembler.h +++ b/deps/v8/src/regexp-macro-assembler.h @@ -28,7 +28,8 @@ #ifndef V8_REGEXP_MACRO_ASSEMBLER_H_ #define V8_REGEXP_MACRO_ASSEMBLER_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { struct DisjunctDecisionRow { RegExpCharacterClass cc; diff --git a/deps/v8/src/regexp-stack.cc b/deps/v8/src/regexp-stack.cc index 05daa58d4e..83cb6e4ef4 100644 --- a/deps/v8/src/regexp-stack.cc +++ b/deps/v8/src/regexp-stack.cc @@ -29,7 +29,8 @@ #include "top.h" #include "regexp-stack.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { RegExpStack::RegExpStack() { // Initialize, if not already initialized. diff --git a/deps/v8/src/regexp-stack.h b/deps/v8/src/regexp-stack.h index b955e76a0f..6c090daa65 100644 --- a/deps/v8/src/regexp-stack.h +++ b/deps/v8/src/regexp-stack.h @@ -28,7 +28,8 @@ #ifndef V8_REGEXP_STACK_H_ #define V8_REGEXP_STACK_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Maintains a per-v8thread stack area that can be used by irregexp // implementation for its backtracking stack. diff --git a/deps/v8/src/register-allocator-inl.h b/deps/v8/src/register-allocator-inl.h index 9e745b52ea..8fb498b7ff 100644 --- a/deps/v8/src/register-allocator-inl.h +++ b/deps/v8/src/register-allocator-inl.h @@ -28,19 +28,44 @@ #ifndef V8_REGISTER_ALLOCATOR_INL_H_ #define V8_REGISTER_ALLOCATOR_INL_H_ +#include "codegen.h" #include "register-allocator.h" #include "virtual-frame.h" -namespace v8 { namespace internal { +#if V8_TARGET_ARCH_IA32 +#include "ia32/register-allocator-ia32-inl.h" +#elif V8_TARGET_ARCH_X64 +#include "x64/register-allocator-x64-inl.h" +#elif V8_TARGET_ARCH_ARM +#include "arm/register-allocator-arm-inl.h" +#else +#error Unsupported target architecture. +#endif + + +namespace v8 { +namespace internal { Result::~Result() { - if (is_register()) cgen_->allocator()->Unuse(reg()); + if (is_register()) { + CodeGeneratorScope::Current()->allocator()->Unuse(reg()); + } } void Result::Unuse() { - if (is_register()) cgen_->allocator()->Unuse(reg()); - type_ = INVALID; + if (is_register()) { + CodeGeneratorScope::Current()->allocator()->Unuse(reg()); + } + invalidate(); +} + + +void Result::CopyTo(Result* destination) const { + destination->value_ = value_; + if (is_register()) { + CodeGeneratorScope::Current()->allocator()->Use(reg()); + } } diff --git a/deps/v8/src/register-allocator.cc b/deps/v8/src/register-allocator.cc index 94e031fa0b..2599232ce8 100644 --- a/deps/v8/src/register-allocator.cc +++ b/deps/v8/src/register-allocator.cc @@ -30,54 +30,28 @@ #include "codegen-inl.h" #include "register-allocator-inl.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // ------------------------------------------------------------------------- // Result implementation. -Result::Result(Register reg, CodeGenerator* cgen) - : static_type_(), - type_(REGISTER), - cgen_(cgen) { - data_.reg_ = reg; - ASSERT(reg.is_valid()); - cgen_->allocator()->Use(reg); -} - - -Result::Result(Register reg, CodeGenerator* cgen, StaticType static_type) - : static_type_(static_type), - type_(REGISTER), - cgen_(cgen) { - data_.reg_ = reg; - ASSERT(reg.is_valid()); - cgen_->allocator()->Use(reg); -} - -void Result::CopyTo(Result* destination) const { - destination->static_type_ = static_type_; - destination->type_ = type(); - destination->cgen_ = cgen_; - - if (is_register()) { - destination->data_.reg_ = reg(); - cgen_->allocator()->Use(reg()); - } else if (is_constant()) { - destination->data_.handle_ = data_.handle_; - } else { - ASSERT(!is_valid()); - } +Result::Result(Register reg) { + ASSERT(reg.is_valid() && !RegisterAllocator::IsReserved(reg)); + CodeGeneratorScope::Current()->allocator()->Use(reg); + value_ = StaticTypeField::encode(StaticType::UNKNOWN_TYPE) + | TypeField::encode(REGISTER) + | DataField::encode(reg.code_); } -// ------------------------------------------------------------------------- -// RegisterFile implementation. - -void RegisterFile::CopyTo(RegisterFile* other) { - for (int i = 0; i < kNumRegisters; i++) { - other->ref_counts_[i] = ref_counts_[i]; - } +Result::Result(Register reg, StaticType type) { + ASSERT(reg.is_valid() && !RegisterAllocator::IsReserved(reg)); + CodeGeneratorScope::Current()->allocator()->Use(reg); + value_ = StaticTypeField::encode(type.static_type_) + | TypeField::encode(REGISTER) + | DataField::encode(reg.code_); } @@ -87,12 +61,11 @@ void RegisterFile::CopyTo(RegisterFile* other) { Result RegisterAllocator::AllocateWithoutSpilling() { // Return the first free register, if any. - int free_reg = registers_.ScanForFreeRegister(); - if (free_reg < kNumRegisters) { - Register free_result = { free_reg }; - return Result(free_result, cgen_); + int num = registers_.ScanForFreeRegister(); + if (num == RegisterAllocator::kInvalidRegister) { + return Result(); } - return Result(cgen_); + return Result(RegisterAllocator::ToRegister(num)); } @@ -104,7 +77,7 @@ Result RegisterAllocator::Allocate() { Register free_reg = cgen_->frame()->SpillAnyRegister(); if (free_reg.is_valid()) { ASSERT(!is_used(free_reg)); - return Result(free_reg, cgen_); + return Result(free_reg); } } return result; @@ -114,7 +87,7 @@ Result RegisterAllocator::Allocate() { Result RegisterAllocator::Allocate(Register target) { // If the target is not referenced, it can simply be allocated. if (!is_used(target)) { - return Result(target, cgen_); + return Result(target); } // If the target is only referenced in the frame, it can be spilled and // then allocated. @@ -122,10 +95,10 @@ Result RegisterAllocator::Allocate(Register target) { if (cgen_->frame()->is_used(target) && count(target) == 1) { cgen_->frame()->Spill(target); ASSERT(!is_used(target)); - return Result(target, cgen_); + return Result(target); } // Otherwise (if it's referenced outside the frame) we cannot allocate it. - return Result(cgen_); + return Result(); } diff --git a/deps/v8/src/register-allocator.h b/deps/v8/src/register-allocator.h index f79d6cfdfd..c5391918d5 100644 --- a/deps/v8/src/register-allocator.h +++ b/deps/v8/src/register-allocator.h @@ -30,7 +30,18 @@ #include "macro-assembler.h" -namespace v8 { namespace internal { +#if V8_TARGET_ARCH_IA32 +#include "ia32/register-allocator-ia32.h" +#elif V8_TARGET_ARCH_X64 +#include "x64/register-allocator-x64.h" +#elif V8_TARGET_ARCH_ARM +#include "arm/register-allocator-arm.h" +#else +#error Unsupported target architecture. +#endif + +namespace v8 { +namespace internal { // ------------------------------------------------------------------------- @@ -100,7 +111,10 @@ class StaticType BASE_EMBEDDED { explicit StaticType(StaticTypeEnum static_type) : static_type_(static_type) {} // StaticTypeEnum static_type_; - byte static_type_; + StaticTypeEnum static_type_; + + friend class FrameElement; + friend class Result; }; @@ -119,26 +133,20 @@ class Result BASE_EMBEDDED { }; // Construct an invalid result. - explicit Result(CodeGenerator* cgen) - : static_type_(), - type_(INVALID), - cgen_(cgen) {} + Result() { invalidate(); } // Construct a register Result. - Result(Register reg, - CodeGenerator* cgen); + explicit Result(Register reg); // Construct a register Result with a known static type. - Result(Register reg, - CodeGenerator* cgen, - StaticType static_type); + Result(Register reg, StaticType static_type); // Construct a Result whose value is a compile-time constant. - Result(Handle<Object> value, CodeGenerator * cgen) - : static_type_(StaticType::TypeOf(*value)), - type_(CONSTANT), - cgen_(cgen) { - data_.handle_ = value.location(); + explicit Result(Handle<Object> value) { + value_ = StaticTypeField::encode(StaticType::TypeOf(*value).static_type_) + | TypeField::encode(CONSTANT) + | DataField::encode(ConstantList()->length()); + ConstantList()->Add(value); } // The copy constructor and assignment operators could each create a new @@ -157,25 +165,51 @@ class Result BASE_EMBEDDED { inline ~Result(); + // Static indirection table for handles to constants. If a Result + // represents a constant, the data contains an index into this table + // of handles to the actual constants. + typedef ZoneList<Handle<Object> > ZoneObjectList; + + static ZoneObjectList* ConstantList() { + static ZoneObjectList list(10); + return &list; + } + + // Clear the constants indirection table. + static void ClearConstantList() { + ConstantList()->Clear(); + } + inline void Unuse(); - StaticType static_type() const { return static_type_; } - void set_static_type(StaticType static_type) { static_type_ = static_type; } + StaticType static_type() const { + return StaticType(StaticTypeField::decode(value_)); + } - Type type() const { return static_cast<Type>(type_); } + void set_static_type(StaticType type) { + value_ = value_ & ~StaticTypeField::mask(); + value_ = value_ | StaticTypeField::encode(type.static_type_); + } + + Type type() const { return TypeField::decode(value_); } + + void invalidate() { value_ = TypeField::encode(INVALID); } bool is_valid() const { return type() != INVALID; } bool is_register() const { return type() == REGISTER; } bool is_constant() const { return type() == CONSTANT; } Register reg() const { - ASSERT(type() == REGISTER); - return data_.reg_; + ASSERT(is_register()); + uint32_t reg = DataField::decode(value_); + Register result; + result.code_ = reg; + return result; } Handle<Object> handle() const { ASSERT(type() == CONSTANT); - return Handle<Object>(data_.handle_); + return ConstantList()->at(DataField::decode(value_)); } // Move this result to an arbitrary register. The register is not @@ -189,17 +223,15 @@ class Result BASE_EMBEDDED { void ToRegister(Register reg); private: - StaticType static_type_; - byte type_; + uint32_t value_; - union { - Register reg_; - Object** handle_; - } data_; + class StaticTypeField: public BitField<StaticType::StaticTypeEnum, 0, 3> {}; + class TypeField: public BitField<Type, 3, 2> {}; + class DataField: public BitField<uint32_t, 5, 32 - 6> {}; - CodeGenerator* cgen_; + inline void CopyTo(Result* destination) const; - void CopyTo(Result* destination) const; + friend class CodeGeneratorScope; }; @@ -219,42 +251,50 @@ class RegisterFile BASE_EMBEDDED { } } - // Predicates and accessors for the reference counts. The versions - // that take a register code rather than a register are for - // convenience in loops over the register codes. - bool is_used(int reg_code) const { return ref_counts_[reg_code] > 0; } - bool is_used(Register reg) const { return is_used(reg.code()); } - int count(int reg_code) const { return ref_counts_[reg_code]; } - int count(Register reg) const { return count(reg.code()); } + // Predicates and accessors for the reference counts. + bool is_used(int num) { + ASSERT(0 <= num && num < kNumRegisters); + return ref_counts_[num] > 0; + } + + int count(int num) { + ASSERT(0 <= num && num < kNumRegisters); + return ref_counts_[num]; + } // Record a use of a register by incrementing its reference count. - void Use(Register reg) { - ref_counts_[reg.code()]++; + void Use(int num) { + ASSERT(0 <= num && num < kNumRegisters); + ref_counts_[num]++; } // Record that a register will no longer be used by decrementing its // reference count. - void Unuse(Register reg) { - ASSERT(!reg.is(no_reg)); - ASSERT(is_used(reg.code())); - ref_counts_[reg.code()]--; + void Unuse(int num) { + ASSERT(is_used(num)); + ref_counts_[num]--; } // Copy the reference counts from this register file to the other. - void CopyTo(RegisterFile* other); + void CopyTo(RegisterFile* other) { + for (int i = 0; i < kNumRegisters; i++) { + other->ref_counts_[i] = ref_counts_[i]; + } + } private: + static const int kNumRegisters = RegisterAllocatorConstants::kNumRegisters; + int ref_counts_[kNumRegisters]; - // Very fast inlined loop to find a free register. - // Used in RegisterAllocator::AllocateWithoutSpilling. - // Returns kNumRegisters if no free register found. - inline int ScanForFreeRegister() { - int i = 0; - for (; i < kNumRegisters ; ++i) { - if (ref_counts_[i] == 0) break; + // Very fast inlined loop to find a free register. Used in + // RegisterAllocator::AllocateWithoutSpilling. Returns + // kInvalidRegister if no free register found. + int ScanForFreeRegister() { + for (int i = 0; i < RegisterAllocatorConstants::kNumRegisters; i++) { + if (!is_used(i)) return i; } - return i; + return RegisterAllocatorConstants::kInvalidRegister; } friend class RegisterAllocator; @@ -267,55 +307,62 @@ class RegisterFile BASE_EMBEDDED { class RegisterAllocator BASE_EMBEDDED { public: - explicit RegisterAllocator(CodeGenerator* cgen) : cgen_(cgen) {} - - // A register file with each of the reserved registers counted once. - static RegisterFile Reserved(); + static const int kNumRegisters = + RegisterAllocatorConstants::kNumRegisters; + static const int kInvalidRegister = + RegisterAllocatorConstants::kInvalidRegister; - // Unuse all the reserved registers in a register file. - static void UnuseReserved(RegisterFile* register_file); + explicit RegisterAllocator(CodeGenerator* cgen) : cgen_(cgen) {} // True if the register is reserved by the code generator, false if it - // can be freely used by the allocator. - static bool IsReserved(int reg_code); - static bool IsReserved(Register reg) { return IsReserved(reg); } + // can be freely used by the allocator Defined in the + // platform-specific XXX-inl.h files.. + static inline bool IsReserved(Register reg); + + // Convert between (unreserved) assembler registers and allocator + // numbers. Defined in the platform-specific XXX-inl.h files. + static inline int ToNumber(Register reg); + static inline Register ToRegister(int num); // Predicates and accessors for the registers' reference counts. - bool is_used(int reg_code) const { return registers_.is_used(reg_code); } - bool is_used(Register reg) const { return registers_.is_used(reg.code()); } - int count(int reg_code) const { return registers_.count(reg_code); } - int count(Register reg) const { return registers_.count(reg.code()); } + bool is_used(int num) { return registers_.is_used(num); } + bool is_used(Register reg) { return registers_.is_used(ToNumber(reg)); } + + int count(int num) { return registers_.count(num); } + int count(Register reg) { return registers_.count(ToNumber(reg)); } // Explicitly record a reference to a register. - void Use(Register reg) { registers_.Use(reg); } + void Use(int num) { registers_.Use(num); } + void Use(Register reg) { registers_.Use(ToNumber(reg)); } // Explicitly record that a register will no longer be used. - void Unuse(Register reg) { registers_.Unuse(reg); } - - // Initialize the register allocator for entry to a JS function. On - // entry, the registers used by the JS calling convention are - // externally referenced (ie, outside the virtual frame); and the - // other registers are free. - void Initialize(); + void Unuse(int num) { registers_.Unuse(num); } + void Unuse(Register reg) { registers_.Unuse(ToNumber(reg)); } // Reset the register reference counts to free all non-reserved registers. - // A frame-external reference is kept to each of the reserved registers. - void Reset(); + void Reset() { registers_.Reset(); } + + // Initialize the register allocator for entry to a JS function. On + // entry, the (non-reserved) registers used by the JS calling + // convention are referenced and the other (non-reserved) registers + // are free. + inline void Initialize(); // Allocate a free register and return a register result if possible or // fail and return an invalid result. Result Allocate(); - // Allocate a specific register if possible, spilling it from the frame if - // necessary, or else fail and return an invalid result. + // Allocate a specific register if possible, spilling it from the + // current frame if necessary, or else fail and return an invalid + // result. Result Allocate(Register target); - // Allocate a free register without spilling any from the current frame or - // fail and return an invalid result. + // Allocate a free register without spilling any from the current + // frame or fail and return an invalid result. Result AllocateWithoutSpilling(); - // Allocate a free byte register without spilling any from the - // current frame or fail and return an invalid result. + // Allocate a free byte register without spilling any from the current + // frame or fail and return an invalid result. Result AllocateByteRegisterWithoutSpilling(); // Copy the internal state to a register file, to be restored later by @@ -324,6 +371,7 @@ class RegisterAllocator BASE_EMBEDDED { registers_.CopyTo(register_file); } + // Restore the internal state. void RestoreFrom(RegisterFile* register_file) { register_file->CopyTo(®isters_); } diff --git a/deps/v8/src/rewriter.cc b/deps/v8/src/rewriter.cc index 4e3676b7da..e0a0226ec8 100644 --- a/deps/v8/src/rewriter.cc +++ b/deps/v8/src/rewriter.cc @@ -32,7 +32,8 @@ #include "scopes.h" #include "rewriter.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { class AstOptimizer: public AstVisitor { @@ -803,6 +804,7 @@ void Processor::VisitThisFunction(ThisFunction* node) { bool Rewriter::Process(FunctionLiteral* function) { + HistogramTimerScope timer(&Counters::rewriting); Scope* scope = function->scope(); if (scope->is_function_scope()) return true; @@ -823,6 +825,7 @@ bool Rewriter::Optimize(FunctionLiteral* function) { ZoneList<Statement*>* body = function->body(); if (FLAG_optimize_ast && !body->is_empty()) { + HistogramTimerScope timer(&Counters::ast_optimization); AstOptimizer optimizer(function->name()); optimizer.Optimize(body); if (optimizer.HasStackOverflow()) { diff --git a/deps/v8/src/rewriter.h b/deps/v8/src/rewriter.h index aa2f981de8..8943e75aa9 100644 --- a/deps/v8/src/rewriter.h +++ b/deps/v8/src/rewriter.h @@ -28,7 +28,8 @@ #ifndef V8_REWRITER_H_ #define V8_REWRITER_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Currently, the rewriter takes function literals (only top-level) diff --git a/deps/v8/src/runtime.cc b/deps/v8/src/runtime.cc index bf6286fe34..78be512925 100644 --- a/deps/v8/src/runtime.cc +++ b/deps/v8/src/runtime.cc @@ -46,7 +46,8 @@ #include "smart-pointer.h" #include "parser.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { #define RUNTIME_ASSERT(value) do { \ @@ -1420,6 +1421,7 @@ class ReplacementStringBuilder { void AddElement(Object* element) { ASSERT(element->IsSmi() || element->IsString()); + ASSERT(parts_->length() > part_count_); parts_->set(part_count_, element); part_count_++; } @@ -1589,6 +1591,7 @@ class CompiledReplacement { if (i > last) { parts->Add(ReplacementPart::ReplacementSubString(last, i)); } + ASSERT(capture_ref <= capture_count); parts->Add(ReplacementPart::SubjectCapture(capture_ref)); last = next_index + 1; } @@ -1723,7 +1726,7 @@ static Object* StringReplaceRegExpWithString(String* subject, int capture_count = regexp_handle->CaptureCount(); // CompiledReplacement uses zone allocation. - ZoneScope zone(DELETE_ON_EXIT); + CompilationZoneScope zone(DELETE_ON_EXIT); CompiledReplacement compiled_replacement; compiled_replacement.Compile(replacement_handle, capture_count, @@ -2035,7 +2038,7 @@ static int BoyerMooreIndexOf(Vector<const schar> subject, BoyerMoorePopulateGoodSuffixTable(pattern, start); pchar last_char = pattern[m - 1]; // Continue search from i. - do { + while (idx <= n - m) { int j = m - 1; schar c; while (last_char != (c = subject[idx + j])) { @@ -2061,7 +2064,7 @@ static int BoyerMooreIndexOf(Vector<const schar> subject, } idx += shift; } - } while (idx <= n - m); + } return -1; } @@ -2376,7 +2379,7 @@ static Object* Runtime_StringMatch(Arguments args) { } int length = subject->length(); - ZoneScope zone_space(DELETE_ON_EXIT); + CompilationZoneScope zone_space(DELETE_ON_EXIT); ZoneList<int> offsets(8); do { int start; @@ -2777,6 +2780,42 @@ Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object, } +Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object, + Handle<Object> key) { + HandleScope scope; + + // Check if the given key is an array index. + uint32_t index; + if (Array::IndexFromObject(*key, &index)) { + // In Firefox/SpiderMonkey, Safari and Opera you can access the + // characters of a string using [] notation. In the case of a + // String object we just need to redirect the deletion to the + // underlying string if the index is in range. Since the + // underlying string does nothing with the deletion, we can ignore + // such deletions. + if (js_object->IsStringObjectWithCharacterAt(index)) { + return Heap::true_value(); + } + + return js_object->DeleteElement(index, JSObject::FORCE_DELETION); + } + + Handle<String> key_string; + if (key->IsString()) { + key_string = Handle<String>::cast(key); + } else { + // Call-back into JavaScript to convert the key to a string. + bool has_pending_exception = false; + Handle<Object> converted = Execution::ToString(key, &has_pending_exception); + if (has_pending_exception) return Failure::Exception(); + key_string = Handle<String>::cast(converted); + } + + key_string->TryFlattenIfNotFlat(); + return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION); +} + + static Object* Runtime_SetProperty(Arguments args) { NoHandleAllocation ha; RUNTIME_ASSERT(args.length() == 3 || args.length() == 4); @@ -2828,7 +2867,7 @@ static Object* Runtime_DeleteProperty(Arguments args) { CONVERT_CHECKED(JSObject, object, args[0]); CONVERT_CHECKED(String, key, args[1]); - return object->DeleteProperty(key); + return object->DeleteProperty(key, JSObject::NORMAL_DELETION); } @@ -4316,9 +4355,15 @@ static Object* Runtime_LazyCompile(Arguments args) { } #endif - // Compile the target function. + // Compile the target function. Here we compile using CompileLazyInLoop in + // order to get the optimized version. This helps code like delta-blue + // that calls performance-critical routines through constructors. A + // constructor call doesn't use a CallIC, it uses a LoadIC followed by a + // direct call. Since the in-loop tracking takes place through CallICs + // this means that things called through constructors are never known to + // be in loops. We compile them as if they are in loops here just in case. ASSERT(!function->is_compiled()); - if (!CompileLazy(function, KEEP_EXCEPTION)) { + if (!CompileLazyInLoop(function, KEEP_EXCEPTION)) { return Failure::Exception(); } @@ -4356,6 +4401,14 @@ static Object* Runtime_GetFunctionDelegate(Arguments args) { } +static Object* Runtime_GetConstructorDelegate(Arguments args) { + HandleScope scope; + ASSERT(args.length() == 1); + RUNTIME_ASSERT(!args[0]->IsJSFunction()); + return *Execution::GetConstructorDelegate(args.at<Object>(0)); +} + + static Object* Runtime_NewContext(Arguments args) { NoHandleAllocation ha; ASSERT(args.length() == 1); @@ -4442,10 +4495,16 @@ static Object* Runtime_LookupContext(Arguments args) { // compiler to do the right thing. // // TODO(1236026): This is a non-portable hack that should be removed. +// TODO(x64): Definitely! typedef uint64_t ObjectPair; static inline ObjectPair MakePair(Object* x, Object* y) { +#if V8_HOST_ARCH_64_BIT + UNIMPLEMENTED(); + return 0; +#else return reinterpret_cast<uint32_t>(x) | (reinterpret_cast<ObjectPair>(y) << 32); +#endif } @@ -4882,16 +4941,14 @@ static Object* Runtime_GlobalReceiver(Arguments args) { static Object* Runtime_CompileString(Arguments args) { HandleScope scope; - ASSERT_EQ(3, args.length()); + ASSERT_EQ(2, args.length()); CONVERT_ARG_CHECKED(String, source, 0); - CONVERT_ARG_CHECKED(Smi, line_offset, 1); - CONVERT_ARG_CHECKED(Oddball, is_json, 2) + CONVERT_ARG_CHECKED(Oddball, is_json, 1) // Compile source string in the global context. Handle<Context> context(Top::context()->global_context()); Handle<JSFunction> boilerplate = Compiler::CompileEval(source, context, - line_offset->value(), true, is_json->IsTrue()); if (boilerplate.is_null()) return Failure::Exception(); @@ -4918,7 +4975,7 @@ static Object* CompileDirectEval(Handle<String> source) { // Compile source string in the current context. Handle<JSFunction> boilerplate = - Compiler::CompileEval(source, context, 0, is_global, false); + Compiler::CompileEval(source, context, is_global, false); if (boilerplate.is_null()) return Failure::Exception(); Handle<JSFunction> fun = Factory::NewFunctionFromBoilerplate(boilerplate, context); @@ -5422,7 +5479,7 @@ static Object* Runtime_DebugBreak(Arguments args) { // Helper functions for wrapping and unwrapping stack frame ids. static Smi* WrapFrameId(StackFrame::Id id) { - ASSERT(IsAligned(OffsetFrom(id), 4)); + ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4))); return Smi::FromInt(id >> 2); } @@ -5471,7 +5528,8 @@ static int LocalPrototypeChainLength(JSObject* obj) { } -static Object* DebugLookupResultValue(Object* receiver, LookupResult* result, +static Object* DebugLookupResultValue(Object* receiver, String* name, + LookupResult* result, bool* caught_exception) { Object* value; switch (result->type()) { @@ -5496,11 +5554,9 @@ static Object* DebugLookupResultValue(Object* receiver, LookupResult* result, return result->GetConstantFunction(); case CALLBACKS: { Object* structure = result->GetCallbackObject(); - if (structure->IsProxy()) { - AccessorDescriptor* callback = - reinterpret_cast<AccessorDescriptor*>( - Proxy::cast(structure)->proxy()); - value = (callback->getter)(receiver, callback->data); + if (structure->IsProxy() || structure->IsAccessorInfo()) { + value = receiver->GetPropertyWithCallback( + receiver, structure, name, result->holder()); if (value->IsException()) { value = Top::pending_exception(); Top::clear_pending_exception(); @@ -5546,6 +5602,17 @@ static Object* Runtime_DebugGetPropertyDetails(Arguments args) { CONVERT_ARG_CHECKED(JSObject, obj, 0); CONVERT_ARG_CHECKED(String, name, 1); + // Make sure to set the current context to the context before the debugger was + // entered (if the debugger is entered). The reason for switching context here + // is that for some property lookups (accessors and interceptors) callbacks + // into the embedding application can occour, and the embedding application + // could have the assumption that its own global context is the current + // context and not some internal debugger context. + SaveContext save; + if (Debug::InDebugger()) { + Top::set_context(*Debug::debugger_entry()->GetContext()); + } + // Skip the global proxy as it has no properties and always delegates to the // real global object. if (obj->IsJSGlobalProxy()) { @@ -5580,15 +5647,29 @@ static Object* Runtime_DebugGetPropertyDetails(Arguments args) { } if (result.IsProperty()) { + // LookupResult is not GC safe as all its members are raw object pointers. + // When calling DebugLookupResultValue GC can happen as this might invoke + // callbacks. After the call to DebugLookupResultValue the callback object + // in the LookupResult might still be needed. Put it into a handle for later + // use. + PropertyType result_type = result.type(); + Handle<Object> result_callback_obj; + if (result_type == CALLBACKS) { + result_callback_obj = Handle<Object>(result.GetCallbackObject()); + } + + // Find the actual value. Don't use result after this call as it's content + // can be invalid. bool caught_exception = false; - Object* value = DebugLookupResultValue(*obj, &result, + Object* value = DebugLookupResultValue(*obj, *name, &result, &caught_exception); if (value->IsFailure()) return value; Handle<Object> value_handle(value); + // If the callback object is a fixed array then it contains JavaScript // getter and/or setter. - bool hasJavaScriptAccessors = result.type() == CALLBACKS && - result.GetCallbackObject()->IsFixedArray(); + bool hasJavaScriptAccessors = result_type == CALLBACKS && + result_callback_obj->IsFixedArray(); Handle<FixedArray> details = Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2); details->set(0, *value_handle); @@ -5617,7 +5698,7 @@ static Object* Runtime_DebugGetProperty(Arguments args) { LookupResult result; obj->Lookup(*name, &result); if (result.IsProperty()) { - return DebugLookupResultValue(*obj, &result, NULL); + return DebugLookupResultValue(*obj, *name, &result, NULL); } return Heap::undefined_value(); } @@ -6031,6 +6112,11 @@ static Object* Runtime_GetCFrames(Arguments args) { Object* result = Runtime_CheckExecutionState(args); if (result->IsFailure()) return result; +#if V8_HOST_ARCH_64_BIT + UNIMPLEMENTED(); + return Heap::undefined_value(); +#else + static const int kMaxCFramesSize = 200; ScopedVector<OS::StackFrame> frames(kMaxCFramesSize); int frames_count = OS::StackWalk(frames); @@ -6062,6 +6148,7 @@ static Object* Runtime_GetCFrames(Arguments args) { frames_array->set(i, *frame_value); } return *Factory::NewJSArrayWithElements(frames_array); +#endif // V8_HOST_ARCH_64_BIT } @@ -6545,7 +6632,6 @@ static Object* Runtime_DebugEvaluate(Arguments args) { Handle<JSFunction> boilerplate = Compiler::CompileEval(function_source, context, - 0, context->IsGlobalContext(), false); if (boilerplate.is_null()) return Failure::Exception(); @@ -6607,7 +6693,6 @@ static Object* Runtime_DebugEvaluateGlobal(Arguments args) { Handle<JSFunction> boilerplate = Handle<JSFunction>(Compiler::CompileEval(source, context, - 0, true, false)); if (boilerplate.is_null()) return Failure::Exception(); @@ -6626,67 +6711,15 @@ static Object* Runtime_DebugEvaluateGlobal(Arguments args) { } -// If an object given is an external string, check that the underlying -// resource is accessible. For other kinds of objects, always return true. -static bool IsExternalStringValid(Object* str) { - if (!str->IsString() || !StringShape(String::cast(str)).IsExternal()) { - return true; - } - if (String::cast(str)->IsAsciiRepresentation()) { - return ExternalAsciiString::cast(str)->resource() != NULL; - } else if (String::cast(str)->IsTwoByteRepresentation()) { - return ExternalTwoByteString::cast(str)->resource() != NULL; - } else { - return true; - } -} - - -// Helper function used by Runtime_DebugGetLoadedScripts below. -static int DebugGetLoadedScripts(FixedArray* instances, int instances_size) { - NoHandleAllocation ha; - AssertNoAllocation no_alloc; - - // Scan heap for Script objects. - int count = 0; - HeapIterator iterator; - while (iterator.has_next()) { - HeapObject* obj = iterator.next(); - ASSERT(obj != NULL); - if (obj->IsScript() && IsExternalStringValid(Script::cast(obj)->source())) { - if (instances != NULL && count < instances_size) { - instances->set(count, obj); - } - count++; - } - } - - return count; -} - - static Object* Runtime_DebugGetLoadedScripts(Arguments args) { HandleScope scope; ASSERT(args.length() == 0); - // Perform two GCs to get rid of all unreferenced scripts. The first GC gets - // rid of all the cached script wrappers and the second gets rid of the - // scripts which is no longer referenced. - Heap::CollectAllGarbage(); - Heap::CollectAllGarbage(); - - // Get the number of scripts. - int count; - count = DebugGetLoadedScripts(NULL, 0); - - // Allocate an array to hold the result. - Handle<FixedArray> instances = Factory::NewFixedArray(count); - // Fill the script objects. - count = DebugGetLoadedScripts(*instances, count); + Handle<FixedArray> instances = Debug::GetLoadedScripts(); // Convert the script objects to proper JS objects. - for (int i = 0; i < count; i++) { + for (int i = 0; i < instances->length(); i++) { Handle<Script> script = Handle<Script>(Script::cast(instances->get(i))); // Get the script wrapper in a local handle before calling GetScriptWrapper, // because using diff --git a/deps/v8/src/runtime.h b/deps/v8/src/runtime.h index 2041295151..30bb7c5aec 100644 --- a/deps/v8/src/runtime.h +++ b/deps/v8/src/runtime.h @@ -28,7 +28,8 @@ #ifndef V8_RUNTIME_H_ #define V8_RUNTIME_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // The interface to C++ runtime functions. @@ -37,7 +38,10 @@ namespace v8 { namespace internal { // release and debug mode. // This macro should only be used by the macro RUNTIME_FUNCTION_LIST. -#define RUNTIME_FUNCTION_LIST_ALWAYS(F) \ +// WARNING: RUNTIME_FUNCTION_LIST_ALWAYS_* is a very large macro that caused +// MSVC Intellisense to crash. It was broken into two macros to work around +// this problem. Please avoid large recursive macros whenever possible. +#define RUNTIME_FUNCTION_LIST_ALWAYS_1(F) \ /* Property access */ \ F(GetProperty, 2) \ F(KeyedGetProperty, 2) \ @@ -60,6 +64,7 @@ namespace v8 { namespace internal { /* Utilities */ \ F(GetCalledFunction, 0) \ F(GetFunctionDelegate, 1) \ + F(GetConstructorDelegate, 1) \ F(NewArguments, 1) \ F(NewArgumentsFast, 3) \ F(LazyCompile, 1) \ @@ -153,8 +158,9 @@ namespace v8 { namespace internal { F(NumberToRadixString, 2) \ F(NumberToFixed, 2) \ F(NumberToExponential, 2) \ - F(NumberToPrecision, 2) \ - \ + F(NumberToPrecision, 2) + +#define RUNTIME_FUNCTION_LIST_ALWAYS_2(F) \ /* Reflection */ \ F(FunctionSetInstanceClassName, 2) \ F(FunctionSetLength, 2) \ @@ -195,7 +201,7 @@ namespace v8 { namespace internal { F(NumberIsFinite, 1) \ \ /* Globals */ \ - F(CompileString, 3) \ + F(CompileString, 2) \ F(GlobalPrint, 1) \ \ /* Eval */ \ @@ -320,7 +326,8 @@ namespace v8 { namespace internal { // via a native call by name (from within JS code). #define RUNTIME_FUNCTION_LIST(F) \ - RUNTIME_FUNCTION_LIST_ALWAYS(F) \ + RUNTIME_FUNCTION_LIST_ALWAYS_1(F) \ + RUNTIME_FUNCTION_LIST_ALWAYS_2(F) \ RUNTIME_FUNCTION_LIST_DEBUG(F) \ RUNTIME_FUNCTION_LIST_DEBUGGER_SUPPORT(F) @@ -380,6 +387,9 @@ class Runtime : public AllStatic { Handle<Object> value, PropertyAttributes attr); + static Object* ForceDeleteObjectProperty(Handle<JSObject> object, + Handle<Object> key); + static Object* GetObjectProperty(Handle<Object> object, Handle<Object> key); // This function is used in FunctionNameUsing* tests. diff --git a/deps/v8/src/runtime.js b/deps/v8/src/runtime.js index 63e92921a4..c8ccf9f846 100644 --- a/deps/v8/src/runtime.js +++ b/deps/v8/src/runtime.js @@ -327,6 +327,18 @@ function CALL_NON_FUNCTION() { } +function CALL_NON_FUNCTION_AS_CONSTRUCTOR() { + var callee = %GetCalledFunction(); + var delegate = %GetConstructorDelegate(callee); + if (!IS_FUNCTION(delegate)) { + throw %MakeTypeError('called_non_callable', [typeof callee]); + } + + var parameters = %NewArguments(delegate); + return delegate.apply(callee, parameters); +} + + function APPLY_PREPARE(args) { var length; // First check whether length is a positive Smi and args is an array. This is the diff --git a/deps/v8/src/scanner.cc b/deps/v8/src/scanner.cc index 65ec0f8b7a..24a6d4be9c 100644 --- a/deps/v8/src/scanner.cc +++ b/deps/v8/src/scanner.cc @@ -30,7 +30,8 @@ #include "ast.h" #include "scanner.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // ---------------------------------------------------------------------------- // Character predicates @@ -48,8 +49,12 @@ StaticResource<Scanner::Utf8Decoder> Scanner::utf8_decoder_; // ---------------------------------------------------------------------------- // UTF8Buffer -UTF8Buffer::UTF8Buffer() : data_(NULL) { - Initialize(NULL, 0); +UTF8Buffer::UTF8Buffer() { + static const int kInitialCapacity = 1 * KB; + data_ = NewArray<char>(kInitialCapacity); + limit_ = ComputeLimit(data_, kInitialCapacity); + Reset(); + ASSERT(Capacity() == kInitialCapacity && pos() == 0); } @@ -58,33 +63,27 @@ UTF8Buffer::~UTF8Buffer() { } -void UTF8Buffer::Initialize(char* src, int length) { - DeleteArray(data_); - data_ = src; - size_ = length; - Reset(); -} - - -void UTF8Buffer::AddChar(uc32 c) { - const int min_size = 1024; - if (pos_ + static_cast<int>(unibrow::Utf8::kMaxEncodedSize) > size_) { - int new_size = size_ * 2; - if (new_size < min_size) { - new_size = min_size; - } - char* new_data = NewArray<char>(new_size); - memcpy(new_data, data_, pos_); +void UTF8Buffer::AddCharSlow(uc32 c) { + static const int kCapacityGrowthLimit = 1 * MB; + if (cursor_ > limit_) { + int old_capacity = Capacity(); + int old_position = pos(); + int new_capacity = + Min(old_capacity * 2, old_capacity + kCapacityGrowthLimit); + char* new_data = NewArray<char>(new_capacity); + memcpy(new_data, data_, old_position); DeleteArray(data_); data_ = new_data; - size_ = new_size; + cursor_ = new_data + old_position; + limit_ = ComputeLimit(new_data, new_capacity); + ASSERT(Capacity() == new_capacity && pos() == old_position); } - if (static_cast<unsigned>(c) < unibrow::Utf8::kMaxOneByteChar) { - data_[pos_++] = c; // common case: 7bit ASCII + if (static_cast<unsigned>(c) <= unibrow::Utf8::kMaxOneByteChar) { + *cursor_++ = c; // Common case: 7-bit ASCII. } else { - pos_ += unibrow::Utf8::Encode(&data_[pos_], c); + cursor_ += unibrow::Utf8::Encode(cursor_, c); } - ASSERT(pos_ <= size_); + ASSERT(pos() <= Capacity()); } @@ -172,9 +171,10 @@ void Scanner::Init(Handle<String> source, unibrow::CharacterStream* stream, ASSERT(kCharacterLookaheadBufferSize == 1); Advance(); - // Skip initial whitespace (allowing HTML comment ends) and scan - // first token. - SkipWhiteSpace(true); + // Skip initial whitespace allowing HTML comment ends just like + // after a newline and scan first token. + has_line_terminator_before_next_ = true; + SkipWhiteSpace(); Scan(); } @@ -246,18 +246,19 @@ static inline bool IsByteOrderMark(uc32 c) { } -void Scanner::SkipWhiteSpace(bool initial) { - has_line_terminator_before_next_ = initial; +bool Scanner::SkipWhiteSpace() { + int start_position = source_pos(); while (true) { // We treat byte-order marks (BOMs) as whitespace for better // compatibility with Spidermonkey and other JavaScript engines. while (kIsWhiteSpace.get(c0_) || IsByteOrderMark(c0_)) { // IsWhiteSpace() includes line terminators! - if (kIsLineTerminator.get(c0_)) + if (kIsLineTerminator.get(c0_)) { // Ignore line terminators, but remember them. This is necessary // for automatic semicolon insertion. has_line_terminator_before_next_ = true; + } Advance(); } @@ -279,7 +280,8 @@ void Scanner::SkipWhiteSpace(bool initial) { } PushBack('-'); // undo Advance() } - return; + // Return whether or not we skipped any characters. + return source_pos() != start_position; } } @@ -296,7 +298,7 @@ Token::Value Scanner::SkipSingleLineComment() { Advance(); } - return Token::COMMENT; + return Token::WHITESPACE; } @@ -316,7 +318,7 @@ Token::Value Scanner::SkipMultiLineComment() { // matches the behaviour of SpiderMonkey and KJS. if (ch == '*' && c0_ == '/') { c0_ = ' '; - return Token::COMMENT; + return Token::WHITESPACE; } } @@ -342,18 +344,238 @@ Token::Value Scanner::ScanHtmlComment() { void Scanner::Scan() { Token::Value token; - bool has_line_terminator = false; + has_line_terminator_before_next_ = false; do { - SkipWhiteSpace(has_line_terminator); - - // Remember the line terminator in previous loop - has_line_terminator = has_line_terminator_before_next(); - // Remember the position of the next token next_.location.beg_pos = source_pos(); - token = ScanToken(); - } while (token == Token::COMMENT); + switch (c0_) { + case ' ': + case '\t': + Advance(); + token = Token::WHITESPACE; + break; + + case '\n': + Advance(); + has_line_terminator_before_next_ = true; + token = Token::WHITESPACE; + break; + + case '"': case '\'': + token = ScanString(); + break; + + case '<': + // < <= << <<= <!-- + Advance(); + if (c0_ == '=') { + token = Select(Token::LTE); + } else if (c0_ == '<') { + token = Select('=', Token::ASSIGN_SHL, Token::SHL); + } else if (c0_ == '!') { + token = ScanHtmlComment(); + } else { + token = Token::LT; + } + break; + + case '>': + // > >= >> >>= >>> >>>= + Advance(); + if (c0_ == '=') { + token = Select(Token::GTE); + } else if (c0_ == '>') { + // >> >>= >>> >>>= + Advance(); + if (c0_ == '=') { + token = Select(Token::ASSIGN_SAR); + } else if (c0_ == '>') { + token = Select('=', Token::ASSIGN_SHR, Token::SHR); + } else { + token = Token::SAR; + } + } else { + token = Token::GT; + } + break; + + case '=': + // = == === + Advance(); + if (c0_ == '=') { + token = Select('=', Token::EQ_STRICT, Token::EQ); + } else { + token = Token::ASSIGN; + } + break; + + case '!': + // ! != !== + Advance(); + if (c0_ == '=') { + token = Select('=', Token::NE_STRICT, Token::NE); + } else { + token = Token::NOT; + } + break; + + case '+': + // + ++ += + Advance(); + if (c0_ == '+') { + token = Select(Token::INC); + } else if (c0_ == '=') { + token = Select(Token::ASSIGN_ADD); + } else { + token = Token::ADD; + } + break; + + case '-': + // - -- --> -= + Advance(); + if (c0_ == '-') { + Advance(); + if (c0_ == '>' && has_line_terminator_before_next_) { + // For compatibility with SpiderMonkey, we skip lines that + // start with an HTML comment end '-->'. + token = SkipSingleLineComment(); + } else { + token = Token::DEC; + } + } else if (c0_ == '=') { + token = Select(Token::ASSIGN_SUB); + } else { + token = Token::SUB; + } + break; + + case '*': + // * *= + token = Select('=', Token::ASSIGN_MUL, Token::MUL); + break; + + case '%': + // % %= + token = Select('=', Token::ASSIGN_MOD, Token::MOD); + break; + + case '/': + // / // /* /= + Advance(); + if (c0_ == '/') { + token = SkipSingleLineComment(); + } else if (c0_ == '*') { + token = SkipMultiLineComment(); + } else if (c0_ == '=') { + token = Select(Token::ASSIGN_DIV); + } else { + token = Token::DIV; + } + break; + + case '&': + // & && &= + Advance(); + if (c0_ == '&') { + token = Select(Token::AND); + } else if (c0_ == '=') { + token = Select(Token::ASSIGN_BIT_AND); + } else { + token = Token::BIT_AND; + } + break; + + case '|': + // | || |= + Advance(); + if (c0_ == '|') { + token = Select(Token::OR); + } else if (c0_ == '=') { + token = Select(Token::ASSIGN_BIT_OR); + } else { + token = Token::BIT_OR; + } + break; + + case '^': + // ^ ^= + token = Select('=', Token::ASSIGN_BIT_XOR, Token::BIT_XOR); + break; + + case '.': + // . Number + Advance(); + if (IsDecimalDigit(c0_)) { + token = ScanNumber(true); + } else { + token = Token::PERIOD; + } + break; + + case ':': + token = Select(Token::COLON); + break; + + case ';': + token = Select(Token::SEMICOLON); + break; + + case ',': + token = Select(Token::COMMA); + break; + + case '(': + token = Select(Token::LPAREN); + break; + + case ')': + token = Select(Token::RPAREN); + break; + + case '[': + token = Select(Token::LBRACK); + break; + + case ']': + token = Select(Token::RBRACK); + break; + + case '{': + token = Select(Token::LBRACE); + break; + + case '}': + token = Select(Token::RBRACE); + break; + + case '?': + token = Select(Token::CONDITIONAL); + break; + + case '~': + token = Select(Token::BIT_NOT); + break; + + default: + if (kIsIdentifierStart.get(c0_)) { + token = ScanIdentifier(); + } else if (IsDecimalDigit(c0_)) { + token = ScanNumber(false); + } else if (SkipWhiteSpace()) { + token = Token::WHITESPACE; + } else if (c0_ < 0) { + token = Token::EOS; + } else { + token = Select(Token::ILLEGAL); + } + break; + } + + // Continue scanning for tokens as long as we're just skipping + // whitespace. + } while (token == Token::WHITESPACE); next_.location.end_pos = source_pos(); next_.token = token; @@ -495,147 +717,6 @@ Token::Value Scanner::Select(uc32 next, Token::Value then, Token::Value else_) { } -Token::Value Scanner::ScanToken() { - switch (c0_) { - // strings - case '"': case '\'': - return ScanString(); - - case '<': - // < <= << <<= <!-- - Advance(); - if (c0_ == '=') return Select(Token::LTE); - if (c0_ == '<') return Select('=', Token::ASSIGN_SHL, Token::SHL); - if (c0_ == '!') return ScanHtmlComment(); - return Token::LT; - - case '>': - // > >= >> >>= >>> >>>= - Advance(); - if (c0_ == '=') return Select(Token::GTE); - if (c0_ == '>') { - // >> >>= >>> >>>= - Advance(); - if (c0_ == '=') return Select(Token::ASSIGN_SAR); - if (c0_ == '>') return Select('=', Token::ASSIGN_SHR, Token::SHR); - return Token::SAR; - } - return Token::GT; - - case '=': - // = == === - Advance(); - if (c0_ == '=') return Select('=', Token::EQ_STRICT, Token::EQ); - return Token::ASSIGN; - - case '!': - // ! != !== - Advance(); - if (c0_ == '=') return Select('=', Token::NE_STRICT, Token::NE); - return Token::NOT; - - case '+': - // + ++ += - Advance(); - if (c0_ == '+') return Select(Token::INC); - if (c0_ == '=') return Select(Token::ASSIGN_ADD); - return Token::ADD; - - case '-': - // - -- -= - Advance(); - if (c0_ == '-') return Select(Token::DEC); - if (c0_ == '=') return Select(Token::ASSIGN_SUB); - return Token::SUB; - - case '*': - // * *= - return Select('=', Token::ASSIGN_MUL, Token::MUL); - - case '%': - // % %= - return Select('=', Token::ASSIGN_MOD, Token::MOD); - - case '/': - // / // /* /= - Advance(); - if (c0_ == '/') return SkipSingleLineComment(); - if (c0_ == '*') return SkipMultiLineComment(); - if (c0_ == '=') return Select(Token::ASSIGN_DIV); - return Token::DIV; - - case '&': - // & && &= - Advance(); - if (c0_ == '&') return Select(Token::AND); - if (c0_ == '=') return Select(Token::ASSIGN_BIT_AND); - return Token::BIT_AND; - - case '|': - // | || |= - Advance(); - if (c0_ == '|') return Select(Token::OR); - if (c0_ == '=') return Select(Token::ASSIGN_BIT_OR); - return Token::BIT_OR; - - case '^': - // ^ ^= - return Select('=', Token::ASSIGN_BIT_XOR, Token::BIT_XOR); - - case '.': - // . Number - Advance(); - if (IsDecimalDigit(c0_)) return ScanNumber(true); - return Token::PERIOD; - - case ':': - return Select(Token::COLON); - - case ';': - return Select(Token::SEMICOLON); - - case ',': - return Select(Token::COMMA); - - case '(': - return Select(Token::LPAREN); - - case ')': - return Select(Token::RPAREN); - - case '[': - return Select(Token::LBRACK); - - case ']': - return Select(Token::RBRACK); - - case '{': - return Select(Token::LBRACE); - - case '}': - return Select(Token::RBRACE); - - case '?': - return Select(Token::CONDITIONAL); - - case '~': - return Select(Token::BIT_NOT); - - default: - if (kIsIdentifierStart.get(c0_)) - return ScanIdentifier(); - if (IsDecimalDigit(c0_)) - return ScanNumber(false); - if (c0_ < 0) - return Token::EOS; - return Select(Token::ILLEGAL); - } - - UNREACHABLE(); - return Token::ILLEGAL; -} - - // Returns true if any decimal digits were scanned, returns false otherwise. void Scanner::ScanDecimalDigits() { while (IsDecimalDigit(c0_)) @@ -734,7 +815,6 @@ uc32 Scanner::ScanIdentifierUnicodeEscape() { Token::Value Scanner::ScanIdentifier() { ASSERT(kIsIdentifierStart.get(c0_)); - bool has_escapes = false; StartLiteral(); @@ -746,8 +826,10 @@ Token::Value Scanner::ScanIdentifier() { if (!kIsIdentifierStart.get(c)) return Token::ILLEGAL; AddChar(c); } else { - AddCharAdvance(); + AddChar(c0_); + Advance(); } + // Scan the rest of the identifier characters. while (kIsIdentifierPart.get(c0_)) { if (c0_ == '\\') { @@ -757,19 +839,22 @@ Token::Value Scanner::ScanIdentifier() { if (!kIsIdentifierPart.get(c)) return Token::ILLEGAL; AddChar(c); } else { - AddCharAdvance(); + AddChar(c0_); + Advance(); } } TerminateLiteral(); // We don't have any 1-letter keywords (this is probably a common case). - if ((next_.literal_end - next_.literal_pos) == 1) + if ((next_.literal_end - next_.literal_pos) == 1) { return Token::IDENTIFIER; + } // If the identifier contains unicode escapes, it must not be // resolved to a keyword. - if (has_escapes) + if (has_escapes) { return Token::IDENTIFIER; + } return Token::Lookup(&literals_.data()[next_.literal_pos]); } diff --git a/deps/v8/src/scanner.h b/deps/v8/src/scanner.h index 79a4a4c243..eea23a70c2 100644 --- a/deps/v8/src/scanner.h +++ b/deps/v8/src/scanner.h @@ -31,7 +31,8 @@ #include "token.h" #include "char-predicates-inl.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { class UTF8Buffer { @@ -39,16 +40,33 @@ class UTF8Buffer { UTF8Buffer(); ~UTF8Buffer(); - void Initialize(char* src, int length); - void AddChar(uc32 c); - void Reset() { pos_ = 0; } - int pos() const { return pos_; } + void AddChar(uc32 c) { + if (cursor_ <= limit_ && + static_cast<unsigned>(c) <= unibrow::Utf8::kMaxOneByteChar) { + *cursor_++ = static_cast<char>(c); + } else { + AddCharSlow(c); + } + } + + void Reset() { cursor_ = data_; } + int pos() const { return cursor_ - data_; } char* data() const { return data_; } private: char* data_; - int size_; - int pos_; + char* cursor_; + char* limit_; + + int Capacity() const { + return (limit_ - data_) + unibrow::Utf8::kMaxEncodedSize; + } + + static char* ComputeLimit(char* data, int capacity) { + return (data + capacity) - unibrow::Utf8::kMaxEncodedSize; + } + + void AddCharSlow(uc32 c); }; @@ -204,7 +222,7 @@ class Scanner { void Advance(); void PushBack(uc32 ch); - void SkipWhiteSpace(bool initial); + bool SkipWhiteSpace(); Token::Value SkipSingleLineComment(); Token::Value SkipMultiLineComment(); @@ -212,7 +230,6 @@ class Scanner { inline Token::Value Select(uc32 next, Token::Value then, Token::Value else_); void Scan(); - Token::Value ScanToken(); void ScanDecimalDigits(); Token::Value ScanNumber(bool seen_period); Token::Value ScanIdentifier(); diff --git a/deps/v8/src/scopeinfo.cc b/deps/v8/src/scopeinfo.cc index 6d2ade8154..fedfbd64fe 100644 --- a/deps/v8/src/scopeinfo.cc +++ b/deps/v8/src/scopeinfo.cc @@ -32,7 +32,8 @@ #include "scopeinfo.h" #include "scopes.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { static int CompareLocal(Variable* const* v, Variable* const* w) { @@ -566,5 +567,6 @@ void ScopeInfo<Allocator>::Print() { // Make sure the classes get instantiated by the template system. template class ScopeInfo<FreeStoreAllocationPolicy>; template class ScopeInfo<PreallocatedStorage>; +template class ScopeInfo<ZoneListAllocationPolicy>; } } // namespace v8::internal diff --git a/deps/v8/src/scopeinfo.h b/deps/v8/src/scopeinfo.h index dbe235ad0f..a097d34f9f 100644 --- a/deps/v8/src/scopeinfo.h +++ b/deps/v8/src/scopeinfo.h @@ -30,7 +30,8 @@ #include "variables.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Scope information represents information about a functions's // scopes (currently only one, because we don't do any inlining) @@ -150,6 +151,18 @@ class ScopeInfo BASE_EMBEDDED { List<Variable::Mode, Allocator > context_modes_; }; +class ZoneScopeInfo: public ScopeInfo<ZoneListAllocationPolicy> { + public: + // Create a ZoneScopeInfo instance from a scope. + explicit ZoneScopeInfo(Scope* scope) + : ScopeInfo<ZoneListAllocationPolicy>(scope) {} + + // Create a ZoneScopeInfo instance from a Code object. + explicit ZoneScopeInfo(Code* code) + : ScopeInfo<ZoneListAllocationPolicy>(code) {} +}; + + } } // namespace v8::internal #endif // V8_SCOPEINFO_H_ diff --git a/deps/v8/src/scopes.cc b/deps/v8/src/scopes.cc index e959f02523..7122eb03cc 100644 --- a/deps/v8/src/scopes.cc +++ b/deps/v8/src/scopes.cc @@ -31,7 +31,8 @@ #include "scopeinfo.h" #include "scopes.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // ---------------------------------------------------------------------------- // A Zone allocator for use with LocalsMap. @@ -112,7 +113,7 @@ Scope::Scope() locals_(false), temps_(0), params_(0), - nonlocals_(0), + dynamics_(NULL), unresolved_(0), decls_(0) { } @@ -123,10 +124,9 @@ Scope::Scope(Scope* outer_scope, Type type) inner_scopes_(4), type_(type), scope_name_(Factory::empty_symbol()), - locals_(), temps_(4), params_(4), - nonlocals_(4), + dynamics_(NULL), unresolved_(16), decls_(4), receiver_(NULL), @@ -302,6 +302,8 @@ template void Scope::CollectUsedVariables( List<Variable*, FreeStoreAllocationPolicy>* locals); template void Scope::CollectUsedVariables( List<Variable*, PreallocatedStorage>* locals); +template void Scope::CollectUsedVariables( + List<Variable*, ZoneListAllocationPolicy>* locals); void Scope::AllocateVariables(Handle<Context> context) { @@ -405,6 +407,14 @@ static void PrintVar(PrettyPrinter* printer, int indent, Variable* var) { } +static void PrintMap(PrettyPrinter* printer, int indent, LocalsMap* map) { + for (LocalsMap::Entry* p = map->Start(); p != NULL; p = map->Next(p)) { + Variable* var = reinterpret_cast<Variable*>(p->value); + PrintVar(printer, indent, var); + } +} + + void Scope::Print(int n) { int n0 = (n > 0 ? n : 0); int n1 = n0 + 2; // indentation @@ -465,14 +475,14 @@ void Scope::Print(int n) { } Indent(n1, "// local vars\n"); - for (LocalsMap::Entry* p = locals_.Start(); p != NULL; p = locals_.Next(p)) { - Variable* var = reinterpret_cast<Variable*>(p->value); - PrintVar(&printer, n1, var); - } + PrintMap(&printer, n1, &locals_); - Indent(n1, "// nonlocal vars\n"); - for (int i = 0; i < nonlocals_.length(); i++) - PrintVar(&printer, n1, nonlocals_[i]); + Indent(n1, "// dynamic vars\n"); + if (dynamics_ != NULL) { + PrintMap(&printer, n1, dynamics_->GetMap(Variable::DYNAMIC)); + PrintMap(&printer, n1, dynamics_->GetMap(Variable::DYNAMIC_LOCAL)); + PrintMap(&printer, n1, dynamics_->GetMap(Variable::DYNAMIC_GLOBAL)); + } // Print inner scopes (disable by providing negative n). if (n >= 0) { @@ -488,22 +498,15 @@ void Scope::Print(int n) { Variable* Scope::NonLocal(Handle<String> name, Variable::Mode mode) { - // Space optimization: reuse existing non-local with the same name - // and mode. - for (int i = 0; i < nonlocals_.length(); i++) { - Variable* var = nonlocals_[i]; - if (var->name().is_identical_to(name) && var->mode() == mode) { - return var; - } + if (dynamics_ == NULL) dynamics_ = new DynamicScopePart(); + LocalsMap* map = dynamics_->GetMap(mode); + Variable* var = map->Lookup(name); + if (var == NULL) { + // Declare a new non-local. + var = map->Declare(NULL, name, mode, true, false); + // Allocate it by giving it a dynamic lookup. + var->rewrite_ = new Slot(var, Slot::LOOKUP, -1); } - - // Otherwise create a new non-local and add it to the list. - Variable* var = new Variable(NULL, name, mode, true, false); - nonlocals_.Add(var); - - // Allocate it by giving it a dynamic lookup. - var->rewrite_ = new Slot(var, Slot::LOOKUP, -1); - return var; } @@ -617,14 +620,6 @@ void Scope::ResolveVariable(Scope* global_scope, ASSERT(global_scope != NULL); var = new Variable(global_scope, proxy->name(), Variable::DYNAMIC, true, false); - // Ideally we simply rewrite these variables into property - // accesses. Unfortunately, we cannot do this here at the - // moment because then we can't differentiate between - // global variable ('x') and global property ('this.x') access. - // If 'x' doesn't exist, the former leads to an error, while the - // latter returns undefined. Sigh... - // var->rewrite_ = new Property(new Literal(env_->global()), - // new Literal(proxy->name())); } else if (scope_inside_with_) { // If we are inside a with statement we give up and look up diff --git a/deps/v8/src/scopes.h b/deps/v8/src/scopes.h index e78bd2a2c1..b2f61ef660 100644 --- a/deps/v8/src/scopes.h +++ b/deps/v8/src/scopes.h @@ -31,11 +31,11 @@ #include "ast.h" #include "hashmap.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // A hash map to support fast local variable declaration and lookup. - class LocalsMap: public HashMap { public: LocalsMap(); @@ -53,6 +53,23 @@ class LocalsMap: public HashMap { }; +// The dynamic scope part holds hash maps for the variables that will +// be looked up dynamically from within eval and with scopes. The objects +// are allocated on-demand from Scope::NonLocal to avoid wasting memory +// and setup time for scopes that don't need them. +class DynamicScopePart : public ZoneObject { + public: + LocalsMap* GetMap(Variable::Mode mode) { + int index = mode - Variable::DYNAMIC; + ASSERT(index >= 0 && index < 3); + return &maps_[index]; + } + + private: + LocalsMap maps_[3]; +}; + + // Global invariants after AST construction: Each reference (i.e. identifier) // to a JavaScript variable (including global properties) is represented by a // VariableProxy node. Immediately after AST construction and before variable @@ -278,7 +295,7 @@ class Scope: public ZoneObject { // parameter list in source order ZoneList<Variable*> params_; // variables that must be looked up dynamically - ZoneList<Variable*> nonlocals_; + DynamicScopePart* dynamics_; // unresolved variables referred to from this scope ZoneList<VariableProxy*> unresolved_; // declarations diff --git a/deps/v8/src/serialize.cc b/deps/v8/src/serialize.cc index 62287bc0df..fb66d2785a 100644 --- a/deps/v8/src/serialize.cc +++ b/deps/v8/src/serialize.cc @@ -39,7 +39,8 @@ #include "stub-cache.h" #include "v8threads.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Encoding: a RelativeAddress must be able to fit in a pointer: // it is encoded as an Address with (from MS to LS bits): diff --git a/deps/v8/src/serialize.h b/deps/v8/src/serialize.h index f6594aca88..7f4eb63216 100644 --- a/deps/v8/src/serialize.h +++ b/deps/v8/src/serialize.h @@ -30,7 +30,8 @@ #include "hashmap.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // A TypeCode is used to distinguish different kinds of external reference. // It is a single bit to make testing for types easy. diff --git a/deps/v8/src/shell.h b/deps/v8/src/shell.h index 6712451287..ca510408cc 100644 --- a/deps/v8/src/shell.h +++ b/deps/v8/src/shell.h @@ -32,7 +32,8 @@ #include "../public/debug.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Debug event handler for interactive debugging. void handle_debug_event(v8::DebugEvent event, diff --git a/deps/v8/src/smart-pointer.h b/deps/v8/src/smart-pointer.h index c39df16869..0fa8224e71 100644 --- a/deps/v8/src/smart-pointer.h +++ b/deps/v8/src/smart-pointer.h @@ -28,7 +28,8 @@ #ifndef V8_SMART_POINTER_H_ #define V8_SMART_POINTER_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // A 'scoped array pointer' that calls DeleteArray on its pointer when the diff --git a/deps/v8/src/snapshot-common.cc b/deps/v8/src/snapshot-common.cc index 3aa1caec01..9c66a50374 100644 --- a/deps/v8/src/snapshot-common.cc +++ b/deps/v8/src/snapshot-common.cc @@ -33,7 +33,8 @@ #include "serialize.h" #include "snapshot.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { bool Snapshot::Deserialize(const byte* content, int len) { Deserializer des(content, len); diff --git a/deps/v8/src/snapshot-empty.cc b/deps/v8/src/snapshot-empty.cc index d4cda19c2d..60ab1e5647 100644 --- a/deps/v8/src/snapshot-empty.cc +++ b/deps/v8/src/snapshot-empty.cc @@ -31,7 +31,8 @@ #include "snapshot.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { const byte Snapshot::data_[] = { 0 }; int Snapshot::size_ = 0; diff --git a/deps/v8/src/snapshot.h b/deps/v8/src/snapshot.h index b3f23d3f71..88ba8db30e 100644 --- a/deps/v8/src/snapshot.h +++ b/deps/v8/src/snapshot.h @@ -28,7 +28,8 @@ #ifndef V8_SNAPSHOT_H_ #define V8_SNAPSHOT_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { class Snapshot { public: diff --git a/deps/v8/src/spaces-inl.h b/deps/v8/src/spaces-inl.h index 3973658472..2f01164f89 100644 --- a/deps/v8/src/spaces-inl.h +++ b/deps/v8/src/spaces-inl.h @@ -31,7 +31,8 @@ #include "memory.h" #include "spaces.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // ----------------------------------------------------------------------------- @@ -92,8 +93,10 @@ Address Page::AllocationTop() { void Page::ClearRSet() { +#ifndef V8_HOST_ARCH_64_BIT // This method can be called in all rset states. memset(RSetStart(), 0, kRSetEndOffset - kRSetStartOffset); +#endif } @@ -157,9 +160,14 @@ void Page::UnsetRSet(Address address, int offset) { bool Page::IsRSetSet(Address address, int offset) { +#ifdef V8_HOST_ARCH_64_BIT + // TODO(X64): Reenable when RSet works. + return true; +#else // V8_HOST_ARCH_64_BIT uint32_t bitmask = 0; Address rset_address = ComputeRSetBitPosition(address, offset, &bitmask); return (Memory::uint32_at(rset_address) & bitmask) != 0; +#endif // V8_HOST_ARCH_64_BIT } @@ -194,7 +202,7 @@ bool MemoryAllocator::IsPageInSpace(Page* p, PagedSpace* space) { Page* MemoryAllocator::GetNextPage(Page* p) { ASSERT(p->is_valid()); - int raw_addr = p->opaque_header & ~Page::kPageAlignmentMask; + intptr_t raw_addr = p->opaque_header & ~Page::kPageAlignmentMask; return Page::FromAddress(AddressFrom<Address>(raw_addr)); } @@ -207,7 +215,7 @@ int MemoryAllocator::GetChunkId(Page* p) { void MemoryAllocator::SetNextPage(Page* prev, Page* next) { ASSERT(prev->is_valid()); - int chunk_id = prev->opaque_header & Page::kPageAlignmentMask; + int chunk_id = GetChunkId(prev); ASSERT_PAGE_ALIGNED(next->address()); prev->opaque_header = OffsetFrom(next->address()) | chunk_id; } diff --git a/deps/v8/src/spaces.cc b/deps/v8/src/spaces.cc index f15af9e720..72b028cde6 100644 --- a/deps/v8/src/spaces.cc +++ b/deps/v8/src/spaces.cc @@ -31,7 +31,8 @@ #include "mark-compact.h" #include "platform.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // For contiguous spaces, top should be in the space (or at the end) and limit // should be the end of the space. @@ -121,6 +122,15 @@ PageIterator::PageIterator(PagedSpace* space, Mode mode) : space_(space) { stop_page_ = space->MCRelocationTopPage(); break; case ALL_PAGES: +#ifdef DEBUG + // Verify that the cached last page in the space is actually the + // last page. + for (Page* p = space->first_page_; p->is_valid(); p = p->next_page()) { + if (!p->next_page()->is_valid()) { + ASSERT(space->last_page_ == p); + } + } +#endif stop_page_ = space->last_page_; break; default: @@ -731,6 +741,7 @@ void PagedSpace::Shrink() { // Since pages are only freed in whole chunks, we may have kept more // than pages_to_keep. Count the extra pages and cache the new last // page in the space. + last_page_ = last_page_to_keep; while (p->is_valid()) { pages_to_keep++; last_page_ = p; @@ -1321,6 +1332,13 @@ int OldSpaceFreeList::Free(Address start, int size_in_bytes) { FreeListNode* node = FreeListNode::FromAddress(start); node->set_size(size_in_bytes); + // We don't use the freelists in compacting mode. This makes it more like a + // GC that only has mark-sweep-compact and doesn't have a mark-sweep + // collector. + if (FLAG_always_compact) { + return size_in_bytes; + } + // Early return to drop too-small blocks on the floor (one or two word // blocks cannot hold a map pointer, a size field, and a pointer to the // next block in the free list). @@ -1352,6 +1370,7 @@ Object* OldSpaceFreeList::Allocate(int size_in_bytes, int* wasted_bytes) { if ((free_[index].head_node_ = node->next()) == NULL) RemoveSize(index); available_ -= size_in_bytes; *wasted_bytes = 0; + ASSERT(!FLAG_always_compact); // We only use the freelists with mark-sweep. return node; } // Search the size list for the best fit. @@ -1363,6 +1382,7 @@ Object* OldSpaceFreeList::Allocate(int size_in_bytes, int* wasted_bytes) { *wasted_bytes = 0; return Failure::RetryAfterGC(size_in_bytes, owner_); } + ASSERT(!FLAG_always_compact); // We only use the freelists with mark-sweep. int rem = cur - index; int rem_bytes = rem << kPointerSizeLog2; FreeListNode* cur_node = FreeListNode::FromAddress(free_[cur].head_node_); @@ -1443,6 +1463,7 @@ void MapSpaceFreeList::Free(Address start) { Memory::Address_at(start + i) = kZapValue; } #endif + ASSERT(!FLAG_always_compact); // We only use the freelists with mark-sweep. FreeListNode* node = FreeListNode::FromAddress(start); node->set_size(Map::kSize); node->set_next(head_); @@ -1456,6 +1477,7 @@ Object* MapSpaceFreeList::Allocate() { return Failure::RetryAfterGC(Map::kSize, owner_); } + ASSERT(!FLAG_always_compact); // We only use the freelists with mark-sweep. FreeListNode* node = FreeListNode::FromAddress(head_); head_ = node->next(); available_ -= Map::kSize; @@ -2412,6 +2434,13 @@ void LargeObjectSpace::ClearRSet() { void LargeObjectSpace::IterateRSet(ObjectSlotCallback copy_object_func) { ASSERT(Page::is_rset_in_use()); + static void* lo_rset_histogram = StatsTable::CreateHistogram( + "V8.RSetLO", + 0, + // Keeping this histogram's buckets the same as the paged space histogram. + Page::kObjectAreaSize / kPointerSize, + 30); + LargeObjectIterator it(this); while (it.has_next()) { // We only have code, sequential strings, or fixed arrays in large @@ -2422,15 +2451,18 @@ void LargeObjectSpace::IterateRSet(ObjectSlotCallback copy_object_func) { // Iterate the normal page remembered set range. Page* page = Page::FromAddress(object->address()); Address object_end = object->address() + object->Size(); - Heap::IterateRSetRange(page->ObjectAreaStart(), - Min(page->ObjectAreaEnd(), object_end), - page->RSetStart(), - copy_object_func); + int count = Heap::IterateRSetRange(page->ObjectAreaStart(), + Min(page->ObjectAreaEnd(), object_end), + page->RSetStart(), + copy_object_func); // Iterate the extra array elements. if (object_end > page->ObjectAreaEnd()) { - Heap::IterateRSetRange(page->ObjectAreaEnd(), object_end, - object_end, copy_object_func); + count += Heap::IterateRSetRange(page->ObjectAreaEnd(), object_end, + object_end, copy_object_func); + } + if (lo_rset_histogram != NULL) { + StatsTable::AddHistogramSample(lo_rset_histogram, count); } } } diff --git a/deps/v8/src/spaces.h b/deps/v8/src/spaces.h index e8504a4276..a62b0a8d3e 100644 --- a/deps/v8/src/spaces.h +++ b/deps/v8/src/spaces.h @@ -31,7 +31,8 @@ #include "list-inl.h" #include "log.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // ----------------------------------------------------------------------------- // Heap structures: @@ -98,6 +99,7 @@ class AllocationInfo; // its page offset by 32. Therefore, the object area in a page starts at the // 256th byte (8K/32). Bytes 0 to 255 do not need the remembered set, so that // the first two words (64 bits) in a page can be used for other purposes. +// TODO(X64): This description only represents the 32-bit layout. // // The mark-compact collector transforms a map pointer into a page index and a // page offset. The map space can have up to 1024 pages, and 8M bytes (1024 * @@ -213,7 +215,7 @@ class Page { static const int kPageSize = 1 << kPageSizeBits; // Page size mask. - static const int kPageAlignmentMask = (1 << kPageSizeBits) - 1; + static const intptr_t kPageAlignmentMask = (1 << kPageSizeBits) - 1; // The end offset of the remembered set in a page // (heaps are aligned to pointer size). @@ -242,7 +244,7 @@ class Page { // in the current page. If a page is in the large object space, the first // word *may* (if the page start and large object chunk start are the // same) contain the address of the next large object chunk. - int opaque_header; + intptr_t opaque_header; // If the page is not in the large object space, the low-order bit of the // second word is set. If the page is in the large object space, the diff --git a/deps/v8/src/string-stream.cc b/deps/v8/src/string-stream.cc index 2e0912fbdb..44ba297461 100644 --- a/deps/v8/src/string-stream.cc +++ b/deps/v8/src/string-stream.cc @@ -30,7 +30,8 @@ #include "factory.h" #include "string-stream.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { static const int kMentionedObjectCacheMaxSize = 256; static List<HeapObject*, PreallocatedStorage>* debug_object_cache = NULL; diff --git a/deps/v8/src/string-stream.h b/deps/v8/src/string-stream.h index fa20064e4a..15a72e0f3a 100644 --- a/deps/v8/src/string-stream.h +++ b/deps/v8/src/string-stream.h @@ -28,7 +28,8 @@ #ifndef V8_STRING_STREAM_H_ #define V8_STRING_STREAM_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { class StringAllocator { diff --git a/deps/v8/src/string.js b/deps/v8/src/string.js index 0bcabc943b..df1f393e01 100644 --- a/deps/v8/src/string.js +++ b/deps/v8/src/string.js @@ -120,20 +120,26 @@ function StringIndexOf(searchString /* position */) { // length == 1 // ECMA-262 section 15.5.4.8 function StringLastIndexOf(searchString /* position */) { // length == 1 var sub = ToString(this); + var subLength = sub.length; var pat = ToString(searchString); - var index = (%_ArgumentsLength() > 1) - ? ToNumber(%_Arguments(1) /* position */) - : $NaN; - var firstIndex; - if ($isNaN(index)) { - firstIndex = sub.length - pat.length; - } else { - firstIndex = TO_INTEGER(index); - if (firstIndex + pat.length > sub.length) { - firstIndex = sub.length - pat.length; + var patLength = pat.length; + var index = subLength - patLength; + if (%_ArgumentsLength() > 1) { + var position = ToNumber(%_Arguments(1)); + if (!$isNaN(position)) { + position = TO_INTEGER(position); + if (position < 0) { + position = 0; + } + if (position + patLength < subLength) { + index = position + } } } - return %StringLastIndexOf(sub, pat, firstIndex); + if (index < 0) { + return -1; + } + return %StringLastIndexOf(sub, pat, index); } diff --git a/deps/v8/src/stub-cache.cc b/deps/v8/src/stub-cache.cc index 6811fd2e7c..f7e5456ef8 100644 --- a/deps/v8/src/stub-cache.cc +++ b/deps/v8/src/stub-cache.cc @@ -1,4 +1,4 @@ -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2006-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: @@ -32,7 +32,8 @@ #include "ic-inl.h" #include "stub-cache.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // ----------------------------------------------------------------------- // StubCache implementation. @@ -369,6 +370,7 @@ Object* StubCache::ComputeKeyedStoreField(String* name, JSObject* receiver, Object* StubCache::ComputeCallConstant(int argc, + InLoopFlag in_loop, String* name, Object* object, JSObject* holder, @@ -387,7 +389,10 @@ Object* StubCache::ComputeCallConstant(int argc, } Code::Flags flags = - Code::ComputeMonomorphicFlags(Code::CALL_IC, CONSTANT_FUNCTION, argc); + Code::ComputeMonomorphicFlags(Code::CALL_IC, + CONSTANT_FUNCTION, + in_loop, + argc); Object* code = map->FindInCodeCache(name, flags); if (code->IsUndefined()) { if (object->IsJSObject()) { @@ -405,7 +410,7 @@ Object* StubCache::ComputeCallConstant(int argc, if (!function->is_compiled()) return Failure::InternalError(); // Compile the stub - only create stubs for fully compiled functions. CallStubCompiler compiler(argc); - code = compiler.CompileCallConstant(object, holder, function, check); + code = compiler.CompileCallConstant(object, holder, function, check, flags); if (code->IsFailure()) return code; LOG(CodeCreateEvent("CallIC", Code::cast(code), name)); Object* result = map->UpdateCodeCache(name, Code::cast(code)); @@ -416,6 +421,7 @@ Object* StubCache::ComputeCallConstant(int argc, Object* StubCache::ComputeCallField(int argc, + InLoopFlag in_loop, String* name, Object* object, JSObject* holder, @@ -430,11 +436,14 @@ Object* StubCache::ComputeCallField(int argc, object = holder; } - Code::Flags flags = Code::ComputeMonomorphicFlags(Code::CALL_IC, FIELD, argc); + Code::Flags flags = Code::ComputeMonomorphicFlags(Code::CALL_IC, + FIELD, + in_loop, + argc); Object* code = map->FindInCodeCache(name, flags); if (code->IsUndefined()) { CallStubCompiler compiler(argc); - code = compiler.CompileCallField(object, holder, index, name); + code = compiler.CompileCallField(object, holder, index, name, flags); if (code->IsFailure()) return code; LOG(CodeCreateEvent("CallIC", Code::cast(code), name)); Object* result = map->UpdateCodeCache(name, Code::cast(code)); @@ -460,7 +469,10 @@ Object* StubCache::ComputeCallInterceptor(int argc, } Code::Flags flags = - Code::ComputeMonomorphicFlags(Code::CALL_IC, INTERCEPTOR, argc); + Code::ComputeMonomorphicFlags(Code::CALL_IC, + INTERCEPTOR, + NOT_IN_LOOP, + argc); Object* code = map->FindInCodeCache(name, flags); if (code->IsUndefined()) { CallStubCompiler compiler(argc); @@ -475,9 +487,10 @@ Object* StubCache::ComputeCallInterceptor(int argc, Object* StubCache::ComputeCallNormal(int argc, + InLoopFlag in_loop, String* name, JSObject* receiver) { - Object* code = ComputeCallNormal(argc); + Object* code = ComputeCallNormal(argc, in_loop); if (code->IsFailure()) return code; return Set(name, receiver->map(), Code::cast(code)); } @@ -522,9 +535,9 @@ static Object* FillCache(Object* code) { } -Code* StubCache::FindCallInitialize(int argc) { +Code* StubCache::FindCallInitialize(int argc, InLoopFlag in_loop) { Code::Flags flags = - Code::ComputeFlags(Code::CALL_IC, UNINITIALIZED, NORMAL, argc); + Code::ComputeFlags(Code::CALL_IC, in_loop, UNINITIALIZED, NORMAL, argc); Object* result = ProbeCache(flags); ASSERT(!result->IsUndefined()); // This might be called during the marking phase of the collector @@ -533,9 +546,9 @@ Code* StubCache::FindCallInitialize(int argc) { } -Object* StubCache::ComputeCallInitialize(int argc) { +Object* StubCache::ComputeCallInitialize(int argc, InLoopFlag in_loop) { Code::Flags flags = - Code::ComputeFlags(Code::CALL_IC, UNINITIALIZED, NORMAL, argc); + Code::ComputeFlags(Code::CALL_IC, in_loop, UNINITIALIZED, NORMAL, argc); Object* probe = ProbeCache(flags); if (!probe->IsUndefined()) return probe; StubCompiler compiler; @@ -543,20 +556,9 @@ Object* StubCache::ComputeCallInitialize(int argc) { } -Object* StubCache::ComputeCallInitializeInLoop(int argc) { +Object* StubCache::ComputeCallPreMonomorphic(int argc, InLoopFlag in_loop) { Code::Flags flags = - Code::ComputeFlags(Code::CALL_IC, UNINITIALIZED_IN_LOOP, NORMAL, argc); - Object* probe = ProbeCache(flags); - if (!probe->IsUndefined()) return probe; - StubCompiler compiler; - return FillCache(compiler.CompileCallInitialize(flags)); -} - - - -Object* StubCache::ComputeCallPreMonomorphic(int argc) { - Code::Flags flags = - Code::ComputeFlags(Code::CALL_IC, PREMONOMORPHIC, NORMAL, argc); + Code::ComputeFlags(Code::CALL_IC, in_loop, PREMONOMORPHIC, NORMAL, argc); Object* probe = ProbeCache(flags); if (!probe->IsUndefined()) return probe; StubCompiler compiler; @@ -564,9 +566,9 @@ Object* StubCache::ComputeCallPreMonomorphic(int argc) { } -Object* StubCache::ComputeCallNormal(int argc) { +Object* StubCache::ComputeCallNormal(int argc, InLoopFlag in_loop) { Code::Flags flags = - Code::ComputeFlags(Code::CALL_IC, MONOMORPHIC, NORMAL, argc); + Code::ComputeFlags(Code::CALL_IC, in_loop, MONOMORPHIC, NORMAL, argc); Object* probe = ProbeCache(flags); if (!probe->IsUndefined()) return probe; StubCompiler compiler; @@ -574,9 +576,9 @@ Object* StubCache::ComputeCallNormal(int argc) { } -Object* StubCache::ComputeCallMegamorphic(int argc) { +Object* StubCache::ComputeCallMegamorphic(int argc, InLoopFlag in_loop) { Code::Flags flags = - Code::ComputeFlags(Code::CALL_IC, MEGAMORPHIC, NORMAL, argc); + Code::ComputeFlags(Code::CALL_IC, in_loop, MEGAMORPHIC, NORMAL, argc); Object* probe = ProbeCache(flags); if (!probe->IsUndefined()) return probe; StubCompiler compiler; @@ -586,7 +588,7 @@ Object* StubCache::ComputeCallMegamorphic(int argc) { Object* StubCache::ComputeCallMiss(int argc) { Code::Flags flags = - Code::ComputeFlags(Code::STUB, MEGAMORPHIC, NORMAL, argc); + Code::ComputeFlags(Code::STUB, NOT_IN_LOOP, MEGAMORPHIC, NORMAL, argc); Object* probe = ProbeCache(flags); if (!probe->IsUndefined()) return probe; StubCompiler compiler; @@ -597,7 +599,7 @@ Object* StubCache::ComputeCallMiss(int argc) { #ifdef ENABLE_DEBUGGER_SUPPORT Object* StubCache::ComputeCallDebugBreak(int argc) { Code::Flags flags = - Code::ComputeFlags(Code::CALL_IC, DEBUG_BREAK, NORMAL, argc); + Code::ComputeFlags(Code::CALL_IC, NOT_IN_LOOP, DEBUG_BREAK, NORMAL, argc); Object* probe = ProbeCache(flags); if (!probe->IsUndefined()) return probe; StubCompiler compiler; @@ -607,7 +609,11 @@ Object* StubCache::ComputeCallDebugBreak(int argc) { Object* StubCache::ComputeCallDebugPrepareStepIn(int argc) { Code::Flags flags = - Code::ComputeFlags(Code::CALL_IC, DEBUG_PREPARE_STEP_IN, NORMAL, argc); + Code::ComputeFlags(Code::CALL_IC, + NOT_IN_LOOP, + DEBUG_PREPARE_STEP_IN, + NORMAL, + argc); Object* probe = ProbeCache(flags); if (!probe->IsUndefined()) return probe; StubCompiler compiler; @@ -618,7 +624,7 @@ Object* StubCache::ComputeCallDebugPrepareStepIn(int argc) { Object* StubCache::ComputeLazyCompile(int argc) { Code::Flags flags = - Code::ComputeFlags(Code::STUB, UNINITIALIZED, NORMAL, argc); + Code::ComputeFlags(Code::STUB, NOT_IN_LOOP, UNINITIALIZED, NORMAL, argc); Object* probe = ProbeCache(flags); if (!probe->IsUndefined()) return probe; StubCompiler compiler; @@ -713,10 +719,12 @@ Object* LoadInterceptorProperty(Arguments args) { JSObject* recv = JSObject::cast(args[0]); JSObject* holder = JSObject::cast(args[1]); String* name = String::cast(args[2]); + Smi* lookup_hint = Smi::cast(args[3]); ASSERT(holder->HasNamedInterceptor()); PropertyAttributes attr = NONE; - Object* result = holder->GetPropertyWithInterceptor(recv, name, &attr); + Object* result = holder->GetInterceptorPropertyWithLookupHint( + recv, lookup_hint, name, &attr); if (result->IsFailure()) return result; // If the property is present, return it. @@ -917,7 +925,10 @@ Object* KeyedStoreStubCompiler::GetCode(PropertyType type, String* name) { Object* CallStubCompiler::GetCode(PropertyType type, String* name) { int argc = arguments_.immediate(); - Code::Flags flags = Code::ComputeMonomorphicFlags(Code::CALL_IC, type, argc); + Code::Flags flags = Code::ComputeMonomorphicFlags(Code::CALL_IC, + type, + NOT_IN_LOOP, + argc); return GetCodeWithFlags(flags, name); } diff --git a/deps/v8/src/stub-cache.h b/deps/v8/src/stub-cache.h index 369b15da90..b79841a366 100644 --- a/deps/v8/src/stub-cache.h +++ b/deps/v8/src/stub-cache.h @@ -30,7 +30,8 @@ #include "macro-assembler.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // The stub cache is used for megamorphic calls and property accesses. @@ -127,18 +128,23 @@ class StubCache : public AllStatic { // --- static Object* ComputeCallField(int argc, + InLoopFlag in_loop, String* name, Object* object, JSObject* holder, int index); static Object* ComputeCallConstant(int argc, + InLoopFlag in_loop, String* name, Object* object, JSObject* holder, JSFunction* function); - static Object* ComputeCallNormal(int argc, String* name, JSObject* receiver); + static Object* ComputeCallNormal(int argc, + InLoopFlag in_loop, + String* name, + JSObject* receiver); static Object* ComputeCallInterceptor(int argc, String* name, @@ -147,15 +153,14 @@ class StubCache : public AllStatic { // --- - static Object* ComputeCallInitialize(int argc); - static Object* ComputeCallInitializeInLoop(int argc); - static Object* ComputeCallPreMonomorphic(int argc); - static Object* ComputeCallNormal(int argc); - static Object* ComputeCallMegamorphic(int argc); + static Object* ComputeCallInitialize(int argc, InLoopFlag in_loop); + static Object* ComputeCallPreMonomorphic(int argc, InLoopFlag in_loop); + static Object* ComputeCallNormal(int argc, InLoopFlag in_loop); + static Object* ComputeCallMegamorphic(int argc, InLoopFlag in_loop); static Object* ComputeCallMiss(int argc); // Finds the Code object stored in the Heap::non_monomorphic_cache(). - static Code* FindCallInitialize(int argc); + static Code* FindCallInitialize(int argc, InLoopFlag in_loop); #ifdef ENABLE_DEBUGGER_SUPPORT static Object* ComputeCallDebugBreak(int argc); @@ -208,8 +213,12 @@ class StubCache : public AllStatic { // 4Gb (and not at all if it isn't). uint32_t map_low32bits = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(map)); + // We always set the in_loop bit to zero when generating the lookup code + // so do it here too so the hash codes match. + uint32_t iflags = + (static_cast<uint32_t>(flags) & ~Code::kFlagsNotUsedInLookup); // Base the offset on a simple combination of name, flags, and map. - uint32_t key = (map_low32bits + field) ^ flags; + uint32_t key = (map_low32bits + field) ^ iflags; return key & ((kPrimaryTableSize - 1) << kHeapObjectTagSize); } @@ -217,7 +226,11 @@ class StubCache : public AllStatic { // Use the seed from the primary cache in the secondary cache. uint32_t string_low32bits = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name)); - uint32_t key = seed - string_low32bits + flags; + // We always set the in_loop bit to zero when generating the lookup code + // so do it here too so the hash codes match. + uint32_t iflags = + (static_cast<uint32_t>(flags) & ~Code::kFlagsICInLoopMask); + uint32_t key = seed - string_low32bits + iflags; return key & ((kSecondaryTableSize - 1) << kHeapObjectTagSize); } @@ -338,6 +351,7 @@ class StubCompiler BASE_EMBEDDED { static void GenerateLoadInterceptor(MacroAssembler* masm, JSObject* object, JSObject* holder, + Smi* lookup_hint, Register receiver, Register name, Register scratch1, @@ -468,11 +482,13 @@ class CallStubCompiler: public StubCompiler { Object* CompileCallField(Object* object, JSObject* holder, int index, - String* name); + String* name, + Code::Flags flags); Object* CompileCallConstant(Object* object, JSObject* holder, JSFunction* function, - CheckType check); + CheckType check, + Code::Flags flags); Object* CompileCallInterceptor(Object* object, JSObject* holder, String* name); diff --git a/deps/v8/src/token.cc b/deps/v8/src/token.cc index 3f92707d02..bb42cead4b 100644 --- a/deps/v8/src/token.cc +++ b/deps/v8/src/token.cc @@ -29,7 +29,8 @@ #include "token.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { #ifdef DEBUG #define T(name, string, precedence) #name, diff --git a/deps/v8/src/token.h b/deps/v8/src/token.h index 0f194a3c94..4d4df63458 100644 --- a/deps/v8/src/token.h +++ b/deps/v8/src/token.h @@ -28,7 +28,8 @@ #ifndef V8_TOKEN_H_ #define V8_TOKEN_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // TOKEN_LIST takes a list of 3 macros M, all of which satisfy the // same signature M(name, string, precedence), where name is the @@ -197,7 +198,7 @@ namespace v8 { namespace internal { T(ILLEGAL, "ILLEGAL", 0) \ \ /* Scanner-internal use only. */ \ - T(COMMENT, NULL, 0) + T(WHITESPACE, NULL, 0) class Token { diff --git a/deps/v8/src/top.cc b/deps/v8/src/top.cc index 96f98a5a24..42a2b7edfb 100644 --- a/deps/v8/src/top.cc +++ b/deps/v8/src/top.cc @@ -34,7 +34,8 @@ #include "string-stream.h" #include "platform.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { ThreadLocalTop Top::thread_local_; Mutex* Top::break_access_ = OS::CreateMutex(); @@ -44,6 +45,7 @@ NoAllocationStringAllocator* preallocated_message_space = NULL; Address top_addresses[] = { #define C(name) reinterpret_cast<Address>(Top::name()), TOP_ADDRESS_LIST(C) + TOP_ADDRESS_LIST_PROF(C) #undef C NULL }; @@ -90,6 +92,9 @@ void Top::Iterate(ObjectVisitor* v) { void Top::InitializeThreadLocal() { thread_local_.c_entry_fp_ = 0; thread_local_.handler_ = 0; +#ifdef ENABLE_LOGGING_AND_PROFILING + thread_local_.js_entry_sp_ = 0; +#endif thread_local_.stack_is_cooked_ = false; thread_local_.try_catch_handler_ = NULL; thread_local_.context_ = NULL; @@ -881,6 +886,15 @@ Handle<Context> Top::global_context() { } +Handle<Context> Top::GetCallingGlobalContext() { + JavaScriptFrameIterator it; + if (it.done()) return Handle<Context>::null(); + JavaScriptFrame* frame = it.frame(); + Context* context = Context::cast(frame->context()); + return Handle<Context>(context->global_context()); +} + + Object* Top::LookupSpecialFunction(JSObject* receiver, JSObject* prototype, JSFunction* function) { diff --git a/deps/v8/src/top.h b/deps/v8/src/top.h index 1e5ec5a8a7..53d67e555f 100644 --- a/deps/v8/src/top.h +++ b/deps/v8/src/top.h @@ -30,7 +30,8 @@ #include "frames-inl.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { #define RETURN_IF_SCHEDULED_EXCEPTION() \ @@ -64,6 +65,9 @@ class ThreadLocalTop BASE_EMBEDDED { // Stack. Address c_entry_fp_; // the frame pointer of the top c entry frame Address handler_; // try-blocks are chained through the stack +#ifdef ENABLE_LOGGING_AND_PROFILING + Address js_entry_sp_; // the stack pointer of the bottom js entry frame +#endif bool stack_is_cooked_; inline bool stack_is_cooked() { return stack_is_cooked_; } inline void set_stack_is_cooked(bool value) { stack_is_cooked_ = value; } @@ -82,11 +86,20 @@ class ThreadLocalTop BASE_EMBEDDED { C(pending_exception_address) \ C(external_caught_exception_address) +#ifdef ENABLE_LOGGING_AND_PROFILING +#define TOP_ADDRESS_LIST_PROF(C) \ + C(js_entry_sp_address) +#else +#define TOP_ADDRESS_LIST_PROF(C) +#endif + + class Top { public: enum AddressId { #define C(name) k_##name, TOP_ADDRESS_LIST(C) + TOP_ADDRESS_LIST_PROF(C) #undef C k_top_address_count }; @@ -178,6 +191,16 @@ class Top { } static inline Address* handler_address() { return &thread_local_.handler_; } +#ifdef ENABLE_LOGGING_AND_PROFILING + // Bottom JS entry (see StackTracer::Trace in log.cc). + static Address js_entry_sp(ThreadLocalTop* thread) { + return thread->js_entry_sp_; + } + static inline Address* js_entry_sp_address() { + return &thread_local_.js_entry_sp_; + } +#endif + // Generated code scratch locations. static void* formal_count_address() { return &thread_local_.formal_count_; } @@ -255,8 +278,13 @@ class Top { return context()->global_proxy(); } + // Returns the current global context. static Handle<Context> global_context(); + // Returns the global context of the calling JavaScript code. That + // is, the global context of the top-most JavaScript frame. + static Handle<Context> GetCallingGlobalContext(); + static Handle<JSBuiltinsObject> builtins() { return Handle<JSBuiltinsObject>(thread_local_.context_->builtins()); } diff --git a/deps/v8/src/usage-analyzer.cc b/deps/v8/src/usage-analyzer.cc index 13176f7a41..36464fa59b 100644 --- a/deps/v8/src/usage-analyzer.cc +++ b/deps/v8/src/usage-analyzer.cc @@ -31,7 +31,8 @@ #include "scopes.h" #include "usage-analyzer.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Weight boundaries static const int MinWeight = 1; @@ -444,6 +445,7 @@ WeightScaler::~WeightScaler() { bool AnalyzeVariableUsage(FunctionLiteral* lit) { if (!FLAG_usage_computation) return true; + HistogramTimerScope timer(&Counters::usage_analysis); return UsageComputer::Traverse(lit); } diff --git a/deps/v8/src/usage-analyzer.h b/deps/v8/src/usage-analyzer.h index 2369422b9c..1b0ea4a0fb 100644 --- a/deps/v8/src/usage-analyzer.h +++ b/deps/v8/src/usage-analyzer.h @@ -28,7 +28,8 @@ #ifndef V8_USAGE_ANALYZER_H_ #define V8_USAGE_ANALYZER_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Compute usage counts for all variables. // Used for variable allocation. diff --git a/deps/v8/src/utils.cc b/deps/v8/src/utils.cc index 392032021c..d56d279809 100644 --- a/deps/v8/src/utils.cc +++ b/deps/v8/src/utils.cc @@ -33,7 +33,8 @@ #include "sys/stat.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Implementation is from "Hacker's Delight" by Henry S. Warren, Jr., @@ -86,6 +87,20 @@ byte* EncodeUnsignedIntBackward(byte* p, unsigned int x) { } +// Thomas Wang, Integer Hash Functions. +// http://www.concentric.net/~Ttwang/tech/inthash.htm +uint32_t ComputeIntegerHash(uint32_t key) { + uint32_t hash = key; + hash = ~hash + (hash << 15); // hash = (hash << 15) - hash - 1; + hash = hash ^ (hash >> 12); + hash = hash + (hash << 2); + hash = hash ^ (hash >> 4); + hash = hash * 2057; // hash = (hash + (hash << 3)) + (hash << 11); + hash = hash ^ (hash >> 16); + return hash; +} + + void PrintF(const char* format, ...) { va_list arguments; va_start(arguments, format); diff --git a/deps/v8/src/utils.h b/deps/v8/src/utils.h index 0febe4a8d4..137e2c4f0c 100644 --- a/deps/v8/src/utils.h +++ b/deps/v8/src/utils.h @@ -30,7 +30,8 @@ #include <stdlib.h> -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // ---------------------------------------------------------------------------- // General helper functions @@ -42,8 +43,6 @@ static inline bool IsPowerOf2(T x) { } - - // The C++ standard leaves the semantics of '>>' undefined for // negative signed operands. Most implementations do the right thing, // though. @@ -56,7 +55,7 @@ static inline int ArithmeticShiftRight(int x, int s) { // This allows conversion of Addresses and integral types into // 0-relative int offsets. template <typename T> -static inline int OffsetFrom(T x) { +static inline intptr_t OffsetFrom(T x) { return x - static_cast<T>(0); } @@ -65,7 +64,7 @@ static inline int OffsetFrom(T x) { // This allows conversion of 0-relative int offsets into Addresses and // integral types. template <typename T> -static inline T AddressFrom(int x) { +static inline T AddressFrom(intptr_t x) { return static_cast<T>(0) + x; } @@ -207,6 +206,12 @@ inline byte* DecodeUnsignedIntBackward(byte* p, unsigned int* x) { // ---------------------------------------------------------------------------- +// Hash function. + +uint32_t ComputeIntegerHash(uint32_t key); + + +// ---------------------------------------------------------------------------- // I/O support. // Our version of printf(). Avoids compilation errors that we get @@ -374,6 +379,9 @@ class Vector { // Factory method for creating empty vectors. static Vector<T> empty() { return Vector<T>(NULL, 0); } + protected: + void set_start(T* start) { start_ = start; } + private: T* start_; int length_; @@ -401,6 +409,22 @@ template <typename T, int kSize> class EmbeddedVector : public Vector<T> { public: EmbeddedVector() : Vector<T>(buffer_, kSize) { } + + // When copying, make underlying Vector to reference our buffer. + EmbeddedVector(const EmbeddedVector& rhs) + : Vector<T>(rhs) { + memcpy(buffer_, rhs.buffer_, sizeof(T) * kSize); + set_start(buffer_); + } + + EmbeddedVector& operator=(const EmbeddedVector& rhs) { + if (this == &rhs) return *this; + Vector<T>::operator=(rhs); + memcpy(buffer_, rhs.buffer_, sizeof(T) * kSize); + set_start(buffer_); + return *this; + } + private: T buffer_[kSize]; }; diff --git a/deps/v8/src/v8-counters.cc b/deps/v8/src/v8-counters.cc index 3a8286a649..de2ce66954 100644 --- a/deps/v8/src/v8-counters.cc +++ b/deps/v8/src/v8-counters.cc @@ -29,7 +29,8 @@ #include "v8-counters.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { #define HT(name, caption) \ HistogramTimer Counters::name = { #caption, NULL, false, 0, 0 }; \ diff --git a/deps/v8/src/v8-counters.h b/deps/v8/src/v8-counters.h index 34156ea6f0..4111312ea0 100644 --- a/deps/v8/src/v8-counters.h +++ b/deps/v8/src/v8-counters.h @@ -30,18 +30,30 @@ #include "counters.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { -#define HISTOGRAM_TIMER_LIST(HT) \ - HT(gc_compactor, V8.GCCompactor) /* GC Compactor time */ \ - HT(gc_scavenger, V8.GCScavenger) /* GC Scavenger time */ \ - HT(gc_context, V8.GCContext) /* GC context cleanup time */ \ - HT(compile, V8.Compile) /* Compile time*/ \ - HT(compile_eval, V8.CompileEval) /* Eval compile time */ \ - HT(compile_lazy, V8.CompileLazy) /* Lazy compile time */ \ - HT(parse, V8.Parse) /* Parse time */ \ - HT(parse_lazy, V8.ParseLazy) /* Lazy parse time */ \ - HT(pre_parse, V8.PreParse) /* Pre-parse time */ +#define HISTOGRAM_TIMER_LIST(HT) \ + /* Garbage collection timers. */ \ + HT(gc_compactor, V8.GCCompactor) \ + HT(gc_scavenger, V8.GCScavenger) \ + HT(gc_context, V8.GCContext) /* GC context cleanup time */ \ + /* Parsing timers. */ \ + HT(parse, V8.Parse) \ + HT(parse_lazy, V8.ParseLazy) \ + HT(pre_parse, V8.PreParse) \ + /* Total compilation times. */ \ + HT(compile, V8.Compile) \ + HT(compile_eval, V8.CompileEval) \ + HT(compile_lazy, V8.CompileLazy) \ + /* Individual compiler passes. */ \ + HT(rewriting, V8.Rewriting) \ + HT(usage_analysis, V8.UsageAnalysis) \ + HT(variable_allocation, V8.VariableAllocation) \ + HT(ast_optimization, V8.ASTOptimization) \ + HT(code_generation, V8.CodeGeneration) \ + HT(deferred_code_generation, V8.DeferredCodeGeneration) \ + HT(code_creation, V8.CodeCreation) // WARNING: STATS_COUNTER_LIST_* is a very large macro that is causing MSVC // Intellisense to crash. It was broken into two macros (each of length 40 @@ -124,7 +136,8 @@ namespace v8 { namespace internal { SC(enum_cache_misses, V8.EnumCacheMisses) \ SC(reloc_info_count, V8.RelocInfoCount) \ SC(reloc_info_size, V8.RelocInfoSize) \ - SC(zone_segment_bytes, V8.ZoneSegmentBytes) + SC(zone_segment_bytes, V8.ZoneSegmentBytes) \ + SC(compute_entry_frame, V8.ComputeEntryFrame) // This file contains all the v8 counters that are in use. diff --git a/deps/v8/src/v8.cc b/deps/v8/src/v8.cc index c0124e4de0..17cb2dfe78 100644 --- a/deps/v8/src/v8.cc +++ b/deps/v8/src/v8.cc @@ -33,16 +33,23 @@ #include "stub-cache.h" #include "oprofile-agent.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { +bool V8::is_running_ = false; bool V8::has_been_setup_ = false; bool V8::has_been_disposed_ = false; +bool V8::has_fatal_error_ = false; bool V8::Initialize(Deserializer *des) { bool create_heap_objects = des == NULL; - if (HasBeenDisposed()) return false; - if (HasBeenSetup()) return true; + if (has_been_disposed_ || has_fatal_error_) return false; + if (IsRunning()) return true; + + is_running_ = true; has_been_setup_ = true; + has_fatal_error_ = false; + has_been_disposed_ = false; #ifdef DEBUG // The initialization process does not handle memory exhaustion. DisallowAllocationFailure disallow_allocation_failure; @@ -58,7 +65,7 @@ bool V8::Initialize(Deserializer *des) { // Setup the object heap ASSERT(!Heap::HasBeenSetup()); if (!Heap::Setup(create_heap_objects)) { - has_been_setup_ = false; + SetFatalError(); return false; } @@ -94,9 +101,14 @@ bool V8::Initialize(Deserializer *des) { } +void V8::SetFatalError() { + is_running_ = false; + has_fatal_error_ = true; +} + + void V8::TearDown() { - if (HasBeenDisposed()) return; - if (!HasBeenSetup()) return; + if (!has_been_setup_ || has_been_disposed_) return; OProfileAgent::TearDown(); @@ -113,8 +125,9 @@ void V8::TearDown() { Heap::TearDown(); Logger::TearDown(); - has_been_setup_ = false; + is_running_ = false; has_been_disposed_ = true; } + } } // namespace v8::internal diff --git a/deps/v8/src/v8.h b/deps/v8/src/v8.h index 4ced0d2b46..8cb3c7da16 100644 --- a/deps/v8/src/v8.h +++ b/deps/v8/src/v8.h @@ -73,7 +73,8 @@ #include "heap-inl.h" #include "messages.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { class V8 : public AllStatic { public: @@ -85,13 +86,23 @@ class V8 : public AllStatic { // deserialized data into an empty heap. static bool Initialize(Deserializer* des); static void TearDown(); - static bool HasBeenSetup() { return has_been_setup_; } - static bool HasBeenDisposed() { return has_been_disposed_; } + static bool IsRunning() { return is_running_; } + // To be dead you have to have lived + static bool IsDead() { return has_fatal_error_ || has_been_disposed_; } + static void SetFatalError(); // Report process out of memory. Implementation found in api.cc. static void FatalProcessOutOfMemory(const char* location); private: + // True if engine is currently running + static bool is_running_; + // True if V8 has ever been run static bool has_been_setup_; + // True if error has been signaled for current engine + // (reset to false if engine is restarted) + static bool has_fatal_error_; + // True if engine has been shut down + // (reset if engine is restarted) static bool has_been_disposed_; }; diff --git a/deps/v8/src/v8natives.js b/deps/v8/src/v8natives.js index 55bc9f8fff..fe463513d9 100644 --- a/deps/v8/src/v8natives.js +++ b/deps/v8/src/v8natives.js @@ -115,12 +115,16 @@ function GlobalParseFloat(string) { function GlobalEval(x) { if (!IS_STRING(x)) return x; - if (this !== global && this !== %GlobalReceiver(global)) { - throw new $EvalError('The "this" object passed to eval must ' + + var global_receiver = %GlobalReceiver(global); + var this_is_global_receiver = (this === global_receiver); + var global_is_detached = (global === global_receiver); + + if (!this_is_global_receiver || global_is_detached) { + throw new $EvalError('The "this" object passed to eval must ' + 'be the global object from which eval originated'); } - - var f = %CompileString(x, 0, false); + + var f = %CompileString(x, false); if (!IS_FUNCTION(f)) return f; return f.call(this); @@ -131,7 +135,7 @@ function GlobalEval(x) { function GlobalExecScript(expr, lang) { // NOTE: We don't care about the character casing. if (!lang || /javascript/i.test(lang)) { - var f = %CompileString(ToString(expr), 0, false); + var f = %CompileString(ToString(expr), false); f.call(%GlobalReceiver(global)); } return null; @@ -550,7 +554,7 @@ function NewFunction(arg1) { // length == 1 // The call to SetNewFunctionAttributes will ensure the prototype // property of the resulting function is enumerable (ECMA262, 15.3.5.2). - var f = %CompileString(source, -1, false)(); + var f = %CompileString(source, false)(); %FunctionSetName(f, "anonymous"); return %SetNewFunctionAttributes(f); } diff --git a/deps/v8/src/v8threads.cc b/deps/v8/src/v8threads.cc index 838cae772d..c5fc9fa7e6 100644 --- a/deps/v8/src/v8threads.cc +++ b/deps/v8/src/v8threads.cc @@ -261,6 +261,8 @@ void ThreadManager::EagerlyArchiveThread() { ThreadState* state = lazily_archived_thread_state_; state->LinkInto(ThreadState::IN_USE_LIST); char* to = state->data(); + // Ensure that data containing GC roots are archived first, and handle them + // in ThreadManager::Iterate(ObjectVisitor*). to = HandleScopeImplementer::ArchiveThread(to); to = Top::ArchiveThread(to); #ifdef ENABLE_DEBUGGER_SUPPORT diff --git a/deps/v8/src/v8threads.h b/deps/v8/src/v8threads.h index b651fc3444..83f69f060e 100644 --- a/deps/v8/src/v8threads.h +++ b/deps/v8/src/v8threads.h @@ -28,7 +28,8 @@ #ifndef V8_V8THREADS_H_ #define V8_V8THREADS_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { class ThreadState { diff --git a/deps/v8/src/variables.cc b/deps/v8/src/variables.cc index 51eb8caf8e..6c9f82f080 100644 --- a/deps/v8/src/variables.cc +++ b/deps/v8/src/variables.cc @@ -31,7 +31,8 @@ #include "scopes.h" #include "variables.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // ---------------------------------------------------------------------------- // Implementation UseCount. diff --git a/deps/v8/src/variables.h b/deps/v8/src/variables.h index 275f498e82..50620718cf 100644 --- a/deps/v8/src/variables.h +++ b/deps/v8/src/variables.h @@ -30,7 +30,8 @@ #include "zone.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { class UseCount BASE_EMBEDDED { public: diff --git a/deps/v8/src/version.cc b/deps/v8/src/version.cc index 0d2840d44e..d613e940d3 100644 --- a/deps/v8/src/version.cc +++ b/deps/v8/src/version.cc @@ -34,7 +34,7 @@ // cannot be changed without changing the SCons build script. #define MAJOR_VERSION 1 #define MINOR_VERSION 2 -#define BUILD_NUMBER 3 +#define BUILD_NUMBER 7 #define PATCH_LEVEL 0 #define CANDIDATE_VERSION false @@ -43,7 +43,8 @@ // number. This define is mainly used by the SCons build script. #define SONAME "" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { int Version::major_ = MAJOR_VERSION; int Version::minor_ = MINOR_VERSION; diff --git a/deps/v8/src/version.h b/deps/v8/src/version.h index 423b5f7a11..c322a2fc02 100644 --- a/deps/v8/src/version.h +++ b/deps/v8/src/version.h @@ -28,7 +28,8 @@ #ifndef V8_VERSION_H_ #define V8_VERSION_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { class Version { public: diff --git a/deps/v8/src/virtual-frame.cc b/deps/v8/src/virtual-frame.cc index 566fcdbc0e..39dbf17350 100644 --- a/deps/v8/src/virtual-frame.cc +++ b/deps/v8/src/virtual-frame.cc @@ -30,47 +30,27 @@ #include "codegen-inl.h" #include "register-allocator-inl.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // ------------------------------------------------------------------------- // VirtualFrame implementation. -VirtualFrame::SpilledScope::SpilledScope(CodeGenerator* cgen) - : cgen_(cgen), - previous_state_(cgen->in_spilled_code()) { - ASSERT(cgen->has_valid_frame()); - cgen->frame()->SpillAll(); - cgen->set_in_spilled_code(true); -} - - -VirtualFrame::SpilledScope::~SpilledScope() { - cgen_->set_in_spilled_code(previous_state_); -} - - // When cloned, a frame is a deep copy of the original. VirtualFrame::VirtualFrame(VirtualFrame* original) - : cgen_(original->cgen_), - masm_(original->masm_), - elements_(original->elements_.capacity()), - parameter_count_(original->parameter_count_), - local_count_(original->local_count_), - stack_pointer_(original->stack_pointer_), - frame_pointer_(original->frame_pointer_) { - // Copy all the elements from the original. - for (int i = 0; i < original->elements_.length(); i++) { - elements_.Add(original->elements_[i]); - } - for (int i = 0; i < kNumRegisters; i++) { - register_locations_[i] = original->register_locations_[i]; - } + : elements_(original->element_count()), + stack_pointer_(original->stack_pointer_) { + elements_.AddAll(original->elements_); + // Copy register locations from original. + memcpy(®ister_locations_, + original->register_locations_, + sizeof(register_locations_)); } FrameElement VirtualFrame::CopyElementAt(int index) { ASSERT(index >= 0); - ASSERT(index < elements_.length()); + ASSERT(index < element_count()); FrameElement target = elements_[index]; FrameElement result; @@ -94,10 +74,10 @@ FrameElement VirtualFrame::CopyElementAt(int index) { case FrameElement::REGISTER: // All copies are backed by memory or register locations. result.set_static_type(target.static_type()); - result.type_ = FrameElement::COPY; - result.copied_ = false; - result.synced_ = false; - result.data_.index_ = index; + result.set_type(FrameElement::COPY); + result.clear_copied(); + result.clear_sync(); + result.set_index(index); elements_[index].set_copied(); break; @@ -116,7 +96,7 @@ FrameElement VirtualFrame::CopyElementAt(int index) { // pushing an exception handler). No code is emitted. void VirtualFrame::Adjust(int count) { ASSERT(count >= 0); - ASSERT(stack_pointer_ == elements_.length() - 1); + ASSERT(stack_pointer_ == element_count() - 1); for (int i = 0; i < count; i++) { elements_.Add(FrameElement::MemoryElement()); @@ -125,22 +105,9 @@ void VirtualFrame::Adjust(int count) { } -// Modify the state of the virtual frame to match the actual frame by -// removing elements from the top of the virtual frame. The elements will -// be externally popped from the actual frame (eg, by a runtime call). No -// code is emitted. -void VirtualFrame::Forget(int count) { - ASSERT(count >= 0); - ASSERT(stack_pointer_ == elements_.length() - 1); - - stack_pointer_ -= count; - ForgetElements(count); -} - - void VirtualFrame::ForgetElements(int count) { ASSERT(count >= 0); - ASSERT(elements_.length() >= count); + ASSERT(element_count() >= count); for (int i = 0; i < count; i++) { FrameElement last = elements_.RemoveLast(); @@ -148,47 +115,25 @@ void VirtualFrame::ForgetElements(int count) { // A hack to properly count register references for the code // generator's current frame and also for other frames. The // same code appears in PrepareMergeTo. - if (cgen_->frame() == this) { + if (cgen()->frame() == this) { Unuse(last.reg()); } else { - register_locations_[last.reg().code()] = kIllegalIndex; + set_register_location(last.reg(), kIllegalIndex); } } } } -void VirtualFrame::Use(Register reg, int index) { - ASSERT(register_locations_[reg.code()] == kIllegalIndex); - register_locations_[reg.code()] = index; - cgen_->allocator()->Use(reg); -} - - -void VirtualFrame::Unuse(Register reg) { - ASSERT(register_locations_[reg.code()] != kIllegalIndex); - register_locations_[reg.code()] = kIllegalIndex; - cgen_->allocator()->Unuse(reg); -} - - -void VirtualFrame::Spill(Register target) { - if (is_used(target)) { - SpillElementAt(register_index(target)); - } -} - - // If there are any registers referenced only by the frame, spill one. Register VirtualFrame::SpillAnyRegister() { - // Find the leftmost (ordered by register code) register whose only + // Find the leftmost (ordered by register number) register whose only // reference is in the frame. - for (int i = 0; i < kNumRegisters; i++) { - if (is_used(i) && cgen_->allocator()->count(i) == 1) { - Register result = { i }; - Spill(result); - ASSERT(!cgen_->allocator()->is_used(result)); - return result; + for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) { + if (is_used(i) && cgen()->allocator()->count(i) == 1) { + SpillElementAt(register_location(i)); + ASSERT(!cgen()->allocator()->is_used(i)); + return RegisterAllocator::ToRegister(i); } } return no_reg; @@ -227,7 +172,7 @@ void VirtualFrame::SyncElementAt(int index) { // Make the type of all elements be MEMORY. void VirtualFrame::SpillAll() { - for (int i = 0; i < elements_.length(); i++) { + for (int i = 0; i < element_count(); i++) { SpillElementAt(i); } } @@ -237,7 +182,7 @@ void VirtualFrame::PrepareMergeTo(VirtualFrame* expected) { // Perform state changes on this frame that will make merge to the // expected frame simpler or else increase the likelihood that his // frame will match another. - for (int i = 0; i < elements_.length(); i++) { + for (int i = 0; i < element_count(); i++) { FrameElement source = elements_[i]; FrameElement target = expected->elements_[i]; @@ -251,10 +196,10 @@ void VirtualFrame::PrepareMergeTo(VirtualFrame* expected) { // If the frame is the code generator's current frame, we have // to decrement both the frame-internal and global register // counts. - if (cgen_->frame() == this) { + if (cgen()->frame() == this) { Unuse(source.reg()); } else { - register_locations_[source.reg().code()] = kIllegalIndex; + set_register_location(source.reg(), kIllegalIndex); } } elements_[i] = target; @@ -266,12 +211,6 @@ void VirtualFrame::PrepareMergeTo(VirtualFrame* expected) { ASSERT(source.is_valid()); elements_[i].clear_sync(); } - - elements_[i].clear_copied(); - if (elements_[i].is_copy()) { - elements_[elements_[i].index()].set_copied(); - } - // No code needs to be generated to change the static type of an // element. elements_[i].set_static_type(target.static_type()); @@ -284,16 +223,16 @@ void VirtualFrame::PrepareForCall(int spilled_args, int dropped_args) { ASSERT(height() >= spilled_args); ASSERT(dropped_args <= spilled_args); - SyncRange(0, elements_.length() - 1); + SyncRange(0, element_count() - 1); // Spill registers. - for (int i = 0; i < kNumRegisters; i++) { + for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) { if (is_used(i)) { - SpillElementAt(register_locations_[i]); + SpillElementAt(register_location(i)); } } // Spill the arguments. - for (int i = elements_.length() - spilled_args; i < elements_.length(); i++) { + for (int i = element_count() - spilled_args; i < element_count(); i++) { if (!elements_[i].is_memory()) { SpillElementAt(i); } @@ -304,55 +243,32 @@ void VirtualFrame::PrepareForCall(int spilled_args, int dropped_args) { } -void VirtualFrame::DetachFromCodeGenerator() { - // Tell the global register allocator that it is free to reallocate all - // register references contained in this frame. The frame elements remain - // register references, so the frame-internal reference count is not - // decremented. - for (int i = 0; i < elements_.length(); i++) { - if (elements_[i].is_register()) { - cgen_->allocator()->Unuse(elements_[i].reg()); - } - } -} - - -void VirtualFrame::AttachToCodeGenerator() { - // Tell the global register allocator that the frame-internal register - // references are live again. - for (int i = 0; i < elements_.length(); i++) { - if (elements_[i].is_register()) { - cgen_->allocator()->Use(elements_[i].reg()); - } - } -} - - void VirtualFrame::PrepareForReturn() { // Spill all locals. This is necessary to make sure all locals have // the right value when breaking at the return site in the debugger. - // - // TODO(203): It is also necessary to ensure that merging at the - // return site does not generate code to overwrite eax, where the - // return value is kept in a non-refcounted register reference. - for (int i = 0; i < expression_base_index(); i++) SpillElementAt(i); + // Set their static type to unknown so that they will match the known + // return frame. + for (int i = 0; i < expression_base_index(); i++) { + SpillElementAt(i); + elements_[i].set_static_type(StaticType::unknown()); + } } void VirtualFrame::SetElementAt(int index, Result* value) { - int frame_index = elements_.length() - index - 1; + int frame_index = element_count() - index - 1; ASSERT(frame_index >= 0); - ASSERT(frame_index < elements_.length()); + ASSERT(frame_index < element_count()); ASSERT(value->is_valid()); FrameElement original = elements_[frame_index]; // Early exit if the element is the same as the one being set. bool same_register = original.is_register() - && value->is_register() - && original.reg().is(value->reg()); + && value->is_register() + && original.reg().is(value->reg()); bool same_constant = original.is_constant() - && value->is_constant() - && original.handle().is_identical_to(value->handle()); + && value->is_constant() + && original.handle().is_identical_to(value->handle()); if (same_register || same_constant) { value->Unuse(); return; @@ -366,7 +282,7 @@ void VirtualFrame::SetElementAt(int index, Result* value) { // The register already appears on the frame. Either the existing // register element, or the new element at frame_index, must be made // a copy. - int i = register_index(value->reg()); + int i = register_location(value->reg()); ASSERT(value->static_type() == elements_[i].static_type()); if (i < frame_index) { @@ -382,8 +298,8 @@ void VirtualFrame::SetElementAt(int index, Result* value) { elements_[i].set_sync(); } elements_[frame_index].clear_sync(); - register_locations_[value->reg().code()] = frame_index; - for (int j = i + 1; j < elements_.length(); j++) { + set_register_location(value->reg(), frame_index); + for (int j = i + 1; j < element_count(); j++) { if (elements_[j].is_copy() && elements_[j].index() == i) { elements_[j].set_index(frame_index); } @@ -408,25 +324,18 @@ void VirtualFrame::SetElementAt(int index, Result* value) { void VirtualFrame::PushFrameSlotAt(int index) { - FrameElement new_element = CopyElementAt(index); - elements_.Add(new_element); -} - - -Result VirtualFrame::CallStub(CodeStub* stub, int arg_count) { - PrepareForCall(arg_count, arg_count); - return RawCallStub(stub); + elements_.Add(CopyElementAt(index)); } void VirtualFrame::Push(Register reg, StaticType static_type) { if (is_used(reg)) { - int index = register_index(reg); + int index = register_location(reg); FrameElement element = CopyElementAt(index); ASSERT(static_type.merge(element.static_type()) == element.static_type()); elements_.Add(element); } else { - Use(reg, elements_.length()); + Use(reg, element_count()); FrameElement element = FrameElement::RegisterElement(reg, FrameElement::NOT_SYNCED, @@ -443,17 +352,6 @@ void VirtualFrame::Push(Handle<Object> value) { } -void VirtualFrame::Push(Result* result) { - if (result->is_register()) { - Push(result->reg(), result->static_type()); - } else { - ASSERT(result->is_constant()); - Push(result->handle()); - } - result->Unuse(); -} - - void VirtualFrame::Nip(int num_dropped) { ASSERT(num_dropped >= 0); if (num_dropped == 0) return; @@ -465,42 +363,17 @@ void VirtualFrame::Nip(int num_dropped) { } -bool FrameElement::Equals(FrameElement other) { - if (type_ != other.type_ || - copied_ != other.copied_ || - synced_ != other.synced_) return false; - - if (is_register()) { - if (!reg().is(other.reg())) return false; - } else if (is_constant()) { - if (!handle().is_identical_to(other.handle())) return false; - } else if (is_copy()) { - if (index() != other.index()) return false; - } - - return true; -} - - bool VirtualFrame::Equals(VirtualFrame* other) { #ifdef DEBUG - // These are sanity checks in debug builds, but we do not need to - // use them to distinguish frames at merge points. - if (cgen_ != other->cgen_) return false; - if (masm_ != other->masm_) return false; - if (parameter_count_ != other->parameter_count_) return false; - if (local_count_ != other->local_count_) return false; - if (frame_pointer_ != other->frame_pointer_) return false; - - for (int i = 0; i < kNumRegisters; i++) { - if (register_locations_[i] != other->register_locations_[i]) { + for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) { + if (register_location(i) != other->register_location(i)) { return false; } } - if (elements_.length() != other->elements_.length()) return false; + if (element_count() != other->element_count()) return false; #endif if (stack_pointer_ != other->stack_pointer_) return false; - for (int i = 0; i < elements_.length(); i++) { + for (int i = 0; i < element_count(); i++) { if (!elements_[i].Equals(other->elements_[i])) return false; } diff --git a/deps/v8/src/virtual-frame.h b/deps/v8/src/virtual-frame.h index 794f1567c3..293f9e534f 100644 --- a/deps/v8/src/virtual-frame.h +++ b/deps/v8/src/virtual-frame.h @@ -28,180 +28,9 @@ #ifndef V8_VIRTUAL_FRAME_H_ #define V8_VIRTUAL_FRAME_H_ +#include "frame-element.h" #include "macro-assembler.h" -namespace v8 { namespace internal { - -// ------------------------------------------------------------------------- -// Virtual frame elements -// -// The internal elements of the virtual frames. There are several kinds of -// elements: -// * Invalid: elements that are uninitialized or not actually part -// of the virtual frame. They should not be read. -// * 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 { - NOT_SYNCED, - SYNCED - }; - - // The default constructor creates an invalid frame element. - FrameElement() - : static_type_(), type_(INVALID), copied_(false), synced_(false) { - data_.reg_ = no_reg; - } - - // Factory function to construct an invalid frame element. - static FrameElement InvalidElement() { - FrameElement result; - return result; - } - - // Factory function to construct an in-memory frame element. - static FrameElement MemoryElement() { - FrameElement result(MEMORY, no_reg, SYNCED); - return result; - } - - // Factory function to construct an in-register frame element. - static FrameElement RegisterElement(Register reg, - SyncFlag is_synced, - StaticType static_type = StaticType()) { - return FrameElement(REGISTER, reg, is_synced, static_type); - } - - // Factory function to construct a frame element whose value is known at - // compile time. - static FrameElement ConstantElement(Handle<Object> value, - SyncFlag is_synced) { - FrameElement result(value, is_synced); - return result; - } - - bool is_synced() const { return synced_; } - - void set_sync() { - ASSERT(type() != MEMORY); - synced_ = true; - } - - void clear_sync() { - ASSERT(type() != MEMORY); - synced_ = false; - } - - bool is_valid() const { return type() != INVALID; } - bool is_memory() const { return type() == MEMORY; } - bool is_register() const { return type() == REGISTER; } - bool is_constant() const { return type() == CONSTANT; } - bool is_copy() const { return type() == COPY; } - - bool is_copied() const { return copied_; } - void set_copied() { copied_ = true; } - void clear_copied() { copied_ = false; } - - Register reg() const { - ASSERT(is_register()); - return data_.reg_; - } - - Handle<Object> handle() const { - ASSERT(is_constant()); - return Handle<Object>(data_.handle_); - } - - int index() const { - ASSERT(is_copy()); - return data_.index_; - } - - bool Equals(FrameElement other); - - StaticType static_type() { return static_type_; } - - void set_static_type(StaticType static_type) { - // TODO(lrn): If it's s copy, it would be better to update the real one, - // but we can't from here. The caller must handle this. - static_type_ = static_type; - } - - private: - enum Type { - INVALID, - MEMORY, - REGISTER, - CONSTANT, - COPY - }; - - Type type() const { return static_cast<Type>(type_); } - - StaticType static_type_; - - // The element's type. - byte type_; - - bool copied_; - - // The element's dirty-bit. The dirty bit can be cleared - // for non-memory elements to indicate that the element agrees with - // the value in memory in the actual frame. - bool synced_; - - union { - Register reg_; - Object** handle_; - int index_; - } data_; - - // Used to construct memory and register elements. - FrameElement(Type type, Register reg, SyncFlag is_synced) - : static_type_(), - type_(type), - copied_(false), - synced_(is_synced != NOT_SYNCED) { - data_.reg_ = reg; - } - - FrameElement(Type type, Register reg, SyncFlag is_synced, StaticType stype) - : static_type_(stype), - type_(type), - copied_(false), - synced_(is_synced != NOT_SYNCED) { - data_.reg_ = reg; - } - - // Used to construct constant elements. - FrameElement(Handle<Object> value, SyncFlag is_synced) - : static_type_(StaticType::TypeOf(*value)), - type_(CONSTANT), - copied_(false), - synced_(is_synced != NOT_SYNCED) { - data_.handle_ = value.location(); - } - - void set_index(int new_index) { - ASSERT(is_copy()); - data_.index_ = new_index; - } - - void set_reg(Register new_reg) { - ASSERT(is_register()); - data_.reg_ = new_reg; - } - - friend class VirtualFrame; -}; - - -} } // namespace v8::internal - #if V8_TARGET_ARCH_IA32 #include "ia32/virtual-frame-ia32.h" #elif V8_TARGET_ARCH_X64 diff --git a/deps/v8/src/x64/assembler-x64-inl.h b/deps/v8/src/x64/assembler-x64-inl.h index 0b018490db..18225681ed 100644 --- a/deps/v8/src/x64/assembler-x64-inl.h +++ b/deps/v8/src/x64/assembler-x64-inl.h @@ -28,12 +28,141 @@ #ifndef V8_X64_ASSEMBLER_X64_INL_H_ #define V8_X64_ASSEMBLER_X64_INL_H_ -namespace v8 { namespace internal { +#include "cpu.h" + +namespace v8 { +namespace internal { Condition NegateCondition(Condition cc) { return static_cast<Condition>(cc ^ 1); } +// ----------------------------------------------------------------------------- + +Immediate::Immediate(Smi* value) { + value_ = static_cast<int32_t>(reinterpret_cast<intptr_t>(value)); +} + +// ----------------------------------------------------------------------------- +// Implementation of Assembler + + + +void Assembler::emitl(uint32_t x) { + Memory::uint32_at(pc_) = x; + pc_ += sizeof(uint32_t); +} + + +void Assembler::emitq(uint64_t x, RelocInfo::Mode rmode) { + Memory::uint64_at(pc_) = x; + if (rmode != RelocInfo::NONE) { + RecordRelocInfo(rmode, x); + } + pc_ += sizeof(uint64_t); +} + + +void Assembler::emitw(uint16_t x) { + Memory::uint16_at(pc_) = x; + pc_ += sizeof(uint16_t); +} + + +void Assembler::emit_rex_64(Register reg, Register rm_reg) { + emit(0x48 | (reg.code() & 0x8) >> 1 | rm_reg.code() >> 3); +} + + +void Assembler::emit_rex_64(Register reg, const Operand& op) { + emit(0x48 | (reg.code() & 0x8) >> 1 | op.rex_); +} + + +void Assembler::emit_rex_64(Register rm_reg) { + ASSERT_EQ(rm_reg.code() & 0xf, rm_reg.code()); + emit(0x48 | (rm_reg.code() >> 3)); +} + + +void Assembler::emit_rex_64(const Operand& op) { + emit(0x48 | op.rex_); +} + + +void Assembler::emit_rex_32(Register reg, Register rm_reg) { + emit(0x40 | (reg.code() & 0x8) >> 1 | rm_reg.code() >> 3); +} + + +void Assembler::emit_rex_32(Register reg, const Operand& op) { + emit(0x40 | (reg.code() & 0x8) >> 1 | op.rex_); +} + + +void Assembler::emit_rex_32(Register rm_reg) { + emit(0x40 | (rm_reg.code() & 0x8) >> 3); +} + + +void Assembler::emit_rex_32(const Operand& op) { + emit(0x40 | op.rex_); +} + + +void Assembler::emit_optional_rex_32(Register reg, Register rm_reg) { + byte rex_bits = (reg.code() & 0x8) >> 1 | rm_reg.code() >> 3; + if (rex_bits != 0) emit(0x40 | rex_bits); +} + + +void Assembler::emit_optional_rex_32(Register reg, const Operand& op) { + byte rex_bits = (reg.code() & 0x8) >> 1 | op.rex_; + if (rex_bits != 0) emit(0x40 | rex_bits); +} + + +void Assembler::emit_optional_rex_32(Register rm_reg) { + if (rm_reg.code() & 0x8 != 0) emit(0x41); +} + + +void Assembler::emit_optional_rex_32(const Operand& op) { + if (op.rex_ != 0) emit(0x40 | op.rex_); +} + + +Address Assembler::target_address_at(Address pc) { + return Memory::Address_at(pc); +} + + +void Assembler::set_target_address_at(Address pc, Address target) { + Memory::Address_at(pc) = target; + CPU::FlushICache(pc, sizeof(intptr_t)); +} + + +// ----------------------------------------------------------------------------- +// Implementation of RelocInfo + +// The modes possibly affected by apply must be in kApplyMask. +void RelocInfo::apply(int delta) { + if (rmode_ == RUNTIME_ENTRY || IsCodeTarget(rmode_)) { + intptr_t* p = reinterpret_cast<intptr_t*>(pc_); + *p -= delta; // relocate entry + } else if (rmode_ == JS_RETURN && IsCallInstruction()) { + // Special handling of js_return when a break point is set (call + // instruction has been inserted). + intptr_t* p = reinterpret_cast<intptr_t*>(pc_ + 1); + *p -= delta; // relocate entry + } else if (IsInternalReference(rmode_)) { + // absolute code pointer inside code object moves with the code object. + intptr_t* p = reinterpret_cast<intptr_t*>(pc_); + *p += delta; // relocate entry + } +} + Address RelocInfo::target_address() { ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY); @@ -53,16 +182,125 @@ void RelocInfo::set_target_address(Address target) { } -void Assembler::set_target_address_at(byte* location, byte* value) { - UNIMPLEMENTED(); +Object* RelocInfo::target_object() { + ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); + return *reinterpret_cast<Object**>(pc_); } -byte* Assembler::target_address_at(byte* location) { - UNIMPLEMENTED(); - return NULL; +Object** RelocInfo::target_object_address() { + ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); + return reinterpret_cast<Object**>(pc_); } + +Address* RelocInfo::target_reference_address() { + ASSERT(rmode_ == RelocInfo::EXTERNAL_REFERENCE); + return reinterpret_cast<Address*>(pc_); +} + + +void RelocInfo::set_target_object(Object* target) { + ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); + *reinterpret_cast<Object**>(pc_) = target; +} + + +bool RelocInfo::IsCallInstruction() { + UNIMPLEMENTED(); // IA32 code below. + return *pc_ == 0xE8; +} + + +Address RelocInfo::call_address() { + UNIMPLEMENTED(); // IA32 code below. + ASSERT(IsCallInstruction()); + return Assembler::target_address_at(pc_ + 1); +} + + +void RelocInfo::set_call_address(Address target) { + UNIMPLEMENTED(); // IA32 code below. + ASSERT(IsCallInstruction()); + Assembler::set_target_address_at(pc_ + 1, target); +} + + +Object* RelocInfo::call_object() { + UNIMPLEMENTED(); // IA32 code below. + ASSERT(IsCallInstruction()); + return *call_object_address(); +} + + +void RelocInfo::set_call_object(Object* target) { + UNIMPLEMENTED(); // IA32 code below. + ASSERT(IsCallInstruction()); + *call_object_address() = target; +} + + +Object** RelocInfo::call_object_address() { + UNIMPLEMENTED(); // IA32 code below. + ASSERT(IsCallInstruction()); + return reinterpret_cast<Object**>(pc_ + 1); +} + +// ----------------------------------------------------------------------------- +// Implementation of Operand + +Operand::Operand(Register base, int32_t disp) { + len_ = 1; + if (base.is(rsp) || base.is(r12)) { + // SIB byte is needed to encode (rsp + offset) or (r12 + offset). + set_sib(kTimes1, rsp, base); + } + + if (disp == 0 && !base.is(rbp) && !base.is(r13)) { + set_modrm(0, rsp); + } else if (is_int8(disp)) { + set_modrm(1, base); + set_disp8(disp); + } else { + set_modrm(2, base); + set_disp32(disp); + } +} + +void Operand::set_modrm(int mod, Register rm) { + ASSERT((mod & -4) == 0); + buf_[0] = mod << 6 | (rm.code() & 0x7); + // Set REX.B to the high bit of rm.code(). + rex_ |= (rm.code() >> 3); +} + + +void Operand::set_sib(ScaleFactor scale, Register index, Register base) { + ASSERT(len_ == 1); + ASSERT(is_uint2(scale)); + // Use SIB with no index register only for base rsp or r12. + ASSERT(!index.is(rsp) || base.is(rsp) || base.is(r12)); + buf_[1] = scale << 6 | (index.code() & 0x7) << 3 | (base.code() & 0x7); + rex_ |= (index.code() >> 3) << 1 | base.code() >> 3; + len_ = 2; +} + +void Operand::set_disp8(int disp) { + ASSERT(is_int8(disp)); + ASSERT(len_ == 1 || len_ == 2); + int8_t* p = reinterpret_cast<int8_t*>(&buf_[len_]); + *p = disp; + len_ += sizeof(int8_t); +} + +void Operand::set_disp32(int disp) { + ASSERT(len_ == 1 || len_ == 2); + int32_t* p = reinterpret_cast<int32_t*>(&buf_[len_]); + *p = disp; + len_ += sizeof(int32_t); +} + + } } // namespace v8::internal #endif // V8_X64_ASSEMBLER_X64_INL_H_ diff --git a/deps/v8/src/x64/assembler-x64.cc b/deps/v8/src/x64/assembler-x64.cc index 6e2c42a128..77bbf52405 100644 --- a/deps/v8/src/x64/assembler-x64.cc +++ b/deps/v8/src/x64/assembler-x64.cc @@ -28,9 +28,1520 @@ #include "v8.h" #include "macro-assembler.h" +#include "serialize.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { + +// ----------------------------------------------------------------------------- +// Implementation of Register + +Register rax = { 0 }; +Register rcx = { 1 }; +Register rdx = { 2 }; +Register rbx = { 3 }; +Register rsp = { 4 }; +Register rbp = { 5 }; +Register rsi = { 6 }; +Register rdi = { 7 }; +Register r8 = { 8 }; +Register r9 = { 9 }; +Register r10 = { 10 }; +Register r11 = { 11 }; +Register r12 = { 12 }; +Register r13 = { 13 }; +Register r14 = { 14 }; +Register r15 = { 15 }; Register no_reg = { -1 }; +XMMRegister xmm0 = { 0 }; +XMMRegister xmm1 = { 1 }; +XMMRegister xmm2 = { 2 }; +XMMRegister xmm3 = { 3 }; +XMMRegister xmm4 = { 4 }; +XMMRegister xmm5 = { 5 }; +XMMRegister xmm6 = { 6 }; +XMMRegister xmm7 = { 7 }; +XMMRegister xmm8 = { 8 }; +XMMRegister xmm9 = { 9 }; +XMMRegister xmm10 = { 10 }; +XMMRegister xmm11 = { 11 }; +XMMRegister xmm12 = { 12 }; +XMMRegister xmm13 = { 13 }; +XMMRegister xmm14 = { 14 }; +XMMRegister xmm15 = { 15 }; + +// Safe default is no features. +uint64_t CpuFeatures::supported_ = 0; +uint64_t CpuFeatures::enabled_ = 0; + +void CpuFeatures::Probe() { + ASSERT(Heap::HasBeenSetup()); + ASSERT(supported_ == 0); + if (Serializer::enabled()) return; // No features if we might serialize. + + Assembler assm(NULL, 0); + Label cpuid, done; +#define __ assm. + // Save old esp, since we are going to modify the stack. + __ push(rbp); + __ pushfq(); + __ push(rcx); + __ push(rbx); + __ movq(rbp, rsp); + + // If we can modify bit 21 of the EFLAGS register, then CPUID is supported. + __ pushfq(); + __ pop(rax); + __ movq(rdx, rax); + __ xor_(rax, Immediate(0x200000)); // Flip bit 21. + __ push(rax); + __ popfq(); + __ pushfq(); + __ pop(rax); + __ xor_(rax, rdx); // Different if CPUID is supported. + __ j(not_zero, &cpuid); + + // CPUID not supported. Clear the supported features in edx:eax. + __ xor_(rax, rax); + __ jmp(&done); + + // Invoke CPUID with 1 in eax to get feature information in + // ecx:edx. Temporarily enable CPUID support because we know it's + // safe here. + __ bind(&cpuid); + __ movq(rax, Immediate(1)); + supported_ = (1 << CPUID); + { Scope fscope(CPUID); + __ cpuid(); + } + supported_ = 0; + + // Move the result from ecx:edx to rax and make sure to mark the + // CPUID feature as supported. + __ movl(rax, rdx); // Zero-extended to 64 bits. + __ shl(rcx, Immediate(32)); + __ or_(rax, rcx); + __ or_(rax, Immediate(1 << CPUID)); + + // Done. + __ bind(&done); + __ movq(rsp, rbp); + __ pop(rbx); + __ pop(rcx); + __ popfq(); + __ pop(rbp); + __ ret(0); +#undef __ + + CodeDesc desc; + assm.GetCode(&desc); + Object* code = + Heap::CreateCode(desc, NULL, Code::ComputeFlags(Code::STUB), NULL); + if (!code->IsCode()) return; + LOG(CodeCreateEvent("Builtin", Code::cast(code), "CpuFeatures::Probe")); + typedef uint64_t (*F0)(); + F0 probe = FUNCTION_CAST<F0>(Code::cast(code)->entry()); + supported_ = probe(); +} + +// ----------------------------------------------------------------------------- +// Implementation of Assembler + +#ifdef GENERATED_CODE_COVERAGE +static void InitCoverageLog(); +#endif + +byte* Assembler::spare_buffer_ = NULL; + +Assembler::Assembler(void* buffer, int buffer_size) { + if (buffer == NULL) { + // do our own buffer management + if (buffer_size <= kMinimalBufferSize) { + buffer_size = kMinimalBufferSize; + + if (spare_buffer_ != NULL) { + buffer = spare_buffer_; + spare_buffer_ = NULL; + } + } + if (buffer == NULL) { + buffer_ = NewArray<byte>(buffer_size); + } else { + buffer_ = static_cast<byte*>(buffer); + } + buffer_size_ = buffer_size; + own_buffer_ = true; + } else { + // use externally provided buffer instead + ASSERT(buffer_size > 0); + buffer_ = static_cast<byte*>(buffer); + buffer_size_ = buffer_size; + own_buffer_ = false; + } + + // Clear the buffer in debug mode unless it was provided by the + // caller in which case we can't be sure it's okay to overwrite + // existing code in it; see CodePatcher::CodePatcher(...). +#ifdef DEBUG + if (own_buffer_) { + memset(buffer_, 0xCC, buffer_size); // int3 + } +#endif + + // setup buffer pointers + ASSERT(buffer_ != NULL); + pc_ = buffer_; + reloc_info_writer.Reposition(buffer_ + buffer_size, pc_); + + last_pc_ = NULL; + current_statement_position_ = RelocInfo::kNoPosition; + current_position_ = RelocInfo::kNoPosition; + written_statement_position_ = current_statement_position_; + written_position_ = current_position_; +#ifdef GENERATED_CODE_COVERAGE + InitCoverageLog(); +#endif +} + + +Assembler::~Assembler() { + if (own_buffer_) { + if (spare_buffer_ == NULL && buffer_size_ == kMinimalBufferSize) { + spare_buffer_ = buffer_; + } else { + DeleteArray(buffer_); + } + } +} + + +void Assembler::GetCode(CodeDesc* desc) { + // finalize code + // (at this point overflow() may be true, but the gap ensures that + // we are still not overlapping instructions and relocation info) + ASSERT(pc_ <= reloc_info_writer.pos()); // no overlap + // setup desc + desc->buffer = buffer_; + desc->buffer_size = buffer_size_; + desc->instr_size = pc_offset(); + desc->reloc_size = (buffer_ + buffer_size_) - reloc_info_writer.pos(); + desc->origin = this; + + Counters::reloc_info_size.Increment(desc->reloc_size); +} + + +void Assembler::Align(int m) { + ASSERT(IsPowerOf2(m)); + while ((pc_offset() & (m - 1)) != 0) { + nop(); + } +} + + +void Assembler::bind_to(Label* L, int pos) { + ASSERT(!L->is_bound()); // Label may only be bound once. + last_pc_ = NULL; + ASSERT(0 <= pos && pos <= pc_offset()); // Position must be valid. + if (L->is_linked()) { + int current = L->pos(); + int next = long_at(current); + while (next != current) { + // relative address, relative to point after address + int imm32 = pos - (current + sizeof(int32_t)); + long_at_put(current, imm32); + current = next; + next = long_at(next); + } + // Fix up last fixup on linked list. + int last_imm32 = pos - (current + sizeof(int32_t)); + long_at_put(current, last_imm32); + } + L->bind_to(pos); +} + + +void Assembler::bind(Label* L) { + bind_to(L, pc_offset()); +} + + +void Assembler::GrowBuffer() { + ASSERT(overflow()); // should not call this otherwise + if (!own_buffer_) FATAL("external code buffer is too small"); + + // compute new buffer size + CodeDesc desc; // the new buffer + if (buffer_size_ < 4*KB) { + desc.buffer_size = 4*KB; + } else { + desc.buffer_size = 2*buffer_size_; + } + // Some internal data structures overflow for very large buffers, + // they must ensure that kMaximalBufferSize is not too large. + if ((desc.buffer_size > kMaximalBufferSize) || + (desc.buffer_size > Heap::OldGenerationSize())) { + V8::FatalProcessOutOfMemory("Assembler::GrowBuffer"); + } + + // setup new buffer + desc.buffer = NewArray<byte>(desc.buffer_size); + desc.instr_size = pc_offset(); + desc.reloc_size = (buffer_ + buffer_size_) - (reloc_info_writer.pos()); + + // Clear the buffer in debug mode. Use 'int3' instructions to make + // sure to get into problems if we ever run uninitialized code. +#ifdef DEBUG + memset(desc.buffer, 0xCC, desc.buffer_size); +#endif + + // copy the data + int pc_delta = desc.buffer - buffer_; + int rc_delta = (desc.buffer + desc.buffer_size) - (buffer_ + buffer_size_); + memmove(desc.buffer, buffer_, desc.instr_size); + memmove(rc_delta + reloc_info_writer.pos(), + reloc_info_writer.pos(), desc.reloc_size); + + // switch buffers + if (spare_buffer_ == NULL && buffer_size_ == kMinimalBufferSize) { + spare_buffer_ = buffer_; + } else { + DeleteArray(buffer_); + } + buffer_ = desc.buffer; + buffer_size_ = desc.buffer_size; + pc_ += pc_delta; + if (last_pc_ != NULL) { + last_pc_ += pc_delta; + } + reloc_info_writer.Reposition(reloc_info_writer.pos() + rc_delta, + reloc_info_writer.last_pc() + pc_delta); + + // relocate runtime entries + for (RelocIterator it(desc); !it.done(); it.next()) { + RelocInfo::Mode rmode = it.rinfo()->rmode(); + if (rmode == RelocInfo::RUNTIME_ENTRY) { + int32_t* p = reinterpret_cast<int32_t*>(it.rinfo()->pc()); + *p -= pc_delta; // relocate entry + } else if (rmode == RelocInfo::INTERNAL_REFERENCE) { + int32_t* p = reinterpret_cast<int32_t*>(it.rinfo()->pc()); + if (*p != 0) { // 0 means uninitialized. + *p += pc_delta; + } + } + } + + ASSERT(!overflow()); +} + + +void Assembler::emit_operand(int rm, const Operand& adr) { + ASSERT_EQ(rm & 0x07, rm); + const unsigned length = adr.len_; + ASSERT(length > 0); + + // Emit updated ModR/M byte containing the given register. + pc_[0] = (adr.buf_[0] & ~0x38) | (rm << 3); + + // Emit the rest of the encoded operand. + for (unsigned i = 1; i < length; i++) pc_[i] = adr.buf_[i]; + pc_ += length; +} + + +// Assembler Instruction implementations + +void Assembler::arithmetic_op(byte opcode, Register reg, const Operand& op) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_64(reg, op); + emit(opcode); + emit_operand(reg, op); +} + + +void Assembler::arithmetic_op(byte opcode, Register dst, Register src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_64(dst, src); + emit(opcode); + emit_modrm(dst, src); +} + +void Assembler::immediate_arithmetic_op(byte subcode, + Register dst, + Immediate src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_64(dst); + if (is_int8(src.value_)) { + emit(0x83); + emit_modrm(subcode, dst); + emit(src.value_); + } else if (dst.is(rax)) { + emit(0x05 | (subcode << 3)); + emitl(src.value_); + } else { + emit(0x81); + emit_modrm(subcode, dst); + emitl(src.value_); + } +} + +void Assembler::immediate_arithmetic_op(byte subcode, + const Operand& dst, + Immediate src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_64(dst); + if (is_int8(src.value_)) { + emit(0x83); + emit_operand(Register::toRegister(subcode), dst); + emit(src.value_); + } else { + emit(0x81); + emit_operand(Register::toRegister(subcode), dst); + emitl(src.value_); + } +} + + +void Assembler::shift(Register dst, Immediate shift_amount, int subcode) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + ASSERT(is_uint6(shift_amount.value_)); // illegal shift count + if (shift_amount.value_ == 1) { + emit_rex_64(dst); + emit(0xD1); + emit_modrm(subcode, dst); + } else { + emit_rex_64(dst); + emit(0xC1); + emit_modrm(subcode, dst); + emit(shift_amount.value_); + } +} + + +void Assembler::shift(Register dst, int subcode) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_64(dst); + emit(0xD3); + emit_modrm(subcode, dst); +} + + +void Assembler::bt(const Operand& dst, Register src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_64(src, dst); + emit(0x0F); + emit(0xA3); + emit_operand(src, dst); +} + + +void Assembler::bts(const Operand& dst, Register src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_64(src, dst); + emit(0x0F); + emit(0xAB); + emit_operand(src, dst); +} + + +void Assembler::call(Label* L) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + // 1110 1000 #32-bit disp + emit(0xE8); + if (L->is_bound()) { + int offset = L->pos() - pc_offset() - sizeof(int32_t); + ASSERT(offset <= 0); + emitl(offset); + } else if (L->is_linked()) { + emitl(L->pos()); + L->link_to(pc_offset() - sizeof(int32_t)); + } else { + ASSERT(L->is_unused()); + int32_t current = pc_offset(); + emitl(current); + L->link_to(current); + } +} + + +void Assembler::call(Register adr) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + // Opcode: FF /2 r64 + if (adr.code() > 7) { + emit_rex_64(adr); + } + emit(0xFF); + emit_modrm(0x2, adr); +} + +void Assembler::cpuid() { + ASSERT(CpuFeatures::IsEnabled(CpuFeatures::CPUID)); + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit(0x0F); + emit(0xA2); +} + + +void Assembler::call(const Operand& op) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + // Opcode: FF /2 m64 + emit_rex_64(op); + emit(0xFF); + emit_operand(2, op); +} + + +void Assembler::cqo() { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_64(); + emit(0x99); +} + + +void Assembler::dec(Register dst) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_64(dst); + emit(0xFF); + emit_modrm(0x1, dst); +} + + +void Assembler::dec(const Operand& dst) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_64(dst); + emit(0xFF); + emit_operand(1, dst); +} + + +void Assembler::enter(Immediate size) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit(0xC8); + emitw(size.value_); // 16 bit operand, always. + emit(0); +} + + +void Assembler::hlt() { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit(0xF4); +} + + +void Assembler::idiv(Register src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_64(src); + emit(0xF7); + emit_modrm(0x7, src); +} + + +void Assembler::imul(Register dst, const Operand& src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_64(dst, src); + emit(0x0F); + emit(0xAF); + emit_operand(dst, src); +} + + +void Assembler::imul(Register dst, Register src, Immediate imm) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_64(dst, src); + if (is_int8(imm.value_)) { + emit(0x6B); + emit_modrm(dst, src); + emit(imm.value_); + } else { + emit(0x69); + emit_modrm(dst, src); + emitl(imm.value_); + } +} + + +void Assembler::inc(Register dst) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_64(dst); + emit(0xFF); + emit_modrm(0x0, dst); +} + + +void Assembler::inc(const Operand& dst) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_64(dst); + emit(0xFF); + emit_operand(0, dst); +} + + +void Assembler::int3() { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit(0xCC); +} + + +void Assembler::j(Condition cc, Label* L) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + ASSERT(0 <= cc && cc < 16); + if (L->is_bound()) { + const int short_size = 2; + const int long_size = 6; + int offs = L->pos() - pc_offset(); + ASSERT(offs <= 0); + if (is_int8(offs - short_size)) { + // 0111 tttn #8-bit disp + emit(0x70 | cc); + emit((offs - short_size) & 0xFF); + } else { + // 0000 1111 1000 tttn #32-bit disp + emit(0x0F); + emit(0x80 | cc); + emitl(offs - long_size); + } + } else if (L->is_linked()) { + // 0000 1111 1000 tttn #32-bit disp + emit(0x0F); + emit(0x80 | cc); + emitl(L->pos()); + L->link_to(pc_offset() - sizeof(int32_t)); + } else { + ASSERT(L->is_unused()); + emit(0x0F); + emit(0x80 | cc); + int32_t current = pc_offset(); + emitl(current); + L->link_to(current); + } +} + + +void Assembler::jmp(Label* L) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + if (L->is_bound()) { + int offs = L->pos() - pc_offset() - 1; + ASSERT(offs <= 0); + if (is_int8(offs - sizeof(int8_t))) { + // 1110 1011 #8-bit disp + emit(0xEB); + emit((offs - sizeof(int8_t)) & 0xFF); + } else { + // 1110 1001 #32-bit disp + emit(0xE9); + emitl(offs - sizeof(int32_t)); + } + } else if (L->is_linked()) { + // 1110 1001 #32-bit disp + emit(0xE9); + emitl(L->pos()); + L->link_to(pc_offset() - sizeof(int32_t)); + } else { + // 1110 1001 #32-bit disp + ASSERT(L->is_unused()); + emit(0xE9); + int32_t current = pc_offset(); + emitl(current); + L->link_to(current); + } +} + + +void Assembler::jmp(Register target) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + // Opcode FF/4 r64 + if (target.code() > 7) { + emit_rex_64(target); + } + emit(0xFF); + emit_modrm(0x4, target); +} + + +void Assembler::lea(Register dst, const Operand& src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_64(dst, src); + emit(0x8D); + emit_operand(dst, src); +} + + +void Assembler::load_rax(void* value, RelocInfo::Mode mode) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit(0x48); // REX.W + emit(0xA1); + emitq(reinterpret_cast<uintptr_t>(value), mode); +} + + +void Assembler::load_rax(ExternalReference ref) { + load_rax(ref.address(), RelocInfo::EXTERNAL_REFERENCE); +} + + +void Assembler::leave() { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit(0xC9); +} + + +void Assembler::movb(Register dst, const Operand& src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_32(dst, src); + emit(0x8A); + emit_operand(dst, src); +} + +void Assembler::movb(Register dst, Immediate imm) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_32(dst); + emit(0xC6); + emit_modrm(0x0, dst); + emit(imm.value_); +} + +void Assembler::movb(const Operand& dst, Register src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_32(src, dst); + emit(0x88); + emit_operand(src, dst); +} + +void Assembler::movl(Register dst, const Operand& src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_optional_rex_32(dst, src); + emit(0x8B); + emit_operand(dst, src); +} + + +void Assembler::movl(Register dst, Register src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_optional_rex_32(dst, src); + emit(0x8B); + emit_modrm(dst, src); +} + + +void Assembler::movl(const Operand& dst, Register src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_optional_rex_32(src, dst); + emit(0x89); + emit_operand(src, dst); +} + + +void Assembler::movl(Register dst, Immediate value) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_optional_rex_32(dst); + emit(0xC7); + emit_modrm(0x0, dst); + emit(value); // Only 32-bit immediates are possible, not 8-bit immediates. +} + + +void Assembler::movq(Register dst, const Operand& src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_64(dst, src); + emit(0x8B); + emit_operand(dst, src); +} + + +void Assembler::movq(Register dst, Register src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_64(dst, src); + emit(0x8B); + emit_modrm(dst, src); +} + + +void Assembler::movq(Register dst, Immediate value) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_64(dst); + emit(0xC7); + emit_modrm(0x0, dst); + emit(value); // Only 32-bit immediates are possible, not 8-bit immediates. +} + + +void Assembler::movq(const Operand& dst, Register src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_64(src, dst); + emit(0x89); + emit_operand(src, dst); +} + + +void Assembler::movq(Register dst, void* value, RelocInfo::Mode rmode) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_64(dst); + emit(0xB8 | (dst.code() & 0x7)); + emitq(reinterpret_cast<uintptr_t>(value), rmode); +} + + +void Assembler::movq(Register dst, int64_t value, RelocInfo::Mode rmode) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_64(dst); + emit(0xB8 | (dst.code() & 0x7)); // Not a ModR/M byte. + emitq(value, rmode); +} + + +void Assembler::movq(Register dst, ExternalReference ref) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_64(dst); + emit(0xB8 | (dst.code() & 0x7)); + emitq(reinterpret_cast<uintptr_t>(ref.address()), + RelocInfo::EXTERNAL_REFERENCE); +} + + +void Assembler::mul(Register src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_64(src); + emit(0xF7); + emit_modrm(0x4, src); +} + + +void Assembler::neg(Register dst) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_64(dst); + emit(0xF7); + emit_modrm(0x3, dst); +} + + +void Assembler::neg(const Operand& dst) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_64(dst); + emit(0xF7); + emit_operand(3, dst); +} + + +void Assembler::nop() { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit(0x90); +} + + +void Assembler::not_(Register dst) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_64(dst); + emit(0xF7); + emit_modrm(0x2, dst); +} + + +void Assembler::not_(const Operand& dst) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_64(dst); + emit(0xF7); + emit_operand(2, dst); +} + + +void Assembler::nop(int n) { + // The recommended muti-byte sequences of NOP instructions from the Intel 64 + // and IA-32 Architectures Software Developer's Manual. + // + // Length Assembly Byte Sequence + // 2 bytes 66 NOP 66 90H + // 3 bytes NOP DWORD ptr [EAX] 0F 1F 00H + // 4 bytes NOP DWORD ptr [EAX + 00H] 0F 1F 40 00H + // 5 bytes NOP DWORD ptr [EAX + EAX*1 + 00H] 0F 1F 44 00 00H + // 6 bytes 66 NOP DWORD ptr [EAX + EAX*1 + 00H] 66 0F 1F 44 00 00H + // 7 bytes NOP DWORD ptr [EAX + 00000000H] 0F 1F 80 00 00 00 00H + // 8 bytes NOP DWORD ptr [EAX + EAX*1 + 00000000H] 0F 1F 84 00 00 00 00 00H + // 9 bytes 66 NOP DWORD ptr [EAX + EAX*1 + 66 0F 1F 84 00 00 00 00 + // 00000000H] 00H + + ASSERT(1 <= n); + ASSERT(n <= 9); + EnsureSpace ensure_space(this); + last_pc_ = pc_; + switch (n) { + case 1: + emit(0x90); + return; + case 2: + emit(0x66); + emit(0x90); + return; + case 3: + emit(0x0f); + emit(0x1f); + emit(0x00); + return; + case 4: + emit(0x0f); + emit(0x1f); + emit(0x40); + emit(0x00); + return; + case 5: + emit(0x0f); + emit(0x1f); + emit(0x44); + emit(0x00); + emit(0x00); + return; + case 6: + emit(0x66); + emit(0x0f); + emit(0x1f); + emit(0x44); + emit(0x00); + emit(0x00); + return; + case 7: + emit(0x0f); + emit(0x1f); + emit(0x80); + emit(0x00); + emit(0x00); + emit(0x00); + emit(0x00); + return; + case 8: + emit(0x0f); + emit(0x1f); + emit(0x84); + emit(0x00); + emit(0x00); + emit(0x00); + emit(0x00); + emit(0x00); + return; + case 9: + emit(0x66); + emit(0x0f); + emit(0x1f); + emit(0x84); + emit(0x00); + emit(0x00); + emit(0x00); + emit(0x00); + emit(0x00); + return; + } +} + + +void Assembler::pop(Register dst) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + if (dst.code() > 7) { + emit_rex_64(dst); + } + emit(0x58 | (dst.code() & 0x7)); +} + + +void Assembler::pop(const Operand& dst) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_64(dst); // Could be omitted in some cases. + emit(0x8F); + emit_operand(0, dst); +} + + +void Assembler::popfq() { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit(0x9D); +} + + +void Assembler::push(Register src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + if (src.code() > 7) { + emit_rex_64(src); + } + emit(0x50 | (src.code() & 0x7)); +} + + +void Assembler::push(const Operand& src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_64(src); // Could be omitted in some cases. + emit(0xFF); + emit_operand(6, src); +} + + +void Assembler::push(Immediate value) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + if (is_int8(value.value_)) { + emit(0x6A); + emit(value.value_); // Emit low byte of value. + } else { + emit(0x68); + emitl(value.value_); + } +} + + +void Assembler::pushfq() { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit(0x9C); +} + + +void Assembler::rcl(Register dst, uint8_t imm8) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + ASSERT(is_uint6(imm8)); // illegal shift count + if (imm8 == 1) { + emit_rex_64(dst); + emit(0xD1); + emit_modrm(0x2, dst); + } else { + emit_rex_64(dst); + emit(0xC1); + emit_modrm(0x2, dst); + emit(imm8); + } +} + + +void Assembler::ret(int imm16) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + ASSERT(is_uint16(imm16)); + if (imm16 == 0) { + emit(0xC3); + } else { + emit(0xC2); + emit(imm16 & 0xFF); + emit((imm16 >> 8) & 0xFF); + } +} + + +void Assembler::shld(Register dst, Register src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_64(src, dst); + emit(0x0F); + emit(0xA5); + emit_modrm(src, dst); +} + + +void Assembler::shrd(Register dst, Register src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_64(src, dst); + emit(0x0F); + emit(0xAD); + emit_modrm(src, dst); +} + + +void Assembler::xchg(Register dst, Register src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + if (src.is(rax) || dst.is(rax)) { // Single-byte encoding + Register other = src.is(rax) ? dst : src; + emit_rex_64(other); + emit(0x90 | (other.code() & 0x7)); + } else { + emit_rex_64(src, dst); + emit(0x87); + emit_modrm(src, dst); + } +} + + +void Assembler::store_rax(void* dst, RelocInfo::Mode mode) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit(0x48); // REX.W + emit(0xA3); + emitq(reinterpret_cast<uintptr_t>(dst), mode); +} + + +void Assembler::store_rax(ExternalReference ref) { + store_rax(ref.address(), RelocInfo::EXTERNAL_REFERENCE); +} + + +void Assembler::testb(Register reg, Immediate mask) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + if (reg.is(rax)) { + emit(0xA8); + emit(mask); + } else { + if (reg.code() > 3) { + // Register is not one of al, bl, cl, dl. Its encoding needs REX. + emit_rex_32(reg); + } + emit(0xF6); + emit_modrm(0x0, reg); + emit(mask.value_); // Low byte emitted. + } +} + + +void Assembler::testb(const Operand& op, Immediate mask) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_optional_rex_32(rax, op); + emit(0xF6); + emit_operand(rax, op); // Operation code 0 + emit(mask.value_); // Low byte emitted. +} + + +void Assembler::testl(Register reg, Immediate mask) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + if (reg.is(rax)) { + emit(0xA9); + emit(mask); + } else { + emit_optional_rex_32(rax, reg); + emit(0xF7); + emit_modrm(0x0, reg); + emit(mask); + } +} + + +void Assembler::testl(const Operand& op, Immediate mask) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_optional_rex_32(rax, op); + emit(0xF7); + emit_operand(rax, op); // Operation code 0 + emit(mask); +} + + +void Assembler::testq(const Operand& op, Register reg) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_64(reg, op); + emit(0x85); + emit_operand(reg, op); +} + + +void Assembler::testq(Register dst, Register src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_rex_64(dst, src); + emit(0x85); + emit_modrm(dst, src); +} + + +// Relocation information implementations + +void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) { + ASSERT(rmode != RelocInfo::NONE); + // Don't record external references unless the heap will be serialized. + if (rmode == RelocInfo::EXTERNAL_REFERENCE && + !Serializer::enabled() && + !FLAG_debug_code) { + return; + } + RelocInfo rinfo(pc_, rmode, data); + reloc_info_writer.Write(&rinfo); +} + +void Assembler::RecordJSReturn() { + WriteRecordedPositions(); + EnsureSpace ensure_space(this); + RecordRelocInfo(RelocInfo::JS_RETURN); +} + + +void Assembler::RecordComment(const char* msg) { + if (FLAG_debug_code) { + EnsureSpace ensure_space(this); + RecordRelocInfo(RelocInfo::COMMENT, reinterpret_cast<intptr_t>(msg)); + } +} + + +void Assembler::RecordPosition(int pos) { + ASSERT(pos != RelocInfo::kNoPosition); + ASSERT(pos >= 0); + current_position_ = pos; +} + + +void Assembler::RecordStatementPosition(int pos) { + ASSERT(pos != RelocInfo::kNoPosition); + ASSERT(pos >= 0); + current_statement_position_ = pos; +} + + +void Assembler::WriteRecordedPositions() { + // Write the statement position if it is different from what was written last + // time. + if (current_statement_position_ != written_statement_position_) { + EnsureSpace ensure_space(this); + RecordRelocInfo(RelocInfo::STATEMENT_POSITION, current_statement_position_); + written_statement_position_ = current_statement_position_; + } + + // Write the position if it is different from what was written last time and + // also different from the written statement position. + if (current_position_ != written_position_ && + current_position_ != written_statement_position_) { + EnsureSpace ensure_space(this); + RecordRelocInfo(RelocInfo::POSITION, current_position_); + written_position_ = current_position_; + } +} + + +const int RelocInfo::kApplyMask = + RelocInfo::kCodeTargetMask | 1 << RelocInfo::RUNTIME_ENTRY | + 1 << RelocInfo::JS_RETURN | 1 << RelocInfo::INTERNAL_REFERENCE; + + +} } // namespace v8::internal + + +// TODO(x64): Implement and move these to their correct cc-files: +#include "ast.h" +#include "bootstrapper.h" +#include "codegen-inl.h" +#include "cpu.h" +#include "debug.h" +#include "disasm.h" +#include "disassembler.h" +#include "frames-inl.h" +#include "x64/macro-assembler-x64.h" +#include "x64/regexp-macro-assembler-x64.h" +#include "ic-inl.h" +#include "log.h" +#include "macro-assembler.h" +#include "parser.h" +#include "regexp-macro-assembler.h" +#include "regexp-stack.h" +#include "register-allocator-inl.h" +#include "register-allocator.h" +#include "runtime.h" +#include "scopes.h" +#include "serialize.h" +#include "stub-cache.h" +#include "unicode.h" + +namespace v8 { +namespace internal { + +void ArgumentsAccessStub::GenerateNewObject(MacroAssembler* a) { + UNIMPLEMENTED(); +} + +void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* a) { + UNIMPLEMENTED(); +} + +void ArgumentsAccessStub::GenerateReadLength(MacroAssembler* a) { + UNIMPLEMENTED(); +} + + +void BreakLocationIterator::ClearDebugBreakAtReturn() { + UNIMPLEMENTED(); +} + +bool BreakLocationIterator::IsDebugBreakAtReturn() { + UNIMPLEMENTED(); + return false; +} + +void BreakLocationIterator::SetDebugBreakAtReturn() { + UNIMPLEMENTED(); +} + +void CallIC::Generate(MacroAssembler* a, int b, ExternalReference const& c) { + UNIMPLEMENTED(); +} + +void CallIC::GenerateMegamorphic(MacroAssembler* a, int b) { + UNIMPLEMENTED(); +} + +void CallIC::GenerateNormal(MacroAssembler* a, int b) { + UNIMPLEMENTED(); +} + +Object* CallStubCompiler::CompileCallConstant(Object* a, + JSObject* b, + JSFunction* c, + StubCompiler::CheckType d, + Code::Flags flags) { + UNIMPLEMENTED(); + return NULL; +} + +Object* CallStubCompiler::CompileCallField(Object* a, + JSObject* b, + int c, + String* d, + Code::Flags flags) { + UNIMPLEMENTED(); + return NULL; +} + +Object* CallStubCompiler::CompileCallInterceptor(Object* a, + JSObject* b, + String* c) { + UNIMPLEMENTED(); + return NULL; +} + + +StackFrame::Type ExitFrame::GetStateForFramePointer(unsigned char* a, + StackFrame::State* b) { + // TODO(X64): UNIMPLEMENTED + return NONE; +} + +int JavaScriptFrame::GetProvidedParametersCount() const { + UNIMPLEMENTED(); + return 0; +} + +void JumpTarget::DoBind(int a) { + UNIMPLEMENTED(); +} + +void JumpTarget::DoBranch(Condition a, Hint b) { + UNIMPLEMENTED(); +} + +void JumpTarget::DoJump() { + UNIMPLEMENTED(); +} + + +Object* LoadStubCompiler::CompileLoadCallback(JSObject* a, + JSObject* b, + AccessorInfo* c, + String* d) { + UNIMPLEMENTED(); + return NULL; +} + +Object* LoadStubCompiler::CompileLoadConstant(JSObject* a, + JSObject* b, + Object* c, + String* d) { + UNIMPLEMENTED(); + return NULL; +} + +Object* LoadStubCompiler::CompileLoadField(JSObject* a, + JSObject* b, + int c, + String* d) { + UNIMPLEMENTED(); + return NULL; +} + +Object* LoadStubCompiler::CompileLoadInterceptor(JSObject* a, + JSObject* b, + String* c) { + UNIMPLEMENTED(); + return NULL; +} + +StackFrame::Type StackFrame::ComputeType(StackFrame::State* a) { + UNIMPLEMENTED(); + return NONE; +} + +Object* StoreStubCompiler::CompileStoreCallback(JSObject* a, + AccessorInfo* b, + String* c) { + UNIMPLEMENTED(); + return NULL; +} + +Object* StoreStubCompiler::CompileStoreField(JSObject* a, + int b, + Map* c, + String* d) { + UNIMPLEMENTED(); + return NULL; +} + +Object* StoreStubCompiler::CompileStoreInterceptor(JSObject* a, String* b) { + UNIMPLEMENTED(); + return NULL; +} + +Object* StubCompiler::CompileLazyCompile(Code::Flags a) { + UNIMPLEMENTED(); + return NULL; +} + +void VirtualFrame::Drop(int a) { + UNIMPLEMENTED(); +} + +int VirtualFrame::InvalidateFrameSlotAt(int a) { + UNIMPLEMENTED(); + return -1; +} + +void VirtualFrame::MergeTo(VirtualFrame* a) { + UNIMPLEMENTED(); +} + +Result VirtualFrame::Pop() { + UNIMPLEMENTED(); + return Result(NULL); +} + +Result VirtualFrame::RawCallStub(CodeStub* a) { + UNIMPLEMENTED(); + return Result(NULL); +} + +void VirtualFrame::SyncElementBelowStackPointer(int a) { + UNIMPLEMENTED(); +} + +void VirtualFrame::SyncElementByPushing(int a) { + UNIMPLEMENTED(); +} + +void VirtualFrame::SyncRange(int a, int b) { + UNIMPLEMENTED(); +} + +VirtualFrame::VirtualFrame() : elements_(0) { + UNIMPLEMENTED(); +} + +byte* ArgumentsAdaptorFrame::GetCallerStackPointer() const { + UNIMPLEMENTED(); + return NULL; +} + +void CodeGenerator::GenerateArgumentsAccess(ZoneList<Expression*>* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::GenerateArgumentsLength(ZoneList<Expression*>* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::GenerateFastCharCodeAt(ZoneList<Expression*>* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::GenerateIsNonNegativeSmi(ZoneList<Expression*>* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::GenerateIsSmi(ZoneList<Expression*>* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::GenerateLog(ZoneList<Expression*>* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::GenerateObjectEquals(ZoneList<Expression*>* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::GenerateSetValueOf(ZoneList<Expression*>* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::GenerateValueOf(ZoneList<Expression*>* a) { + UNIMPLEMENTED(); +} + +void ExitFrame::Iterate(ObjectVisitor* a) const { + UNIMPLEMENTED(); +} + +byte* InternalFrame::GetCallerStackPointer() const { + UNIMPLEMENTED(); + return NULL; +} + +byte* JavaScriptFrame::GetCallerStackPointer() const { + UNIMPLEMENTED(); + return NULL; +} + } } // namespace v8::internal diff --git a/deps/v8/src/x64/assembler-x64.h b/deps/v8/src/x64/assembler-x64.h index 40fcdd32bc..b4882571e2 100644 --- a/deps/v8/src/x64/assembler-x64.h +++ b/deps/v8/src/x64/assembler-x64.h @@ -37,7 +37,21 @@ #ifndef V8_X64_ASSEMBLER_X64_H_ #define V8_X64_ASSEMBLER_X64_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { + +// Utility functions + +// Test whether a 64-bit value is in a specific range. +static inline bool is_uint32(int64_t x) { + const int64_t kUInt32Mask = V8_INT64_C(0xffffffff); + return x == x & kUInt32Mask; +} + +static inline bool is_int32(int64_t x) { + const int64_t kMinIntValue = V8_INT64_C(-0x80000000); + return is_uint32(x - kMinIntValue); +} // CPU Registers. // @@ -60,10 +74,13 @@ namespace v8 { namespace internal { // mode. This way we get the compile-time error checking in debug mode // and best performance in optimized code. // -const int kNumRegisters = 16; struct Register { - bool is_valid() const { return 0 <= code_ && code_ < kNumRegisters; } + static Register toRegister(int code) { + Register r = {code}; + return r; + } + bool is_valid() const { return 0 <= code_ && code_ < 16; } bool is(Register reg) const { return code_ == reg.code_; } // The byte-register distinction of ai32 has dissapeared. bool is_byte_register() const { return false; } @@ -98,7 +115,6 @@ extern Register r14; extern Register r15; extern Register no_reg; - struct XMMRegister { bool is_valid() const { return 0 <= code_ && code_ < 2; } int code() const { @@ -117,6 +133,14 @@ extern XMMRegister xmm4; extern XMMRegister xmm5; extern XMMRegister xmm6; extern XMMRegister xmm7; +extern XMMRegister xmm8; +extern XMMRegister xmm9; +extern XMMRegister xmm10; +extern XMMRegister xmm11; +extern XMMRegister xmm12; +extern XMMRegister xmm13; +extern XMMRegister xmm14; +extern XMMRegister xmm15; enum Condition { // any value < 0 is considered no_condition @@ -200,34 +224,11 @@ inline Hint NegateHint(Hint hint) { class Immediate BASE_EMBEDDED { public: - inline explicit Immediate(int64_t x); - inline explicit Immediate(const char* s); - inline explicit Immediate(const ExternalReference& ext); - inline explicit Immediate(Handle<Object> handle); + explicit Immediate(int32_t value) : value_(value) {} inline explicit Immediate(Smi* value); - static Immediate CodeRelativeOffset(Label* label) { - return Immediate(label); - } - - bool is_zero() const { return x_ == 0 && rmode_ == RelocInfo::NONE; } - bool is_int8() const { - return -128 <= x_ && x_ < 128 && rmode_ == RelocInfo::NONE; - } - bool is_int16() const { - return -32768 <= x_ && x_ < 32768 && rmode_ == RelocInfo::NONE; - } - bool is_int32() const { - return V8_INT64_C(-2147483648) <= x_ - && x_ < V8_INT64_C(2147483648) - && rmode_ == RelocInfo::NONE; - } - private: - inline explicit Immediate(Label* value) { UNIMPLEMENTED(); } - - int64_t x_; - RelocInfo::Mode rmode_; + int32_t value_; friend class Assembler; }; @@ -237,177 +238,55 @@ class Immediate BASE_EMBEDDED { // Machine instruction Operands enum ScaleFactor { - times_1 = 0, - times_2 = 1, - times_4 = 2, - times_8 = 3 + kTimes1 = 0, + kTimes2 = 1, + kTimes4 = 2, + kTimes8 = 3, + kTimesIntSize = kTimes4, + kTimesPointerSize = kTimes8 }; class Operand BASE_EMBEDDED { public: - // reg - INLINE(explicit Operand(Register reg)); - - // MemoryOperand - INLINE(explicit Operand()) { UNIMPLEMENTED(); } - - // Returns true if this Operand is a wrapper for the specified register. - bool is_reg(Register reg) const; - - // These constructors have been moved to MemOperand, and should - // be removed from Operand as soon as all their uses use MemOperands instead. - // [disp/r] - INLINE(explicit Operand(intptr_t disp, RelocInfo::Mode rmode)) { - UNIMPLEMENTED(); - } - // disp only must always be relocated - // [base + disp/r] - explicit Operand(Register base, intptr_t disp, - RelocInfo::Mode rmode = RelocInfo::NONE); + INLINE(Operand(Register base, int32_t disp)); // [base + index*scale + disp/r] - explicit Operand(Register base, - Register index, - ScaleFactor scale, - intptr_t disp, - RelocInfo::Mode rmode = RelocInfo::NONE); + Operand(Register base, + Register index, + ScaleFactor scale, + int32_t disp); // [index*scale + disp/r] - explicit Operand(Register index, - ScaleFactor scale, - intptr_t disp, - RelocInfo::Mode rmode = RelocInfo::NONE); - - static Operand StaticVariable(const ExternalReference& ext) { - return Operand(reinterpret_cast<intptr_t>(ext.address()), - RelocInfo::EXTERNAL_REFERENCE); - } - - static Operand StaticArray(Register index, - ScaleFactor scale, - const ExternalReference& arr) { - return Operand(index, scale, reinterpret_cast<intptr_t>(arr.address()), - RelocInfo::EXTERNAL_REFERENCE); - } - - // End of constructors and methods that have been moved to MemOperand. + Operand(Register index, + ScaleFactor scale, + int32_t disp); private: byte rex_; byte buf_[10]; // The number of bytes in buf_. unsigned int len_; - // Only valid if len_ > 4. RelocInfo::Mode rmode_; - // Set the ModRM byte without an encoded 'reg' register. The + // Set the ModR/M byte without an encoded 'reg' register. The // register is encoded later as part of the emit_operand operation. + // set_modrm can be called before or after set_sib and set_disp*. inline void set_modrm(int mod, Register rm); + // Set the SIB byte if one is needed. Sets the length to 2 rather than 1. inline void set_sib(ScaleFactor scale, Register index, Register base); - inline void set_disp8(int8_t disp); - inline void set_disp32(int32_t disp); - inline void set_dispr(intptr_t disp, RelocInfo::Mode rmode); - - friend class Assembler; -}; - -class MemOperand : public Operand { - public: - // [disp/r] - INLINE(explicit MemOperand(intptr_t disp, RelocInfo::Mode rmode)) : - Operand() { - UNIMPLEMENTED(); - } - // disp only must always be relocated - - // [base + disp/r] - explicit MemOperand(Register base, intptr_t disp, - RelocInfo::Mode rmode = RelocInfo::NONE); - - // [base + index*scale + disp/r] - explicit MemOperand(Register base, - Register index, - ScaleFactor scale, - intptr_t disp, - RelocInfo::Mode rmode = RelocInfo::NONE); - // [index*scale + disp/r] - explicit MemOperand(Register index, - ScaleFactor scale, - intptr_t disp, - RelocInfo::Mode rmode = RelocInfo::NONE); - - static MemOperand StaticVariable(const ExternalReference& ext) { - return MemOperand(reinterpret_cast<intptr_t>(ext.address()), - RelocInfo::EXTERNAL_REFERENCE); - } - - static MemOperand StaticArray(Register index, - ScaleFactor scale, - const ExternalReference& arr) { - return MemOperand(index, scale, reinterpret_cast<intptr_t>(arr.address()), - RelocInfo::EXTERNAL_REFERENCE); - } -}; - -// ----------------------------------------------------------------------------- -// A Displacement describes the 32bit immediate field of an instruction which -// may be used together with a Label in order to refer to a yet unknown code -// position. Displacements stored in the instruction stream are used to describe -// the instruction and to chain a list of instructions using the same Label. -// A Displacement contains 2 different fields: -// -// next field: position of next displacement in the chain (0 = end of list) -// type field: instruction type -// -// A next value of null (0) indicates the end of a chain (note that there can -// be no displacement at position zero, because there is always at least one -// instruction byte before the displacement). -// -// Displacement _data field layout -// -// |31.....2|1......0| -// [ next | type | - -class Displacement BASE_EMBEDDED { - public: - enum Type { - UNCONDITIONAL_JUMP, - CODE_RELATIVE, - OTHER - }; - - int data() const { return data_; } - Type type() const { return TypeField::decode(data_); } - void next(Label* L) const { - int n = NextField::decode(data_); - n > 0 ? L->link_to(n) : L->Unuse(); - } - void link_to(Label* L) { init(L, type()); } - - explicit Displacement(int data) { data_ = data; } - - Displacement(Label* L, Type type) { init(L, type); } + // Adds operand displacement fields (offsets added to the memory address). + // Needs to be called after set_sib, not before it. + inline void set_disp8(int disp); + inline void set_disp32(int disp); - void print() { - PrintF("%s (%x) ", (type() == UNCONDITIONAL_JUMP ? "jmp" : "[other]"), - NextField::decode(data_)); - } - - private: - int data_; - - class TypeField: public BitField<Type, 0, 2> {}; - class NextField: public BitField<int, 2, 32-2> {}; - - void init(Label* L, Type type); + friend class Assembler; }; - // CpuFeatures keeps track of which features are supported by the target CPU. // Supported features must be enabled by a Scope before use. // Example: @@ -428,11 +307,11 @@ class CpuFeatures : public AllStatic { static void Probe(); // Check whether a feature is supported by the target CPU. static bool IsSupported(Feature f) { - return (supported_ & (static_cast<uint64_t>(1) << f)) != 0; + return (supported_ & (V8_UINT64_C(1) << f)) != 0; } // Check whether a feature is currently enabled. static bool IsEnabled(Feature f) { - return (enabled_ & (static_cast<uint64_t>(1) << f)) != 0; + return (enabled_ & (V8_UINT64_C(1) << f)) != 0; } // Enable a specified feature within a scope. class Scope BASE_EMBEDDED { @@ -441,7 +320,7 @@ class CpuFeatures : public AllStatic { explicit Scope(Feature f) { ASSERT(CpuFeatures::IsSupported(f)); old_enabled_ = CpuFeatures::enabled_; - CpuFeatures::enabled_ |= (static_cast<uint64_t>(1) << f); + CpuFeatures::enabled_ |= (V8_UINT64_C(1) << f); } ~Scope() { CpuFeatures::enabled_ = old_enabled_; } private: @@ -461,7 +340,8 @@ class Assembler : public Malloced { private: // The relocation writer's position is kGap bytes below the end of // the generated instructions. This leaves enough space for the - // longest possible ia32 instruction (17 bytes as of 9/26/06) and + // longest possible x64 instruction (There is a 15 byte limit on + // instruction length, ruling out some otherwise valid instructions) and // allows for a single, fast space check per instruction. static const int kGap = 32; @@ -488,8 +368,9 @@ class Assembler : public Malloced { void GetCode(CodeDesc* desc); // Read/Modify the code target in the branch/call instruction at pc. - inline static Address target_address_at(Address pc); - inline static void set_target_address_at(Address pc, Address target); + // On the x64 architecture, the address is absolute, not relative. + static inline Address target_address_at(Address pc); + static inline void set_target_address_at(Address pc, Address target); // Distance between the address of the code target in the call instruction // and the return address @@ -499,22 +380,20 @@ class Assembler : public Malloced { // --------------------------------------------------------------------------- // Code generation // - // - function names correspond one-to-one to ia32 instruction mnemonics - // - unless specified otherwise, instructions operate on 32bit operands - // - instructions on 8bit (byte) operands/registers have a trailing '_b' - // - instructions on 16bit (word) operands/registers have a trailing '_w' - // - naming conflicts with C++ keywords are resolved via a trailing '_' - - // NOTE ON INTERFACE: Currently, the interface is not very consistent - // in the sense that some operations (e.g. mov()) can be called in more - // the one way to generate the same instruction: The Register argument - // can in some cases be replaced with an Operand(Register) argument. - // This should be cleaned up and made more orthogonal. The questions - // is: should we always use Operands instead of Registers where an - // Operand is possible, or should we have a Register (overloaded) form - // instead? We must be careful to make sure that the selected instruction - // is obvious from the parameters to avoid hard-to-find code generation - // bugs. + // Function names correspond one-to-one to x64 instruction mnemonics. + // Unless specified otherwise, instructions operate on 64-bit operands. + // + // If we need versions of an assembly instruction that operate on different + // width arguments, we add a single-letter suffix specifying the width. + // This is done for the following instructions: mov, cmp. + // There are no versions of these instructions without the suffix. + // - Instructions on 8-bit (byte) operands/registers have a trailing 'b'. + // - Instructions on 16-bit (word) operands/registers have a trailing 'w'. + // - Instructions on 32-bit (doubleword) operands/registers use 'l'. + // - Instructions on 64-bit (quadword) operands/registers use 'q'. + // + // Some mnemonics, such as "and", are the same as C++ keywords. + // Naming conflicts with C++ keywords are resolved by adding a trailing '_'. // Insert the smallest number of nop instructions // possible to align the pc offset to a multiple @@ -522,13 +401,10 @@ class Assembler : public Malloced { void Align(int m); // Stack - void pushad(); - void popad(); + void pushfq(); + void popfq(); - void pushfd(); - void popfd(); - - void push(const Immediate& x); + void push(Immediate value); void push(Register src); void push(const Operand& src); void push(Label* label, RelocInfo::Mode relocation_mode); @@ -536,25 +412,42 @@ class Assembler : public Malloced { void pop(Register dst); void pop(const Operand& dst); - void enter(const Immediate& size); + void enter(Immediate size); void leave(); // Moves - void mov_b(Register dst, const Operand& src); - void mov_b(const Operand& dst, int8_t imm8); - void mov_b(const Operand& dst, Register src); - - void mov_w(Register dst, const Operand& src); - void mov_w(const Operand& dst, Register src); - - void mov(Register dst, int32_t imm32); - void mov(Register dst, const Immediate& x); - void mov(Register dst, Handle<Object> handle); - void mov(Register dst, const Operand& src); - void mov(Register dst, Register src); - void mov(const Operand& dst, const Immediate& x); - void mov(const Operand& dst, Handle<Object> handle); - void mov(const Operand& dst, Register src); + void movb(Register dst, const Operand& src); + void movb(Register dst, Immediate imm); + void movb(const Operand& dst, Register src); + + void movl(Register dst, Register src); + void movl(Register dst, const Operand& src); + void movl(const Operand& dst, Register src); + // Load a 32-bit immediate value, zero-extended to 64 bits. + void movl(Register dst, Immediate imm32); + + void movq(Register dst, int32_t imm32); + void movq(Register dst, const Operand& src); + // Sign extends immediate 32-bit value to 64 bits. + void movq(Register dst, Immediate x); + void movq(Register dst, Register src); + + // Move 64 bit register value to 64-bit memory location. + void movq(const Operand& dst, Register src); + + // New x64 instructions to load a 64-bit immediate into a register. + // All 64-bit immediates must have a relocation mode. + void movq(Register dst, void* ptr, RelocInfo::Mode rmode); + void movq(Register dst, int64_t value, RelocInfo::Mode rmode); + void movq(Register dst, const char* s, RelocInfo::Mode rmode); + // Moves the address of the external reference into the register. + void movq(Register dst, ExternalReference ext); + void movq(Register dst, Handle<Object> handle, RelocInfo::Mode rmode); + + + // New x64 instruction to load from an immediate 64-bit pointer into RAX. + void load_rax(void* ptr, RelocInfo::Mode rmode); + void load_rax(ExternalReference ext); void movsx_b(Register dst, const Operand& src); @@ -573,84 +466,208 @@ class Assembler : public Malloced { void xchg(Register dst, Register src); // Arithmetics - void adc(Register dst, int32_t imm32); - void adc(Register dst, const Operand& src); + void add(Register dst, Register src) { + arithmetic_op(0x03, dst, src); + } - void add(Register dst, const Operand& src); - void add(const Operand& dst, const Immediate& x); + void add(Register dst, const Operand& src) { + arithmetic_op(0x03, dst, src); + } - void and_(Register dst, int32_t imm32); - void and_(Register dst, const Operand& src); - void and_(const Operand& src, Register dst); - void and_(const Operand& dst, const Immediate& x); + + void add(const Operand& dst, Register src) { + arithmetic_op(0x01, src, dst); + } + + void add(Register dst, Immediate src) { + immediate_arithmetic_op(0x0, dst, src); + } + + void add(const Operand& dst, Immediate src) { + immediate_arithmetic_op(0x0, dst, src); + } + + void cmp(Register dst, Register src) { + arithmetic_op(0x3B, dst, src); + } + + void cmp(Register dst, const Operand& src) { + arithmetic_op(0x3B, dst, src); + } + + void cmp(const Operand& dst, Register src) { + arithmetic_op(0x39, src, dst); + } + + void cmp(Register dst, Immediate src) { + immediate_arithmetic_op(0x7, dst, src); + } + + void cmp(const Operand& dst, Immediate src) { + immediate_arithmetic_op(0x7, dst, src); + } + + void and_(Register dst, Register src) { + arithmetic_op(0x23, dst, src); + } + + void and_(Register dst, const Operand& src) { + arithmetic_op(0x23, dst, src); + } + + void and_(const Operand& dst, Register src) { + arithmetic_op(0x21, src, dst); + } + + void and_(Register dst, Immediate src) { + immediate_arithmetic_op(0x4, dst, src); + } + + void and_(const Operand& dst, Immediate src) { + immediate_arithmetic_op(0x4, dst, src); + } void cmpb(const Operand& op, int8_t imm8); void cmpb_al(const Operand& op); void cmpw_ax(const Operand& op); void cmpw(const Operand& op, Immediate imm16); - void cmp(Register reg, int32_t imm32); - void cmp(Register reg, Handle<Object> handle); - void cmp(Register reg, const Operand& op); - void cmp(const Operand& op, const Immediate& imm); void dec_b(Register dst); void dec(Register dst); void dec(const Operand& dst); - void cdq(); + // Sign-extends rax into rdx:rax. + void cqo(); + // Divide rdx:rax by src. Quotient in rax, remainder in rdx. void idiv(Register src); + void imul(Register dst, Register src); void imul(Register dst, const Operand& src); - void imul(Register dst, Register src, int32_t imm32); + // Performs the operation dst = src * imm. + void imul(Register dst, Register src, Immediate imm); void inc(Register dst); void inc(const Operand& dst); void lea(Register dst, const Operand& src); + // Multiply rax by src, put the result in rdx:rax. void mul(Register src); void neg(Register dst); + void neg(const Operand& dst); void not_(Register dst); + void not_(const Operand& dst); + + void or_(Register dst, Register src) { + arithmetic_op(0x0B, dst, src); + } + + void or_(Register dst, const Operand& src) { + arithmetic_op(0x0B, dst, src); + } + + void or_(const Operand& dst, Register src) { + arithmetic_op(0x09, src, dst); + } + + void or_(Register dst, Immediate src) { + immediate_arithmetic_op(0x1, dst, src); + } + + void or_(const Operand& dst, Immediate src) { + immediate_arithmetic_op(0x1, dst, src); + } - void or_(Register dst, int32_t imm32); - void or_(Register dst, const Operand& src); - void or_(const Operand& dst, Register src); - void or_(const Operand& dst, const Immediate& x); void rcl(Register dst, uint8_t imm8); - void sar(Register dst, uint8_t imm8); - void sar(Register dst); + // Shifts dst:src left by cl bits, affecting only dst. + void shld(Register dst, Register src); - void sbb(Register dst, const Operand& src); + // Shifts src:dst right by cl bits, affecting only dst. + void shrd(Register dst, Register src); - void shld(Register dst, const Operand& src); + // Shifts dst right, duplicating sign bit, by shift_amount bits. + // Shifting by 1 is handled efficiently. + void sar(Register dst, Immediate shift_amount) { + shift(dst, shift_amount, 0x7); + } - void shl(Register dst, uint8_t imm8); - void shl(Register dst); + // Shifts dst right, duplicating sign bit, by cl % 64 bits. + void sar(Register dst) { + shift(dst, 0x7); + } - void shrd(Register dst, const Operand& src); + void shl(Register dst, Immediate shift_amount) { + shift(dst, shift_amount, 0x4); + } - void shr(Register dst, uint8_t imm8); - void shr(Register dst); - void shr_cl(Register dst); + void shl(Register dst) { + shift(dst, 0x4); + } - void sub(const Operand& dst, const Immediate& x); - void sub(Register dst, const Operand& src); - void sub(const Operand& dst, Register src); + void shr(Register dst, Immediate shift_amount) { + shift(dst, shift_amount, 0x5); + } - void test(Register reg, const Immediate& imm); - void test(Register reg, const Operand& op); - void test(const Operand& op, const Immediate& imm); + void shr(Register dst) { + shift(dst, 0x5); + } + + void store_rax(void* dst, RelocInfo::Mode mode); + void store_rax(ExternalReference ref); + + void sub(Register dst, Register src) { + arithmetic_op(0x2B, dst, src); + } + + void sub(Register dst, const Operand& src) { + arithmetic_op(0x2B, dst, src); + } + + void sub(const Operand& dst, Register src) { + arithmetic_op(0x29, src, dst); + } + + void sub(Register dst, Immediate src) { + immediate_arithmetic_op(0x5, dst, src); + } + + void sub(const Operand& dst, Immediate src) { + immediate_arithmetic_op(0x5, dst, src); + } + + void testb(Register reg, Immediate mask); + void testb(const Operand& op, Immediate mask); + void testl(Register reg, Immediate mask); + void testl(const Operand& op, Immediate mask); + void testq(const Operand& op, Register reg); + void testq(Register dst, Register src); + + void xor_(Register dst, Register src) { + arithmetic_op(0x33, dst, src); + } + + void xor_(Register dst, const Operand& src) { + arithmetic_op(0x33, dst, src); + } + + void xor_(const Operand& dst, Register src) { + arithmetic_op(0x31, src, dst); + } + + void xor_(Register dst, Immediate src) { + immediate_arithmetic_op(0x6, dst, src); + } + + void xor_(const Operand& dst, Immediate src) { + immediate_arithmetic_op(0x6, dst, src); + } - void xor_(Register dst, int32_t imm32); - void xor_(Register dst, const Operand& src); - void xor_(const Operand& src, Register dst); - void xor_(const Operand& dst, const Immediate& x); // Bit operations. void bt(const Operand& dst, Register src); @@ -660,6 +677,7 @@ class Assembler : public Malloced { void hlt(); void int3(); void nop(); + void nop(int n); void rdtsc(); void ret(int imm16); @@ -681,21 +699,26 @@ class Assembler : public Malloced { void bind(Label* L); // binds an unbound label L to the current code position // Calls + // Call near relative 32-bit displacement, relative to next instruction. void call(Label* L); - void call(byte* entry, RelocInfo::Mode rmode); - void call(const Operand& adr); - void call(Handle<Code> code, RelocInfo::Mode rmode); + + // Call near absolute indirect, address in register + void call(Register adr); + + // Call near indirect + void call(const Operand& operand); // Jumps + // Jump short or near relative. void jmp(Label* L); // unconditional jump to L - void jmp(byte* entry, RelocInfo::Mode rmode); - void jmp(const Operand& adr); - void jmp(Handle<Code> code, RelocInfo::Mode rmode); + + // Jump near absolute indirect (r64) + void jmp(Register adr); // Conditional jumps - void j(Condition cc, Label* L, Hint hint = no_hint); - void j(Condition cc, byte* entry, RelocInfo::Mode rmode, Hint hint = no_hint); - void j(Condition cc, Handle<Code> code, Hint hint = no_hint); + void j(Condition cc, Label* L); + void j(Condition cc, byte* entry, RelocInfo::Mode rmode); + void j(Condition cc, Handle<Code> code); // Floating-point operations void fld(int i); @@ -788,9 +811,13 @@ class Assembler : public Malloced { void RecordStatementPosition(int pos); void WriteRecordedPositions(); - // Writes a single word of data in the code stream. + // Writes a doubleword of data in the code stream. + // Used for inline tables, e.g., jump-tables. + void dd(uint32_t data); + + // Writes a quadword of data in the code stream. // Used for inline tables, e.g., jump-tables. - void dd(uint32_t data, RelocInfo::Mode reloc_info); + void dd(uint64_t data, RelocInfo::Mode reloc_info); // Writes the absolute address of a bound label at the given position in // the generated code. That positions should have the relocation mode @@ -833,25 +860,119 @@ class Assembler : public Malloced { // code emission void GrowBuffer(); - inline void emit(uint32_t x); + + void emit(byte x) { *pc_++ = x; } + inline void emitl(uint32_t x); inline void emit(Handle<Object> handle); - inline void emit(uint32_t x, RelocInfo::Mode rmode); - inline void emit(const Immediate& x); - inline void emit_w(const Immediate& x); + inline void emitq(uint64_t x, RelocInfo::Mode rmode); + inline void emitw(uint16_t x); + void emit(Immediate x) { emitl(x.value_); } + + // Emits a REX prefix that encodes a 64-bit operand size and + // the top bit of both register codes. + // High bit of reg goes to REX.R, high bit of rm_reg goes to REX.B. + // REX.W is set. + inline void emit_rex_64(Register reg, Register rm_reg); + + // Emits a REX prefix that encodes a 64-bit operand size and + // the top bit of the destination, index, and base register codes. + // The high bit of reg is used for REX.R, the high bit of op's base + // register is used for REX.B, and the high bit of op's index register + // is used for REX.X. REX.W is set. + inline void emit_rex_64(Register reg, const Operand& op); + + // Emits a REX prefix that encodes a 64-bit operand size and + // the top bit of the register code. + // The high bit of register is used for REX.B. + // REX.W is set and REX.R and REX.X are clear. + inline void emit_rex_64(Register rm_reg); + + // Emits a REX prefix that encodes a 64-bit operand size and + // the top bit of the index and base register codes. + // The high bit of op's base register is used for REX.B, and the high + // bit of op's index register is used for REX.X. + // REX.W is set and REX.R clear. + inline void emit_rex_64(const Operand& op); + + // Emit a REX prefix that only sets REX.W to choose a 64-bit operand size. + void emit_rex_64() { emit(0x48); } + + // High bit of reg goes to REX.R, high bit of rm_reg goes to REX.B. + // REX.W is clear. + inline void emit_rex_32(Register reg, Register rm_reg); + + // The high bit of reg is used for REX.R, the high bit of op's base + // register is used for REX.B, and the high bit of op's index register + // is used for REX.X. REX.W is cleared. + inline void emit_rex_32(Register reg, const Operand& op); + + // High bit of rm_reg goes to REX.B. + // REX.W, REX.R and REX.X are clear. + inline void emit_rex_32(Register rm_reg); + + // High bit of base goes to REX.B and high bit of index to REX.X. + // REX.W and REX.R are clear. + inline void emit_rex_32(const Operand& op); + + // High bit of reg goes to REX.R, high bit of rm_reg goes to REX.B. + // REX.W is cleared. If no REX bits are set, no byte is emitted. + inline void emit_optional_rex_32(Register reg, Register rm_reg); + + // The high bit of reg is used for REX.R, the high bit of op's base + // register is used for REX.B, and the high bit of op's index register + // is used for REX.X. REX.W is cleared. If no REX bits are set, nothing + // is emitted. + inline void emit_optional_rex_32(Register reg, const Operand& op); + + // Optionally do as emit_rex_32(Register) if the register number has + // the high bit set. + inline void emit_optional_rex_32(Register rm_reg); + + // Optionally do as emit_rex_32(const Operand&) if the operand register + // numbers have a high bit set. + inline void emit_optional_rex_32(const Operand& op); + + + // Emit the ModR/M byte, and optionally the SIB byte and + // 1- or 4-byte offset for a memory operand. Also encodes + // the second operand of the operation, a register or operation + // subcode, into the reg field of the ModR/M byte. + void emit_operand(Register reg, const Operand& adr) { + emit_operand(reg.code() & 0x07, adr); + } - // Emit the code-object-relative offset of the label's position - inline void emit_code_relative_offset(Label* label); + // Emit the ModR/M byte, and optionally the SIB byte and + // 1- or 4-byte offset for a memory operand. Also used to encode + // a three-bit opcode extension into the ModR/M byte. + void emit_operand(int rm, const Operand& adr); - // instruction generation - void emit_arith_b(int op1, int op2, Register dst, int imm8); + // Emit a ModR/M byte with registers coded in the reg and rm_reg fields. + void emit_modrm(Register reg, Register rm_reg) { + emit(0xC0 | (reg.code() & 0x7) << 3 | (rm_reg.code() & 0x7)); + } - // Emit a basic arithmetic instruction (i.e. first byte of the family is 0x81) - // with a given destination expression and an immediate operand. It attempts - // to use the shortest encoding possible. - // sel specifies the /n in the modrm byte (see the Intel PRM). - void emit_arith(int sel, Operand dst, const Immediate& x); + // Emit a ModR/M byte with an operation subcode in the reg field and + // a register in the rm_reg field. + void emit_modrm(int code, Register rm_reg) { + ASSERT((code & ~0x7) == 0); + emit(0xC0 | (code & 0x7) << 3 | (rm_reg.code() & 0x7)); + } - void emit_operand(Register reg, const Operand& adr); + // Emit the code-object-relative offset of the label's position + inline void emit_code_relative_offset(Label* label); + + // Emit machine code for one of the operations ADD, ADC, SUB, SBC, + // AND, OR, XOR, or CMP. The encodings of these operations are all + // similar, differing just in the opcode or in the reg field of the + // ModR/M byte. + void arithmetic_op(byte opcode, Register dst, Register src); + void arithmetic_op(byte opcode, Register reg, const Operand& op); + void immediate_arithmetic_op(byte subcode, Register dst, Immediate src); + void immediate_arithmetic_op(byte subcode, const Operand& dst, Immediate src); + // Emit machine code for a shift operation. + void shift(Register dst, Immediate shift_amount, int subcode); + // Shift dst by cl % 64 bits. + void shift(Register dst, int subcode); void emit_farith(int b1, int b2, int i); @@ -860,11 +981,6 @@ class Assembler : public Malloced { void bind_to(Label* L, int pos); void link_to(Label* L, Label* appendix); - // displacements - inline Displacement disp_at(Label* L); - inline void disp_at_put(Label* L, Displacement disp); - inline void emit_disp(Label* L, Displacement::Type type); - // record reloc info for current pc_ void RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data = 0); @@ -877,6 +993,8 @@ class Assembler : public Malloced { int buffer_size_; // True if the assembler owns the buffer, false if buffer is external. bool own_buffer_; + // A previously allocated buffer of kMinimalBufferSize bytes, or NULL. + static byte* spare_buffer_; // code generation byte* pc_; // the program counter; moves forward diff --git a/deps/v8/src/x64/builtins-x64.cc b/deps/v8/src/x64/builtins-x64.cc index 209aa2d307..3f1cd9faba 100644 --- a/deps/v8/src/x64/builtins-x64.cc +++ b/deps/v8/src/x64/builtins-x64.cc @@ -25,3 +25,41 @@ // (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-inl.h" + +namespace v8 { +namespace internal { + +void Builtins::Generate_Adaptor(MacroAssembler* masm, + Builtins::CFunctionId id) { + masm->int3(); // UNIMPLEMENTED. +} + +void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { + masm->int3(); // UNIMPLEMENTED. +} + +void Builtins::Generate_FunctionApply(MacroAssembler* masm) { + masm->int3(); // UNIMPLEMENTED. +} + +void Builtins::Generate_FunctionCall(MacroAssembler* masm) { + masm->int3(); // UNIMPLEMENTED. +} + +void Builtins::Generate_JSConstructCall(MacroAssembler* masm) { + masm->int3(); // UNIMPLEMENTED. +} + +void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) { + masm->int3(); // UNIMPLEMENTED. +} + +void Builtins::Generate_JSEntryTrampoline(MacroAssembler* masm) { + masm->int3(); // UNIMPLEMENTED. +} + +} } // namespace v8::internal + + diff --git a/deps/v8/src/x64/codegen-x64-inl.h b/deps/v8/src/x64/codegen-x64-inl.h new file mode 100644 index 0000000000..0d5b0e21dc --- /dev/null +++ b/deps/v8/src/x64/codegen-x64-inl.h @@ -0,0 +1,42 @@ +// 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_X64_CODEGEN_X64_INL_H_ +#define V8_X64_CODEGEN_X64_INL_H_ + +namespace v8 { +namespace internal { + +// Platform-specific inline functions. + +void DeferredCode::Jump() { UNIMPLEMENTED(); } +void DeferredCode::Branch(Condition cc) { UNIMPLEMENTED(); } + +} } // namespace v8::internal + +#endif // V8_X64_CODEGEN_X64_INL_H_ diff --git a/deps/v8/src/x64/codegen-x64.cc b/deps/v8/src/x64/codegen-x64.cc index 209aa2d307..ca58e09a75 100644 --- a/deps/v8/src/x64/codegen-x64.cc +++ b/deps/v8/src/x64/codegen-x64.cc @@ -25,3 +25,312 @@ // (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 "macro-assembler.h" +#include "register-allocator-inl.h" +#include "codegen.h" + +namespace v8 { +namespace internal { + +// ------------------------------------------------------------------------- +// Platform-specific DeferredCode functions. + +void DeferredCode::SaveRegisters() { UNIMPLEMENTED(); } + +void DeferredCode::RestoreRegisters() { UNIMPLEMENTED(); } + + +CodeGenerator::CodeGenerator(int buffer_size, + Handle<Script> script, + bool is_eval) + : is_eval_(is_eval), + script_(script), + deferred_(8), + masm_(new MacroAssembler(NULL, buffer_size)), + scope_(NULL), + frame_(NULL), + allocator_(NULL), + state_(NULL), + loop_nesting_(0), + function_return_is_shadowed_(false), + in_spilled_code_(false) { +} + +#define __ masm-> + + +void CodeGenerator::DeclareGlobals(Handle<FixedArray> a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::GenCode(FunctionLiteral* a) { + masm_->int3(); // UNIMPLEMENTED +} + +void CodeGenerator::GenerateFastCaseSwitchJumpTable(SwitchStatement* a, + int b, + int c, + Label* d, + Vector<Label*> e, + Vector<Label> f) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitStatements(ZoneList<Statement*>* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitBlock(Block* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitDeclaration(Declaration* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitExpressionStatement(ExpressionStatement* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitEmptyStatement(EmptyStatement* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitIfStatement(IfStatement* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitContinueStatement(ContinueStatement* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitBreakStatement(BreakStatement* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitReturnStatement(ReturnStatement* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitWithEnterStatement(WithEnterStatement* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitWithExitStatement(WithExitStatement* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitSwitchStatement(SwitchStatement* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitLoopStatement(LoopStatement* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitForInStatement(ForInStatement* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitTryCatch(TryCatch* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitTryFinally(TryFinally* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitDebuggerStatement(DebuggerStatement* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitFunctionLiteral(FunctionLiteral* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitFunctionBoilerplateLiteral( + FunctionBoilerplateLiteral* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitConditional(Conditional* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitSlot(Slot* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitVariableProxy(VariableProxy* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitLiteral(Literal* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitRegExpLiteral(RegExpLiteral* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitObjectLiteral(ObjectLiteral* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitArrayLiteral(ArrayLiteral* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitAssignment(Assignment* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitThrow(Throw* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitProperty(Property* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitCall(Call* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitCallEval(CallEval* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitCallNew(CallNew* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitCallRuntime(CallRuntime* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitUnaryOperation(UnaryOperation* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitCountOperation(CountOperation* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitBinaryOperation(BinaryOperation* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitCompareOperation(CompareOperation* a) { + UNIMPLEMENTED(); +} + +void CodeGenerator::VisitThisFunction(ThisFunction* a) { + UNIMPLEMENTED(); +} + + +void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { + masm->int3(); // TODO(X64): UNIMPLEMENTED. +} + + +void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { + Label invoke, exit; + + // Setup frame. + __ push(rbp); + __ movq(rbp, rsp); + + // Save callee-saved registers (X64 calling conventions). + int marker = is_construct ? StackFrame::ENTRY_CONSTRUCT : StackFrame::ENTRY; + // Push something that is not an arguments adaptor. + __ push(Immediate(ArgumentsAdaptorFrame::NON_SENTINEL)); + __ push(Immediate(Smi::FromInt(marker))); // @ function offset + __ push(r12); + __ push(r13); + __ push(r14); + __ push(r15); + __ push(rdi); + __ push(rsi); + __ push(rbx); + // TODO(X64): Push XMM6-XMM15 (low 64 bits) as well, or make them + // callee-save in JS code as well. + + // Save copies of the top frame descriptor on the stack. + ExternalReference c_entry_fp(Top::k_c_entry_fp_address); + __ load_rax(c_entry_fp); + __ push(rax); + + // Call a faked try-block that does the invoke. + __ call(&invoke); + + // Caught exception: Store result (exception) in the pending + // exception field in the JSEnv and return a failure sentinel. + ExternalReference pending_exception(Top::k_pending_exception_address); + __ store_rax(pending_exception); + __ movq(rax, Failure::Exception(), RelocInfo::NONE); + __ jmp(&exit); + + // Invoke: Link this frame into the handler chain. + __ bind(&invoke); + __ PushTryHandler(IN_JS_ENTRY, JS_ENTRY_HANDLER); + __ push(rax); // flush TOS + + // Clear any pending exceptions. + __ load_rax(ExternalReference::the_hole_value_location()); + __ store_rax(pending_exception); + + // Fake a receiver (NULL). + __ push(Immediate(0)); // receiver + + // Invoke the function by calling through JS entry trampoline + // builtin and pop the faked function when we return. We load the address + // from an external reference instead of inlining the call target address + // directly in the code, because the builtin stubs may not have been + // generated yet at the time this code is generated. + if (is_construct) { + ExternalReference construct_entry(Builtins::JSConstructEntryTrampoline); + __ load_rax(construct_entry); + } else { + ExternalReference entry(Builtins::JSEntryTrampoline); + __ load_rax(entry); + } + __ call(FieldOperand(rax, Code::kHeaderSize)); + + // Unlink this frame from the handler chain. + __ movq(kScratchRegister, ExternalReference(Top::k_handler_address)); + __ pop(Operand(kScratchRegister, 0)); + // Pop next_sp. + __ add(rsp, Immediate(StackHandlerConstants::kSize - kPointerSize)); + + // Restore the top frame descriptor from the stack. + __ bind(&exit); + __ movq(kScratchRegister, ExternalReference(Top::k_c_entry_fp_address)); + __ pop(Operand(kScratchRegister, 0)); + + // Restore callee-saved registers (X64 conventions). + __ pop(rbx); + __ pop(rsi); + __ pop(rdi); + __ pop(r15); + __ pop(r14); + __ pop(r13); + __ pop(r12); + __ add(rsp, Immediate(2 * kPointerSize)); // remove markers + + // Restore frame pointer and return. + __ pop(rbp); + __ ret(0); +} + + +#undef __ + +} } // namespace v8::internal diff --git a/deps/v8/src/x64/codegen-x64.h b/deps/v8/src/x64/codegen-x64.h index 4acb0cb7ff..5f5daa422b 100644 --- a/deps/v8/src/x64/codegen-x64.h +++ b/deps/v8/src/x64/codegen-x64.h @@ -28,7 +28,8 @@ #ifndef V8_X64_CODEGEN_X64_H_ #define V8_X64_CODEGEN_X64_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Forward declarations class DeferredCode; @@ -332,8 +333,7 @@ class CodeGenerator: public AstVisitor { // Accessors Scope* scope() const { return scope_; } - // Clearing and generating deferred code. - void ClearDeferred(); + // Generating deferred code. void ProcessDeferred(); bool is_eval() { return is_eval_; } @@ -473,12 +473,19 @@ class CodeGenerator: public AstVisitor { void CheckStack(); + struct InlineRuntimeLUT { + void (CodeGenerator::*method)(ZoneList<Expression*>*); + const char* name; + }; + static InlineRuntimeLUT* FindInlineRuntimeLUT(Handle<String> name); bool CheckForInlineRuntimeCall(CallRuntime* node); + static bool PatchInlineRuntimeEntry(Handle<String> name, + const InlineRuntimeLUT& new_entry, + InlineRuntimeLUT* old_entry); Handle<JSFunction> BuildBoilerplate(FunctionLiteral* node); void ProcessDeclarations(ZoneList<Declaration*>* declarations); - Handle<Code> ComputeCallInitialize(int argc); - Handle<Code> ComputeCallInitializeInLoop(int argc); + Handle<Code> ComputeCallInitialize(int argc, InLoopFlag in_loop); // Declare global variables and functions in the given array of // name/value pairs. @@ -570,14 +577,14 @@ class CodeGenerator: public AstVisitor { void CodeForSourcePosition(int pos); #ifdef DEBUG - // True if the registers are valid for entry to a block. There should be - // no frame-external references to eax, ebx, ecx, edx, or edi. + // True if the registers are valid for entry to a block. There should + // be no frame-external references to (non-reserved) registers. bool HasValidEntryRegisters(); #endif bool is_eval_; // Tells whether code is generated for eval. Handle<Script> script_; - List<DeferredCode*> deferred_; + ZoneList<DeferredCode*> deferred_; // Assembler MacroAssembler* masm_; // to generate code @@ -604,6 +611,8 @@ class CodeGenerator: public AstVisitor { // in a spilled state. bool in_spilled_code_; + static InlineRuntimeLUT kInlineRuntimeLUT[]; + friend class VirtualFrame; friend class JumpTarget; friend class Reference; diff --git a/deps/v8/src/x64/cpu-x64.cc b/deps/v8/src/x64/cpu-x64.cc index 209aa2d307..8df0ab7e97 100644 --- a/deps/v8/src/x64/cpu-x64.cc +++ b/deps/v8/src/x64/cpu-x64.cc @@ -25,3 +25,42 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// CPU specific code for x64 independent of OS goes here. + +#include "v8.h" + +#include "cpu.h" +#include "macro-assembler.h" + +namespace v8 { +namespace internal { + +void CPU::Setup() { + CpuFeatures::Probe(); +} + + +void CPU::FlushICache(void* start, size_t size) { + // No need to flush the instruction cache on Intel. On Intel instruction + // cache flushing is only necessary when multiple cores running the same + // code simultaneously. V8 (and JavaScript) is single threaded and when code + // is patched on an intel CPU the core performing the patching will have its + // own instruction cache updated automatically. + + // If flushing of the instruction cache becomes necessary Windows has the + // API function FlushInstructionCache. +} + + +void CPU::DebugBreak() { +#ifdef _MSC_VER + // To avoid Visual Studio runtime support the following code can be used + // instead + // __asm { int 3 } + __debugbreak(); +#else + asm("int $3"); +#endif +} + +} } // namespace v8::internal diff --git a/deps/v8/src/x64/debug-x64.cc b/deps/v8/src/x64/debug-x64.cc index 209aa2d307..3b101325e6 100644 --- a/deps/v8/src/x64/debug-x64.cc +++ b/deps/v8/src/x64/debug-x64.cc @@ -25,3 +25,59 @@ // (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-inl.h" +#include "debug.h" + + +namespace v8 { +namespace internal { + +#ifdef ENABLE_DEBUGGER_SUPPORT + +bool Debug::IsDebugBreakAtReturn(v8::internal::RelocInfo* rinfo) { + UNIMPLEMENTED(); + return false; +} + +void Debug::GenerateCallICDebugBreak(MacroAssembler* masm) { + masm->int3(); // UNIMPLEMENTED +} + +void Debug::GenerateConstructCallDebugBreak(MacroAssembler* masm) { + masm->int3(); // UNIMPLEMENTED +} + +void Debug::GenerateKeyedLoadICDebugBreak(MacroAssembler* masm) { + masm->int3(); // UNIMPLEMENTED +} + +void Debug::GenerateKeyedStoreICDebugBreak(MacroAssembler* masm) { + masm->int3(); // UNIMPLEMENTED +} + +void Debug::GenerateLoadICDebugBreak(MacroAssembler* masm) { + masm->int3(); // UNIMPLEMENTED +} + +void Debug::GenerateReturnDebugBreak(MacroAssembler* masm) { + masm->int3(); // UNIMPLEMENTED +} + +void Debug::GenerateReturnDebugBreakEntry(MacroAssembler* masm) { + masm->int3(); // UNIMPLEMENTED +} + +void Debug::GenerateStoreICDebugBreak(MacroAssembler* masm) { + masm->int3(); // UNIMPLEMENTED +} + +void Debug::GenerateStubNoRegistersDebugBreak(MacroAssembler* masm) { + masm->int3(); // UNIMPLEMENTED +} + +#endif // ENABLE_DEBUGGER_SUPPORT + +} } // namespace v8::internal diff --git a/deps/v8/src/x64/disasm-x64.cc b/deps/v8/src/x64/disasm-x64.cc index 209aa2d307..767b1247a5 100644 --- a/deps/v8/src/x64/disasm-x64.cc +++ b/deps/v8/src/x64/disasm-x64.cc @@ -25,3 +25,64 @@ // (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 "disasm.h" + +namespace disasm { + +Disassembler::Disassembler(NameConverter const& converter) + : converter_(converter) { + UNIMPLEMENTED(); +} + + +Disassembler::~Disassembler() { + UNIMPLEMENTED(); +} + + +const char* NameConverter::NameOfAddress(unsigned char* addr) const { + UNIMPLEMENTED(); + return NULL; +} + + +const char* NameConverter::NameOfCPURegister(int reg) const { + UNIMPLEMENTED(); + return NULL; +} + + +int Disassembler::ConstantPoolSizeAt(unsigned char* addr) { + UNIMPLEMENTED(); + return 0; +} + + +int Disassembler::InstructionDecode(v8::internal::Vector<char> buffer, + unsigned char* instruction) { + UNIMPLEMENTED(); + return 0; +} + +const char* NameConverter::NameOfByteCPURegister(int a) const { + UNIMPLEMENTED(); + return NULL; +} + +const char* NameConverter::NameOfXMMRegister(int a) const { + UNIMPLEMENTED(); + return NULL; +} + +const char* NameConverter::NameOfConstant(unsigned char* a) const { + UNIMPLEMENTED(); + return NULL; +} + +const char* NameConverter::NameInCode(unsigned char* a) const { + UNIMPLEMENTED(); + return NULL; +} + +} // namespace disasm diff --git a/deps/v8/src/x64/frames-x64.h b/deps/v8/src/x64/frames-x64.h index f4468f6a2f..3416f51dea 100644 --- a/deps/v8/src/x64/frames-x64.h +++ b/deps/v8/src/x64/frames-x64.h @@ -28,7 +28,8 @@ #ifndef V8_X64_FRAMES_X64_H_ #define V8_X64_FRAMES_X64_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // TODO(x64): This is a stub, mostly just a copy of the ia32 bit version. // This will all need to change to be correct for x64. @@ -40,17 +41,17 @@ typedef Object* JSCallerSavedBuffer[kNumJSCallerSaved]; class StackHandlerConstants : public AllStatic { public: - static const int kNextOffset = -1 * kPointerSize; - static const int kPPOffset = -1 * kPointerSize; - static const int kFPOffset = -1 * kPointerSize; + static const int kNextOffset = 0 * kPointerSize; + static const int kPPOffset = 1 * kPointerSize; + static const int kFPOffset = 2 * kPointerSize; - static const int kCodeOffset = -1 * kPointerSize; + static const int kCodeOffset = 3 * kPointerSize; - static const int kStateOffset = -1 * kPointerSize; - static const int kPCOffset = -1 * kPointerSize; + static const int kStateOffset = 4 * kPointerSize; + static const int kPCOffset = 5 * kPointerSize; static const int kAddressDisplacement = -1 * kPointerSize; - static const int kSize = kPCOffset + kPointerSize; + static const int kSize = 6 * kPointerSize; }; diff --git a/deps/v8/src/x64/ic-x64.cc b/deps/v8/src/x64/ic-x64.cc index 209aa2d307..71a3a9ab85 100644 --- a/deps/v8/src/x64/ic-x64.cc +++ b/deps/v8/src/x64/ic-x64.cc @@ -25,3 +25,152 @@ // (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-inl.h" +#include "ic-inl.h" +#include "runtime.h" +#include "stub-cache.h" + +namespace v8 { +namespace internal { + + +void KeyedLoadIC::ClearInlinedVersion(Address address) { + UNIMPLEMENTED(); +} + +void KeyedLoadIC::Generate(MacroAssembler* masm, + ExternalReference const& f) { + masm->int3(); // UNIMPLEMENTED. +} + +void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { + masm->int3(); // UNIMPLEMENTED. +} + +void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) { + masm->int3(); // UNIMPLEMENTED. +} + +bool KeyedLoadIC::PatchInlinedLoad(Address address, Object* map) { + UNIMPLEMENTED(); + return false; +} + +Object* KeyedLoadStubCompiler::CompileLoadArrayLength(String* name) { + UNIMPLEMENTED(); + return NULL; +} + +Object* KeyedLoadStubCompiler::CompileLoadCallback(String* name, + JSObject* object, + JSObject* holder, + AccessorInfo* callback) { + UNIMPLEMENTED(); + return NULL; +} + +Object* KeyedLoadStubCompiler::CompileLoadConstant(String* name, + JSObject* object, + JSObject* holder, + Object* callback) { + UNIMPLEMENTED(); + return NULL; +} + +Object* KeyedLoadStubCompiler::CompileLoadField(String* name, + JSObject* object, + JSObject* holder, + int index) { + UNIMPLEMENTED(); + return NULL; +} + +Object* KeyedLoadStubCompiler::CompileLoadFunctionPrototype(String* name) { + UNIMPLEMENTED(); + return NULL; +} + +Object* KeyedLoadStubCompiler::CompileLoadInterceptor(JSObject* object, + JSObject* holder, + String* name) { + UNIMPLEMENTED(); + return NULL; +} + +Object* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) { + UNIMPLEMENTED(); + return NULL; +} + +void KeyedStoreIC::Generate(MacroAssembler* masm, ExternalReference const& f) { + masm->int3(); // UNIMPLEMENTED. +} + +void KeyedStoreIC::GenerateExtendStorage(MacroAssembler* masm) { + masm->int3(); // UNIMPLEMENTED. +} + +void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) { + masm->int3(); // UNIMPLEMENTED. +} + +Object* KeyedStoreStubCompiler::CompileStoreField(JSObject* object, + int index, + Map* transition, + String* name) { + UNIMPLEMENTED(); + return NULL; +} + +void LoadIC::ClearInlinedVersion(Address address) { + UNIMPLEMENTED(); +} + +void LoadIC::Generate(MacroAssembler* masm, ExternalReference const& f) { + masm->int3(); // UNIMPLEMENTED. +} + +void LoadIC::GenerateArrayLength(MacroAssembler* masm) { + masm->int3(); // UNIMPLEMENTED. +} + +void LoadIC::GenerateFunctionPrototype(MacroAssembler* masm) { + masm->int3(); // UNIMPLEMENTED. +} + +void LoadIC::GenerateMegamorphic(MacroAssembler* masm) { + masm->int3(); // UNIMPLEMENTED. +} + +void LoadIC::GenerateMiss(MacroAssembler* masm) { + masm->int3(); // UNIMPLEMENTED. +} + +void LoadIC::GenerateNormal(MacroAssembler* masm) { + masm->int3(); // UNIMPLEMENTED. +} + +void LoadIC::GenerateStringLength(MacroAssembler* masm) { + masm->int3(); // UNIMPLEMENTED. +} + +bool LoadIC::PatchInlinedLoad(Address address, Object* map, int index) { + UNIMPLEMENTED(); + return false; +} + +void StoreIC::Generate(MacroAssembler* masm, ExternalReference const& f) { + masm->int3(); // UNIMPLEMENTED. +} + +void StoreIC::GenerateExtendStorage(MacroAssembler* masm) { + masm->int3(); // UNIMPLEMENTED. +} + +void StoreIC::GenerateMegamorphic(MacroAssembler* masm) { + masm->int3(); // UNIMPLEMENTED. +} + +} } // namespace v8::internal diff --git a/deps/v8/src/x64/macro-assembler-x64.cc b/deps/v8/src/x64/macro-assembler-x64.cc index 209aa2d307..54c299dbfa 100644 --- a/deps/v8/src/x64/macro-assembler-x64.cc +++ b/deps/v8/src/x64/macro-assembler-x64.cc @@ -25,3 +25,92 @@ // (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 "bootstrapper.h" +#include "codegen-inl.h" +#include "macro-assembler-x64.h" + +namespace v8 { +namespace internal { + +MacroAssembler::MacroAssembler(void* buffer, int size) + : Assembler(buffer, size), + unresolved_(0), + generating_stub_(false), + allow_stub_calls_(true), + code_object_(Heap::undefined_value()) { +} + + +void MacroAssembler::TailCallRuntime(ExternalReference const& a, int b) { + UNIMPLEMENTED(); +} + + +void MacroAssembler::Set(Register dst, int64_t x) { + if (is_int32(x)) { + movq(dst, Immediate(x)); + } else if (is_uint32(x)) { + movl(dst, Immediate(x)); + } else { + movq(dst, x, RelocInfo::NONE); + } +} + + +void MacroAssembler::Set(const Operand& dst, int64_t x) { + if (is_int32(x)) { + movq(kScratchRegister, Immediate(x)); + } else if (is_uint32(x)) { + movl(kScratchRegister, Immediate(x)); + } else { + movq(kScratchRegister, x, RelocInfo::NONE); + } + movq(dst, kScratchRegister); +} + + +void MacroAssembler::PushTryHandler(CodeLocation try_location, + HandlerType type) { + // The pc (return address) is already on TOS. + // This code pushes state, code, frame pointer and parameter pointer. + // Check that they are expected next on the stack, int that order. + ASSERT_EQ(StackHandlerConstants::kStateOffset, + StackHandlerConstants::kPCOffset - kPointerSize); + ASSERT_EQ(StackHandlerConstants::kCodeOffset, + StackHandlerConstants::kStateOffset - kPointerSize); + ASSERT_EQ(StackHandlerConstants::kFPOffset, + StackHandlerConstants::kCodeOffset - kPointerSize); + ASSERT_EQ(StackHandlerConstants::kPPOffset, + StackHandlerConstants::kFPOffset - kPointerSize); + + if (try_location == IN_JAVASCRIPT) { + if (type == TRY_CATCH_HANDLER) { + push(Immediate(StackHandler::TRY_CATCH)); + } else { + push(Immediate(StackHandler::TRY_FINALLY)); + } + push(Immediate(Smi::FromInt(StackHandler::kCodeNotPresent))); + push(rbp); + push(rdi); + } else { + ASSERT(try_location == IN_JS_ENTRY); + // The parameter pointer is meaningless here and ebp does not + // point to a JS frame. So we save NULL for both pp and ebp. We + // expect the code throwing an exception to check ebp before + // dereferencing it to restore the context. + push(Immediate(StackHandler::ENTRY)); + push(Immediate(Smi::FromInt(StackHandler::kCodeNotPresent))); + push(Immediate(0)); // NULL frame pointer + push(Immediate(0)); // NULL parameter pointer + } + movq(kScratchRegister, ExternalReference(Top::k_handler_address)); + // Cached TOS. + movq(rax, Operand(kScratchRegister, 0)); + // Link this handler. + movq(Operand(kScratchRegister, 0), rsp); +} + + +} } // namespace v8::internal diff --git a/deps/v8/src/x64/macro-assembler-x64.h b/deps/v8/src/x64/macro-assembler-x64.h index 159d0c4f35..4af372a81a 100644 --- a/deps/v8/src/x64/macro-assembler-x64.h +++ b/deps/v8/src/x64/macro-assembler-x64.h @@ -30,7 +30,13 @@ #include "assembler.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { + +// Default scratch register used by MacroAssembler (and other code that needs +// a spare register). The register isn't callee save, and not used by the +// function calling convention. +static const Register kScratchRegister = r10; // Forward declaration. class JumpTarget; @@ -136,8 +142,8 @@ class MacroAssembler: public Assembler { void GetBuiltinEntry(Register target, Builtins::JavaScript id); // Expression support - void Set(Register dst, const Immediate& x); - void Set(const Operand& dst, const Immediate& x); + void Set(Register dst, int64_t x); + void Set(const Operand& dst, int64_t x); // Compare object type for heap object. // Incoming register is heap_object and outgoing register is map. @@ -155,7 +161,7 @@ class MacroAssembler: public Assembler { // Push a new try handler and link into try handler chain. // The return address must be pushed before calling this helper. - // On exit, eax contains TOS (next_sp). + // On exit, rax contains TOS (next_sp). void PushTryHandler(CodeLocation try_location, HandlerType type); diff --git a/deps/v8/src/x64/register-allocator-x64-inl.h b/deps/v8/src/x64/register-allocator-x64-inl.h new file mode 100644 index 0000000000..f369d7d8d9 --- /dev/null +++ b/deps/v8/src/x64/register-allocator-x64-inl.h @@ -0,0 +1,69 @@ +// 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_X64_REGISTER_ALLOCATOR_X64_INL_H_ +#define V8_X64_REGISTER_ALLOCATOR_X64_INL_H_ + +#include "v8.h" + +namespace v8 { +namespace internal { + +// ------------------------------------------------------------------------- +// RegisterAllocator implementation. + +bool RegisterAllocator::IsReserved(Register reg) { + // All registers are reserved for now. + return true; +} + + +// The register allocator uses small integers to represent the +// non-reserved assembler registers. + +int RegisterAllocator::ToNumber(Register reg) { + ASSERT(reg.is_valid() && !IsReserved(reg)); + UNIMPLEMENTED(); + return -1; +} + + +Register RegisterAllocator::ToRegister(int num) { + ASSERT(num >= 0 && num < kNumRegisters); + UNIMPLEMENTED(); + return no_reg; +} + + +void RegisterAllocator::Initialize() { + UNIMPLEMENTED(); +} + + +} } // namespace v8::internal + +#endif // V8_X64_REGISTER_ALLOCATOR_X64_INL_H_ diff --git a/deps/v8/src/x64/register-allocator-x64.h b/deps/v8/src/x64/register-allocator-x64.h new file mode 100644 index 0000000000..bc08112479 --- /dev/null +++ b/deps/v8/src/x64/register-allocator-x64.h @@ -0,0 +1,45 @@ +// 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_X64_REGISTER_ALLOCATOR_X64_H_ +#define V8_X64_REGISTER_ALLOCATOR_X64_H_ + +namespace v8 { +namespace internal { + +class RegisterAllocatorConstants : public AllStatic { + public: + // Register allocation is not yet implemented on x64, but C++ + // forbids 0-length arrays so we use 1 as the number of registers. + static const int kNumRegisters = 1; + static const int kInvalidRegister = -1; +}; + + +} } // namespace v8::internal + +#endif // V8_X64_REGISTER_ALLOCATOR_X64_H_ diff --git a/deps/v8/src/x64/virtual-frame-x64.h b/deps/v8/src/x64/virtual-frame-x64.h index f71766d033..d341a1eee4 100644 --- a/deps/v8/src/x64/virtual-frame-x64.h +++ b/deps/v8/src/x64/virtual-frame-x64.h @@ -29,8 +29,10 @@ #define V8_X64_VIRTUAL_FRAME_X64_H_ #include "register-allocator.h" +#include "scopes.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // ------------------------------------------------------------------------- // Virtual frames @@ -41,7 +43,7 @@ namespace v8 { namespace internal { // as random access to the expression stack elements, locals, and // parameters. -class VirtualFrame : public Malloced { +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 @@ -50,42 +52,66 @@ class VirtualFrame : public Malloced { // generator is being transformed. class SpilledScope BASE_EMBEDDED { public: - explicit SpilledScope(CodeGenerator* cgen); + SpilledScope() : previous_state_(cgen()->in_spilled_code()) { + ASSERT(cgen()->has_valid_frame()); + cgen()->frame()->SpillAll(); + cgen()->set_in_spilled_code(true); + } - ~SpilledScope(); + ~SpilledScope() { + cgen()->set_in_spilled_code(previous_state_); + } private: - CodeGenerator* cgen_; bool previous_state_; + + CodeGenerator* cgen() { return CodeGeneratorScope::Current(); } }; // An illegal index into the virtual frame. static const int kIllegalIndex = -1; // Construct an initial virtual frame on entry to a JS function. - explicit VirtualFrame(CodeGenerator* cgen); + VirtualFrame(); // Construct a virtual frame as a clone of an existing one. explicit VirtualFrame(VirtualFrame* original); + CodeGenerator* cgen() { return CodeGeneratorScope::Current(); } + MacroAssembler* masm() { return cgen()->masm(); } + // Create a duplicate of an existing valid frame element. FrameElement CopyElementAt(int index); + // The number of elements on the virtual frame. + int element_count() { return elements_.length(); } + // The height of the virtual expression stack. - int height() const { - return elements_.length() - expression_base_index(); + int height() { + return element_count() - expression_base_index(); + } + + int register_location(int num) { + ASSERT(num >= 0 && num < RegisterAllocator::kNumRegisters); + return register_locations_[num]; } - int register_index(Register reg) { - return register_locations_[reg.code()]; + int register_location(Register reg) { + return register_locations_[RegisterAllocator::ToNumber(reg)]; } - bool is_used(int reg_code) { - return register_locations_[reg_code] != kIllegalIndex; + void set_register_location(Register reg, int index) { + register_locations_[RegisterAllocator::ToNumber(reg)] = index; + } + + bool is_used(int num) { + ASSERT(num >= 0 && num < RegisterAllocator::kNumRegisters); + return register_locations_[num] != kIllegalIndex; } bool is_used(Register reg) { - return is_used(reg.code()); + return register_locations_[RegisterAllocator::ToNumber(reg)] + != kIllegalIndex; } // Add extra in-memory elements to the top of the frame to match an actual @@ -98,7 +124,12 @@ class VirtualFrame : public Malloced { // 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); + 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 @@ -109,13 +140,25 @@ class VirtualFrame : public Malloced { void SpillAll(); // Spill all occurrences of a specific register from the frame. - void Spill(Register reg); + void Spill(Register reg) { + if (is_used(reg)) SpillElementAt(register_location(reg)); + } // 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(); + // 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 + // topmost mergable_elements elements of the frame. A + // mergable_elements of JumpTarget::kAllElements indicates constants + // and copies are should be removed from the entire frame. + void MakeMergable(int mergable_elements); + // 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. @@ -130,13 +173,23 @@ class VirtualFrame : public Malloced { // 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(); + 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(); + 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 @@ -151,7 +204,7 @@ class VirtualFrame : public Malloced { void PrepareForReturn(); // Allocate and initialize the frame-allocated locals. - void AllocateStackSlots(int count); + void AllocateStackSlots(); // An element of the expression stack as an assembly operand. Operand ElementAt(int index) const { @@ -164,22 +217,22 @@ class VirtualFrame : public Malloced { // Set a frame element to a constant. The index is frame-top relative. void SetElementAt(int index, Handle<Object> value) { - Result temp(value, cgen_); + Result temp(value); SetElementAt(index, &temp); } void PushElementAt(int index) { - PushFrameSlotAt(elements_.length() - index - 1); + PushFrameSlotAt(element_count() - index - 1); } void StoreToElementAt(int index) { - StoreToFrameSlotAt(elements_.length() - index - 1); + StoreToFrameSlotAt(element_count() - index - 1); } // A frame-allocated local as an assembly operand. - Operand LocalAt(int index) const { + Operand LocalAt(int index) { ASSERT(0 <= index); - ASSERT(index < local_count_); + ASSERT(index < local_count()); return Operand(rbp, kLocal0Offset - index * kPointerSize); } @@ -215,10 +268,10 @@ class VirtualFrame : public Malloced { void RestoreContextRegister(); // A parameter as an assembly operand. - Operand ParameterAt(int index) const { + Operand ParameterAt(int index) { ASSERT(-1 <= index); // -1 is the receiver. - ASSERT(index < parameter_count_); - return Operand(rbp, (1 + parameter_count_ - index) * kPointerSize); + ASSERT(index < parameter_count()); + return Operand(rbp, (1 + parameter_count() - index) * kPointerSize); } // Push a copy of the value of a parameter frame slot on top of the frame. @@ -240,14 +293,17 @@ class VirtualFrame : public Malloced { } // The receiver frame slot. - Operand Receiver() const { return ParameterAt(-1); } + 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. - Result CallStub(CodeStub* stub, int arg_count); + Result CallStub(CodeStub* stub, int arg_count) { + PrepareForCall(arg_count, arg_count); + return RawCallStub(stub); + } // 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 @@ -307,7 +363,7 @@ class VirtualFrame : public Malloced { void Drop() { Drop(1); } // Duplicate the top element of the frame. - void Dup() { PushFrameSlotAt(elements_.length() - 1); } + 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. @@ -331,7 +387,15 @@ class VirtualFrame : public Malloced { // Pushing a result invalidates it (its contents become owned by the // frame). - void Push(Result* result); + void Push(Result* result) { + if (result->is_register()) { + Push(result->reg(), result->static_type()); + } else { + ASSERT(result->is_constant()); + Push(result->handle()); + } + result->Unuse(); + } // Nip removes zero or more elements from immediately below the top // of the frame, leaving the previous top-of-frame value on top of @@ -346,70 +410,69 @@ class VirtualFrame : public Malloced { static const int kHandlerSize = StackHandlerConstants::kSize / kPointerSize; static const int kPreallocatedElements = 5 + 8; // 8 expression stack slots. - CodeGenerator* cgen_; - MacroAssembler* masm_; - - List<FrameElement> elements_; - - // The number of frame-allocated locals and parameters respectively. - int parameter_count_; - int local_count_; + 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 element that is at the processor's frame pointer - // (the ebp register). - int frame_pointer_; - // The index of the register frame element using each register, or // kIllegalIndex if a register is not on the frame. - int register_locations_[kNumRegisters]; + int register_locations_[RegisterAllocator::kNumRegisters]; + + // The number of frame-allocated locals and parameters respectively. + int parameter_count() { return cgen()->scope()->num_parameters(); } + int local_count() { return cgen()->scope()->num_stack_slots(); } + + // 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() const { return 1; } + int param0_index() { 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 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 lies above the context - // slot. - int function_index() const { - ASSERT(frame_pointer_ != kIllegalIndex); - return frame_pointer_ + 2; - } + // 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 parameters and the locals - // lie the return address, the saved frame pointer, the context, and the - // function. - int local0_index() const { - ASSERT(frame_pointer_ != kIllegalIndex); - return frame_pointer_ + 3; - } + // 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() const { return local0_index() + local_count_; } + 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) const { - return (frame_pointer_ - index) * kPointerSize; + 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); + 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); + 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 @@ -421,9 +484,6 @@ class VirtualFrame : public Malloced { // Keep the element type as register or constant, and clear the dirty bit. void SyncElementAt(int index); - // Sync the range of elements in [begin, end). - void SyncRange(int begin, int end); - // Sync a single unsynced element that lies beneath or at the stack pointer. void SyncElementBelowStackPointer(int index); @@ -485,9 +545,12 @@ class VirtualFrame : public Malloced { bool Equals(VirtualFrame* other); + // Classes that need raw access to the elements_ array. + friend class DeferredCode; friend class JumpTarget; }; + } } // namespace v8::internal #endif // V8_X64_VIRTUAL_FRAME_X64_H_ diff --git a/deps/v8/src/zone-inl.h b/deps/v8/src/zone-inl.h index 69b9a0a21f..9af6251bf1 100644 --- a/deps/v8/src/zone-inl.h +++ b/deps/v8/src/zone-inl.h @@ -31,7 +31,8 @@ #include "zone.h" #include "v8-counters.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { inline void* Zone::New(int size) { diff --git a/deps/v8/src/zone.cc b/deps/v8/src/zone.cc index c8f9c85a29..d78c19b891 100644 --- a/deps/v8/src/zone.cc +++ b/deps/v8/src/zone.cc @@ -29,7 +29,8 @@ #include "zone-inl.h" -namespace v8 { namespace internal { +namespace v8 { +namespace internal { Address Zone::position_ = 0; diff --git a/deps/v8/src/zone.h b/deps/v8/src/zone.h index df69155520..a8b26e9fd2 100644 --- a/deps/v8/src/zone.h +++ b/deps/v8/src/zone.h @@ -28,7 +28,8 @@ #ifndef V8_ZONE_H_ #define V8_ZONE_H_ -namespace v8 { namespace internal { +namespace v8 { +namespace internal { // Zone scopes are in one of two modes. Either they delete the zone @@ -180,8 +181,13 @@ class ZoneScope BASE_EMBEDDED { nesting_++; } - ~ZoneScope() { - if (--nesting_ == 0 && mode_ == DELETE_ON_EXIT) Zone::DeleteAll(); + virtual ~ZoneScope() { + if (ShouldDeleteOnExit()) Zone::DeleteAll(); + --nesting_; + } + + bool ShouldDeleteOnExit() { + return nesting_ == 1 && mode_ == DELETE_ON_EXIT; } // For ZoneScopes that do not delete on exit by default, call this |