Optimizing HandleScope. Also fixed HandleScope destruction when API getter throws an exception.

Review URL: http://codereview.chromium.org/3792003

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5689 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
serya@chromium.org 2010-10-21 14:21:00 +00:00
parent 1589238329
commit 0ea6c1e09f
20 changed files with 306 additions and 326 deletions

View File

@ -467,16 +467,21 @@ class V8EXPORT HandleScope {
// typedef in the ImplementationUtilities class.
class V8EXPORT Data {
public:
int extensions;
internal::Object** next;
internal::Object** limit;
int level;
inline void Initialize() {
extensions = -1;
next = limit = NULL;
level = 0;
}
};
void Leave();
Data previous_;
internal::Object** prev_next_;
internal::Object** prev_limit_;
// Allow for the active closing of HandleScopes which allows to pass a handle
// from the HandleScope being closed to the next top most HandleScope.

View File

@ -457,19 +457,37 @@ void V8::DisposeGlobal(i::Object** obj) {
// --- H a n d l e s ---
HandleScope::HandleScope() : is_closed_(false) {
HandleScope::HandleScope()
: prev_next_(i::HandleScope::current_.next),
prev_limit_(i::HandleScope::current_.limit),
is_closed_(false) {
API_ENTRY_CHECK("HandleScope::HandleScope");
i::HandleScope::Enter(&previous_);
i::HandleScope::current_.level++;
}
HandleScope::~HandleScope() {
if (!is_closed_) {
i::HandleScope::Leave(&previous_);
Leave();
}
}
void HandleScope::Leave() {
i::HandleScope::current_.level--;
ASSERT(i::HandleScope::current_.level >= 0);
i::HandleScope::current_.next = prev_next_;
if (i::HandleScope::current_.limit != prev_limit_) {
i::HandleScope::current_.limit = prev_limit_;
i::HandleScope::DeleteExtensions();
}
#ifdef DEBUG
i::HandleScope::ZapRange(prev_next_, prev_limit_);
#endif
}
int HandleScope::NumberOfHandles() {
return i::HandleScope::NumberOfHandles();
}
@ -553,7 +571,7 @@ i::Object** v8::HandleScope::RawClose(i::Object** value) {
result = *value;
}
is_closed_ = true;
i::HandleScope::Leave(&previous_);
Leave();
if (value == NULL) {
return NULL;

View File

@ -353,7 +353,7 @@ class HandleScopeImplementer {
inline internal::Object** GetSpareOrNewBlock();
inline void DeleteExtensions(int extensions);
inline void DeleteExtensions(internal::Object** prev_limit);
inline void IncrementCallDepth() {call_depth_++;}
inline void DecrementCallDepth() {call_depth_--;}
@ -465,25 +465,28 @@ internal::Object** HandleScopeImplementer::GetSpareOrNewBlock() {
}
void HandleScopeImplementer::DeleteExtensions(int extensions) {
if (spare_ != NULL) {
DeleteArray(spare_);
spare_ = NULL;
}
for (int i = extensions; i > 1; --i) {
internal::Object** block = blocks_.RemoveLast();
void HandleScopeImplementer::DeleteExtensions(internal::Object** prev_limit) {
while (!blocks_.is_empty()) {
internal::Object** block_start = blocks_.last();
internal::Object** block_limit = block_start + kHandleBlockSize;
#ifdef DEBUG
v8::ImplementationUtilities::ZapHandleRange(block,
&block[kHandleBlockSize]);
// NoHandleAllocation may make the prev_limit to point inside the block.
if (block_start <= prev_limit && prev_limit <= block_limit) break;
#else
if (prev_limit == block_limit) break;
#endif
DeleteArray(block);
}
spare_ = blocks_.RemoveLast();
blocks_.RemoveLast();
#ifdef DEBUG
v8::ImplementationUtilities::ZapHandleRange(
spare_,
&spare_[kHandleBlockSize]);
v8::ImplementationUtilities::ZapHandleRange(block_start, block_limit);
#endif
if (spare_ != NULL) {
DeleteArray(spare_);
}
spare_ = block_start;
}
ASSERT((blocks_.is_empty() && prev_limit == NULL) ||
(!blocks_.is_empty() && prev_limit != NULL));
}
} } // namespace v8::internal

View File

@ -583,6 +583,12 @@ ExternalReference ExternalReference::fill_heap_number_with_random_function() {
}
ExternalReference ExternalReference::delete_handle_scope_extensions() {
return ExternalReference(Redirect(FUNCTION_ADDR(
HandleScope::DeleteExtensions)));
}
ExternalReference ExternalReference::random_uint32_function() {
return ExternalReference(Redirect(FUNCTION_ADDR(V8::Random)));
}
@ -653,8 +659,8 @@ ExternalReference ExternalReference::new_space_allocation_limit_address() {
}
ExternalReference ExternalReference::handle_scope_extensions_address() {
return ExternalReference(HandleScope::current_extensions_address());
ExternalReference ExternalReference::handle_scope_level_address() {
return ExternalReference(HandleScope::current_level_address());
}

View File

@ -482,6 +482,7 @@ class ExternalReference BASE_EMBEDDED {
static ExternalReference fill_heap_number_with_random_function();
static ExternalReference random_uint32_function();
static ExternalReference transcendental_cache_array_address();
static ExternalReference delete_handle_scope_extensions();
// Static data in the keyed lookup cache.
static ExternalReference keyed_lookup_cache_keys();
@ -519,9 +520,9 @@ class ExternalReference BASE_EMBEDDED {
static ExternalReference double_fp_operation(Token::Value operation);
static ExternalReference compare_doubles();
static ExternalReference handle_scope_extensions_address();
static ExternalReference handle_scope_next_address();
static ExternalReference handle_scope_limit_address();
static ExternalReference handle_scope_level_address();
static ExternalReference scheduled_exception_address();

View File

@ -55,18 +55,22 @@ inline T* Handle<T>::operator*() const {
inline NoHandleAllocation::NoHandleAllocation() {
v8::ImplementationUtilities::HandleScopeData* current =
v8::ImplementationUtilities::CurrentHandleScope();
extensions_ = current->extensions;
// Shrink the current handle scope to make it impossible to do
// handle allocations without an explicit handle scope.
current->limit = current->next;
current->extensions = -1;
level_ = current->level;
current->level = 0;
}
inline NoHandleAllocation::~NoHandleAllocation() {
// Restore state in current handle scope to re-enable handle
// allocations.
v8::ImplementationUtilities::CurrentHandleScope()->extensions = extensions_;
v8::ImplementationUtilities::HandleScopeData* current =
v8::ImplementationUtilities::CurrentHandleScope();
ASSERT_EQ(0, current->level);
current->level = level_;
}
#endif

View File

@ -44,7 +44,7 @@ namespace internal {
v8::ImplementationUtilities::HandleScopeData HandleScope::current_ =
{ -1, NULL, NULL };
{ NULL, NULL, 0 };
int HandleScope::NumberOfHandles() {
@ -61,7 +61,7 @@ Object** HandleScope::Extend() {
ASSERT(result == current_.limit);
// Make sure there's at least one scope on the stack and that the
// top of the scope stack isn't a barrier.
if (current_.extensions < 0) {
if (current_.level == 0) {
Utils::ReportApiFailure("v8::HandleScope::CreateHandle()",
"Cannot create a handle without a HandleScope");
return NULL;
@ -73,6 +73,7 @@ Object** HandleScope::Extend() {
Object** limit = &impl->blocks()->last()[kHandleBlockSize];
if (current_.limit != limit) {
current_.limit = limit;
ASSERT(limit - current_.next < kHandleBlockSize);
}
}
@ -84,7 +85,6 @@ Object** HandleScope::Extend() {
// Add the extension to the global list of blocks, but count the
// extension as part of the current scope.
impl->blocks()->Add(result);
current_.extensions++;
current_.limit = &result[kHandleBlockSize];
}
@ -93,21 +93,20 @@ Object** HandleScope::Extend() {
void HandleScope::DeleteExtensions() {
ASSERT(current_.extensions != 0);
HandleScopeImplementer::instance()->DeleteExtensions(current_.extensions);
HandleScopeImplementer::instance()->DeleteExtensions(current_.limit);
}
void HandleScope::ZapRange(Object** start, Object** end) {
if (start == NULL) return;
for (Object** p = start; p < end; p++) {
ASSERT(end - start <= kHandleBlockSize);
for (Object** p = start; p != end; p++) {
*reinterpret_cast<Address*>(p) = v8::internal::kHandleZapValue;
}
}
Address HandleScope::current_extensions_address() {
return reinterpret_cast<Address>(&current_.extensions);
Address HandleScope::current_level_address() {
return reinterpret_cast<Address>(&current_.level);
}

View File

@ -107,12 +107,20 @@ class Handle {
// for which the handle scope has been deleted is undefined.
class HandleScope {
public:
HandleScope() : previous_(current_) {
current_.extensions = 0;
HandleScope() : prev_next_(current_.next), prev_limit_(current_.limit) {
current_.level++;
}
~HandleScope() {
Leave(&previous_);
current_.next = prev_next_;
current_.level--;
if (current_.limit != prev_limit_) {
current_.limit = prev_limit_;
DeleteExtensions();
}
#ifdef DEBUG
ZapRange(prev_next_, prev_limit_);
#endif
}
// Counts the number of allocated handles.
@ -136,9 +144,9 @@ class HandleScope {
// Deallocates any extensions used by the current scope.
static void DeleteExtensions();
static Address current_extensions_address();
static Address current_next_address();
static Address current_limit_address();
static Address current_level_address();
private:
// Prevent heap allocation or illegal handle scopes.
@ -148,27 +156,8 @@ class HandleScope {
void operator delete(void* size_t);
static v8::ImplementationUtilities::HandleScopeData current_;
const v8::ImplementationUtilities::HandleScopeData previous_;
// Pushes a fresh handle scope to be used when allocating new handles.
static void Enter(
v8::ImplementationUtilities::HandleScopeData* previous) {
*previous = current_;
current_.extensions = 0;
}
// Re-establishes the previous scope state. Should be called only
// once, and only for the current scope.
static void Leave(
const v8::ImplementationUtilities::HandleScopeData* previous) {
if (current_.extensions > 0) {
DeleteExtensions();
}
current_ = *previous;
#ifdef DEBUG
ZapRange(current_.next, current_.limit);
#endif
}
Object** const prev_next_;
Object** const prev_limit_;
// Extend the handle scope making room for more handles.
static internal::Object** Extend();
@ -362,7 +351,7 @@ class NoHandleAllocation BASE_EMBEDDED {
inline NoHandleAllocation();
inline ~NoHandleAllocation();
private:
int extensions_;
int level_;
#endif
};

View File

@ -695,7 +695,6 @@ class Assembler : public Malloced {
void call(Label* L);
void call(byte* entry, RelocInfo::Mode rmode);
void call(const Operand& adr);
void call(const ExternalReference& target);
void call(Handle<Code> code, RelocInfo::Mode rmode);
// Jumps

View File

@ -3058,74 +3058,12 @@ void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) {
}
// If true, a Handle<T> passed by value is passed and returned by
// using the location_ field directly. If false, it is passed and
// returned as a pointer to a handle.
#ifdef USING_BSD_ABI
static const bool kPassHandlesDirectly = true;
#else
static const bool kPassHandlesDirectly = false;
#endif
void ApiGetterEntryStub::Generate(MacroAssembler* masm) {
Label empty_handle;
Label prologue;
Label promote_scheduled_exception;
__ EnterApiExitFrame(kStackSpace, kArgc);
STATIC_ASSERT(kArgc == 4);
if (kPassHandlesDirectly) {
// When handles as passed directly we don't have to allocate extra
// space for and pass an out parameter.
__ mov(Operand(esp, 0 * kPointerSize), ebx); // name.
__ mov(Operand(esp, 1 * kPointerSize), eax); // arguments pointer.
} else {
// The function expects three arguments to be passed but we allocate
// four to get space for the output cell. The argument slots are filled
// as follows:
//
// 3: output cell
// 2: arguments pointer
// 1: name
// 0: pointer to the output cell
//
// Note that this is one more "argument" than the function expects
// so the out cell will have to be popped explicitly after returning
// from the function.
__ mov(Operand(esp, 1 * kPointerSize), ebx); // name.
__ mov(Operand(esp, 2 * kPointerSize), eax); // arguments pointer.
__ mov(ebx, esp);
__ add(Operand(ebx), Immediate(3 * kPointerSize));
__ mov(Operand(esp, 0 * kPointerSize), ebx); // output
__ mov(Operand(esp, 3 * kPointerSize), Immediate(0)); // out cell.
}
// Call the api function!
__ call(fun()->address(), RelocInfo::RUNTIME_ENTRY);
// Check if the function scheduled an exception.
ExternalReference scheduled_exception_address =
ExternalReference::scheduled_exception_address();
__ cmp(Operand::StaticVariable(scheduled_exception_address),
Immediate(Factory::the_hole_value()));
__ j(not_equal, &promote_scheduled_exception, not_taken);
if (!kPassHandlesDirectly) {
// The returned value is a pointer to the handle holding the result.
// Dereference this to get to the location.
__ mov(eax, Operand(eax, 0));
}
// Check if the result handle holds 0.
__ test(eax, Operand(eax));
__ j(zero, &empty_handle, not_taken);
// It was non-zero. Dereference to get the result value.
__ mov(eax, Operand(eax, 0));
__ bind(&prologue);
__ LeaveExitFrame();
__ ret(0);
__ bind(&promote_scheduled_exception);
__ TailCallRuntime(Runtime::kPromoteScheduledException, 0, 1);
__ bind(&empty_handle);
// It was zero; the result is undefined.
__ mov(eax, Factory::undefined_value());
__ jmp(&prologue);
__ PrepareCallApiFunction(kStackSpace, kArgc);
STATIC_ASSERT(kArgc == 2);
__ mov(ApiParameterOperand(0), ebx); // name.
__ mov(ApiParameterOperand(1), eax); // arguments pointer.
__ CallApiFunctionAndReturn(fun(), kArgc);
}

View File

@ -1115,66 +1115,116 @@ void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid,
}
void MacroAssembler::PushHandleScope(Register scratch) {
// Push the number of extensions, smi-tagged so the gc will ignore it.
ExternalReference extensions_address =
ExternalReference::handle_scope_extensions_address();
mov(scratch, Operand::StaticVariable(extensions_address));
SmiTag(scratch);
push(scratch);
mov(Operand::StaticVariable(extensions_address), Immediate(0));
// Push next and limit pointers which will be wordsize aligned and
// hence automatically smi tagged.
// If true, a Handle<T> passed by value is passed and returned by
// using the location_ field directly. If false, it is passed and
// returned as a pointer to a handle.
#ifdef USING_BSD_ABI
static const bool kPassHandlesDirectly = true;
#else
static const bool kPassHandlesDirectly = false;
#endif
Operand ApiParameterOperand(int index) {
return Operand(esp, (index + (kPassHandlesDirectly ? 0 : 1)) * kPointerSize);
}
void MacroAssembler::PrepareCallApiFunction(int stack_space, int argc) {
if (kPassHandlesDirectly) {
EnterApiExitFrame(stack_space, argc);
// When handles as passed directly we don't have to allocate extra
// space for and pass an out parameter.
} else {
// We allocate two additional slots: return value and pointer to it.
EnterApiExitFrame(stack_space, argc + 2);
}
}
void MacroAssembler::CallApiFunctionAndReturn(ApiFunction* function, int argc) {
if (!kPassHandlesDirectly) {
// The argument slots are filled as follows:
//
// n + 1: output cell
// n: arg n
// ...
// 1: arg1
// 0: pointer to the output cell
//
// Note that this is one more "argument" than the function expects
// so the out cell will have to be popped explicitly after returning
// from the function. The out cell contains Handle.
lea(eax, Operand(esp, (argc + 1) * kPointerSize)); // pointer to out cell.
mov(Operand(esp, 0 * kPointerSize), eax); // output.
mov(Operand(esp, (argc + 1) * kPointerSize), Immediate(0)); // out cell.
}
ExternalReference next_address =
ExternalReference::handle_scope_next_address();
push(Operand::StaticVariable(next_address));
ExternalReference limit_address =
ExternalReference::handle_scope_limit_address();
push(Operand::StaticVariable(limit_address));
}
ExternalReference level_address =
ExternalReference::handle_scope_level_address();
// Allocate HandleScope in callee-save registers.
mov(ebx, Operand::StaticVariable(next_address));
mov(edi, Operand::StaticVariable(limit_address));
add(Operand::StaticVariable(level_address), Immediate(1));
Object* MacroAssembler::PopHandleScopeHelper(Register saved,
Register scratch,
bool gc_allowed) {
Object* result = NULL;
ExternalReference extensions_address =
ExternalReference::handle_scope_extensions_address();
Label write_back;
mov(scratch, Operand::StaticVariable(extensions_address));
cmp(Operand(scratch), Immediate(0));
j(equal, &write_back);
push(saved);
if (gc_allowed) {
CallRuntime(Runtime::kDeleteHandleScopeExtensions, 0);
} else {
result = TryCallRuntime(Runtime::kDeleteHandleScopeExtensions, 0);
if (result->IsFailure()) return result;
// Call the api function!
call(function->address(), RelocInfo::RUNTIME_ENTRY);
if (!kPassHandlesDirectly) {
// The returned value is a pointer to the handle holding the result.
// Dereference this to get to the location.
mov(eax, Operand(eax, 0));
}
pop(saved);
bind(&write_back);
ExternalReference limit_address =
ExternalReference::handle_scope_limit_address();
pop(Operand::StaticVariable(limit_address));
ExternalReference next_address =
ExternalReference::handle_scope_next_address();
pop(Operand::StaticVariable(next_address));
pop(scratch);
SmiUntag(scratch);
mov(Operand::StaticVariable(extensions_address), scratch);
Label empty_handle;
Label prologue;
Label promote_scheduled_exception;
Label delete_allocated_handles;
Label leave_exit_frame;
return result;
}
// Check if the result handle holds 0.
test(eax, Operand(eax));
j(zero, &empty_handle, not_taken);
// It was non-zero. Dereference to get the result value.
mov(eax, Operand(eax, 0));
bind(&prologue);
// No more valid handles (the result handle was the last one). Restore
// previous handle scope.
mov(Operand::StaticVariable(next_address), ebx);
sub(Operand::StaticVariable(level_address), Immediate(1));
Assert(above_equal, "Invalid HandleScope level");
cmp(edi, Operand::StaticVariable(limit_address));
j(not_equal, &delete_allocated_handles, not_taken);
bind(&leave_exit_frame);
// Check if the function scheduled an exception.
ExternalReference scheduled_exception_address =
ExternalReference::scheduled_exception_address();
cmp(Operand::StaticVariable(scheduled_exception_address),
Immediate(Factory::the_hole_value()));
j(not_equal, &promote_scheduled_exception, not_taken);
LeaveExitFrame();
ret(0);
bind(&promote_scheduled_exception);
TailCallRuntime(Runtime::kPromoteScheduledException, 0, 1);
bind(&empty_handle);
// It was zero; the result is undefined.
mov(eax, Factory::undefined_value());
jmp(&prologue);
void MacroAssembler::PopHandleScope(Register saved, Register scratch) {
PopHandleScopeHelper(saved, scratch, true);
}
Object* MacroAssembler::TryPopHandleScope(Register saved, Register scratch) {
return PopHandleScopeHelper(saved, scratch, false);
// HandleScope limit has changed. Delete allocated extensions.
bind(&delete_allocated_handles);
mov(Operand::StaticVariable(limit_address), edi);
mov(edi, eax);
mov(eax, Immediate(ExternalReference::delete_handle_scope_extensions()));
call(Operand(eax));
mov(eax, edi);
jmp(&leave_exit_frame);
}

View File

@ -480,15 +480,16 @@ class MacroAssembler: public Assembler {
void CallCFunction(ExternalReference function, int num_arguments);
void CallCFunction(Register function, int num_arguments);
void PushHandleScope(Register scratch);
// Prepares stack to put arguments (aligns and so on). Reserves
// space for return value if needed (assumes the return value is a handle).
// Uses callee-saved esi to restore stack state after call. Arguments must be
// stored in ApiParameterOperand(0), ApiParameterOperand(1) etc.
void PrepareCallApiFunction(int stack_space, int argc);
// Pops a handle scope using the specified scratch register and
// ensuring that saved register is left unchanged.
void PopHandleScope(Register saved, Register scratch);
// As PopHandleScope, but does not perform a GC. Instead, returns a
// retry after GC failure object if GC is necessary.
Object* TryPopHandleScope(Register saved, Register scratch);
// Tail call an API function (jump). Allocates HandleScope, extracts
// returned value from handle and propagates exceptions.
// Clobbers ebx, esi, edi and caller-save registers.
void CallApiFunctionAndReturn(ApiFunction* function, int argc);
// Jump to a runtime routine.
void JumpToExternalReference(const ExternalReference& ext);
@ -639,6 +640,9 @@ static inline Operand FieldOperand(Register object,
return Operand(object, index, scale, offset - kHeapObjectTag);
}
// Generates an Operand for saving parameters after PrepareCallApiFunction.
Operand ApiParameterOperand(int index);
#ifdef GENERATED_CODE_COVERAGE
extern void LogGeneratedCodeCoverage(const char* file_line);

View File

@ -1035,7 +1035,6 @@ bool StubCompiler::GenerateLoadCallback(JSObject* object,
Handle<AccessorInfo> callback_handle(callback);
__ EnterInternalFrame();
__ PushHandleScope(scratch2);
// Push the stack address where the list of arguments ends.
__ mov(scratch2, esp);
__ sub(Operand(scratch2), Immediate(2 * kPointerSize));
@ -1070,17 +1069,6 @@ bool StubCompiler::GenerateLoadCallback(JSObject* object,
*failure = Failure::cast(result);
return false;
}
// We need to avoid using eax since that now holds the result.
Register tmp = scratch2.is(eax) ? reg : scratch2;
// Emitting PopHandleScope may try to allocate. Do not allow the
// assembler to perform a garbage collection but instead return a
// failure object.
result = masm()->TryPopHandleScope(eax, tmp);
if (result->IsFailure()) {
*failure = Failure::cast(result);
return false;
}
__ LeaveInternalFrame();
__ ret(0);

View File

@ -9951,13 +9951,6 @@ static Object* Runtime_Abort(Arguments args) {
}
static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) {
ASSERT(args.length() == 0);
HandleScope::DeleteExtensions();
return Heap::undefined_value();
}
static Object* CacheMiss(FixedArray* cache_obj, int index, Object* key_obj) {
ASSERT(index % 2 == 0); // index of the key
ASSERT(index >= JSFunctionResultCache::kEntriesIndex);

View File

@ -297,8 +297,6 @@ namespace internal {
F(Log, 2, 1) \
/* ES5 */ \
F(LocalKeys, 1, 1) \
/* Handle scopes */ \
F(DeleteHandleScopeExtensions, 0, 1) \
/* Cache suport */ \
F(GetFromCache, 2, 1) \
\

View File

@ -337,6 +337,11 @@ void ExternalReferenceTable::PopulateTable() {
3,
"V8::Random");
Add(ExternalReference::delete_handle_scope_extensions().address(),
RUNTIME_ENTRY,
3,
"HandleScope::DeleteExtensions");
// Miscellaneous
Add(ExternalReference::the_hole_value_location().address(),
UNCLASSIFIED,
@ -457,6 +462,18 @@ void ExternalReferenceTable::PopulateTable() {
UNCLASSIFIED,
29,
"TranscendentalCache::caches()");
Add(ExternalReference::handle_scope_next_address().address(),
UNCLASSIFIED,
30,
"HandleScope::next");
Add(ExternalReference::handle_scope_limit_address().address(),
UNCLASSIFIED,
31,
"HandleScope::limit");
Add(ExternalReference::handle_scope_level_address().address(),
UNCLASSIFIED,
32,
"HandleScope::level");
}

View File

@ -2484,11 +2484,7 @@ void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) {
void ApiGetterEntryStub::Generate(MacroAssembler* masm) {
Label empty_result;
Label prologue;
Label promote_scheduled_exception;
__ EnterApiExitFrame(kStackSpace, 0);
ASSERT_EQ(kArgc, 4);
__ PrepareCallApiFunction(kStackSpace);
#ifdef _WIN64
// All the parameters should be set up by a caller.
#else
@ -2497,35 +2493,7 @@ void ApiGetterEntryStub::Generate(MacroAssembler* masm) {
// Second parameter register rdi should be set with pointer to AccessorInfo
// by a caller.
#endif
// Call the api function!
__ movq(rax,
reinterpret_cast<int64_t>(fun()->address()),
RelocInfo::RUNTIME_ENTRY);
__ call(rax);
// Check if the function scheduled an exception.
ExternalReference scheduled_exception_address =
ExternalReference::scheduled_exception_address();
__ movq(rsi, scheduled_exception_address);
__ Cmp(Operand(rsi, 0), Factory::the_hole_value());
__ j(not_equal, &promote_scheduled_exception);
#ifdef _WIN64
// rax keeps a pointer to v8::Handle, unpack it.
__ movq(rax, Operand(rax, 0));
#endif
// Check if the result handle holds 0.
__ testq(rax, rax);
__ j(zero, &empty_result);
// It was non-zero. Dereference to get the result value.
__ movq(rax, Operand(rax, 0));
__ bind(&prologue);
__ LeaveExitFrame();
__ ret(0);
__ bind(&promote_scheduled_exception);
__ TailCallRuntime(Runtime::kPromoteScheduledException, 0, 1);
__ bind(&empty_result);
// It was zero; the result is undefined.
__ Move(rax, Factory::undefined_value());
__ jmp(&prologue);
__ CallApiFunctionAndReturn(fun());
}

View File

@ -469,76 +469,89 @@ static int Offset(ExternalReference ref0, ExternalReference ref1) {
}
void MacroAssembler::PushHandleScope(Register scratch) {
ExternalReference extensions_address =
ExternalReference::handle_scope_extensions_address();
const int kExtensionsOffset = 0;
const int kNextOffset = Offset(
ExternalReference::handle_scope_next_address(),
extensions_address);
const int kLimitOffset = Offset(
ExternalReference::handle_scope_limit_address(),
extensions_address);
// Push the number of extensions, smi-tagged so the gc will ignore it.
movq(kScratchRegister, extensions_address);
movq(scratch, Operand(kScratchRegister, kExtensionsOffset));
movq(Operand(kScratchRegister, kExtensionsOffset), Immediate(0));
Integer32ToSmi(scratch, scratch);
push(scratch);
// Push next and limit pointers which will be wordsize aligned and
// hence automatically smi tagged.
push(Operand(kScratchRegister, kNextOffset));
push(Operand(kScratchRegister, kLimitOffset));
void MacroAssembler::PrepareCallApiFunction(int stack_space) {
EnterApiExitFrame(stack_space, 0);
}
Object* MacroAssembler::PopHandleScopeHelper(Register saved,
Register scratch,
bool gc_allowed) {
ExternalReference extensions_address =
ExternalReference::handle_scope_extensions_address();
const int kExtensionsOffset = 0;
const int kNextOffset = Offset(
ExternalReference::handle_scope_next_address(),
extensions_address);
const int kLimitOffset = Offset(
ExternalReference::handle_scope_limit_address(),
extensions_address);
Object* result = NULL;
void MacroAssembler::CallApiFunctionAndReturn(ApiFunction* function) {
Label empty_result;
Label prologue;
Label promote_scheduled_exception;
Label delete_allocated_handles;
Label leave_exit_frame;
Label write_back;
movq(kScratchRegister, extensions_address);
cmpq(Operand(kScratchRegister, kExtensionsOffset), Immediate(0));
j(equal, &write_back);
push(saved);
if (gc_allowed) {
CallRuntime(Runtime::kDeleteHandleScopeExtensions, 0);
} else {
result = TryCallRuntime(Runtime::kDeleteHandleScopeExtensions, 0);
if (result->IsFailure()) return result;
}
pop(saved);
movq(kScratchRegister, extensions_address);
bind(&write_back);
pop(Operand(kScratchRegister, kLimitOffset));
pop(Operand(kScratchRegister, kNextOffset));
pop(scratch);
SmiToInteger32(scratch, scratch);
movq(Operand(kScratchRegister, kExtensionsOffset), scratch);
ExternalReference next_address =
ExternalReference::handle_scope_next_address();
const int kNextOffset = 0;
const int kLimitOffset = Offset(
ExternalReference::handle_scope_limit_address(),
next_address);
const int kLevelOffset = Offset(
ExternalReference::handle_scope_level_address(),
next_address);
ExternalReference scheduled_exception_address =
ExternalReference::scheduled_exception_address();
return result;
}
// Allocate HandleScope in callee-save registers.
Register prev_next_address_reg = r14;
Register prev_limit_reg = rbx;
Register base_reg = kSmiConstantRegister;
movq(base_reg, next_address);
movq(prev_next_address_reg, Operand(base_reg, kNextOffset));
movq(prev_limit_reg, Operand(base_reg, kLimitOffset));
addl(Operand(base_reg, kLevelOffset), Immediate(1));
// Call the api function!
movq(rax,
reinterpret_cast<int64_t>(function->address()),
RelocInfo::RUNTIME_ENTRY);
call(rax);
#ifdef _WIN64
// rax keeps a pointer to v8::Handle, unpack it.
movq(rax, Operand(rax, 0));
#endif
// Check if the result handle holds 0.
testq(rax, rax);
j(zero, &empty_result);
// It was non-zero. Dereference to get the result value.
movq(rax, Operand(rax, 0));
bind(&prologue);
void MacroAssembler::PopHandleScope(Register saved, Register scratch) {
PopHandleScopeHelper(saved, scratch, true);
}
// No more valid handles (the result handle was the last one). Restore
// previous handle scope.
subl(Operand(base_reg, kLevelOffset), Immediate(1));
movq(Operand(base_reg, kNextOffset), prev_next_address_reg);
cmpq(prev_limit_reg, Operand(base_reg, kLimitOffset));
j(not_equal, &delete_allocated_handles);
bind(&leave_exit_frame);
InitializeSmiConstantRegister();
// Check if the function scheduled an exception.
movq(rsi, scheduled_exception_address);
Cmp(Operand(rsi, 0), Factory::the_hole_value());
j(not_equal, &promote_scheduled_exception);
Object* MacroAssembler::TryPopHandleScope(Register saved, Register scratch) {
return PopHandleScopeHelper(saved, scratch, false);
LeaveExitFrame();
ret(0);
bind(&promote_scheduled_exception);
TailCallRuntime(Runtime::kPromoteScheduledException, 0, 1);
bind(&empty_result);
// It was zero; the result is undefined.
Move(rax, Factory::undefined_value());
jmp(&prologue);
// HandleScope limit has changed. Delete allocated extensions.
bind(&delete_allocated_handles);
movq(Operand(base_reg, kLimitOffset), prev_limit_reg);
movq(prev_limit_reg, rax);
movq(rax, ExternalReference::delete_handle_scope_extensions());
call(rax);
movq(rax, prev_limit_reg);
jmp(&leave_exit_frame);
}

View File

@ -816,19 +816,18 @@ class MacroAssembler: public Assembler {
int num_arguments,
int result_size);
void PushHandleScope(Register scratch);
// Pops a handle scope using the specified scratch register and
// ensuring that saved register is left unchanged.
void PopHandleScope(Register saved, Register scratch);
// As PopHandleScope, but does not perform a GC. Instead, returns a
// retry after GC failure object if GC is necessary.
Object* TryPopHandleScope(Register saved, Register scratch);
// Jump to a runtime routine.
void JumpToExternalReference(const ExternalReference& ext, int result_size);
// Prepares stack to put arguments (aligns and so on).
// Uses calle-saved esi to restore stack state after call.
void PrepareCallApiFunction(int stack_space);
// Tail call an API function (jump). Allocates HandleScope, extracts
// returned value from handle and propogates exceptions.
// Clobbers ebx, edi and caller-save registers.
void CallApiFunctionAndReturn(ApiFunction* function);
// Before calling a C-function from generated code, align arguments on stack.
// After aligning the frame, arguments must be stored in esp[0], esp[4],
// etc., not pushed. The argument count assumes all arguments are word sized.

View File

@ -2547,7 +2547,6 @@ bool StubCompiler::GenerateLoadCallback(JSObject* object,
Handle<AccessorInfo> callback_handle(callback);
__ EnterInternalFrame();
__ PushHandleScope(scratch2);
// Push the stack address where the list of arguments ends.
__ movq(scratch2, rsp);
__ subq(scratch2, Immediate(2 * kPointerSize));
@ -2601,17 +2600,6 @@ bool StubCompiler::GenerateLoadCallback(JSObject* object,
// Discard allocated slot.
__ addq(rsp, Immediate(kPointerSize));
#endif
// We need to avoid using rax since that now holds the result.
Register tmp = scratch2.is(rax) ? reg : scratch2;
// Emitting PopHandleScope may try to allocate. Do not allow the
// assembler to perform a garbage collection but instead return a
// failure object.
result = masm()->TryPopHandleScope(rax, tmp);
if (result->IsFailure()) {
*failure = Failure::cast(result);
return false;
}
__ LeaveInternalFrame();
__ ret(0);