From: erik.corry@gmail.com Date: Thu, 1 Oct 2009 10:33:05 +0000 (+0000) Subject: Fix the stack limits setting API so it is usable. X-Git-Tag: upstream/4.7.83~23204 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=f2de3fd6d218105e98adbadea4f0eb0c59b66645;p=platform%2Fupstream%2Fv8.git Fix the stack limits setting API so it is usable. Review URL: http://codereview.chromium.org/242074 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3005 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/include/v8.h b/include/v8.h index 2232cb99d..4992d7554 100644 --- a/include/v8.h +++ b/include/v8.h @@ -1,4 +1,4 @@ -// Copyright 2007-2008 the V8 project authors. All rights reserved. +// Copyright 2007-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: @@ -1981,8 +1981,13 @@ Handle V8EXPORT False(); /** - * A set of constraints that specifies the limits of the runtime's - * memory use. + * A set of constraints that specifies the limits of the runtime's memory use. + * You must set the heap size before initializing the VM - the size cannot be + * adjusted after the VM is initialized. + * + * If you are using threads then you should hold the V8::Locker lock while + * setting the stack limit and you must set a non-default stack limit separately + * for each thread. */ class V8EXPORT ResourceConstraints { public: @@ -1992,6 +1997,7 @@ class V8EXPORT ResourceConstraints { int max_old_space_size() const { return max_old_space_size_; } void set_max_old_space_size(int value) { max_old_space_size_ = value; } uint32_t* stack_limit() const { return stack_limit_; } + // Sets an address beyond which the VM's stack may not grow. void set_stack_limit(uint32_t* value) { stack_limit_ = value; } private: int max_young_space_size_; @@ -2199,7 +2205,8 @@ class V8EXPORT V8 { /** * Initializes from snapshot if possible. Otherwise, attempts to - * initialize from scratch. + * initialize from scratch. This function is called implicitly if + * you use the API without calling it first. */ static bool Initialize(); @@ -2749,15 +2756,15 @@ class Internals { return ((reinterpret_cast(value) & kHeapObjectTagMask) == kHeapObjectTag); } - + static inline bool HasSmiTag(internal::Object* value) { return ((reinterpret_cast(value) & kSmiTagMask) == kSmiTag); } - + static inline int SmiValue(internal::Object* value) { return static_cast(reinterpret_cast(value)) >> kSmiTagSize; } - + static inline bool IsExternalTwoByteString(int instance_type) { int representation = (instance_type & kFullStringRepresentationMask); return representation == kExternalTwoByteRepresentationTag; diff --git a/src/api.cc b/src/api.cc index 7c1bd60dd..90da1bca3 100644 --- a/src/api.cc +++ b/src/api.cc @@ -342,9 +342,12 @@ ResourceConstraints::ResourceConstraints() bool SetResourceConstraints(ResourceConstraints* constraints) { - bool result = i::Heap::ConfigureHeap(constraints->max_young_space_size(), - constraints->max_old_space_size()); - if (!result) return false; + int semispace_size = constraints->max_young_space_size(); + int old_gen_size = constraints->max_old_space_size(); + if (semispace_size != 0 || old_gen_size != 0) { + bool result = i::Heap::ConfigureHeap(semispace_size, old_gen_size); + if (!result) return false; + } if (constraints->stack_limit() != NULL) { uintptr_t limit = reinterpret_cast(constraints->stack_limit()); i::StackGuard::SetStackLimit(limit); diff --git a/src/arm/simulator-arm.cc b/src/arm/simulator-arm.cc index 70dfcd2a9..22bec8220 100644 --- a/src/arm/simulator-arm.cc +++ b/src/arm/simulator-arm.cc @@ -409,7 +409,7 @@ void Simulator::Initialize() { Simulator::Simulator() { - ASSERT(initialized_); + Initialize(); // Setup simulator support first. Some of this information is needed to // setup the architecture state. size_t stack_size = 1 * 1024*1024; // allocate 1MB for stack @@ -501,6 +501,7 @@ void* Simulator::RedirectExternalReference(void* external_function, // Get the active Simulator for the current thread. Simulator* Simulator::current() { + Initialize(); Simulator* sim = reinterpret_cast( v8::internal::Thread::GetThreadLocal(simulator_key)); if (sim == NULL) { diff --git a/src/arm/simulator-arm.h b/src/arm/simulator-arm.h index 3917d6a5a..ff6bbf430 100644 --- a/src/arm/simulator-arm.h +++ b/src/arm/simulator-arm.h @@ -36,18 +36,23 @@ #ifndef V8_ARM_SIMULATOR_ARM_H_ #define V8_ARM_SIMULATOR_ARM_H_ +#include "allocation.h" + #if defined(__arm__) // When running without a simulator we call the entry directly. #define CALL_GENERATED_CODE(entry, p0, p1, p2, p3, p4) \ (entry(p0, p1, p2, p3, p4)) -// Calculated the stack limit beyond which we will throw stack overflow errors. -// This macro must be called from a C++ method. It relies on being able to take -// the address of "this" to get a value on the current execution stack and then -// calculates the stack limit based on that value. -#define GENERATED_CODE_STACK_LIMIT(limit) \ - (reinterpret_cast(this) - limit) +// The stack limit beyond which we will throw stack overflow errors in +// generated code. Because generated code on arm uses the C stack, we +// just use the C stack limit. +class SimulatorStack : public v8::internal::AllStatic { + public: + static inline uintptr_t JsLimitFromCLimit(uintptr_t c_limit) { + return c_limit; + } +}; // Call the generated regexp code directly. The entry function pointer should @@ -64,12 +69,6 @@ assembler::arm::Simulator::current()->Call(FUNCTION_ADDR(entry), 5, \ p0, p1, p2, p3, p4)) -// The simulator has its own stack. Thus it has a different stack limit from -// the C-based native code. -#define GENERATED_CODE_STACK_LIMIT(limit) \ - (assembler::arm::Simulator::current()->StackLimit()) - - #define CALL_GENERATED_REGEXP_CODE(entry, p0, p1, p2, p3, p4, p5, p6) \ assembler::arm::Simulator::current()->Call( \ FUNCTION_ADDR(entry), 7, p0, p1, p2, p3, p4, p5, p6) @@ -219,6 +218,20 @@ class Simulator { } } // namespace assembler::arm + +// The simulator has its own stack. Thus it has a different stack limit from +// the C-based native code. Setting the c_limit to indicate a very small +// stack cause stack overflow errors, since the simulator ignores the input. +// This is unlikely to be an issue in practice, though it might cause testing +// trouble down the line. +class SimulatorStack : public v8::internal::AllStatic { + public: + static inline uintptr_t JsLimitFromCLimit(uintptr_t c_limit) { + return assembler::arm::Simulator::current()->StackLimit(); + } +}; + + #endif // defined(__arm__) #endif // V8_ARM_SIMULATOR_ARM_H_ diff --git a/src/compiler.cc b/src/compiler.cc index bbb5b45f1..6ba7a9a9d 100644 --- a/src/compiler.cc +++ b/src/compiler.cc @@ -104,8 +104,6 @@ static Handle MakeFunction(bool is_global, ScriptDataImpl* pre_data) { CompilationZoneScope zone_scope(DELETE_ON_EXIT); - // Make sure we have an initial stack limit. - StackGuard guard; PostponeInterruptsScope postpone; ASSERT(!i::Top::global_context().is_null()); @@ -334,8 +332,6 @@ bool Compiler::CompileLazy(Handle shared, // The VM is in the COMPILER state until exiting this function. VMState state(COMPILER); - // Make sure we have an initial stack limit. - StackGuard guard; PostponeInterruptsScope postpone; // Compute name, source code and script data. diff --git a/src/execution.cc b/src/execution.cc index 6a758a9c6..8bc6b74e1 100644 --- a/src/execution.cc +++ b/src/execution.cc @@ -61,9 +61,6 @@ static Handle Invoke(bool construct, // Entering JavaScript. VMState state(JS); - // Guard the stack against too much recursion. - StackGuard guard; - // Placeholder for return value. Object* value = reinterpret_cast(kZapValue); @@ -217,55 +214,6 @@ Handle Execution::GetConstructorDelegate(Handle object) { StackGuard::ThreadLocal StackGuard::thread_local_; -StackGuard::StackGuard() { - // NOTE: Overall the StackGuard code assumes that the stack grows towards - // lower addresses. - ExecutionAccess access; - if (thread_local_.nesting_++ == 0) { - // Initial StackGuard is being set. We will set the stack limits based on - // the current stack pointer allowing the stack to grow kLimitSize from - // here. - - // Ensure that either the stack limits are unset (kIllegalLimit) or that - // they indicate a pending interruption. The interrupt limit will be - // temporarily reset through the code below and reestablished if the - // interrupt flags indicate that an interrupt is pending. - ASSERT(thread_local_.jslimit_ == kIllegalLimit || - (thread_local_.jslimit_ == kInterruptLimit && - thread_local_.interrupt_flags_ != 0)); - ASSERT(thread_local_.climit_ == kIllegalLimit || - (thread_local_.climit_ == kInterruptLimit && - thread_local_.interrupt_flags_ != 0)); - - uintptr_t limit = GENERATED_CODE_STACK_LIMIT(kLimitSize); - thread_local_.initial_jslimit_ = thread_local_.jslimit_ = limit; - Heap::SetStackLimit(limit); - // NOTE: The check for overflow is not safe as there is no guarantee that - // the running thread has its stack in all memory up to address 0x00000000. - thread_local_.initial_climit_ = thread_local_.climit_ = - reinterpret_cast(this) >= kLimitSize ? - reinterpret_cast(this) - kLimitSize : 0; - - if (thread_local_.interrupt_flags_ != 0) { - set_limits(kInterruptLimit, access); - } - } - // Ensure that proper limits have been set. - ASSERT(thread_local_.jslimit_ != kIllegalLimit && - thread_local_.climit_ != kIllegalLimit); - ASSERT(thread_local_.initial_jslimit_ != kIllegalLimit && - thread_local_.initial_climit_ != kIllegalLimit); -} - - -StackGuard::~StackGuard() { - ExecutionAccess access; - if (--thread_local_.nesting_ == 0) { - set_limits(kIllegalLimit, access); - } -} - - bool StackGuard::IsStackOverflow() { ExecutionAccess access; return (thread_local_.jslimit_ != kInterruptLimit && @@ -285,15 +233,16 @@ void StackGuard::SetStackLimit(uintptr_t limit) { ExecutionAccess access; // If the current limits are special (eg due to a pending interrupt) then // leave them alone. + uintptr_t jslimit = SimulatorStack::JsLimitFromCLimit(limit); if (thread_local_.jslimit_ == thread_local_.initial_jslimit_) { - thread_local_.jslimit_ = limit; - Heap::SetStackLimit(limit); + thread_local_.jslimit_ = jslimit; + Heap::SetStackLimit(jslimit); } if (thread_local_.climit_ == thread_local_.initial_climit_) { thread_local_.climit_ = limit; } thread_local_.initial_climit_ = limit; - thread_local_.initial_jslimit_ = limit; + thread_local_.initial_jslimit_ = jslimit; } @@ -407,7 +356,58 @@ char* StackGuard::RestoreStackGuard(char* from) { } +static internal::Thread::LocalStorageKey stack_limit_key = + internal::Thread::CreateThreadLocalKey(); + + void StackGuard::FreeThreadResources() { + Thread::SetThreadLocal( + stack_limit_key, + reinterpret_cast(thread_local_.initial_climit_)); +} + + +void StackGuard::ThreadLocal::Clear() { + initial_jslimit_ = kIllegalLimit; + jslimit_ = kIllegalLimit; + initial_climit_ = kIllegalLimit; + climit_ = kIllegalLimit; + nesting_ = 0; + postpone_interrupts_nesting_ = 0; + interrupt_flags_ = 0; + Heap::SetStackLimit(kIllegalLimit); +} + + +void StackGuard::ThreadLocal::Initialize() { + if (initial_climit_ == kIllegalLimit) { + // Takes the address of the limit variable in order to find out where + // the top of stack is right now. + intptr_t limit = reinterpret_cast(&limit) - kLimitSize; + initial_jslimit_ = SimulatorStack::JsLimitFromCLimit(limit); + jslimit_ = SimulatorStack::JsLimitFromCLimit(limit); + initial_climit_ = limit; + climit_ = limit; + Heap::SetStackLimit(SimulatorStack::JsLimitFromCLimit(limit)); + } + nesting_ = 0; + postpone_interrupts_nesting_ = 0; + interrupt_flags_ = 0; +} + + +void StackGuard::ClearThread(const ExecutionAccess& lock) { + thread_local_.Clear(); +} + + +void StackGuard::InitThread(const ExecutionAccess& lock) { + thread_local_.Initialize(); + void* stored_limit = Thread::GetThreadLocal(stack_limit_key); + // You should hold the ExecutionAccess lock when you call this. + if (stored_limit != NULL) { + StackGuard::SetStackLimit(reinterpret_cast(stored_limit)); + } } diff --git a/src/execution.h b/src/execution.h index d2e6ec088..55307f71f 100644 --- a/src/execution.h +++ b/src/execution.h @@ -141,14 +141,13 @@ class Execution : public AllStatic { class ExecutionAccess; -// Stack guards are used to limit the number of nested invocations of -// JavaScript and the stack size used in each invocation. -class StackGuard BASE_EMBEDDED { +// StackGuard contains the handling of the limits that are used to limit the +// number of nested invocations of JavaScript and the stack size used in each +// invocation. +class StackGuard : public AllStatic { public: - StackGuard(); - - ~StackGuard(); - + // Pass the address beyond which the stack should not grow. The stack + // is assumed to grow downwards. static void SetStackLimit(uintptr_t limit); static Address address_of_jslimit() { @@ -160,6 +159,12 @@ class StackGuard BASE_EMBEDDED { static char* RestoreStackGuard(char* from); static int ArchiveSpacePerThread(); static void FreeThreadResources(); + // Sets up the default stack guard for this thread if it has not + // already been set up. + static void InitThread(const ExecutionAccess& lock); + // Clears the stack guard for this thread so it does not look as if + // it has been set up. + static void ClearThread(const ExecutionAccess& lock); static bool IsStackOverflow(); static bool IsPreempted(); @@ -176,6 +181,13 @@ class StackGuard BASE_EMBEDDED { #endif static void Continue(InterruptFlag after_what); + // This provides an asynchronous read of the stack limit for the current + // thread. There are no locks protecting this, but it is assumed that you + // have the global V8 lock if you are using multiple V8 threads. + static uintptr_t climit() { + return thread_local_.climit_; + } + static uintptr_t jslimit() { return thread_local_.jslimit_; } @@ -184,13 +196,6 @@ class StackGuard BASE_EMBEDDED { // You should hold the ExecutionAccess lock when calling this method. static bool IsSet(const ExecutionAccess& lock); - // This provides an asynchronous read of the stack limit for the current - // thread. There are no locks protecting this, but it is assumed that you - // have the global V8 lock if you are using multiple V8 threads. - static uintptr_t climit() { - return thread_local_.climit_; - } - // You should hold the ExecutionAccess lock when calling this method. static void set_limits(uintptr_t value, const ExecutionAccess& lock) { Heap::SetStackLimit(value); @@ -201,14 +206,9 @@ class StackGuard BASE_EMBEDDED { // Reset limits to initial values. For example after handling interrupt. // You should hold the ExecutionAccess lock when calling this method. static void reset_limits(const ExecutionAccess& lock) { - if (thread_local_.nesting_ == 0) { - // No limits have been set yet. - set_limits(kIllegalLimit, lock); - } else { - thread_local_.jslimit_ = thread_local_.initial_jslimit_; - Heap::SetStackLimit(thread_local_.jslimit_); - thread_local_.climit_ = thread_local_.initial_climit_; - } + thread_local_.jslimit_ = thread_local_.initial_jslimit_; + Heap::SetStackLimit(thread_local_.jslimit_); + thread_local_.climit_ = thread_local_.initial_climit_; } // Enable or disable interrupts. @@ -218,24 +218,19 @@ class StackGuard BASE_EMBEDDED { static const uintptr_t kLimitSize = kPointerSize * 128 * KB; #ifdef V8_TARGET_ARCH_X64 static const uintptr_t kInterruptLimit = V8_UINT64_C(0xfffffffffffffffe); - static const uintptr_t kIllegalLimit = V8_UINT64_C(0xffffffffffffffff); + static const uintptr_t kIllegalLimit = V8_UINT64_C(0xfffffffffffffff8); #else static const uintptr_t kInterruptLimit = 0xfffffffe; - static const uintptr_t kIllegalLimit = 0xffffffff; + static const uintptr_t kIllegalLimit = 0xfffffff8; #endif class ThreadLocal { public: - ThreadLocal() - : initial_jslimit_(kIllegalLimit), - jslimit_(kIllegalLimit), - initial_climit_(kIllegalLimit), - climit_(kIllegalLimit), - nesting_(0), - postpone_interrupts_nesting_(0), - interrupt_flags_(0) { - Heap::SetStackLimit(kIllegalLimit); - } + ThreadLocal() { Clear(); } + // You should hold the ExecutionAccess lock when you call Initialize or + // Clear. + void Initialize(); + void Clear(); uintptr_t initial_jslimit_; uintptr_t jslimit_; uintptr_t initial_climit_; diff --git a/src/ia32/simulator-ia32.h b/src/ia32/simulator-ia32.h index 3bed2681f..8fa4287f7 100644 --- a/src/ia32/simulator-ia32.h +++ b/src/ia32/simulator-ia32.h @@ -28,21 +28,22 @@ #ifndef V8_IA32_SIMULATOR_IA32_H_ #define V8_IA32_SIMULATOR_IA32_H_ +#include "allocation.h" // Since there is no simulator for the ia32 architecture the only thing we can // do is to call the entry directly. #define CALL_GENERATED_CODE(entry, p0, p1, p2, p3, p4) \ entry(p0, p1, p2, p3, p4); -// Calculated the stack limit beyond which we will throw stack overflow errors. -// This macro must be called from a C++ method. It relies on being able to take -// the address of "this" to get a value on the current execution stack and then -// calculates the stack limit based on that value. -// NOTE: The check for overflow is not safe as there is no guarantee that the -// running thread has its stack in all memory up to address 0x00000000. -#define GENERATED_CODE_STACK_LIMIT(limit) \ - (reinterpret_cast(this) >= limit ? \ - reinterpret_cast(this) - limit : 0) +// The stack limit beyond which we will throw stack overflow errors in +// generated code. Because generated code on ia32 uses the C stack, we +// just use the C stack limit. +class SimulatorStack : public v8::internal::AllStatic { + public: + static inline uintptr_t JsLimitFromCLimit(uintptr_t c_limit) { + return c_limit; + } +}; // Call the generated regexp code directly. The entry function pointer should // expect seven int/pointer sized arguments and return an int. diff --git a/src/parser.cc b/src/parser.cc index 757b042e9..4dbd4720c 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -1184,7 +1184,6 @@ Parser::Parser(Handle