// Copyright 2012 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #if V8_TARGET_ARCH_IA32 #include "src/api-arguments-inl.h" #include "src/assembler-inl.h" #include "src/base/bits.h" #include "src/bootstrapper.h" #include "src/code-stubs.h" #include "src/frame-constants.h" #include "src/frames.h" #include "src/heap/heap-inl.h" #include "src/ic/ic.h" #include "src/ic/stub-cache.h" #include "src/isolate.h" #include "src/objects/api-callbacks.h" #include "src/regexp/jsregexp.h" #include "src/regexp/regexp-macro-assembler.h" #include "src/runtime/runtime.h" namespace v8 { namespace internal { #define __ ACCESS_MASM(masm) void JSEntryStub::Generate(MacroAssembler* masm) { Label invoke, handler_entry, exit; Label not_outermost_js, not_outermost_js_2; ProfileEntryHookStub::MaybeCallEntryHook(masm); // Set up frame. __ push(ebp); __ mov(ebp, esp); // Push marker in two places. StackFrame::Type marker = type(); __ push(Immediate(StackFrame::TypeToMarker(marker))); // marker ExternalReference context_address = ExternalReference::Create(IsolateAddressId::kContextAddress, isolate()); __ push(__ StaticVariable(context_address)); // context // Save callee-saved registers (C calling conventions). __ push(edi); __ push(esi); __ push(ebx); __ InitializeRootRegister(); // Save copies of the top frame descriptor on the stack. ExternalReference c_entry_fp = ExternalReference::Create(IsolateAddressId::kCEntryFPAddress, isolate()); __ push(__ StaticVariable(c_entry_fp)); // If this is the outermost JS call, set js_entry_sp value. ExternalReference js_entry_sp = ExternalReference::Create(IsolateAddressId::kJSEntrySPAddress, isolate()); __ cmp(__ StaticVariable(js_entry_sp), Immediate(0)); __ j(not_equal, ¬_outermost_js, Label::kNear); __ mov(__ StaticVariable(js_entry_sp), ebp); __ push(Immediate(StackFrame::OUTERMOST_JSENTRY_FRAME)); __ jmp(&invoke, Label::kNear); __ bind(¬_outermost_js); __ push(Immediate(StackFrame::INNER_JSENTRY_FRAME)); // Jump to a faked try block that does the invoke, with a faked catch // block that sets the pending exception. __ jmp(&invoke); __ bind(&handler_entry); handler_offset_ = handler_entry.pos(); // Caught exception: Store result (exception) in the pending exception // field in the JSEnv and return a failure sentinel. ExternalReference pending_exception = ExternalReference::Create( IsolateAddressId::kPendingExceptionAddress, isolate()); __ mov(__ StaticVariable(pending_exception), eax); __ mov(eax, Immediate(isolate()->factory()->exception())); __ jmp(&exit); // Invoke: Link this frame into the handler chain. __ bind(&invoke); __ PushStackHandler(); // Invoke the function by calling through JS entry trampoline builtin and // pop the faked function when we return. Notice that we cannot store a // reference to the trampoline code directly in this stub, because the // builtin stubs may not have been generated yet. __ Call(EntryTrampoline(), RelocInfo::CODE_TARGET); // Unlink this frame from the handler chain. __ PopStackHandler(); __ bind(&exit); __ VerifyRootRegister(); // Check if the current stack frame is marked as the outermost JS frame. __ pop(ebx); __ cmp(ebx, Immediate(StackFrame::OUTERMOST_JSENTRY_FRAME)); __ j(not_equal, ¬_outermost_js_2); __ mov(__ StaticVariable(js_entry_sp), Immediate(0)); __ bind(¬_outermost_js_2); // Restore the top frame descriptor from the stack. __ pop(__ StaticVariable(ExternalReference::Create( IsolateAddressId::kCEntryFPAddress, isolate()))); // Restore callee-saved registers (C calling conventions). __ pop(ebx); __ pop(esi); __ pop(edi); __ add(esp, Immediate(2 * kPointerSize)); // remove markers // Restore frame pointer and return. __ pop(ebp); __ ret(0); } void ProfileEntryHookStub::MaybeCallEntryHookDelayed(TurboAssembler* tasm, Zone* zone) { if (tasm->isolate()->function_entry_hook() != nullptr) { tasm->CallStubDelayed(new (zone) ProfileEntryHookStub(nullptr)); } } void ProfileEntryHookStub::MaybeCallEntryHook(MacroAssembler* masm) { if (masm->isolate()->function_entry_hook() != nullptr) { ProfileEntryHookStub stub(masm->isolate()); masm->CallStub(&stub); } } void ProfileEntryHookStub::Generate(MacroAssembler* masm) { // Save volatile registers. const int kNumSavedRegisters = 3; __ push(eax); __ push(ecx); __ push(edx); // Calculate and push the original stack pointer. __ lea(eax, Operand(esp, (kNumSavedRegisters + 1) * kPointerSize)); __ push(eax); // Retrieve our return address and use it to calculate the calling // function's address. __ mov(eax, Operand(esp, (kNumSavedRegisters + 1) * kPointerSize)); __ sub(eax, Immediate(Assembler::kCallInstructionLength)); __ push(eax); // Call the entry hook. DCHECK_NOT_NULL(isolate()->function_entry_hook()); __ call(FUNCTION_ADDR(isolate()->function_entry_hook()), RelocInfo::RUNTIME_ENTRY); __ add(esp, Immediate(2 * kPointerSize)); // Restore ecx. __ pop(edx); __ pop(ecx); __ pop(eax); __ ret(0); } // Generates an Operand for saving parameters after PrepareCallApiFunction. static Operand ApiParameterOperand(int index) { return Operand(esp, index * kPointerSize); } // Prepares stack to put arguments (aligns and so on). Reserves // space for return value if needed (assumes the return value is a handle). // Arguments must be stored in ApiParameterOperand(0), ApiParameterOperand(1) // etc. Saves context (esi). If space was reserved for return value then // stores the pointer to the reserved slot into esi. static void PrepareCallApiFunction(MacroAssembler* masm, int argc) { __ EnterApiExitFrame(argc); if (__ emit_debug_code()) { __ mov(esi, Immediate(bit_cast(kZapValue))); } } // Calls an API function. Allocates HandleScope, extracts returned value // from handle and propagates exceptions. Clobbers ebx, edi and // caller-save registers. Restores context. On return removes // stack_space * kPointerSize (GCed). static void CallApiFunctionAndReturn(MacroAssembler* masm, Register function_address, ExternalReference thunk_ref, Operand thunk_last_arg, int stack_space, Operand* stack_space_operand, Operand return_value_operand) { Isolate* isolate = masm->isolate(); ExternalReference next_address = ExternalReference::handle_scope_next_address(isolate); ExternalReference limit_address = ExternalReference::handle_scope_limit_address(isolate); ExternalReference level_address = ExternalReference::handle_scope_level_address(isolate); DCHECK(edx == function_address); // Allocate HandleScope in callee-save registers. __ mov(ebx, __ StaticVariable(next_address)); __ mov(edi, __ StaticVariable(limit_address)); __ add(__ StaticVariable(level_address), Immediate(1)); if (FLAG_log_timer_events) { FrameScope frame(masm, StackFrame::MANUAL); __ PushSafepointRegisters(); __ PrepareCallCFunction(1, eax); __ mov(Operand(esp, 0), Immediate(ExternalReference::isolate_address(isolate))); __ CallCFunction(ExternalReference::log_enter_external_function(), 1); __ PopSafepointRegisters(); } Label profiler_disabled; Label end_profiler_check; __ mov(eax, Immediate(ExternalReference::is_profiling_address(isolate))); __ cmpb(Operand(eax, 0), Immediate(0)); __ j(zero, &profiler_disabled); // Additional parameter is the address of the actual getter function. __ mov(thunk_last_arg, function_address); // Call the api function. __ mov(eax, Immediate(thunk_ref)); __ call(eax); __ jmp(&end_profiler_check); __ bind(&profiler_disabled); // Call the api function. __ call(function_address); __ bind(&end_profiler_check); if (FLAG_log_timer_events) { FrameScope frame(masm, StackFrame::MANUAL); __ PushSafepointRegisters(); __ PrepareCallCFunction(1, eax); __ mov(Operand(esp, 0), Immediate(ExternalReference::isolate_address(isolate))); __ CallCFunction(ExternalReference::log_leave_external_function(), 1); __ PopSafepointRegisters(); } Label prologue; // Load the value from ReturnValue __ mov(eax, return_value_operand); Label promote_scheduled_exception; Label delete_allocated_handles; Label leave_exit_frame; __ bind(&prologue); // No more valid handles (the result handle was the last one). Restore // previous handle scope. __ mov(__ StaticVariable(next_address), ebx); __ sub(__ StaticVariable(level_address), Immediate(1)); __ Assert(above_equal, AbortReason::kInvalidHandleScopeLevel); __ cmp(edi, __ StaticVariable(limit_address)); __ j(not_equal, &delete_allocated_handles); // Leave the API exit frame. __ bind(&leave_exit_frame); if (stack_space_operand != nullptr) { __ mov(ebx, *stack_space_operand); } __ LeaveApiExitFrame(); // Check if the function scheduled an exception. ExternalReference scheduled_exception_address = ExternalReference::scheduled_exception_address(isolate); __ cmp(__ StaticVariable(scheduled_exception_address), Immediate(isolate->factory()->the_hole_value())); __ j(not_equal, &promote_scheduled_exception); #if DEBUG // Check if the function returned a valid JavaScript value. Label ok; Register return_value = eax; Register map = ecx; __ JumpIfSmi(return_value, &ok, Label::kNear); __ mov(map, FieldOperand(return_value, HeapObject::kMapOffset)); __ CmpInstanceType(map, LAST_NAME_TYPE); __ j(below_equal, &ok, Label::kNear); __ CmpInstanceType(map, FIRST_JS_RECEIVER_TYPE); __ j(above_equal, &ok, Label::kNear); __ cmp(map, isolate->factory()->heap_number_map()); __ j(equal, &ok, Label::kNear); __ cmp(return_value, isolate->factory()->undefined_value()); __ j(equal, &ok, Label::kNear); __ cmp(return_value, isolate->factory()->true_value()); __ j(equal, &ok, Label::kNear); __ cmp(return_value, isolate->factory()->false_value()); __ j(equal, &ok, Label::kNear); __ cmp(return_value, isolate->factory()->null_value()); __ j(equal, &ok, Label::kNear); __ Abort(AbortReason::kAPICallReturnedInvalidObject); __ bind(&ok); #endif if (stack_space_operand != nullptr) { DCHECK_EQ(0, stack_space); __ pop(ecx); __ add(esp, ebx); __ jmp(ecx); } else { __ ret(stack_space * kPointerSize); } // Re-throw by promoting a scheduled exception. __ bind(&promote_scheduled_exception); __ TailCallRuntime(Runtime::kPromoteScheduledException); // HandleScope limit has changed. Delete allocated extensions. ExternalReference delete_extensions = ExternalReference::delete_handle_scope_extensions(); __ bind(&delete_allocated_handles); __ mov(__ StaticVariable(limit_address), edi); __ mov(edi, eax); __ mov(Operand(esp, 0), Immediate(ExternalReference::isolate_address(isolate))); __ mov(eax, Immediate(delete_extensions)); __ call(eax); __ mov(eax, edi); __ jmp(&leave_exit_frame); } void CallApiCallbackStub::Generate(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- ebx : call_data // -- ecx : holder // -- edx : api_function_address // -- esi : context // -- // -- esp[0] : return address // -- esp[4] : last argument // -- ... // -- esp[argc * 4] : first argument // -- esp[(argc + 1) * 4] : receiver // ----------------------------------- Register call_data = ebx; Register holder = ecx; Register api_function_address = edx; Register return_address = eax; typedef FunctionCallbackArguments FCA; STATIC_ASSERT(FCA::kArgsLength == 6); STATIC_ASSERT(FCA::kNewTargetIndex == 5); STATIC_ASSERT(FCA::kDataIndex == 4); STATIC_ASSERT(FCA::kReturnValueOffset == 3); STATIC_ASSERT(FCA::kReturnValueDefaultValueIndex == 2); STATIC_ASSERT(FCA::kIsolateIndex == 1); STATIC_ASSERT(FCA::kHolderIndex == 0); __ pop(return_address); // new target __ PushRoot(Heap::kUndefinedValueRootIndex); // call data __ push(call_data); // return value __ PushRoot(Heap::kUndefinedValueRootIndex); // return value default __ PushRoot(Heap::kUndefinedValueRootIndex); // isolate __ push(Immediate(ExternalReference::isolate_address(isolate()))); // holder __ push(holder); Register scratch = call_data; __ mov(scratch, esp); // push return address __ push(return_address); // API function gets reference to the v8::Arguments. If CPU profiler // is enabled wrapper function will be called and we need to pass // address of the callback as additional parameter, always allocate // space for it. const int kApiArgc = 1 + 1; // Allocate the v8::Arguments structure in the arguments' space since // it's not controlled by GC. const int kApiStackSpace = 3; PrepareCallApiFunction(masm, kApiArgc + kApiStackSpace); // FunctionCallbackInfo::implicit_args_. __ mov(ApiParameterOperand(2), scratch); __ add(scratch, Immediate((argc() + FCA::kArgsLength - 1) * kPointerSize)); // FunctionCallbackInfo::values_. __ mov(ApiParameterOperand(3), scratch); // FunctionCallbackInfo::length_. __ Move(ApiParameterOperand(4), Immediate(argc())); // v8::InvocationCallback's argument. __ lea(scratch, ApiParameterOperand(2)); __ mov(ApiParameterOperand(0), scratch); ExternalReference thunk_ref = ExternalReference::invoke_function_callback(); // Stores return the first js argument int return_value_offset = 2 + FCA::kReturnValueOffset; Operand return_value_operand(ebp, return_value_offset * kPointerSize); const int stack_space = argc() + FCA::kArgsLength + 1; Operand* stack_space_operand = nullptr; CallApiFunctionAndReturn(masm, api_function_address, thunk_ref, ApiParameterOperand(1), stack_space, stack_space_operand, return_value_operand); } void CallApiGetterStub::Generate(MacroAssembler* masm) { // Build v8::PropertyCallbackInfo::args_ array on the stack and push property // name below the exit frame to make GC aware of them. STATIC_ASSERT(PropertyCallbackArguments::kShouldThrowOnErrorIndex == 0); STATIC_ASSERT(PropertyCallbackArguments::kHolderIndex == 1); STATIC_ASSERT(PropertyCallbackArguments::kIsolateIndex == 2); STATIC_ASSERT(PropertyCallbackArguments::kReturnValueDefaultValueIndex == 3); STATIC_ASSERT(PropertyCallbackArguments::kReturnValueOffset == 4); STATIC_ASSERT(PropertyCallbackArguments::kDataIndex == 5); STATIC_ASSERT(PropertyCallbackArguments::kThisIndex == 6); STATIC_ASSERT(PropertyCallbackArguments::kArgsLength == 7); Register receiver = ApiGetterDescriptor::ReceiverRegister(); Register holder = ApiGetterDescriptor::HolderRegister(); Register callback = ApiGetterDescriptor::CallbackRegister(); Register scratch = ebx; DCHECK(!AreAliased(receiver, holder, callback, scratch)); __ pop(scratch); // Pop return address to extend the frame. __ push(receiver); __ push(FieldOperand(callback, AccessorInfo::kDataOffset)); __ PushRoot(Heap::kUndefinedValueRootIndex); // ReturnValue // ReturnValue default value __ PushRoot(Heap::kUndefinedValueRootIndex); __ push(Immediate(ExternalReference::isolate_address(isolate()))); __ push(holder); __ push(Immediate(Smi::kZero)); // should_throw_on_error -> false __ push(FieldOperand(callback, AccessorInfo::kNameOffset)); __ push(scratch); // Restore return address. // v8::PropertyCallbackInfo::args_ array and name handle. const int kStackUnwindSpace = PropertyCallbackArguments::kArgsLength + 1; // Allocate v8::PropertyCallbackInfo object, arguments for callback and // space for optional callback address parameter (in case CPU profiler is // active) in non-GCed stack space. const int kApiArgc = 3 + 1; // Load address of v8::PropertyAccessorInfo::args_ array. __ lea(scratch, Operand(esp, 2 * kPointerSize)); PrepareCallApiFunction(masm, kApiArgc); // Create v8::PropertyCallbackInfo object on the stack and initialize // it's args_ field. Operand info_object = ApiParameterOperand(3); __ mov(info_object, scratch); // Name as handle. __ sub(scratch, Immediate(kPointerSize)); __ mov(ApiParameterOperand(0), scratch); // Arguments pointer. __ lea(scratch, info_object); __ mov(ApiParameterOperand(1), scratch); // Reserve space for optional callback address parameter. Operand thunk_last_arg = ApiParameterOperand(2); ExternalReference thunk_ref = ExternalReference::invoke_accessor_getter_callback(); __ mov(scratch, FieldOperand(callback, AccessorInfo::kJsGetterOffset)); Register function_address = edx; __ mov(function_address, FieldOperand(scratch, Foreign::kForeignAddressOffset)); // +3 is to skip prolog, return address and name handle. Operand return_value_operand( ebp, (PropertyCallbackArguments::kReturnValueOffset + 3) * kPointerSize); CallApiFunctionAndReturn(masm, function_address, thunk_ref, thunk_last_arg, kStackUnwindSpace, nullptr, return_value_operand); } #undef __ } // namespace internal } // namespace v8 #endif // V8_TARGET_ARCH_IA32