diff --git a/BUILD.gn b/BUILD.gn index dd96cdb56d..950ef9547c 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -112,9 +112,9 @@ declare_args() { v8_experimental_extra_library_files = [ "//test/cctest/test-experimental-extra.js" ] - v8_enable_gdbjit = ((v8_current_cpu == "x86" || v8_current_cpu == "x64" || - v8_current_cpu == "x87") && (is_linux || is_mac)) || - (v8_current_cpu == "ppc64" && is_linux) + v8_enable_gdbjit = + ((v8_current_cpu == "x86" || v8_current_cpu == "x64") && + (is_linux || is_mac)) || (v8_current_cpu == "ppc64" && is_linux) # Temporary flag to allow embedders to update their microtasks scopes # while rolling in a new version of V8. @@ -439,9 +439,6 @@ config("toolchain") { ldflags += [ "/STACK:2097152" ] } } - if (v8_current_cpu == "x87") { - defines += [ "V8_TARGET_ARCH_X87" ] - } if (is_android && v8_android_log_stdout) { defines += [ "V8_ANDROID_LOG_STDOUT" ] } @@ -1039,11 +1036,6 @@ v8_source_set("v8_builtins_generators") { ### gcmole(arch:s390) ### "src/builtins/s390/builtins-s390.cc", ] - } else if (v8_current_cpu == "x87") { - sources += [ - ### gcmole(arch:x87) ### - "src/builtins/x87/builtins-x87.cc", - ] } if (!v8_enable_i18n_support) { @@ -2313,37 +2305,6 @@ v8_source_set("v8_base") { "src/s390/simulator-s390.cc", "src/s390/simulator-s390.h", ] - } else if (v8_current_cpu == "x87") { - sources += [ ### gcmole(arch:x87) ### - "src/compiler/x87/code-generator-x87.cc", - "src/compiler/x87/instruction-codes-x87.h", - "src/compiler/x87/instruction-scheduler-x87.cc", - "src/compiler/x87/instruction-selector-x87.cc", - "src/debug/x87/debug-x87.cc", - "src/full-codegen/x87/full-codegen-x87.cc", - "src/ic/x87/access-compiler-x87.cc", - "src/ic/x87/handler-compiler-x87.cc", - "src/ic/x87/ic-x87.cc", - "src/regexp/x87/regexp-macro-assembler-x87.cc", - "src/regexp/x87/regexp-macro-assembler-x87.h", - "src/x87/assembler-x87-inl.h", - "src/x87/assembler-x87.cc", - "src/x87/assembler-x87.h", - "src/x87/code-stubs-x87.cc", - "src/x87/code-stubs-x87.h", - "src/x87/codegen-x87.cc", - "src/x87/codegen-x87.h", - "src/x87/cpu-x87.cc", - "src/x87/deoptimizer-x87.cc", - "src/x87/disasm-x87.cc", - "src/x87/frames-x87.cc", - "src/x87/frames-x87.h", - "src/x87/interface-descriptors-x87.cc", - "src/x87/macro-assembler-x87.cc", - "src/x87/macro-assembler-x87.h", - "src/x87/simulator-x87.cc", - "src/x87/simulator-x87.h", - ] } configs = [ ":internal_config" ] diff --git a/Makefile b/Makefile index b2fe24b208..b381918355 100644 --- a/Makefile +++ b/Makefile @@ -255,14 +255,13 @@ endif # Architectures and modes to be compiled. Consider these to be internal # variables, don't override them (use the targets instead). -ARCHES = ia32 x64 arm arm64 mips mipsel mips64 mips64el x87 ppc ppc64 s390 \ - s390x -ARCHES32 = ia32 arm mips mipsel x87 ppc s390 +ARCHES = ia32 x64 arm arm64 mips mipsel mips64 mips64el ppc ppc64 s390 s390x +ARCHES32 = ia32 arm mips mipsel ppc s390 DEFAULT_ARCHES = ia32 x64 arm MODES = release debug optdebug DEFAULT_MODES = release debug ANDROID_ARCHES = android_ia32 android_x64 android_arm android_arm64 \ - android_mipsel android_x87 + android_mipsel # List of files that trigger Makefile regeneration: GYPFILES = third_party/icu/icu.gypi third_party/icu/icu.gyp \ diff --git a/gypfiles/standalone.gypi b/gypfiles/standalone.gypi index 48bc6677bd..a30373be61 100644 --- a/gypfiles/standalone.gypi +++ b/gypfiles/standalone.gypi @@ -262,14 +262,14 @@ # goma doesn't support PDB yet. 'fastbuild%': 1, }], - ['((v8_target_arch=="ia32" or v8_target_arch=="x64" or v8_target_arch=="x87") and \ + ['((v8_target_arch=="ia32" or v8_target_arch=="x64") and \ (OS=="linux" or OS=="mac")) or (v8_target_arch=="ppc64" and OS=="linux")', { 'v8_enable_gdbjit%': 1, }, { 'v8_enable_gdbjit%': 0, }], ['(OS=="linux" or OS=="mac") and (target_arch=="ia32" or target_arch=="x64") and \ - (v8_target_arch!="x87" and v8_target_arch!="x32")', { + v8_target_arch!="x32"', { 'clang%': 1, }, { 'clang%': 0, @@ -1207,7 +1207,7 @@ '-L<(android_libcpp_libs)/arm64-v8a', ], }], - ['target_arch=="ia32" or target_arch=="x87"', { + ['target_arch=="ia32"', { # The x86 toolchain currently has problems with stack-protector. 'cflags!': [ '-fstack-protector', diff --git a/gypfiles/toolchain.gypi b/gypfiles/toolchain.gypi index 96d9e89d93..5733d2d54c 100644 --- a/gypfiles/toolchain.gypi +++ b/gypfiles/toolchain.gypi @@ -144,7 +144,7 @@ 'host_cxx_is_biarch%': 0, }, }], - ['target_arch=="ia32" or target_arch=="x64" or target_arch=="x87" or \ + ['target_arch=="ia32" or target_arch=="x64" or \ target_arch=="ppc" or target_arch=="ppc64" or target_arch=="s390" or \ target_arch=="s390x" or clang==1', { 'variables': { @@ -342,12 +342,6 @@ 'V8_TARGET_ARCH_IA32', ], }], # v8_target_arch=="ia32" - ['v8_target_arch=="x87"', { - 'defines': [ - 'V8_TARGET_ARCH_X87', - ], - 'cflags': ['-march=i586'], - }], # v8_target_arch=="x87" ['v8_target_arch=="mips" or v8_target_arch=="mipsel" \ or v8_target_arch=="mips64" or v8_target_arch=="mips64el"', { 'target_conditions': [ @@ -1006,9 +1000,8 @@ ['(OS=="linux" or OS=="freebsd" or OS=="openbsd" or OS=="solaris" \ or OS=="netbsd" or OS=="mac" or OS=="android" or OS=="qnx") and \ (v8_target_arch=="arm" or v8_target_arch=="ia32" or \ - v8_target_arch=="x87" or v8_target_arch=="mips" or \ - v8_target_arch=="mipsel" or v8_target_arch=="ppc" or \ - v8_target_arch=="s390")', { + v8_target_arch=="mips" or v8_target_arch=="mipsel" or \ + v8_target_arch=="ppc" or v8_target_arch=="s390")', { 'target_conditions': [ ['_toolset=="host"', { 'conditions': [ diff --git a/src/assembler-inl.h b/src/assembler-inl.h index 24d0377ce5..5cf4fae63a 100644 --- a/src/assembler-inl.h +++ b/src/assembler-inl.h @@ -23,8 +23,6 @@ #include "src/mips64/assembler-mips64-inl.h" #elif V8_TARGET_ARCH_S390 #include "src/s390/assembler-s390-inl.h" -#elif V8_TARGET_ARCH_X87 -#include "src/x87/assembler-x87-inl.h" #else #error Unknown architecture. #endif diff --git a/src/assembler.cc b/src/assembler.cc index c75afdc9f0..c561050ed6 100644 --- a/src/assembler.cc +++ b/src/assembler.cc @@ -85,8 +85,6 @@ #include "src/regexp/mips64/regexp-macro-assembler-mips64.h" // NOLINT #elif V8_TARGET_ARCH_S390 #include "src/regexp/s390/regexp-macro-assembler-s390.h" // NOLINT -#elif V8_TARGET_ARCH_X87 -#include "src/regexp/x87/regexp-macro-assembler-x87.h" // NOLINT #else // Unknown architecture. #error "Unknown architecture." #endif // Target architecture. @@ -1324,8 +1322,6 @@ ExternalReference ExternalReference::re_check_stack_guard_state( function = FUNCTION_ADDR(RegExpMacroAssemblerMIPS::CheckStackGuardState); #elif V8_TARGET_ARCH_S390 function = FUNCTION_ADDR(RegExpMacroAssemblerS390::CheckStackGuardState); -#elif V8_TARGET_ARCH_X87 - function = FUNCTION_ADDR(RegExpMacroAssemblerX87::CheckStackGuardState); #else UNREACHABLE(); #endif diff --git a/src/base/build_config.h b/src/base/build_config.h index 3ee7810afc..73488de5bd 100644 --- a/src/base/build_config.h +++ b/src/base/build_config.h @@ -76,9 +76,9 @@ // Target architecture detection. This may be set externally. If not, detect // in the same way as the host architecture, that is, target the native // environment as presented by the compiler. -#if !V8_TARGET_ARCH_X64 && !V8_TARGET_ARCH_IA32 && !V8_TARGET_ARCH_X87 && \ - !V8_TARGET_ARCH_ARM && !V8_TARGET_ARCH_ARM64 && !V8_TARGET_ARCH_MIPS && \ - !V8_TARGET_ARCH_MIPS64 && !V8_TARGET_ARCH_PPC && !V8_TARGET_ARCH_S390 +#if !V8_TARGET_ARCH_X64 && !V8_TARGET_ARCH_IA32 && !V8_TARGET_ARCH_ARM && \ + !V8_TARGET_ARCH_ARM64 && !V8_TARGET_ARCH_MIPS && !V8_TARGET_ARCH_MIPS64 && \ + !V8_TARGET_ARCH_PPC && !V8_TARGET_ARCH_S390 #if defined(_M_X64) || defined(__x86_64__) #define V8_TARGET_ARCH_X64 1 #elif defined(_M_IX86) || defined(__i386__) @@ -129,8 +129,6 @@ #else #define V8_TARGET_ARCH_32_BIT 1 #endif -#elif V8_TARGET_ARCH_X87 -#define V8_TARGET_ARCH_32_BIT 1 #else #error Unknown target architecture pointer size #endif @@ -181,8 +179,6 @@ #else #define V8_TARGET_LITTLE_ENDIAN 1 #endif -#elif V8_TARGET_ARCH_X87 -#define V8_TARGET_LITTLE_ENDIAN 1 #elif __BIG_ENDIAN__ // FOR PPCGR on AIX #define V8_TARGET_BIG_ENDIAN 1 #elif V8_TARGET_ARCH_PPC_LE @@ -199,8 +195,7 @@ #error Unknown target architecture endianness #endif -#if defined(V8_TARGET_ARCH_IA32) || defined(V8_TARGET_ARCH_X64) || \ - defined(V8_TARGET_ARCH_X87) +#if defined(V8_TARGET_ARCH_IA32) || defined(V8_TARGET_ARCH_X64) #define V8_TARGET_ARCH_STORES_RETURN_ADDRESS_ON_STACK 1 #else #define V8_TARGET_ARCH_STORES_RETURN_ADDRESS_ON_STACK 0 diff --git a/src/builtins/x87/OWNERS b/src/builtins/x87/OWNERS deleted file mode 100644 index 61245ae8e2..0000000000 --- a/src/builtins/x87/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -weiliang.lin@intel.com -chunyang.dai@intel.com diff --git a/src/builtins/x87/builtins-x87.cc b/src/builtins/x87/builtins-x87.cc deleted file mode 100644 index b06c92ee49..0000000000 --- a/src/builtins/x87/builtins-x87.cc +++ /dev/null @@ -1,3143 +0,0 @@ -// 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_X87 - -#include "src/code-factory.h" -#include "src/codegen.h" -#include "src/deoptimizer.h" -#include "src/full-codegen/full-codegen.h" -#include "src/x87/frames-x87.h" - -namespace v8 { -namespace internal { - -#define __ ACCESS_MASM(masm) - -void Builtins::Generate_Adaptor(MacroAssembler* masm, Address address, - ExitFrameType exit_frame_type) { - // ----------- S t a t e ------------- - // -- eax : number of arguments excluding receiver - // -- edi : target - // -- edx : new.target - // -- esp[0] : return address - // -- esp[4] : last argument - // -- ... - // -- esp[4 * argc] : first argument - // -- esp[4 * (argc +1)] : receiver - // ----------------------------------- - __ AssertFunction(edi); - - // Make sure we operate in the context of the called function (for example - // ConstructStubs implemented in C++ will be run in the context of the caller - // instead of the callee, due to the way that [[Construct]] is defined for - // ordinary functions). - __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); - - // JumpToExternalReference expects eax to contain the number of arguments - // including the receiver and the extra arguments. - const int num_extra_args = 3; - __ add(eax, Immediate(num_extra_args + 1)); - - // Insert extra arguments. - __ PopReturnAddressTo(ecx); - __ SmiTag(eax); - __ Push(eax); - __ SmiUntag(eax); - __ Push(edi); - __ Push(edx); - __ PushReturnAddressFrom(ecx); - - __ JumpToExternalReference(ExternalReference(address, masm->isolate()), - exit_frame_type == BUILTIN_EXIT); -} - -static void GenerateTailCallToReturnedCode(MacroAssembler* masm, - Runtime::FunctionId function_id) { - // ----------- S t a t e ------------- - // -- eax : argument count (preserved for callee) - // -- edx : new target (preserved for callee) - // -- edi : target function (preserved for callee) - // ----------------------------------- - { - FrameScope scope(masm, StackFrame::INTERNAL); - // Push the number of arguments to the callee. - __ SmiTag(eax); - __ push(eax); - // Push a copy of the target function and the new target. - __ push(edi); - __ push(edx); - // Function is also the parameter to the runtime call. - __ push(edi); - - __ CallRuntime(function_id, 1); - __ mov(ebx, eax); - - // Restore target function and new target. - __ pop(edx); - __ pop(edi); - __ pop(eax); - __ SmiUntag(eax); - } - - __ lea(ebx, FieldOperand(ebx, Code::kHeaderSize)); - __ jmp(ebx); -} - -static void GenerateTailCallToSharedCode(MacroAssembler* masm) { - __ mov(ebx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); - __ mov(ebx, FieldOperand(ebx, SharedFunctionInfo::kCodeOffset)); - __ lea(ebx, FieldOperand(ebx, Code::kHeaderSize)); - __ jmp(ebx); -} - -void Builtins::Generate_InOptimizationQueue(MacroAssembler* masm) { - // Checking whether the queued function is ready for install is optional, - // since we come across interrupts and stack checks elsewhere. However, - // not checking may delay installing ready functions, and always checking - // would be quite expensive. A good compromise is to first check against - // stack limit as a cue for an interrupt signal. - Label ok; - ExternalReference stack_limit = - ExternalReference::address_of_stack_limit(masm->isolate()); - __ cmp(esp, Operand::StaticVariable(stack_limit)); - __ j(above_equal, &ok, Label::kNear); - - GenerateTailCallToReturnedCode(masm, Runtime::kTryInstallOptimizedCode); - - __ bind(&ok); - GenerateTailCallToSharedCode(masm); -} - -namespace { - -void Generate_JSConstructStubHelper(MacroAssembler* masm, bool is_api_function, - bool create_implicit_receiver, - bool check_derived_construct) { - // ----------- S t a t e ------------- - // -- eax: number of arguments - // -- esi: context - // -- edi: constructor function - // -- edx: new target - // ----------------------------------- - - // Enter a construct frame. - { - FrameScope scope(masm, StackFrame::CONSTRUCT); - - // Preserve the incoming parameters on the stack. - __ SmiTag(eax); - __ push(esi); - __ push(eax); - - if (create_implicit_receiver) { - // Allocate the new receiver object. - __ Push(edi); - __ Push(edx); - __ Call(Builtins::CallableFor(masm->isolate(), Builtins::kFastNewObject) - .code(), - RelocInfo::CODE_TARGET); - __ mov(ebx, eax); - __ Pop(edx); - __ Pop(edi); - - // ----------- S t a t e ------------- - // -- edi: constructor function - // -- ebx: newly allocated object - // -- edx: new target - // ----------------------------------- - - // Retrieve smi-tagged arguments count from the stack. - __ mov(eax, Operand(esp, 0)); - } - - __ SmiUntag(eax); - - if (create_implicit_receiver) { - // Push the allocated receiver to the stack. We need two copies - // because we may have to return the original one and the calling - // conventions dictate that the called function pops the receiver. - __ push(ebx); - __ push(ebx); - } else { - __ PushRoot(Heap::kTheHoleValueRootIndex); - } - - // Set up pointer to last argument. - __ lea(ebx, Operand(ebp, StandardFrameConstants::kCallerSPOffset)); - - // Copy arguments and receiver to the expression stack. - Label loop, entry; - __ mov(ecx, eax); - __ jmp(&entry); - __ bind(&loop); - __ push(Operand(ebx, ecx, times_4, 0)); - __ bind(&entry); - __ dec(ecx); - __ j(greater_equal, &loop); - - // Call the function. - ParameterCount actual(eax); - __ InvokeFunction(edi, edx, actual, CALL_FUNCTION, - CheckDebugStepCallWrapper()); - - // Store offset of return address for deoptimizer. - if (create_implicit_receiver && !is_api_function) { - masm->isolate()->heap()->SetConstructStubDeoptPCOffset(masm->pc_offset()); - } - - // Restore context from the frame. - __ mov(esi, Operand(ebp, ConstructFrameConstants::kContextOffset)); - - if (create_implicit_receiver) { - // If the result is an object (in the ECMA sense), we should get rid - // of the receiver and use the result; see ECMA-262 section 13.2.2-7 - // on page 74. - Label use_receiver, exit; - - // If the result is a smi, it is *not* an object in the ECMA sense. - __ JumpIfSmi(eax, &use_receiver, Label::kNear); - - // If the type of the result (stored in its map) is less than - // FIRST_JS_RECEIVER_TYPE, it is not an object in the ECMA sense. - __ CmpObjectType(eax, FIRST_JS_RECEIVER_TYPE, ecx); - __ j(above_equal, &exit, Label::kNear); - - // Throw away the result of the constructor invocation and use the - // on-stack receiver as the result. - __ bind(&use_receiver); - __ mov(eax, Operand(esp, 0)); - - // Restore the arguments count and leave the construct frame. The - // arguments count is stored below the receiver. - __ bind(&exit); - __ mov(ebx, Operand(esp, 1 * kPointerSize)); - } else { - __ mov(ebx, Operand(esp, 0)); - } - - // Leave construct frame. - } - - // ES6 9.2.2. Step 13+ - // Check that the result is not a Smi, indicating that the constructor result - // from a derived class is neither undefined nor an Object. - if (check_derived_construct) { - Label dont_throw; - __ JumpIfNotSmi(eax, &dont_throw); - { - FrameScope scope(masm, StackFrame::INTERNAL); - __ CallRuntime(Runtime::kThrowDerivedConstructorReturnedNonObject); - } - __ bind(&dont_throw); - } - - // Remove caller arguments from the stack and return. - STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0); - __ pop(ecx); - __ lea(esp, Operand(esp, ebx, times_2, 1 * kPointerSize)); // 1 ~ receiver - __ push(ecx); - if (create_implicit_receiver) { - __ IncrementCounter(masm->isolate()->counters()->constructed_objects(), 1); - } - __ ret(0); -} - -} // namespace - -void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) { - Generate_JSConstructStubHelper(masm, false, true, false); -} - -void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) { - Generate_JSConstructStubHelper(masm, true, false, false); -} - -void Builtins::Generate_JSBuiltinsConstructStub(MacroAssembler* masm) { - Generate_JSConstructStubHelper(masm, false, false, false); -} - -void Builtins::Generate_JSBuiltinsConstructStubForDerived( - MacroAssembler* masm) { - Generate_JSConstructStubHelper(masm, false, false, true); -} - -void Builtins::Generate_ConstructedNonConstructable(MacroAssembler* masm) { - FrameScope scope(masm, StackFrame::INTERNAL); - __ push(edi); - __ CallRuntime(Runtime::kThrowConstructedNonConstructable); -} - -enum IsTagged { kEaxIsSmiTagged, kEaxIsUntaggedInt }; - -// Clobbers ecx, edx, edi; preserves all other registers. -static void Generate_CheckStackOverflow(MacroAssembler* masm, - IsTagged eax_is_tagged) { - // eax : the number of items to be pushed to the stack - // - // Check the stack for overflow. We are not trying to catch - // interruptions (e.g. debug break and preemption) here, so the "real stack - // limit" is checked. - Label okay; - ExternalReference real_stack_limit = - ExternalReference::address_of_real_stack_limit(masm->isolate()); - __ mov(edi, Operand::StaticVariable(real_stack_limit)); - // Make ecx the space we have left. The stack might already be overflowed - // here which will cause ecx to become negative. - __ mov(ecx, esp); - __ sub(ecx, edi); - // Make edx the space we need for the array when it is unrolled onto the - // stack. - __ mov(edx, eax); - int smi_tag = eax_is_tagged == kEaxIsSmiTagged ? kSmiTagSize : 0; - __ shl(edx, kPointerSizeLog2 - smi_tag); - // Check if the arguments will overflow the stack. - __ cmp(ecx, edx); - __ j(greater, &okay); // Signed comparison. - - // Out of stack space. - __ CallRuntime(Runtime::kThrowStackOverflow); - - __ bind(&okay); -} - -static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, - bool is_construct) { - ProfileEntryHookStub::MaybeCallEntryHook(masm); - - { - FrameScope scope(masm, StackFrame::INTERNAL); - - // Setup the context (we need to use the caller context from the isolate). - ExternalReference context_address(IsolateAddressId::kContextAddress, - masm->isolate()); - __ mov(esi, Operand::StaticVariable(context_address)); - - // Load the previous frame pointer (ebx) to access C arguments - __ mov(ebx, Operand(ebp, 0)); - - // Push the function and the receiver onto the stack. - __ push(Operand(ebx, EntryFrameConstants::kFunctionArgOffset)); - __ push(Operand(ebx, EntryFrameConstants::kReceiverArgOffset)); - - // Load the number of arguments and setup pointer to the arguments. - __ mov(eax, Operand(ebx, EntryFrameConstants::kArgcOffset)); - __ mov(ebx, Operand(ebx, EntryFrameConstants::kArgvOffset)); - - // Check if we have enough stack space to push all arguments. - // Expects argument count in eax. Clobbers ecx, edx, edi. - Generate_CheckStackOverflow(masm, kEaxIsUntaggedInt); - - // Copy arguments to the stack in a loop. - Label loop, entry; - __ Move(ecx, Immediate(0)); - __ jmp(&entry, Label::kNear); - __ bind(&loop); - __ mov(edx, Operand(ebx, ecx, times_4, 0)); // push parameter from argv - __ push(Operand(edx, 0)); // dereference handle - __ inc(ecx); - __ bind(&entry); - __ cmp(ecx, eax); - __ j(not_equal, &loop); - - // Load the previous frame pointer (ebx) to access C arguments - __ mov(ebx, Operand(ebp, 0)); - - // Get the new.target and function from the frame. - __ mov(edx, Operand(ebx, EntryFrameConstants::kNewTargetArgOffset)); - __ mov(edi, Operand(ebx, EntryFrameConstants::kFunctionArgOffset)); - - // Invoke the code. - Handle builtin = is_construct - ? masm->isolate()->builtins()->Construct() - : masm->isolate()->builtins()->Call(); - __ Call(builtin, RelocInfo::CODE_TARGET); - - // Exit the internal frame. Notice that this also removes the empty. - // context and the function left on the stack by the code - // invocation. - } - __ ret(kPointerSize); // Remove receiver. -} - -void Builtins::Generate_JSEntryTrampoline(MacroAssembler* masm) { - Generate_JSEntryTrampolineHelper(masm, false); -} - -void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) { - Generate_JSEntryTrampolineHelper(masm, true); -} - -// static -void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- eax : the value to pass to the generator - // -- ebx : the JSGeneratorObject to resume - // -- edx : the resume mode (tagged) - // -- esp[0] : return address - // ----------------------------------- - __ AssertGeneratorObject(ebx); - - // Store input value into generator object. - __ mov(FieldOperand(ebx, JSGeneratorObject::kInputOrDebugPosOffset), eax); - __ RecordWriteField(ebx, JSGeneratorObject::kInputOrDebugPosOffset, eax, ecx, - kDontSaveFPRegs); - - // Store resume mode into generator object. - __ mov(FieldOperand(ebx, JSGeneratorObject::kResumeModeOffset), edx); - - // Load suspended function and context. - __ mov(edi, FieldOperand(ebx, JSGeneratorObject::kFunctionOffset)); - __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); - - // Flood function if we are stepping. - Label prepare_step_in_if_stepping, prepare_step_in_suspended_generator; - Label stepping_prepared; - ExternalReference debug_hook = - ExternalReference::debug_hook_on_function_call_address(masm->isolate()); - __ cmpb(Operand::StaticVariable(debug_hook), Immediate(0)); - __ j(not_equal, &prepare_step_in_if_stepping); - - // Flood function if we need to continue stepping in the suspended generator. - ExternalReference debug_suspended_generator = - ExternalReference::debug_suspended_generator_address(masm->isolate()); - __ cmp(ebx, Operand::StaticVariable(debug_suspended_generator)); - __ j(equal, &prepare_step_in_suspended_generator); - __ bind(&stepping_prepared); - - // Pop return address. - __ PopReturnAddressTo(eax); - - // Push receiver. - __ Push(FieldOperand(ebx, JSGeneratorObject::kReceiverOffset)); - - // ----------- S t a t e ------------- - // -- eax : return address - // -- ebx : the JSGeneratorObject to resume - // -- edx : the resume mode (tagged) - // -- edi : generator function - // -- esi : generator context - // -- esp[0] : generator receiver - // ----------------------------------- - - // Push holes for arguments to generator function. Since the parser forced - // context allocation for any variables in generators, the actual argument - // values have already been copied into the context and these dummy values - // will never be used. - __ mov(ecx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); - __ mov(ecx, - FieldOperand(ecx, SharedFunctionInfo::kFormalParameterCountOffset)); - { - Label done_loop, loop; - __ bind(&loop); - __ sub(ecx, Immediate(1)); - __ j(carry, &done_loop, Label::kNear); - __ PushRoot(Heap::kTheHoleValueRootIndex); - __ jmp(&loop); - __ bind(&done_loop); - } - - // Underlying function needs to have bytecode available. - if (FLAG_debug_code) { - __ mov(ecx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); - __ mov(ecx, FieldOperand(ecx, SharedFunctionInfo::kFunctionDataOffset)); - __ CmpObjectType(ecx, BYTECODE_ARRAY_TYPE, ecx); - __ Assert(equal, kMissingBytecodeArray); - } - - // Resume (Ignition/TurboFan) generator object. - { - __ PushReturnAddressFrom(eax); - __ mov(eax, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); - __ mov(eax, - FieldOperand(eax, SharedFunctionInfo::kFormalParameterCountOffset)); - // We abuse new.target both to indicate that this is a resume call and to - // pass in the generator object. In ordinary calls, new.target is always - // undefined because generator functions are non-constructable. - __ mov(edx, ebx); - __ jmp(FieldOperand(edi, JSFunction::kCodeEntryOffset)); - } - - __ bind(&prepare_step_in_if_stepping); - { - FrameScope scope(masm, StackFrame::INTERNAL); - __ Push(ebx); - __ Push(edx); - __ Push(edi); - __ CallRuntime(Runtime::kDebugOnFunctionCall); - __ Pop(edx); - __ Pop(ebx); - __ mov(edi, FieldOperand(ebx, JSGeneratorObject::kFunctionOffset)); - } - __ jmp(&stepping_prepared); - - __ bind(&prepare_step_in_suspended_generator); - { - FrameScope scope(masm, StackFrame::INTERNAL); - __ Push(ebx); - __ Push(edx); - __ CallRuntime(Runtime::kDebugPrepareStepInSuspendedGenerator); - __ Pop(edx); - __ Pop(ebx); - __ mov(edi, FieldOperand(ebx, JSGeneratorObject::kFunctionOffset)); - } - __ jmp(&stepping_prepared); -} - -static void LeaveInterpreterFrame(MacroAssembler* masm, Register scratch1, - Register scratch2) { - Register args_count = scratch1; - Register return_pc = scratch2; - - // Get the arguments + reciever count. - __ mov(args_count, - Operand(ebp, InterpreterFrameConstants::kBytecodeArrayFromFp)); - __ mov(args_count, - FieldOperand(args_count, BytecodeArray::kParameterSizeOffset)); - - // Leave the frame (also dropping the register file). - __ leave(); - - // Drop receiver + arguments. - __ pop(return_pc); - __ add(esp, args_count); - __ push(return_pc); -} - -// Generate code for entering a JS function with the interpreter. -// On entry to the function the receiver and arguments have been pushed on the -// stack left to right. The actual argument count matches the formal parameter -// count expected by the function. -// -// The live registers are: -// o edi: the JS function object being called -// o edx: the new target -// o esi: our context -// o ebp: the caller's frame pointer -// o esp: stack pointer (pointing to return address) -// -// The function builds an interpreter frame. See InterpreterFrameConstants in -// frames.h for its layout. -void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) { - ProfileEntryHookStub::MaybeCallEntryHook(masm); - - // Open a frame scope to indicate that there is a frame on the stack. The - // MANUAL indicates that the scope shouldn't actually generate code to set up - // the frame (that is done below). - FrameScope frame_scope(masm, StackFrame::MANUAL); - __ push(ebp); // Caller's frame pointer. - __ mov(ebp, esp); - __ push(esi); // Callee's context. - __ push(edi); // Callee's JS function. - __ push(edx); // Callee's new target. - - // Get the bytecode array from the function object (or from the DebugInfo if - // it is present) and load it into kInterpreterBytecodeArrayRegister. - __ mov(eax, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); - Label load_debug_bytecode_array, bytecode_array_loaded; - __ JumpIfNotSmi(FieldOperand(eax, SharedFunctionInfo::kDebugInfoOffset), - &load_debug_bytecode_array); - __ mov(kInterpreterBytecodeArrayRegister, - FieldOperand(eax, SharedFunctionInfo::kFunctionDataOffset)); - __ bind(&bytecode_array_loaded); - - // Check whether we should continue to use the interpreter. - // TODO(rmcilroy) Remove self healing once liveedit only has to deal with - // Ignition bytecode. - Label switch_to_different_code_kind; - __ Move(ecx, masm->CodeObject()); // Self-reference to this code. - __ cmp(ecx, FieldOperand(eax, SharedFunctionInfo::kCodeOffset)); - __ j(not_equal, &switch_to_different_code_kind); - - // Increment invocation count for the function. - __ EmitLoadFeedbackVector(ecx); - __ add( - FieldOperand(ecx, FeedbackVector::kInvocationCountIndex * kPointerSize + - FeedbackVector::kHeaderSize), - Immediate(Smi::FromInt(1))); - - // Check function data field is actually a BytecodeArray object. - if (FLAG_debug_code) { - __ AssertNotSmi(kInterpreterBytecodeArrayRegister); - __ CmpObjectType(kInterpreterBytecodeArrayRegister, BYTECODE_ARRAY_TYPE, - eax); - __ Assert(equal, kFunctionDataShouldBeBytecodeArrayOnInterpreterEntry); - } - - // Reset code age. - __ mov_b(FieldOperand(kInterpreterBytecodeArrayRegister, - BytecodeArray::kBytecodeAgeOffset), - Immediate(BytecodeArray::kNoAgeBytecodeAge)); - - // Push bytecode array. - __ push(kInterpreterBytecodeArrayRegister); - // Push Smi tagged initial bytecode array offset. - __ push(Immediate(Smi::FromInt(BytecodeArray::kHeaderSize - kHeapObjectTag))); - - // Allocate the local and temporary register file on the stack. - { - // Load frame size from the BytecodeArray object. - __ mov(ebx, FieldOperand(kInterpreterBytecodeArrayRegister, - BytecodeArray::kFrameSizeOffset)); - - // Do a stack check to ensure we don't go over the limit. - Label ok; - __ mov(ecx, esp); - __ sub(ecx, ebx); - ExternalReference stack_limit = - ExternalReference::address_of_real_stack_limit(masm->isolate()); - __ cmp(ecx, Operand::StaticVariable(stack_limit)); - __ j(above_equal, &ok); - __ CallRuntime(Runtime::kThrowStackOverflow); - __ bind(&ok); - - // If ok, push undefined as the initial value for all register file entries. - Label loop_header; - Label loop_check; - __ mov(eax, Immediate(masm->isolate()->factory()->undefined_value())); - __ jmp(&loop_check); - __ bind(&loop_header); - // TODO(rmcilroy): Consider doing more than one push per loop iteration. - __ push(eax); - // Continue loop if not done. - __ bind(&loop_check); - __ sub(ebx, Immediate(kPointerSize)); - __ j(greater_equal, &loop_header); - } - - // Load accumulator, bytecode offset and dispatch table into registers. - __ LoadRoot(kInterpreterAccumulatorRegister, Heap::kUndefinedValueRootIndex); - __ mov(kInterpreterBytecodeOffsetRegister, - Immediate(BytecodeArray::kHeaderSize - kHeapObjectTag)); - __ mov(kInterpreterDispatchTableRegister, - Immediate(ExternalReference::interpreter_dispatch_table_address( - masm->isolate()))); - - // Dispatch to the first bytecode handler for the function. - __ movzx_b(ebx, Operand(kInterpreterBytecodeArrayRegister, - kInterpreterBytecodeOffsetRegister, times_1, 0)); - __ mov(ebx, Operand(kInterpreterDispatchTableRegister, ebx, - times_pointer_size, 0)); - __ call(ebx); - masm->isolate()->heap()->SetInterpreterEntryReturnPCOffset(masm->pc_offset()); - - // The return value is in eax. - LeaveInterpreterFrame(masm, ebx, ecx); - __ ret(0); - - // Load debug copy of the bytecode array. - __ bind(&load_debug_bytecode_array); - Register debug_info = kInterpreterBytecodeArrayRegister; - __ mov(debug_info, FieldOperand(eax, SharedFunctionInfo::kDebugInfoOffset)); - __ mov(kInterpreterBytecodeArrayRegister, - FieldOperand(debug_info, DebugInfo::kDebugBytecodeArrayOffset)); - __ jmp(&bytecode_array_loaded); - - // If the shared code is no longer this entry trampoline, then the underlying - // function has been switched to a different kind of code and we heal the - // closure by switching the code entry field over to the new code as well. - __ bind(&switch_to_different_code_kind); - __ pop(edx); // Callee's new target. - __ pop(edi); // Callee's JS function. - __ pop(esi); // Callee's context. - __ leave(); // Leave the frame so we can tail call. - __ mov(ecx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); - __ mov(ecx, FieldOperand(ecx, SharedFunctionInfo::kCodeOffset)); - __ lea(ecx, FieldOperand(ecx, Code::kHeaderSize)); - __ mov(FieldOperand(edi, JSFunction::kCodeEntryOffset), ecx); - __ RecordWriteCodeEntryField(edi, ecx, ebx); - __ jmp(ecx); -} - -static void Generate_StackOverflowCheck(MacroAssembler* masm, Register num_args, - Register scratch1, Register scratch2, - Label* stack_overflow, - bool include_receiver = false) { - // Check the stack for overflow. We are not trying to catch - // interruptions (e.g. debug break and preemption) here, so the "real stack - // limit" is checked. - ExternalReference real_stack_limit = - ExternalReference::address_of_real_stack_limit(masm->isolate()); - __ mov(scratch1, Operand::StaticVariable(real_stack_limit)); - // Make scratch2 the space we have left. The stack might already be overflowed - // here which will cause scratch2 to become negative. - __ mov(scratch2, esp); - __ sub(scratch2, scratch1); - // Make scratch1 the space we need for the array when it is unrolled onto the - // stack. - __ mov(scratch1, num_args); - if (include_receiver) { - __ add(scratch1, Immediate(1)); - } - __ shl(scratch1, kPointerSizeLog2); - // Check if the arguments will overflow the stack. - __ cmp(scratch2, scratch1); - __ j(less_equal, stack_overflow); // Signed comparison. -} - -static void Generate_InterpreterPushArgs(MacroAssembler* masm, - Register array_limit, - Register start_address) { - // ----------- S t a t e ------------- - // -- start_address : Pointer to the last argument in the args array. - // -- array_limit : Pointer to one before the first argument in the - // args array. - // ----------------------------------- - Label loop_header, loop_check; - __ jmp(&loop_check); - __ bind(&loop_header); - __ Push(Operand(start_address, 0)); - __ sub(start_address, Immediate(kPointerSize)); - __ bind(&loop_check); - __ cmp(start_address, array_limit); - __ j(greater, &loop_header, Label::kNear); -} - -// static -void Builtins::Generate_InterpreterPushArgsThenCallImpl( - MacroAssembler* masm, TailCallMode tail_call_mode, - InterpreterPushArgsMode mode) { - // ----------- S t a t e ------------- - // -- eax : the number of arguments (not including the receiver) - // -- ebx : the address of the first argument to be pushed. Subsequent - // arguments should be consecutive above this, in the same order as - // they are to be pushed onto the stack. - // -- edi : the target to call (can be any Object). - // ----------------------------------- - Label stack_overflow; - // Compute the expected number of arguments. - __ mov(ecx, eax); - __ add(ecx, Immediate(1)); // Add one for receiver. - - // Add a stack check before pushing the arguments. We need an extra register - // to perform a stack check. So push it onto the stack temporarily. This - // might cause stack overflow, but it will be detected by the check. - __ Push(edi); - Generate_StackOverflowCheck(masm, ecx, edx, edi, &stack_overflow); - __ Pop(edi); - - // Pop return address to allow tail-call after pushing arguments. - __ Pop(edx); - - // Find the address of the last argument. - __ shl(ecx, kPointerSizeLog2); - __ neg(ecx); - __ add(ecx, ebx); - Generate_InterpreterPushArgs(masm, ecx, ebx); - - // Call the target. - __ Push(edx); // Re-push return address. - - if (mode == InterpreterPushArgsMode::kJSFunction) { - __ Jump(masm->isolate()->builtins()->CallFunction(ConvertReceiverMode::kAny, - tail_call_mode), - RelocInfo::CODE_TARGET); - } else if (mode == InterpreterPushArgsMode::kWithFinalSpread) { - __ Jump(masm->isolate()->builtins()->CallWithSpread(), - RelocInfo::CODE_TARGET); - } else { - __ Jump(masm->isolate()->builtins()->Call(ConvertReceiverMode::kAny, - tail_call_mode), - RelocInfo::CODE_TARGET); - } - - __ bind(&stack_overflow); - { - // Pop the temporary registers, so that return address is on top of stack. - __ Pop(edi); - - __ TailCallRuntime(Runtime::kThrowStackOverflow); - - // This should be unreachable. - __ int3(); - } -} - -namespace { - -// This function modified start_addr, and only reads the contents of num_args -// register. scratch1 and scratch2 are used as temporary registers. Their -// original values are restored after the use. -void Generate_InterpreterPushArgsThenReturnAddress( - MacroAssembler* masm, Register num_args, Register start_addr, - Register scratch1, Register scratch2, bool receiver_in_args, - int num_slots_above_ret_addr, Label* stack_overflow) { - // We have to move return address and the temporary registers above it - // before we can copy arguments onto the stack. To achieve this: - // Step 1: Increment the stack pointer by num_args + 1 (for receiver). - // Step 2: Move the return address and values above it to the top of stack. - // Step 3: Copy the arguments into the correct locations. - // current stack =====> required stack layout - // | | | scratch1 | (2) <-- esp(1) - // | | | .... | (2) - // | | | scratch-n | (2) - // | | | return addr | (2) - // | | | arg N | (3) - // | scratch1 | <-- esp | .... | - // | .... | | arg 0 | - // | scratch-n | | arg 0 | - // | return addr | | receiver slot | - - // Check for stack overflow before we increment the stack pointer. - Generate_StackOverflowCheck(masm, num_args, scratch1, scratch2, - stack_overflow, true); - -// Step 1 - Update the stack pointer. scratch1 already contains the required -// increment to the stack. i.e. num_args + 1 stack slots. This is computed in -// the Generate_StackOverflowCheck. - -#ifdef _MSC_VER - // TODO(mythria): Move it to macro assembler. - // In windows, we cannot increment the stack size by more than one page - // (mimimum page size is 4KB) without accessing at least one byte on the - // page. Check this: - // https://msdn.microsoft.com/en-us/library/aa227153(v=vs.60).aspx. - const int page_size = 4 * 1024; - Label check_offset, update_stack_pointer; - __ bind(&check_offset); - __ cmp(scratch1, page_size); - __ j(less, &update_stack_pointer); - __ sub(esp, Immediate(page_size)); - // Just to touch the page, before we increment further. - __ mov(Operand(esp, 0), Immediate(0)); - __ sub(scratch1, Immediate(page_size)); - __ jmp(&check_offset); - __ bind(&update_stack_pointer); -#endif - - __ sub(esp, scratch1); - - // Step 2 move return_address and slots above it to the correct locations. - // Move from top to bottom, otherwise we may overwrite when num_args = 0 or 1, - // basically when the source and destination overlap. We at least need one - // extra slot for receiver, so no extra checks are required to avoid copy. - for (int i = 0; i < num_slots_above_ret_addr + 1; i++) { - __ mov(scratch1, - Operand(esp, num_args, times_pointer_size, (i + 1) * kPointerSize)); - __ mov(Operand(esp, i * kPointerSize), scratch1); - } - - // Step 3 copy arguments to correct locations. - if (receiver_in_args) { - __ mov(scratch1, num_args); - __ add(scratch1, Immediate(1)); - } else { - // Slot meant for receiver contains return address. Reset it so that - // we will not incorrectly interpret return address as an object. - __ mov(Operand(esp, num_args, times_pointer_size, - (num_slots_above_ret_addr + 1) * kPointerSize), - Immediate(0)); - __ mov(scratch1, num_args); - } - - Label loop_header, loop_check; - __ jmp(&loop_check); - __ bind(&loop_header); - __ mov(scratch2, Operand(start_addr, 0)); - __ mov(Operand(esp, scratch1, times_pointer_size, - num_slots_above_ret_addr * kPointerSize), - scratch2); - __ sub(start_addr, Immediate(kPointerSize)); - __ sub(scratch1, Immediate(1)); - __ bind(&loop_check); - __ cmp(scratch1, Immediate(0)); - __ j(greater, &loop_header, Label::kNear); -} - -} // end anonymous namespace - -// static -void Builtins::Generate_InterpreterPushArgsThenConstructImpl( - MacroAssembler* masm, InterpreterPushArgsMode mode) { - // ----------- S t a t e ------------- - // -- eax : the number of arguments (not including the receiver) - // -- edx : the new target - // -- edi : the constructor - // -- ebx : allocation site feedback (if available or undefined) - // -- ecx : the address of the first argument to be pushed. Subsequent - // arguments should be consecutive above this, in the same order as - // they are to be pushed onto the stack. - // ----------------------------------- - Label stack_overflow; - // We need two scratch registers. Push edi and edx onto stack. - __ Push(edi); - __ Push(edx); - - // Push arguments and move return address to the top of stack. - // The eax register is readonly. The ecx register will be modified. The edx - // and edi registers will be modified but restored to their original values. - Generate_InterpreterPushArgsThenReturnAddress(masm, eax, ecx, edx, edi, false, - 2, &stack_overflow); - - // Restore edi and edx - __ Pop(edx); - __ Pop(edi); - - __ AssertUndefinedOrAllocationSite(ebx); - if (mode == InterpreterPushArgsMode::kJSFunction) { - // Tail call to the function-specific construct stub (still in the caller - // context at this point). - __ AssertFunction(edi); - - __ mov(ecx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); - __ mov(ecx, FieldOperand(ecx, SharedFunctionInfo::kConstructStubOffset)); - __ lea(ecx, FieldOperand(ecx, Code::kHeaderSize)); - __ jmp(ecx); - } else if (mode == InterpreterPushArgsMode::kWithFinalSpread) { - // Call the constructor with unmodified eax, edi, edx values. - __ Jump(masm->isolate()->builtins()->ConstructWithSpread(), - RelocInfo::CODE_TARGET); - } else { - DCHECK_EQ(InterpreterPushArgsMode::kOther, mode); - // Call the constructor with unmodified eax, edi, edx values. - __ Jump(masm->isolate()->builtins()->Construct(), RelocInfo::CODE_TARGET); - } - - __ bind(&stack_overflow); - { - // Pop the temporary registers, so that return address is on top of stack. - __ Pop(edx); - __ Pop(edi); - - __ TailCallRuntime(Runtime::kThrowStackOverflow); - - // This should be unreachable. - __ int3(); - } -} - -// static -void Builtins::Generate_InterpreterPushArgsThenConstructArray( - MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- eax : the number of arguments (not including the receiver) - // -- edx : the target to call checked to be Array function. - // -- ebx : the allocation site feedback - // -- ecx : the address of the first argument to be pushed. Subsequent - // arguments should be consecutive above this, in the same order as - // they are to be pushed onto the stack. - // ----------------------------------- - Label stack_overflow; - // We need two scratch registers. Register edi is available, push edx onto - // stack. - __ Push(edx); - - // Push arguments and move return address to the top of stack. - // The eax register is readonly. The ecx register will be modified. The edx - // and edi registers will be modified but restored to their original values. - Generate_InterpreterPushArgsThenReturnAddress(masm, eax, ecx, edx, edi, true, - 1, &stack_overflow); - - // Restore edx. - __ Pop(edx); - - // Array constructor expects constructor in edi. It is same as edx here. - __ Move(edi, edx); - - ArrayConstructorStub stub(masm->isolate()); - __ TailCallStub(&stub); - - __ bind(&stack_overflow); - { - // Pop the temporary registers, so that return address is on top of stack. - __ Pop(edx); - - __ TailCallRuntime(Runtime::kThrowStackOverflow); - - // This should be unreachable. - __ int3(); - } -} - -static void Generate_InterpreterEnterBytecode(MacroAssembler* masm) { - // Set the return address to the correct point in the interpreter entry - // trampoline. - Smi* interpreter_entry_return_pc_offset( - masm->isolate()->heap()->interpreter_entry_return_pc_offset()); - DCHECK_NE(interpreter_entry_return_pc_offset, Smi::kZero); - __ LoadHeapObject(ebx, - masm->isolate()->builtins()->InterpreterEntryTrampoline()); - __ add(ebx, Immediate(interpreter_entry_return_pc_offset->value() + - Code::kHeaderSize - kHeapObjectTag)); - __ push(ebx); - - // Initialize the dispatch table register. - __ mov(kInterpreterDispatchTableRegister, - Immediate(ExternalReference::interpreter_dispatch_table_address( - masm->isolate()))); - - // Get the bytecode array pointer from the frame. - __ mov(kInterpreterBytecodeArrayRegister, - Operand(ebp, InterpreterFrameConstants::kBytecodeArrayFromFp)); - - if (FLAG_debug_code) { - // Check function data field is actually a BytecodeArray object. - __ AssertNotSmi(kInterpreterBytecodeArrayRegister); - __ CmpObjectType(kInterpreterBytecodeArrayRegister, BYTECODE_ARRAY_TYPE, - ebx); - __ Assert(equal, kFunctionDataShouldBeBytecodeArrayOnInterpreterEntry); - } - - // Get the target bytecode offset from the frame. - __ mov(kInterpreterBytecodeOffsetRegister, - Operand(ebp, InterpreterFrameConstants::kBytecodeOffsetFromFp)); - __ SmiUntag(kInterpreterBytecodeOffsetRegister); - - // Dispatch to the target bytecode. - __ movzx_b(ebx, Operand(kInterpreterBytecodeArrayRegister, - kInterpreterBytecodeOffsetRegister, times_1, 0)); - __ mov(ebx, Operand(kInterpreterDispatchTableRegister, ebx, - times_pointer_size, 0)); - __ jmp(ebx); -} - -void Builtins::Generate_InterpreterEnterBytecodeAdvance(MacroAssembler* masm) { - // Advance the current bytecode offset stored within the given interpreter - // stack frame. This simulates what all bytecode handlers do upon completion - // of the underlying operation. - __ mov(ebx, Operand(ebp, InterpreterFrameConstants::kBytecodeArrayFromFp)); - __ mov(edx, Operand(ebp, InterpreterFrameConstants::kBytecodeOffsetFromFp)); - __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); - { - FrameScope scope(masm, StackFrame::INTERNAL); - __ Push(kInterpreterAccumulatorRegister); - __ Push(ebx); // First argument is the bytecode array. - __ Push(edx); // Second argument is the bytecode offset. - __ CallRuntime(Runtime::kInterpreterAdvanceBytecodeOffset); - __ Move(edx, eax); // Result is the new bytecode offset. - __ Pop(kInterpreterAccumulatorRegister); - } - __ mov(Operand(ebp, InterpreterFrameConstants::kBytecodeOffsetFromFp), edx); - - Generate_InterpreterEnterBytecode(masm); -} - -void Builtins::Generate_InterpreterEnterBytecodeDispatch(MacroAssembler* masm) { - Generate_InterpreterEnterBytecode(masm); -} - -void Builtins::Generate_CompileLazy(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- eax : argument count (preserved for callee) - // -- edx : new target (preserved for callee) - // -- edi : target function (preserved for callee) - // ----------------------------------- - // First lookup code, maybe we don't need to compile! - Label gotta_call_runtime, gotta_call_runtime_no_stack; - Label try_shared; - Label loop_top, loop_bottom; - - Register closure = edi; - Register new_target = edx; - Register argument_count = eax; - - // Do we have a valid feedback vector? - __ mov(ebx, FieldOperand(closure, JSFunction::kFeedbackVectorOffset)); - __ mov(ebx, FieldOperand(ebx, Cell::kValueOffset)); - __ cmp(ebx, masm->isolate()->factory()->undefined_value()); - __ j(equal, &gotta_call_runtime_no_stack); - - __ push(argument_count); - __ push(new_target); - __ push(closure); - - Register map = argument_count; - Register index = ebx; - __ mov(map, FieldOperand(closure, JSFunction::kSharedFunctionInfoOffset)); - __ mov(map, FieldOperand(map, SharedFunctionInfo::kOptimizedCodeMapOffset)); - __ mov(index, FieldOperand(map, FixedArray::kLengthOffset)); - __ cmp(index, Immediate(Smi::FromInt(2))); - __ j(less, &try_shared); - - // edx : native context - // ebx : length / index - // eax : optimized code map - // stack[0] : new target - // stack[4] : closure - Register native_context = edx; - __ mov(native_context, NativeContextOperand()); - - __ bind(&loop_top); - Register temp = edi; - - // Does the native context match? - __ mov(temp, FieldOperand(map, index, times_half_pointer_size, - SharedFunctionInfo::kOffsetToPreviousContext)); - __ mov(temp, FieldOperand(temp, WeakCell::kValueOffset)); - __ cmp(temp, native_context); - __ j(not_equal, &loop_bottom); - // Code available? - Register entry = ecx; - __ mov(entry, FieldOperand(map, index, times_half_pointer_size, - SharedFunctionInfo::kOffsetToPreviousCachedCode)); - __ mov(entry, FieldOperand(entry, WeakCell::kValueOffset)); - __ JumpIfSmi(entry, &try_shared); - - // Found code. Get it into the closure and return. - __ pop(closure); - // Store code entry in the closure. - __ lea(entry, FieldOperand(entry, Code::kHeaderSize)); - __ mov(FieldOperand(closure, JSFunction::kCodeEntryOffset), entry); - __ RecordWriteCodeEntryField(closure, entry, eax); - - // Link the closure into the optimized function list. - // ecx : code entry - // edx : native context - // edi : closure - __ mov(ebx, - ContextOperand(native_context, Context::OPTIMIZED_FUNCTIONS_LIST)); - __ mov(FieldOperand(closure, JSFunction::kNextFunctionLinkOffset), ebx); - __ RecordWriteField(closure, JSFunction::kNextFunctionLinkOffset, ebx, eax, - kDontSaveFPRegs, EMIT_REMEMBERED_SET, OMIT_SMI_CHECK); - const int function_list_offset = - Context::SlotOffset(Context::OPTIMIZED_FUNCTIONS_LIST); - __ mov(ContextOperand(native_context, Context::OPTIMIZED_FUNCTIONS_LIST), - closure); - // Save closure before the write barrier. - __ mov(ebx, closure); - __ RecordWriteContextSlot(native_context, function_list_offset, closure, eax, - kDontSaveFPRegs); - __ mov(closure, ebx); - __ pop(new_target); - __ pop(argument_count); - __ jmp(entry); - - __ bind(&loop_bottom); - __ sub(index, Immediate(Smi::FromInt(SharedFunctionInfo::kEntryLength))); - __ cmp(index, Immediate(Smi::FromInt(1))); - __ j(greater, &loop_top); - - // We found no code. - __ jmp(&gotta_call_runtime); - - __ bind(&try_shared); - __ pop(closure); - __ pop(new_target); - __ pop(argument_count); - __ mov(entry, FieldOperand(closure, JSFunction::kSharedFunctionInfoOffset)); - // Is the shared function marked for tier up? - __ test(FieldOperand(entry, SharedFunctionInfo::kCompilerHintsOffset), - Immediate(SharedFunctionInfo::MarkedForTierUpBit::kMask)); - __ j(not_zero, &gotta_call_runtime_no_stack); - - // If SFI points to anything other than CompileLazy, install that. - __ mov(entry, FieldOperand(entry, SharedFunctionInfo::kCodeOffset)); - __ Move(ebx, masm->CodeObject()); - __ cmp(entry, ebx); - __ j(equal, &gotta_call_runtime_no_stack); - - // Install the SFI's code entry. - __ lea(entry, FieldOperand(entry, Code::kHeaderSize)); - __ mov(FieldOperand(closure, JSFunction::kCodeEntryOffset), entry); - __ RecordWriteCodeEntryField(closure, entry, ebx); - __ jmp(entry); - - __ bind(&gotta_call_runtime); - __ pop(closure); - __ pop(new_target); - __ pop(argument_count); - __ bind(&gotta_call_runtime_no_stack); - - GenerateTailCallToReturnedCode(masm, Runtime::kCompileLazy); -} - -void Builtins::Generate_CompileOptimized(MacroAssembler* masm) { - GenerateTailCallToReturnedCode(masm, - Runtime::kCompileOptimized_NotConcurrent); -} - -void Builtins::Generate_CompileOptimizedConcurrent(MacroAssembler* masm) { - GenerateTailCallToReturnedCode(masm, Runtime::kCompileOptimized_Concurrent); -} - -void Builtins::Generate_InstantiateAsmJs(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- eax : argument count (preserved for callee) - // -- edx : new target (preserved for callee) - // -- edi : target function (preserved for callee) - // ----------------------------------- - Label failed; - { - FrameScope scope(masm, StackFrame::INTERNAL); - // Preserve argument count for later compare. - __ mov(ecx, eax); - // Push the number of arguments to the callee. - __ SmiTag(eax); - __ push(eax); - // Push a copy of the target function and the new target. - __ push(edi); - __ push(edx); - - // The function. - __ push(edi); - // Copy arguments from caller (stdlib, foreign, heap). - Label args_done; - for (int j = 0; j < 4; ++j) { - Label over; - if (j < 3) { - __ cmp(ecx, Immediate(j)); - __ j(not_equal, &over, Label::kNear); - } - for (int i = j - 1; i >= 0; --i) { - __ Push(Operand( - ebp, StandardFrameConstants::kCallerSPOffset + i * kPointerSize)); - } - for (int i = 0; i < 3 - j; ++i) { - __ PushRoot(Heap::kUndefinedValueRootIndex); - } - if (j < 3) { - __ jmp(&args_done, Label::kNear); - __ bind(&over); - } - } - __ bind(&args_done); - - // Call runtime, on success unwind frame, and parent frame. - __ CallRuntime(Runtime::kInstantiateAsmJs, 4); - // A smi 0 is returned on failure, an object on success. - __ JumpIfSmi(eax, &failed, Label::kNear); - - __ Drop(2); - __ Pop(ecx); - __ SmiUntag(ecx); - scope.GenerateLeaveFrame(); - - __ PopReturnAddressTo(ebx); - __ inc(ecx); - __ lea(esp, Operand(esp, ecx, times_pointer_size, 0)); - __ PushReturnAddressFrom(ebx); - __ ret(0); - - __ bind(&failed); - // Restore target function and new target. - __ pop(edx); - __ pop(edi); - __ pop(eax); - __ SmiUntag(eax); - } - // On failure, tail call back to regular js. - GenerateTailCallToReturnedCode(masm, Runtime::kCompileLazy); -} - -static void GenerateMakeCodeYoungAgainCommon(MacroAssembler* masm) { - // For now, we are relying on the fact that make_code_young doesn't do any - // garbage collection which allows us to save/restore the registers without - // worrying about which of them contain pointers. We also don't build an - // internal frame to make the code faster, since we shouldn't have to do stack - // crawls in MakeCodeYoung. This seems a bit fragile. - - // Re-execute the code that was patched back to the young age when - // the stub returns. - __ sub(Operand(esp, 0), Immediate(5)); - __ pushad(); - __ mov(eax, Operand(esp, 8 * kPointerSize)); - { - FrameScope scope(masm, StackFrame::MANUAL); - __ PrepareCallCFunction(2, ebx); - __ mov(Operand(esp, 1 * kPointerSize), - Immediate(ExternalReference::isolate_address(masm->isolate()))); - __ mov(Operand(esp, 0), eax); - __ CallCFunction( - ExternalReference::get_make_code_young_function(masm->isolate()), 2); - } - __ popad(); - __ ret(0); -} - -#define DEFINE_CODE_AGE_BUILTIN_GENERATOR(C) \ - void Builtins::Generate_Make##C##CodeYoungAgain(MacroAssembler* masm) { \ - GenerateMakeCodeYoungAgainCommon(masm); \ - } -CODE_AGE_LIST(DEFINE_CODE_AGE_BUILTIN_GENERATOR) -#undef DEFINE_CODE_AGE_BUILTIN_GENERATOR - -void Builtins::Generate_MarkCodeAsExecutedOnce(MacroAssembler* masm) { - // For now, as in GenerateMakeCodeYoungAgainCommon, we are relying on the fact - // that make_code_young doesn't do any garbage collection which allows us to - // save/restore the registers without worrying about which of them contain - // pointers. - __ pushad(); - __ mov(eax, Operand(esp, 8 * kPointerSize)); - __ sub(eax, Immediate(Assembler::kCallInstructionLength)); - { // NOLINT - FrameScope scope(masm, StackFrame::MANUAL); - __ PrepareCallCFunction(2, ebx); - __ mov(Operand(esp, 1 * kPointerSize), - Immediate(ExternalReference::isolate_address(masm->isolate()))); - __ mov(Operand(esp, 0), eax); - __ CallCFunction( - ExternalReference::get_mark_code_as_executed_function(masm->isolate()), - 2); - } - __ popad(); - - // Perform prologue operations usually performed by the young code stub. - __ pop(eax); // Pop return address into scratch register. - __ push(ebp); // Caller's frame pointer. - __ mov(ebp, esp); - __ push(esi); // Callee's context. - __ push(edi); // Callee's JS Function. - __ push(eax); // Push return address after frame prologue. - - // Jump to point after the code-age stub. - __ ret(0); -} - -void Builtins::Generate_MarkCodeAsExecutedTwice(MacroAssembler* masm) { - GenerateMakeCodeYoungAgainCommon(masm); -} - -void Builtins::Generate_MarkCodeAsToBeExecutedOnce(MacroAssembler* masm) { - Generate_MarkCodeAsExecutedOnce(masm); -} - -static void Generate_NotifyDeoptimizedHelper(MacroAssembler* masm, - Deoptimizer::BailoutType type) { - { - FrameScope scope(masm, StackFrame::INTERNAL); - - // Pass deoptimization type to the runtime system. - __ push(Immediate(Smi::FromInt(static_cast(type)))); - __ CallRuntime(Runtime::kNotifyDeoptimized); - - // Tear down internal frame. - } - - // Get the full codegen state from the stack and untag it. - __ mov(ecx, Operand(esp, 1 * kPointerSize)); - __ SmiUntag(ecx); - - // Switch on the state. - Label not_no_registers, not_tos_eax; - __ cmp(ecx, static_cast(Deoptimizer::BailoutState::NO_REGISTERS)); - __ j(not_equal, ¬_no_registers, Label::kNear); - __ ret(1 * kPointerSize); // Remove state. - - __ bind(¬_no_registers); - DCHECK_EQ(kInterpreterAccumulatorRegister.code(), eax.code()); - __ mov(eax, Operand(esp, 2 * kPointerSize)); - __ cmp(ecx, static_cast(Deoptimizer::BailoutState::TOS_REGISTER)); - __ j(not_equal, ¬_tos_eax, Label::kNear); - __ ret(2 * kPointerSize); // Remove state, eax. - - __ bind(¬_tos_eax); - __ Abort(kNoCasesLeft); -} - -void Builtins::Generate_NotifyDeoptimized(MacroAssembler* masm) { - Generate_NotifyDeoptimizedHelper(masm, Deoptimizer::EAGER); -} - -void Builtins::Generate_NotifySoftDeoptimized(MacroAssembler* masm) { - Generate_NotifyDeoptimizedHelper(masm, Deoptimizer::SOFT); -} - -void Builtins::Generate_NotifyLazyDeoptimized(MacroAssembler* masm) { - Generate_NotifyDeoptimizedHelper(masm, Deoptimizer::LAZY); -} - -// static -void Builtins::Generate_FunctionPrototypeApply(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- eax : argc - // -- esp[0] : return address - // -- esp[4] : argArray - // -- esp[8] : thisArg - // -- esp[12] : receiver - // ----------------------------------- - - // 1. Load receiver into edi, argArray into eax (if present), remove all - // arguments from the stack (including the receiver), and push thisArg (if - // present) instead. - { - Label no_arg_array, no_this_arg; - __ LoadRoot(edx, Heap::kUndefinedValueRootIndex); - __ mov(ebx, edx); - __ mov(edi, Operand(esp, eax, times_pointer_size, kPointerSize)); - __ test(eax, eax); - __ j(zero, &no_this_arg, Label::kNear); - { - __ mov(edx, Operand(esp, eax, times_pointer_size, 0)); - __ cmp(eax, Immediate(1)); - __ j(equal, &no_arg_array, Label::kNear); - __ mov(ebx, Operand(esp, eax, times_pointer_size, -kPointerSize)); - __ bind(&no_arg_array); - } - __ bind(&no_this_arg); - __ PopReturnAddressTo(ecx); - __ lea(esp, Operand(esp, eax, times_pointer_size, kPointerSize)); - __ Push(edx); - __ PushReturnAddressFrom(ecx); - __ Move(eax, ebx); - } - - // ----------- S t a t e ------------- - // -- eax : argArray - // -- edi : receiver - // -- esp[0] : return address - // -- esp[4] : thisArg - // ----------------------------------- - - // 2. Make sure the receiver is actually callable. - Label receiver_not_callable; - __ JumpIfSmi(edi, &receiver_not_callable, Label::kNear); - __ mov(ecx, FieldOperand(edi, HeapObject::kMapOffset)); - __ test_b(FieldOperand(ecx, Map::kBitFieldOffset), - Immediate(1 << Map::kIsCallable)); - __ j(zero, &receiver_not_callable, Label::kNear); - - // 3. Tail call with no arguments if argArray is null or undefined. - Label no_arguments; - __ JumpIfRoot(eax, Heap::kNullValueRootIndex, &no_arguments, Label::kNear); - __ JumpIfRoot(eax, Heap::kUndefinedValueRootIndex, &no_arguments, - Label::kNear); - - // 4a. Apply the receiver to the given argArray (passing undefined for - // new.target). - __ LoadRoot(edx, Heap::kUndefinedValueRootIndex); - __ Jump(masm->isolate()->builtins()->Apply(), RelocInfo::CODE_TARGET); - - // 4b. The argArray is either null or undefined, so we tail call without any - // arguments to the receiver. - __ bind(&no_arguments); - { - __ Set(eax, 0); - __ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); - } - - // 4c. The receiver is not callable, throw an appropriate TypeError. - __ bind(&receiver_not_callable); - { - __ mov(Operand(esp, kPointerSize), edi); - __ TailCallRuntime(Runtime::kThrowApplyNonFunction); - } -} - -// static -void Builtins::Generate_FunctionPrototypeCall(MacroAssembler* masm) { - // Stack Layout: - // esp[0] : Return address - // esp[8] : Argument n - // esp[16] : Argument n-1 - // ... - // esp[8 * n] : Argument 1 - // esp[8 * (n + 1)] : Receiver (callable to call) - // - // eax contains the number of arguments, n, not counting the receiver. - // - // 1. Make sure we have at least one argument. - { - Label done; - __ test(eax, eax); - __ j(not_zero, &done, Label::kNear); - __ PopReturnAddressTo(ebx); - __ PushRoot(Heap::kUndefinedValueRootIndex); - __ PushReturnAddressFrom(ebx); - __ inc(eax); - __ bind(&done); - } - - // 2. Get the callable to call (passed as receiver) from the stack. - __ mov(edi, Operand(esp, eax, times_pointer_size, kPointerSize)); - - // 3. Shift arguments and return address one slot down on the stack - // (overwriting the original receiver). Adjust argument count to make - // the original first argument the new receiver. - { - Label loop; - __ mov(ecx, eax); - __ bind(&loop); - __ mov(ebx, Operand(esp, ecx, times_pointer_size, 0)); - __ mov(Operand(esp, ecx, times_pointer_size, kPointerSize), ebx); - __ dec(ecx); - __ j(not_sign, &loop); // While non-negative (to copy return address). - __ pop(ebx); // Discard copy of return address. - __ dec(eax); // One fewer argument (first argument is new receiver). - } - - // 4. Call the callable. - __ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); -} - -void Builtins::Generate_ReflectApply(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- eax : argc - // -- esp[0] : return address - // -- esp[4] : argumentsList - // -- esp[8] : thisArgument - // -- esp[12] : target - // -- esp[16] : receiver - // ----------------------------------- - - // 1. Load target into edi (if present), argumentsList into eax (if present), - // remove all arguments from the stack (including the receiver), and push - // thisArgument (if present) instead. - { - Label done; - __ LoadRoot(edi, Heap::kUndefinedValueRootIndex); - __ mov(edx, edi); - __ mov(ebx, edi); - __ cmp(eax, Immediate(1)); - __ j(below, &done, Label::kNear); - __ mov(edi, Operand(esp, eax, times_pointer_size, -0 * kPointerSize)); - __ j(equal, &done, Label::kNear); - __ mov(edx, Operand(esp, eax, times_pointer_size, -1 * kPointerSize)); - __ cmp(eax, Immediate(3)); - __ j(below, &done, Label::kNear); - __ mov(ebx, Operand(esp, eax, times_pointer_size, -2 * kPointerSize)); - __ bind(&done); - __ PopReturnAddressTo(ecx); - __ lea(esp, Operand(esp, eax, times_pointer_size, kPointerSize)); - __ Push(edx); - __ PushReturnAddressFrom(ecx); - __ Move(eax, ebx); - } - - // ----------- S t a t e ------------- - // -- eax : argumentsList - // -- edi : target - // -- esp[0] : return address - // -- esp[4] : thisArgument - // ----------------------------------- - - // 2. Make sure the target is actually callable. - Label target_not_callable; - __ JumpIfSmi(edi, &target_not_callable, Label::kNear); - __ mov(ecx, FieldOperand(edi, HeapObject::kMapOffset)); - __ test_b(FieldOperand(ecx, Map::kBitFieldOffset), - Immediate(1 << Map::kIsCallable)); - __ j(zero, &target_not_callable, Label::kNear); - - // 3a. Apply the target to the given argumentsList (passing undefined for - // new.target). - __ LoadRoot(edx, Heap::kUndefinedValueRootIndex); - __ Jump(masm->isolate()->builtins()->Apply(), RelocInfo::CODE_TARGET); - - // 3b. The target is not callable, throw an appropriate TypeError. - __ bind(&target_not_callable); - { - __ mov(Operand(esp, kPointerSize), edi); - __ TailCallRuntime(Runtime::kThrowApplyNonFunction); - } -} - -void Builtins::Generate_ReflectConstruct(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- eax : argc - // -- esp[0] : return address - // -- esp[4] : new.target (optional) - // -- esp[8] : argumentsList - // -- esp[12] : target - // -- esp[16] : receiver - // ----------------------------------- - - // 1. Load target into edi (if present), argumentsList into eax (if present), - // new.target into edx (if present, otherwise use target), remove all - // arguments from the stack (including the receiver), and push thisArgument - // (if present) instead. - { - Label done; - __ LoadRoot(edi, Heap::kUndefinedValueRootIndex); - __ mov(edx, edi); - __ mov(ebx, edi); - __ cmp(eax, Immediate(1)); - __ j(below, &done, Label::kNear); - __ mov(edi, Operand(esp, eax, times_pointer_size, -0 * kPointerSize)); - __ mov(edx, edi); - __ j(equal, &done, Label::kNear); - __ mov(ebx, Operand(esp, eax, times_pointer_size, -1 * kPointerSize)); - __ cmp(eax, Immediate(3)); - __ j(below, &done, Label::kNear); - __ mov(edx, Operand(esp, eax, times_pointer_size, -2 * kPointerSize)); - __ bind(&done); - __ PopReturnAddressTo(ecx); - __ lea(esp, Operand(esp, eax, times_pointer_size, kPointerSize)); - __ PushRoot(Heap::kUndefinedValueRootIndex); - __ PushReturnAddressFrom(ecx); - __ Move(eax, ebx); - } - - // ----------- S t a t e ------------- - // -- eax : argumentsList - // -- edx : new.target - // -- edi : target - // -- esp[0] : return address - // -- esp[4] : receiver (undefined) - // ----------------------------------- - - // 2. Make sure the target is actually a constructor. - Label target_not_constructor; - __ JumpIfSmi(edi, &target_not_constructor, Label::kNear); - __ mov(ecx, FieldOperand(edi, HeapObject::kMapOffset)); - __ test_b(FieldOperand(ecx, Map::kBitFieldOffset), - Immediate(1 << Map::kIsConstructor)); - __ j(zero, &target_not_constructor, Label::kNear); - - // 3. Make sure the target is actually a constructor. - Label new_target_not_constructor; - __ JumpIfSmi(edx, &new_target_not_constructor, Label::kNear); - __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset)); - __ test_b(FieldOperand(ecx, Map::kBitFieldOffset), - Immediate(1 << Map::kIsConstructor)); - __ j(zero, &new_target_not_constructor, Label::kNear); - - // 4a. Construct the target with the given new.target and argumentsList. - __ Jump(masm->isolate()->builtins()->Apply(), RelocInfo::CODE_TARGET); - - // 4b. The target is not a constructor, throw an appropriate TypeError. - __ bind(&target_not_constructor); - { - __ mov(Operand(esp, kPointerSize), edi); - __ TailCallRuntime(Runtime::kThrowNotConstructor); - } - - // 4c. The new.target is not a constructor, throw an appropriate TypeError. - __ bind(&new_target_not_constructor); - { - __ mov(Operand(esp, kPointerSize), edx); - __ TailCallRuntime(Runtime::kThrowNotConstructor); - } -} - -void Builtins::Generate_InternalArrayCode(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- eax : argc - // -- esp[0] : return address - // -- esp[4] : last argument - // ----------------------------------- - Label generic_array_code; - - // Get the InternalArray function. - __ LoadGlobalFunction(Context::INTERNAL_ARRAY_FUNCTION_INDEX, edi); - - if (FLAG_debug_code) { - // Initial map for the builtin InternalArray function should be a map. - __ mov(ebx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset)); - // Will both indicate a NULL and a Smi. - __ test(ebx, Immediate(kSmiTagMask)); - __ Assert(not_zero, kUnexpectedInitialMapForInternalArrayFunction); - __ CmpObjectType(ebx, MAP_TYPE, ecx); - __ Assert(equal, kUnexpectedInitialMapForInternalArrayFunction); - } - - // Run the native code for the InternalArray function called as a normal - // function. - // tail call a stub - InternalArrayConstructorStub stub(masm->isolate()); - __ TailCallStub(&stub); -} - -void Builtins::Generate_ArrayCode(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- eax : argc - // -- esp[0] : return address - // -- esp[4] : last argument - // ----------------------------------- - Label generic_array_code; - - // Get the Array function. - __ LoadGlobalFunction(Context::ARRAY_FUNCTION_INDEX, edi); - __ mov(edx, edi); - - if (FLAG_debug_code) { - // Initial map for the builtin Array function should be a map. - __ mov(ebx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset)); - // Will both indicate a NULL and a Smi. - __ test(ebx, Immediate(kSmiTagMask)); - __ Assert(not_zero, kUnexpectedInitialMapForArrayFunction); - __ CmpObjectType(ebx, MAP_TYPE, ecx); - __ Assert(equal, kUnexpectedInitialMapForArrayFunction); - } - - // Run the native code for the Array function called as a normal function. - // tail call a stub - __ mov(ebx, masm->isolate()->factory()->undefined_value()); - ArrayConstructorStub stub(masm->isolate()); - __ TailCallStub(&stub); -} - -// static -void Builtins::Generate_NumberConstructor(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- eax : number of arguments - // -- edi : constructor function - // -- esi : context - // -- esp[0] : return address - // -- esp[(argc - n) * 4] : arg[n] (zero-based) - // -- esp[(argc + 1) * 4] : receiver - // ----------------------------------- - - // 1. Load the first argument into ebx. - Label no_arguments; - { - __ test(eax, eax); - __ j(zero, &no_arguments, Label::kNear); - __ mov(ebx, Operand(esp, eax, times_pointer_size, 0)); - } - - // 2a. Convert the first argument to a number. - { - FrameScope scope(masm, StackFrame::MANUAL); - __ SmiTag(eax); - __ EnterBuiltinFrame(esi, edi, eax); - __ mov(eax, ebx); - __ Call(masm->isolate()->builtins()->ToNumber(), RelocInfo::CODE_TARGET); - __ LeaveBuiltinFrame(esi, edi, ebx); // Argc popped to ebx. - __ SmiUntag(ebx); - } - - { - // Drop all arguments including the receiver. - __ PopReturnAddressTo(ecx); - __ lea(esp, Operand(esp, ebx, times_pointer_size, kPointerSize)); - __ PushReturnAddressFrom(ecx); - __ Ret(); - } - - // 2b. No arguments, return +0 (already in eax). - __ bind(&no_arguments); - __ ret(1 * kPointerSize); -} - -// static -void Builtins::Generate_NumberConstructor_ConstructStub(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- eax : number of arguments - // -- edi : constructor function - // -- edx : new target - // -- esi : context - // -- esp[0] : return address - // -- esp[(argc - n) * 4] : arg[n] (zero-based) - // -- esp[(argc + 1) * 4] : receiver - // ----------------------------------- - - // 1. Make sure we operate in the context of the called function. - __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); - - // Store argc in r8. - __ mov(ecx, eax); - __ SmiTag(ecx); - - // 2. Load the first argument into ebx. - { - Label no_arguments, done; - __ test(eax, eax); - __ j(zero, &no_arguments, Label::kNear); - __ mov(ebx, Operand(esp, eax, times_pointer_size, 0)); - __ jmp(&done, Label::kNear); - __ bind(&no_arguments); - __ Move(ebx, Smi::kZero); - __ bind(&done); - } - - // 3. Make sure ebx is a number. - { - Label done_convert; - __ JumpIfSmi(ebx, &done_convert); - __ CompareRoot(FieldOperand(ebx, HeapObject::kMapOffset), - Heap::kHeapNumberMapRootIndex); - __ j(equal, &done_convert); - { - FrameScope scope(masm, StackFrame::MANUAL); - __ EnterBuiltinFrame(esi, edi, ecx); - __ Push(edx); - __ Move(eax, ebx); - __ Call(masm->isolate()->builtins()->ToNumber(), RelocInfo::CODE_TARGET); - __ Move(ebx, eax); - __ Pop(edx); - __ LeaveBuiltinFrame(esi, edi, ecx); - } - __ bind(&done_convert); - } - - // 4. Check if new target and constructor differ. - Label drop_frame_and_ret, done_alloc, new_object; - __ cmp(edx, edi); - __ j(not_equal, &new_object); - - // 5. Allocate a JSValue wrapper for the number. - __ AllocateJSValue(eax, edi, ebx, esi, &done_alloc); - __ jmp(&drop_frame_and_ret); - - __ bind(&done_alloc); - __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); // Restore esi. - - // 6. Fallback to the runtime to create new object. - __ bind(&new_object); - { - FrameScope scope(masm, StackFrame::MANUAL); - __ EnterBuiltinFrame(esi, edi, ecx); - __ Push(ebx); // the first argument - __ Call(masm->isolate()->builtins()->FastNewObject(), - RelocInfo::CODE_TARGET); - __ Pop(FieldOperand(eax, JSValue::kValueOffset)); - __ LeaveBuiltinFrame(esi, edi, ecx); - } - - __ bind(&drop_frame_and_ret); - { - // Drop all arguments including the receiver. - __ PopReturnAddressTo(esi); - __ SmiUntag(ecx); - __ lea(esp, Operand(esp, ecx, times_pointer_size, kPointerSize)); - __ PushReturnAddressFrom(esi); - __ Ret(); - } -} - -// static -void Builtins::Generate_StringConstructor(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- eax : number of arguments - // -- edi : constructor function - // -- esi : context - // -- esp[0] : return address - // -- esp[(argc - n) * 4] : arg[n] (zero-based) - // -- esp[(argc + 1) * 4] : receiver - // ----------------------------------- - - // 1. Load the first argument into eax. - Label no_arguments; - { - __ mov(ebx, eax); // Store argc in ebx. - __ test(eax, eax); - __ j(zero, &no_arguments, Label::kNear); - __ mov(eax, Operand(esp, eax, times_pointer_size, 0)); - } - - // 2a. At least one argument, return eax if it's a string, otherwise - // dispatch to appropriate conversion. - Label drop_frame_and_ret, to_string, symbol_descriptive_string; - { - __ JumpIfSmi(eax, &to_string, Label::kNear); - STATIC_ASSERT(FIRST_NONSTRING_TYPE == SYMBOL_TYPE); - __ CmpObjectType(eax, FIRST_NONSTRING_TYPE, edx); - __ j(above, &to_string, Label::kNear); - __ j(equal, &symbol_descriptive_string, Label::kNear); - __ jmp(&drop_frame_and_ret, Label::kNear); - } - - // 2b. No arguments, return the empty string (and pop the receiver). - __ bind(&no_arguments); - { - __ LoadRoot(eax, Heap::kempty_stringRootIndex); - __ ret(1 * kPointerSize); - } - - // 3a. Convert eax to a string. - __ bind(&to_string); - { - FrameScope scope(masm, StackFrame::MANUAL); - __ SmiTag(ebx); - __ EnterBuiltinFrame(esi, edi, ebx); - __ Call(masm->isolate()->builtins()->ToString(), RelocInfo::CODE_TARGET); - __ LeaveBuiltinFrame(esi, edi, ebx); - __ SmiUntag(ebx); - } - __ jmp(&drop_frame_and_ret, Label::kNear); - - // 3b. Convert symbol in eax to a string. - __ bind(&symbol_descriptive_string); - { - __ PopReturnAddressTo(ecx); - __ lea(esp, Operand(esp, ebx, times_pointer_size, kPointerSize)); - __ Push(eax); - __ PushReturnAddressFrom(ecx); - __ TailCallRuntime(Runtime::kSymbolDescriptiveString); - } - - __ bind(&drop_frame_and_ret); - { - // Drop all arguments including the receiver. - __ PopReturnAddressTo(ecx); - __ lea(esp, Operand(esp, ebx, times_pointer_size, kPointerSize)); - __ PushReturnAddressFrom(ecx); - __ Ret(); - } -} - -// static -void Builtins::Generate_StringConstructor_ConstructStub(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- eax : number of arguments - // -- edi : constructor function - // -- edx : new target - // -- esi : context - // -- esp[0] : return address - // -- esp[(argc - n) * 4] : arg[n] (zero-based) - // -- esp[(argc + 1) * 4] : receiver - // ----------------------------------- - - // 1. Make sure we operate in the context of the called function. - __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); - - __ mov(ebx, eax); - - // 2. Load the first argument into eax. - { - Label no_arguments, done; - __ test(ebx, ebx); - __ j(zero, &no_arguments, Label::kNear); - __ mov(eax, Operand(esp, ebx, times_pointer_size, 0)); - __ jmp(&done, Label::kNear); - __ bind(&no_arguments); - __ LoadRoot(eax, Heap::kempty_stringRootIndex); - __ bind(&done); - } - - // 3. Make sure eax is a string. - { - Label convert, done_convert; - __ JumpIfSmi(eax, &convert, Label::kNear); - __ CmpObjectType(eax, FIRST_NONSTRING_TYPE, ecx); - __ j(below, &done_convert); - __ bind(&convert); - { - FrameScope scope(masm, StackFrame::MANUAL); - __ SmiTag(ebx); - __ EnterBuiltinFrame(esi, edi, ebx); - __ Push(edx); - __ Call(masm->isolate()->builtins()->ToString(), RelocInfo::CODE_TARGET); - __ Pop(edx); - __ LeaveBuiltinFrame(esi, edi, ebx); - __ SmiUntag(ebx); - } - __ bind(&done_convert); - } - - // 4. Check if new target and constructor differ. - Label drop_frame_and_ret, done_alloc, new_object; - __ cmp(edx, edi); - __ j(not_equal, &new_object); - - // 5. Allocate a JSValue wrapper for the string. - // AllocateJSValue can't handle src == dst register. Reuse esi and restore it - // as needed after the call. - __ mov(esi, eax); - __ AllocateJSValue(eax, edi, esi, ecx, &done_alloc); - __ jmp(&drop_frame_and_ret); - - __ bind(&done_alloc); - { - // Restore eax to the first argument and esi to the context. - __ mov(eax, esi); - __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); - } - - // 6. Fallback to the runtime to create new object. - __ bind(&new_object); - { - FrameScope scope(masm, StackFrame::MANUAL); - __ SmiTag(ebx); - __ EnterBuiltinFrame(esi, edi, ebx); - __ Push(eax); // the first argument - __ Call(masm->isolate()->builtins()->FastNewObject(), - RelocInfo::CODE_TARGET); - __ Pop(FieldOperand(eax, JSValue::kValueOffset)); - __ LeaveBuiltinFrame(esi, edi, ebx); - __ SmiUntag(ebx); - } - - __ bind(&drop_frame_and_ret); - { - // Drop all arguments including the receiver. - __ PopReturnAddressTo(ecx); - __ lea(esp, Operand(esp, ebx, times_pointer_size, kPointerSize)); - __ PushReturnAddressFrom(ecx); - __ Ret(); - } -} - -static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) { - __ push(ebp); - __ mov(ebp, esp); - - // Store the arguments adaptor context sentinel. - __ push(Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); - - // Push the function on the stack. - __ push(edi); - - // Preserve the number of arguments on the stack. Must preserve eax, - // ebx and ecx because these registers are used when copying the - // arguments and the receiver. - STATIC_ASSERT(kSmiTagSize == 1); - __ lea(edi, Operand(eax, eax, times_1, kSmiTag)); - __ push(edi); -} - -static void LeaveArgumentsAdaptorFrame(MacroAssembler* masm) { - // Retrieve the number of arguments from the stack. - __ mov(ebx, Operand(ebp, ArgumentsAdaptorFrameConstants::kLengthOffset)); - - // Leave the frame. - __ leave(); - - // Remove caller arguments from the stack. - STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0); - __ pop(ecx); - __ lea(esp, Operand(esp, ebx, times_2, 1 * kPointerSize)); // 1 ~ receiver - __ push(ecx); -} - -// static -void Builtins::Generate_Apply(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- eax : argumentsList - // -- edi : target - // -- edx : new.target (checked to be constructor or undefined) - // -- esp[0] : return address. - // -- esp[4] : thisArgument - // ----------------------------------- - - // Create the list of arguments from the array-like argumentsList. - { - Label create_arguments, create_array, create_holey_array, create_runtime, - done_create; - __ JumpIfSmi(eax, &create_runtime); - - // Load the map of argumentsList into ecx. - __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset)); - - // Load native context into ebx. - __ mov(ebx, NativeContextOperand()); - - // Check if argumentsList is an (unmodified) arguments object. - __ cmp(ecx, ContextOperand(ebx, Context::SLOPPY_ARGUMENTS_MAP_INDEX)); - __ j(equal, &create_arguments); - __ cmp(ecx, ContextOperand(ebx, Context::STRICT_ARGUMENTS_MAP_INDEX)); - __ j(equal, &create_arguments); - - // Check if argumentsList is a fast JSArray. - __ CmpInstanceType(ecx, JS_ARRAY_TYPE); - __ j(equal, &create_array); - - // Ask the runtime to create the list (actually a FixedArray). - __ bind(&create_runtime); - { - FrameScope scope(masm, StackFrame::INTERNAL); - __ Push(edi); - __ Push(edx); - __ Push(eax); - __ CallRuntime(Runtime::kCreateListFromArrayLike); - __ Pop(edx); - __ Pop(edi); - __ mov(ebx, FieldOperand(eax, FixedArray::kLengthOffset)); - __ SmiUntag(ebx); - } - __ jmp(&done_create); - - // Try to create the list from an arguments object. - __ bind(&create_arguments); - __ mov(ebx, FieldOperand(eax, JSArgumentsObject::kLengthOffset)); - __ mov(ecx, FieldOperand(eax, JSObject::kElementsOffset)); - __ cmp(ebx, FieldOperand(ecx, FixedArray::kLengthOffset)); - __ j(not_equal, &create_runtime); - __ SmiUntag(ebx); - __ mov(eax, ecx); - __ jmp(&done_create); - - // For holey JSArrays we need to check that the array prototype chain - // protector is intact and our prototype is the Array.prototype actually. - __ bind(&create_holey_array); - __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset)); - __ mov(ecx, FieldOperand(ecx, Map::kPrototypeOffset)); - __ cmp(ecx, ContextOperand(ebx, Context::INITIAL_ARRAY_PROTOTYPE_INDEX)); - __ j(not_equal, &create_runtime); - __ LoadRoot(ecx, Heap::kArrayProtectorRootIndex); - __ cmp(FieldOperand(ecx, PropertyCell::kValueOffset), - Immediate(Smi::FromInt(Isolate::kProtectorValid))); - __ j(not_equal, &create_runtime); - __ mov(ebx, FieldOperand(eax, JSArray::kLengthOffset)); - __ SmiUntag(ebx); - __ mov(eax, FieldOperand(eax, JSArray::kElementsOffset)); - __ jmp(&done_create); - - // Try to create the list from a JSArray object. - __ bind(&create_array); - __ mov(ecx, FieldOperand(ecx, Map::kBitField2Offset)); - __ DecodeField(ecx); - STATIC_ASSERT(PACKED_SMI_ELEMENTS == 0); - STATIC_ASSERT(HOLEY_SMI_ELEMENTS == 1); - STATIC_ASSERT(PACKED_ELEMENTS == 2); - STATIC_ASSERT(HOLEY_ELEMENTS == 3); - __ cmp(ecx, Immediate(HOLEY_SMI_ELEMENTS)); - __ j(equal, &create_holey_array, Label::kNear); - __ cmp(ecx, Immediate(HOLEY_ELEMENTS)); - __ j(equal, &create_holey_array, Label::kNear); - __ j(above, &create_runtime); - __ mov(ebx, FieldOperand(eax, JSArray::kLengthOffset)); - __ SmiUntag(ebx); - __ mov(eax, FieldOperand(eax, JSArray::kElementsOffset)); - - __ bind(&done_create); - } - - // Check for stack overflow. - { - // Check the stack for overflow. We are not trying to catch interruptions - // (i.e. debug break and preemption) here, so check the "real stack limit". - Label done; - ExternalReference real_stack_limit = - ExternalReference::address_of_real_stack_limit(masm->isolate()); - __ mov(ecx, Operand::StaticVariable(real_stack_limit)); - // Make ecx the space we have left. The stack might already be overflowed - // here which will cause ecx to become negative. - __ neg(ecx); - __ add(ecx, esp); - __ sar(ecx, kPointerSizeLog2); - // Check if the arguments will overflow the stack. - __ cmp(ecx, ebx); - __ j(greater, &done, Label::kNear); // Signed comparison. - __ TailCallRuntime(Runtime::kThrowStackOverflow); - __ bind(&done); - } - - // ----------- S t a t e ------------- - // -- edi : target - // -- eax : args (a FixedArray built from argumentsList) - // -- ebx : len (number of elements to push from args) - // -- edx : new.target (checked to be constructor or undefined) - // -- esp[0] : return address. - // -- esp[4] : thisArgument - // ----------------------------------- - - // Push arguments onto the stack (thisArgument is already on the stack). - { - // Save edx/edi to stX0/stX1. - __ push(edx); - __ push(edi); - __ fld_s(MemOperand(esp, 0)); - __ fld_s(MemOperand(esp, 4)); - __ lea(esp, Operand(esp, 2 * kFloatSize)); - - __ PopReturnAddressTo(edx); - __ Move(ecx, Immediate(0)); - Label done, push, loop; - __ bind(&loop); - __ cmp(ecx, ebx); - __ j(equal, &done, Label::kNear); - // Turn the hole into undefined as we go. - __ mov(edi, - FieldOperand(eax, ecx, times_pointer_size, FixedArray::kHeaderSize)); - __ CompareRoot(edi, Heap::kTheHoleValueRootIndex); - __ j(not_equal, &push, Label::kNear); - __ LoadRoot(edi, Heap::kUndefinedValueRootIndex); - __ bind(&push); - __ Push(edi); - __ inc(ecx); - __ jmp(&loop); - __ bind(&done); - __ PushReturnAddressFrom(edx); - - // Restore edx/edi from stX0/stX1. - __ lea(esp, Operand(esp, -2 * kFloatSize)); - __ fstp_s(MemOperand(esp, 0)); - __ fstp_s(MemOperand(esp, 4)); - __ pop(edx); - __ pop(edi); - - __ Move(eax, ebx); - } - - // Dispatch to Call or Construct depending on whether new.target is undefined. - { - __ CompareRoot(edx, Heap::kUndefinedValueRootIndex); - __ j(equal, masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); - __ Jump(masm->isolate()->builtins()->Construct(), RelocInfo::CODE_TARGET); - } -} - -// static -void Builtins::Generate_CallForwardVarargs(MacroAssembler* masm, - Handle code) { - // ----------- S t a t e ------------- - // -- edi : the target to call (can be any Object) - // -- ecx : start index (to support rest parameters) - // -- esp[0] : return address. - // -- esp[4] : thisArgument - // ----------------------------------- - - // Check if we have an arguments adaptor frame below the function frame. - Label arguments_adaptor, arguments_done; - __ mov(ebx, Operand(ebp, StandardFrameConstants::kCallerFPOffset)); - __ cmp(Operand(ebx, CommonFrameConstants::kContextOrFrameTypeOffset), - Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); - __ j(equal, &arguments_adaptor, Label::kNear); - { - __ mov(eax, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); - __ mov(eax, FieldOperand(eax, JSFunction::kSharedFunctionInfoOffset)); - __ mov(eax, - FieldOperand(eax, SharedFunctionInfo::kFormalParameterCountOffset)); - __ mov(ebx, ebp); - } - __ jmp(&arguments_done, Label::kNear); - __ bind(&arguments_adaptor); - { - // Just load the length from the ArgumentsAdaptorFrame. - __ mov(eax, Operand(ebx, ArgumentsAdaptorFrameConstants::kLengthOffset)); - __ SmiUntag(eax); - } - __ bind(&arguments_done); - - Label stack_empty, stack_done; - __ sub(eax, ecx); - __ j(less_equal, &stack_empty); - { - // Check for stack overflow. - { - // Check the stack for overflow. We are not trying to catch interruptions - // (i.e. debug break and preemption) here, so check the "real stack - // limit". - Label done; - __ LoadRoot(ecx, Heap::kRealStackLimitRootIndex); - // Make ecx the space we have left. The stack might already be - // overflowed here which will cause ecx to become negative. - __ neg(ecx); - __ add(ecx, esp); - __ sar(ecx, kPointerSizeLog2); - // Check if the arguments will overflow the stack. - __ cmp(ecx, eax); - __ j(greater, &done, Label::kNear); // Signed comparison. - __ TailCallRuntime(Runtime::kThrowStackOverflow); - __ bind(&done); - } - - // Forward the arguments from the caller frame. - { - Label loop; - __ mov(ecx, eax); - __ pop(edx); - __ bind(&loop); - { - __ Push(Operand(ebx, ecx, times_pointer_size, 1 * kPointerSize)); - __ dec(ecx); - __ j(not_zero, &loop); - } - __ push(edx); - } - } - __ jmp(&stack_done, Label::kNear); - __ bind(&stack_empty); - { - // We just pass the receiver, which is already on the stack. - __ Move(eax, Immediate(0)); - } - __ bind(&stack_done); - - __ Jump(code, RelocInfo::CODE_TARGET); -} - -namespace { - -// Drops top JavaScript frame and an arguments adaptor frame below it (if -// present) preserving all the arguments prepared for current call. -// Does nothing if debugger is currently active. -// ES6 14.6.3. PrepareForTailCall -// -// Stack structure for the function g() tail calling f(): -// -// ------- Caller frame: ------- -// | ... -// | g()'s arg M -// | ... -// | g()'s arg 1 -// | g()'s receiver arg -// | g()'s caller pc -// ------- g()'s frame: ------- -// | g()'s caller fp <- fp -// | g()'s context -// | function pointer: g -// | ------------------------- -// | ... -// | ... -// | f()'s arg N -// | ... -// | f()'s arg 1 -// | f()'s receiver arg -// | f()'s caller pc <- sp -// ---------------------- -// -void PrepareForTailCall(MacroAssembler* masm, Register args_reg, - Register scratch1, Register scratch2, - Register scratch3) { - DCHECK(!AreAliased(args_reg, scratch1, scratch2, scratch3)); - Comment cmnt(masm, "[ PrepareForTailCall"); - - // Prepare for tail call only if ES2015 tail call elimination is enabled. - Label done; - ExternalReference is_tail_call_elimination_enabled = - ExternalReference::is_tail_call_elimination_enabled_address( - masm->isolate()); - __ movzx_b(scratch1, - Operand::StaticVariable(is_tail_call_elimination_enabled)); - __ cmp(scratch1, Immediate(0)); - __ j(equal, &done, Label::kNear); - - // Drop possible interpreter handler/stub frame. - { - Label no_interpreter_frame; - __ cmp(Operand(ebp, CommonFrameConstants::kContextOrFrameTypeOffset), - Immediate(Smi::FromInt(StackFrame::STUB))); - __ j(not_equal, &no_interpreter_frame, Label::kNear); - __ mov(ebp, Operand(ebp, StandardFrameConstants::kCallerFPOffset)); - __ bind(&no_interpreter_frame); - } - - // Check if next frame is an arguments adaptor frame. - Register caller_args_count_reg = scratch1; - Label no_arguments_adaptor, formal_parameter_count_loaded; - __ mov(scratch2, Operand(ebp, StandardFrameConstants::kCallerFPOffset)); - __ cmp(Operand(scratch2, CommonFrameConstants::kContextOrFrameTypeOffset), - Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); - __ j(not_equal, &no_arguments_adaptor, Label::kNear); - - // Drop current frame and load arguments count from arguments adaptor frame. - __ mov(ebp, scratch2); - __ mov(caller_args_count_reg, - Operand(ebp, ArgumentsAdaptorFrameConstants::kLengthOffset)); - __ SmiUntag(caller_args_count_reg); - __ jmp(&formal_parameter_count_loaded, Label::kNear); - - __ bind(&no_arguments_adaptor); - // Load caller's formal parameter count - __ mov(scratch1, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); - __ mov(scratch1, - FieldOperand(scratch1, JSFunction::kSharedFunctionInfoOffset)); - __ mov( - caller_args_count_reg, - FieldOperand(scratch1, SharedFunctionInfo::kFormalParameterCountOffset)); - - __ bind(&formal_parameter_count_loaded); - - ParameterCount callee_args_count(args_reg); - __ PrepareForTailCall(callee_args_count, caller_args_count_reg, scratch2, - scratch3, ReturnAddressState::kOnStack, 0); - __ bind(&done); -} -} // namespace - -// static -void Builtins::Generate_CallFunction(MacroAssembler* masm, - ConvertReceiverMode mode, - TailCallMode tail_call_mode) { - // ----------- S t a t e ------------- - // -- eax : the number of arguments (not including the receiver) - // -- edi : the function to call (checked to be a JSFunction) - // ----------------------------------- - __ AssertFunction(edi); - - // See ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList) - // Check that the function is not a "classConstructor". - Label class_constructor; - __ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); - __ test(FieldOperand(edx, SharedFunctionInfo::kCompilerHintsOffset), - Immediate(SharedFunctionInfo::kClassConstructorMask)); - __ j(not_zero, &class_constructor); - - // Enter the context of the function; ToObject has to run in the function - // context, and we also need to take the global proxy from the function - // context in case of conversion. - __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); - // We need to convert the receiver for non-native sloppy mode functions. - Label done_convert; - __ test(FieldOperand(edx, SharedFunctionInfo::kCompilerHintsOffset), - Immediate(SharedFunctionInfo::IsNativeBit::kMask | - SharedFunctionInfo::IsStrictBit::kMask)); - __ j(not_zero, &done_convert); - { - // ----------- S t a t e ------------- - // -- eax : the number of arguments (not including the receiver) - // -- edx : the shared function info. - // -- edi : the function to call (checked to be a JSFunction) - // -- esi : the function context. - // ----------------------------------- - - if (mode == ConvertReceiverMode::kNullOrUndefined) { - // Patch receiver to global proxy. - __ LoadGlobalProxy(ecx); - } else { - Label convert_to_object, convert_receiver; - __ mov(ecx, Operand(esp, eax, times_pointer_size, kPointerSize)); - __ JumpIfSmi(ecx, &convert_to_object, Label::kNear); - STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE); - __ CmpObjectType(ecx, FIRST_JS_RECEIVER_TYPE, ebx); - __ j(above_equal, &done_convert); - if (mode != ConvertReceiverMode::kNotNullOrUndefined) { - Label convert_global_proxy; - __ JumpIfRoot(ecx, Heap::kUndefinedValueRootIndex, - &convert_global_proxy, Label::kNear); - __ JumpIfNotRoot(ecx, Heap::kNullValueRootIndex, &convert_to_object, - Label::kNear); - __ bind(&convert_global_proxy); - { - // Patch receiver to global proxy. - __ LoadGlobalProxy(ecx); - } - __ jmp(&convert_receiver); - } - __ bind(&convert_to_object); - { - // Convert receiver using ToObject. - // TODO(bmeurer): Inline the allocation here to avoid building the frame - // in the fast case? (fall back to AllocateInNewSpace?) - FrameScope scope(masm, StackFrame::INTERNAL); - __ SmiTag(eax); - __ Push(eax); - __ Push(edi); - __ mov(eax, ecx); - __ Push(esi); - __ Call(masm->isolate()->builtins()->ToObject(), - RelocInfo::CODE_TARGET); - __ Pop(esi); - __ mov(ecx, eax); - __ Pop(edi); - __ Pop(eax); - __ SmiUntag(eax); - } - __ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); - __ bind(&convert_receiver); - } - __ mov(Operand(esp, eax, times_pointer_size, kPointerSize), ecx); - } - __ bind(&done_convert); - - // ----------- S t a t e ------------- - // -- eax : the number of arguments (not including the receiver) - // -- edx : the shared function info. - // -- edi : the function to call (checked to be a JSFunction) - // -- esi : the function context. - // ----------------------------------- - - if (tail_call_mode == TailCallMode::kAllow) { - PrepareForTailCall(masm, eax, ebx, ecx, edx); - // Reload shared function info. - __ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); - } - - __ mov(ebx, - FieldOperand(edx, SharedFunctionInfo::kFormalParameterCountOffset)); - ParameterCount actual(eax); - ParameterCount expected(ebx); - __ InvokeFunctionCode(edi, no_reg, expected, actual, JUMP_FUNCTION, - CheckDebugStepCallWrapper()); - // The function is a "classConstructor", need to raise an exception. - __ bind(&class_constructor); - { - FrameScope frame(masm, StackFrame::INTERNAL); - __ push(edi); - __ CallRuntime(Runtime::kThrowConstructorNonCallableError); - } -} - -namespace { - -void Generate_PushBoundArguments(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- eax : the number of arguments (not including the receiver) - // -- edx : new.target (only in case of [[Construct]]) - // -- edi : target (checked to be a JSBoundFunction) - // ----------------------------------- - - // Load [[BoundArguments]] into ecx and length of that into ebx. - Label no_bound_arguments; - __ mov(ecx, FieldOperand(edi, JSBoundFunction::kBoundArgumentsOffset)); - __ mov(ebx, FieldOperand(ecx, FixedArray::kLengthOffset)); - __ SmiUntag(ebx); - __ test(ebx, ebx); - __ j(zero, &no_bound_arguments); - { - // ----------- S t a t e ------------- - // -- eax : the number of arguments (not including the receiver) - // -- edx : new.target (only in case of [[Construct]]) - // -- edi : target (checked to be a JSBoundFunction) - // -- ecx : the [[BoundArguments]] (implemented as FixedArray) - // -- ebx : the number of [[BoundArguments]] - // ----------------------------------- - - // Reserve stack space for the [[BoundArguments]]. - { - Label done; - __ lea(ecx, Operand(ebx, times_pointer_size, 0)); - __ sub(esp, ecx); - // Check the stack for overflow. We are not trying to catch interruptions - // (i.e. debug break and preemption) here, so check the "real stack - // limit". - __ CompareRoot(esp, ecx, Heap::kRealStackLimitRootIndex); - __ j(greater, &done, Label::kNear); // Signed comparison. - // Restore the stack pointer. - __ lea(esp, Operand(esp, ebx, times_pointer_size, 0)); - { - FrameScope scope(masm, StackFrame::MANUAL); - __ EnterFrame(StackFrame::INTERNAL); - __ CallRuntime(Runtime::kThrowStackOverflow); - } - __ bind(&done); - } - - // Adjust effective number of arguments to include return address. - __ inc(eax); - - // Relocate arguments and return address down the stack. - { - Label loop; - __ Set(ecx, 0); - __ lea(ebx, Operand(esp, ebx, times_pointer_size, 0)); - __ bind(&loop); - __ fld_s(Operand(ebx, ecx, times_pointer_size, 0)); - __ fstp_s(Operand(esp, ecx, times_pointer_size, 0)); - __ inc(ecx); - __ cmp(ecx, eax); - __ j(less, &loop); - } - - // Copy [[BoundArguments]] to the stack (below the arguments). - { - Label loop; - __ mov(ecx, FieldOperand(edi, JSBoundFunction::kBoundArgumentsOffset)); - __ mov(ebx, FieldOperand(ecx, FixedArray::kLengthOffset)); - __ SmiUntag(ebx); - __ bind(&loop); - __ dec(ebx); - __ fld_s( - FieldOperand(ecx, ebx, times_pointer_size, FixedArray::kHeaderSize)); - __ fstp_s(Operand(esp, eax, times_pointer_size, 0)); - __ lea(eax, Operand(eax, 1)); - __ j(greater, &loop); - } - - // Adjust effective number of arguments (eax contains the number of - // arguments from the call plus return address plus the number of - // [[BoundArguments]]), so we need to subtract one for the return address. - __ dec(eax); - } - __ bind(&no_bound_arguments); -} - -} // namespace - -// static -void Builtins::Generate_CallBoundFunctionImpl(MacroAssembler* masm, - TailCallMode tail_call_mode) { - // ----------- S t a t e ------------- - // -- eax : the number of arguments (not including the receiver) - // -- edi : the function to call (checked to be a JSBoundFunction) - // ----------------------------------- - __ AssertBoundFunction(edi); - - if (tail_call_mode == TailCallMode::kAllow) { - PrepareForTailCall(masm, eax, ebx, ecx, edx); - } - - // Patch the receiver to [[BoundThis]]. - __ mov(ebx, FieldOperand(edi, JSBoundFunction::kBoundThisOffset)); - __ mov(Operand(esp, eax, times_pointer_size, kPointerSize), ebx); - - // Push the [[BoundArguments]] onto the stack. - Generate_PushBoundArguments(masm); - - // Call the [[BoundTargetFunction]] via the Call builtin. - __ mov(edi, FieldOperand(edi, JSBoundFunction::kBoundTargetFunctionOffset)); - __ mov(ecx, Operand::StaticVariable(ExternalReference( - Builtins::kCall_ReceiverIsAny, masm->isolate()))); - __ lea(ecx, FieldOperand(ecx, Code::kHeaderSize)); - __ jmp(ecx); -} - -// static -void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode, - TailCallMode tail_call_mode) { - // ----------- S t a t e ------------- - // -- eax : the number of arguments (not including the receiver) - // -- edi : the target to call (can be any Object). - // ----------------------------------- - - Label non_callable, non_function, non_smi; - __ JumpIfSmi(edi, &non_callable); - __ bind(&non_smi); - __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx); - __ j(equal, masm->isolate()->builtins()->CallFunction(mode, tail_call_mode), - RelocInfo::CODE_TARGET); - __ CmpInstanceType(ecx, JS_BOUND_FUNCTION_TYPE); - __ j(equal, masm->isolate()->builtins()->CallBoundFunction(tail_call_mode), - RelocInfo::CODE_TARGET); - - // Check if target has a [[Call]] internal method. - __ test_b(FieldOperand(ecx, Map::kBitFieldOffset), - Immediate(1 << Map::kIsCallable)); - __ j(zero, &non_callable); - - // Check if target is a proxy and call CallProxy external builtin - __ CmpInstanceType(ecx, JS_PROXY_TYPE); - __ j(not_equal, &non_function); - - __ mov(ecx, Operand::StaticVariable( - ExternalReference(Builtins::kCallProxy, masm->isolate()))); - __ lea(ecx, FieldOperand(ecx, Code::kHeaderSize)); - __ jmp(ecx); - - // 2. Call to something else, which might have a [[Call]] internal method (if - // not we raise an exception). - __ bind(&non_function); - // Overwrite the original receiver with the (original) target. - __ mov(Operand(esp, eax, times_pointer_size, kPointerSize), edi); - // Let the "call_as_function_delegate" take care of the rest. - __ LoadGlobalFunction(Context::CALL_AS_FUNCTION_DELEGATE_INDEX, edi); - __ Jump(masm->isolate()->builtins()->CallFunction( - ConvertReceiverMode::kNotNullOrUndefined, tail_call_mode), - RelocInfo::CODE_TARGET); - - // 3. Call to something that is not callable. - __ bind(&non_callable); - { - FrameScope scope(masm, StackFrame::INTERNAL); - __ Push(edi); - __ CallRuntime(Runtime::kThrowCalledNonCallable); - } -} - -static void CheckSpreadAndPushToStack(MacroAssembler* masm) { - // Free up some registers. - // Save edx/edi to stX0/stX1. - __ push(edx); - __ push(edi); - __ fld_s(MemOperand(esp, 0)); - __ fld_s(MemOperand(esp, 4)); - __ lea(esp, Operand(esp, 2 * kFloatSize)); - - Register argc = eax; - - Register scratch = ecx; - Register scratch2 = edi; - - Register spread = ebx; - Register spread_map = edx; - - Register spread_len = edx; - - Label runtime_call, push_args; - __ mov(spread, Operand(esp, kPointerSize)); - __ JumpIfSmi(spread, &runtime_call); - __ mov(spread_map, FieldOperand(spread, HeapObject::kMapOffset)); - - // Check that the spread is an array. - __ CmpInstanceType(spread_map, JS_ARRAY_TYPE); - __ j(not_equal, &runtime_call); - - // Check that we have the original ArrayPrototype. - __ mov(scratch, FieldOperand(spread_map, Map::kPrototypeOffset)); - __ mov(scratch2, NativeContextOperand()); - __ cmp(scratch, - ContextOperand(scratch2, Context::INITIAL_ARRAY_PROTOTYPE_INDEX)); - __ j(not_equal, &runtime_call); - - // Check that the ArrayPrototype hasn't been modified in a way that would - // affect iteration. - __ LoadRoot(scratch, Heap::kArrayIteratorProtectorRootIndex); - __ cmp(FieldOperand(scratch, PropertyCell::kValueOffset), - Immediate(Smi::FromInt(Isolate::kProtectorValid))); - __ j(not_equal, &runtime_call); - - // Check that the map of the initial array iterator hasn't changed. - __ mov(scratch2, NativeContextOperand()); - __ mov(scratch, - ContextOperand(scratch2, - Context::INITIAL_ARRAY_ITERATOR_PROTOTYPE_INDEX)); - __ mov(scratch, FieldOperand(scratch, HeapObject::kMapOffset)); - __ cmp(scratch, - ContextOperand(scratch2, - Context::INITIAL_ARRAY_ITERATOR_PROTOTYPE_MAP_INDEX)); - __ j(not_equal, &runtime_call); - - // For FastPacked kinds, iteration will have the same effect as simply - // accessing each property in order. - Label no_protector_check; - __ mov(scratch, FieldOperand(spread_map, Map::kBitField2Offset)); - __ DecodeField(scratch); - __ cmp(scratch, Immediate(HOLEY_ELEMENTS)); - __ j(above, &runtime_call); - // For non-FastHoley kinds, we can skip the protector check. - __ cmp(scratch, Immediate(PACKED_SMI_ELEMENTS)); - __ j(equal, &no_protector_check); - __ cmp(scratch, Immediate(PACKED_ELEMENTS)); - __ j(equal, &no_protector_check); - // Check the ArrayProtector cell. - __ LoadRoot(scratch, Heap::kArrayProtectorRootIndex); - __ cmp(FieldOperand(scratch, PropertyCell::kValueOffset), - Immediate(Smi::FromInt(Isolate::kProtectorValid))); - __ j(not_equal, &runtime_call); - - __ bind(&no_protector_check); - // Load the FixedArray backing store, but use the length from the array. - __ mov(spread_len, FieldOperand(spread, JSArray::kLengthOffset)); - __ SmiUntag(spread_len); - __ mov(spread, FieldOperand(spread, JSArray::kElementsOffset)); - __ jmp(&push_args); - - __ bind(&runtime_call); - { - // Call the builtin for the result of the spread. - FrameScope scope(masm, StackFrame::INTERNAL); - // Need to save these on the stack. - // Restore edx/edi from stX0/stX1. - __ lea(esp, Operand(esp, -2 * kFloatSize)); - __ fstp_s(MemOperand(esp, 0)); - __ fstp_s(MemOperand(esp, 4)); - __ pop(edx); - __ pop(edi); - - __ Push(edi); - __ Push(edx); - __ SmiTag(argc); - __ Push(argc); - __ Push(spread); - __ CallRuntime(Runtime::kSpreadIterableFixed); - __ mov(spread, eax); - __ Pop(argc); - __ SmiUntag(argc); - __ Pop(edx); - __ Pop(edi); - // Free up some registers. - // Save edx/edi to stX0/stX1. - __ push(edx); - __ push(edi); - __ fld_s(MemOperand(esp, 0)); - __ fld_s(MemOperand(esp, 4)); - __ lea(esp, Operand(esp, 2 * kFloatSize)); - } - - { - // Calculate the new nargs including the result of the spread. - __ mov(spread_len, FieldOperand(spread, FixedArray::kLengthOffset)); - __ SmiUntag(spread_len); - - __ bind(&push_args); - // argc += spread_len - 1. Subtract 1 for the spread itself. - __ lea(argc, Operand(argc, spread_len, times_1, -1)); - } - - // Check for stack overflow. - { - // Check the stack for overflow. We are not trying to catch interruptions - // (i.e. debug break and preemption) here, so check the "real stack limit". - Label done; - __ LoadRoot(scratch, Heap::kRealStackLimitRootIndex); - // Make scratch the space we have left. The stack might already be - // overflowed here which will cause scratch to become negative. - __ neg(scratch); - __ add(scratch, esp); - __ sar(scratch, kPointerSizeLog2); - // Check if the arguments will overflow the stack. - __ cmp(scratch, spread_len); - __ j(greater, &done, Label::kNear); // Signed comparison. - __ TailCallRuntime(Runtime::kThrowStackOverflow); - __ bind(&done); - } - - // Put the evaluated spread onto the stack as additional arguments. - { - Register return_address = edi; - // Pop the return address and spread argument. - __ PopReturnAddressTo(return_address); - __ Pop(scratch); - - Register scratch2 = esi; - // Save esi to stX0, edx/edi in stX1/stX2 now. - __ push(esi); - __ fld_s(MemOperand(esp, 0)); - __ lea(esp, Operand(esp, 1 * kFloatSize)); - - __ mov(scratch, Immediate(0)); - Label done, push, loop; - __ bind(&loop); - __ cmp(scratch, spread_len); - __ j(equal, &done, Label::kNear); - __ mov(scratch2, FieldOperand(spread, scratch, times_pointer_size, - FixedArray::kHeaderSize)); - __ JumpIfNotRoot(scratch2, Heap::kTheHoleValueRootIndex, &push); - __ LoadRoot(scratch2, Heap::kUndefinedValueRootIndex); - __ bind(&push); - __ Push(scratch2); - __ inc(scratch); - __ jmp(&loop); - __ bind(&done); - __ PushReturnAddressFrom(return_address); - - // Now Restore esi from stX0, edx/edi from stX1/stX2. - __ lea(esp, Operand(esp, -3 * kFloatSize)); - __ fstp_s(MemOperand(esp, 0)); - __ fstp_s(MemOperand(esp, 4)); - __ fstp_s(MemOperand(esp, 8)); - __ pop(esi); - __ pop(edx); - __ pop(edi); - } -} - -// static -void Builtins::Generate_CallWithSpread(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- eax : the number of arguments (not including the receiver) - // -- edi : the target to call (can be any Object) - // ----------------------------------- - - // CheckSpreadAndPushToStack will push edx to save it. - __ LoadRoot(edx, Heap::kUndefinedValueRootIndex); - CheckSpreadAndPushToStack(masm); - __ Jump(masm->isolate()->builtins()->Call(ConvertReceiverMode::kAny, - TailCallMode::kDisallow), - RelocInfo::CODE_TARGET); -} - -// static -void Builtins::Generate_ConstructFunction(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- eax : the number of arguments (not including the receiver) - // -- edx : the new target (checked to be a constructor) - // -- edi : the constructor to call (checked to be a JSFunction) - // ----------------------------------- - __ AssertFunction(edi); - - // Calling convention for function specific ConstructStubs require - // ebx to contain either an AllocationSite or undefined. - __ LoadRoot(ebx, Heap::kUndefinedValueRootIndex); - - // Tail call to the function-specific construct stub (still in the caller - // context at this point). - __ mov(ecx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); - __ mov(ecx, FieldOperand(ecx, SharedFunctionInfo::kConstructStubOffset)); - __ lea(ecx, FieldOperand(ecx, Code::kHeaderSize)); - __ jmp(ecx); -} - -// static -void Builtins::Generate_ConstructBoundFunction(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- eax : the number of arguments (not including the receiver) - // -- edx : the new target (checked to be a constructor) - // -- edi : the constructor to call (checked to be a JSBoundFunction) - // ----------------------------------- - __ AssertBoundFunction(edi); - - // Push the [[BoundArguments]] onto the stack. - Generate_PushBoundArguments(masm); - - // Patch new.target to [[BoundTargetFunction]] if new.target equals target. - { - Label done; - __ cmp(edi, edx); - __ j(not_equal, &done, Label::kNear); - __ mov(edx, FieldOperand(edi, JSBoundFunction::kBoundTargetFunctionOffset)); - __ bind(&done); - } - - // Construct the [[BoundTargetFunction]] via the Construct builtin. - __ mov(edi, FieldOperand(edi, JSBoundFunction::kBoundTargetFunctionOffset)); - __ mov(ecx, Operand::StaticVariable( - ExternalReference(Builtins::kConstruct, masm->isolate()))); - __ lea(ecx, FieldOperand(ecx, Code::kHeaderSize)); - __ jmp(ecx); -} - -// static -void Builtins::Generate_ConstructProxy(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- eax : the number of arguments (not including the receiver) - // -- edi : the constructor to call (checked to be a JSProxy) - // -- edx : the new target (either the same as the constructor or - // the JSFunction on which new was invoked initially) - // ----------------------------------- - - // Call into the Runtime for Proxy [[Construct]]. - __ PopReturnAddressTo(ecx); - __ Push(edi); - __ Push(edx); - __ PushReturnAddressFrom(ecx); - // Include the pushed new_target, constructor and the receiver. - __ add(eax, Immediate(3)); - // Tail-call to the runtime. - __ JumpToExternalReference( - ExternalReference(Runtime::kJSProxyConstruct, masm->isolate())); -} - -// static -void Builtins::Generate_Construct(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- eax : the number of arguments (not including the receiver) - // -- edx : the new target (either the same as the constructor or - // the JSFunction on which new was invoked initially) - // -- edi : the constructor to call (can be any Object) - // ----------------------------------- - - // Check if target is a Smi. - Label non_constructor; - __ JumpIfSmi(edi, &non_constructor, Label::kNear); - - // Dispatch based on instance type. - __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx); - __ j(equal, masm->isolate()->builtins()->ConstructFunction(), - RelocInfo::CODE_TARGET); - - // Check if target has a [[Construct]] internal method. - __ test_b(FieldOperand(ecx, Map::kBitFieldOffset), - Immediate(1 << Map::kIsConstructor)); - __ j(zero, &non_constructor, Label::kNear); - - // Only dispatch to bound functions after checking whether they are - // constructors. - __ CmpInstanceType(ecx, JS_BOUND_FUNCTION_TYPE); - __ j(equal, masm->isolate()->builtins()->ConstructBoundFunction(), - RelocInfo::CODE_TARGET); - - // Only dispatch to proxies after checking whether they are constructors. - __ CmpInstanceType(ecx, JS_PROXY_TYPE); - __ j(equal, masm->isolate()->builtins()->ConstructProxy(), - RelocInfo::CODE_TARGET); - - // Called Construct on an exotic Object with a [[Construct]] internal method. - { - // Overwrite the original receiver with the (original) target. - __ mov(Operand(esp, eax, times_pointer_size, kPointerSize), edi); - // Let the "call_as_constructor_delegate" take care of the rest. - __ LoadGlobalFunction(Context::CALL_AS_CONSTRUCTOR_DELEGATE_INDEX, edi); - __ Jump(masm->isolate()->builtins()->CallFunction(), - RelocInfo::CODE_TARGET); - } - - // Called Construct on an Object that doesn't have a [[Construct]] internal - // method. - __ bind(&non_constructor); - __ Jump(masm->isolate()->builtins()->ConstructedNonConstructable(), - RelocInfo::CODE_TARGET); -} - -// static -void Builtins::Generate_ConstructWithSpread(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- eax : the number of arguments (not including the receiver) - // -- edx : the new target (either the same as the constructor or - // the JSFunction on which new was invoked initially) - // -- edi : the constructor to call (can be any Object) - // ----------------------------------- - - CheckSpreadAndPushToStack(masm); - __ Jump(masm->isolate()->builtins()->Construct(), RelocInfo::CODE_TARGET); -} - -// static -void Builtins::Generate_AllocateInNewSpace(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- edx : requested object size (untagged) - // -- esp[0] : return address - // ----------------------------------- - __ SmiTag(edx); - __ PopReturnAddressTo(ecx); - __ Push(edx); - __ PushReturnAddressFrom(ecx); - __ Move(esi, Smi::kZero); - __ TailCallRuntime(Runtime::kAllocateInNewSpace); -} - -// static -void Builtins::Generate_AllocateInOldSpace(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- edx : requested object size (untagged) - // -- esp[0] : return address - // ----------------------------------- - __ SmiTag(edx); - __ PopReturnAddressTo(ecx); - __ Push(edx); - __ Push(Smi::FromInt(AllocateTargetSpace::encode(OLD_SPACE))); - __ PushReturnAddressFrom(ecx); - __ Move(esi, Smi::kZero); - __ TailCallRuntime(Runtime::kAllocateInTargetSpace); -} - -// static -void Builtins::Generate_Abort(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- edx : message_id as Smi - // -- esp[0] : return address - // ----------------------------------- - __ PopReturnAddressTo(ecx); - __ Push(edx); - __ PushReturnAddressFrom(ecx); - __ Move(esi, Smi::kZero); - __ TailCallRuntime(Runtime::kAbort); -} - -void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- eax : actual number of arguments - // -- ebx : expected number of arguments - // -- edx : new target (passed through to callee) - // -- edi : function (passed through to callee) - // ----------------------------------- - - Label invoke, dont_adapt_arguments, stack_overflow; - __ IncrementCounter(masm->isolate()->counters()->arguments_adaptors(), 1); - - Label enough, too_few; - __ cmp(eax, ebx); - __ j(less, &too_few); - __ cmp(ebx, SharedFunctionInfo::kDontAdaptArgumentsSentinel); - __ j(equal, &dont_adapt_arguments); - - { // Enough parameters: Actual >= expected. - __ bind(&enough); - EnterArgumentsAdaptorFrame(masm); - // edi is used as a scratch register. It should be restored from the frame - // when needed. - Generate_StackOverflowCheck(masm, ebx, ecx, edi, &stack_overflow); - - // Copy receiver and all expected arguments. - const int offset = StandardFrameConstants::kCallerSPOffset; - __ lea(edi, Operand(ebp, eax, times_4, offset)); - __ mov(eax, -1); // account for receiver - - Label copy; - __ bind(©); - __ inc(eax); - __ push(Operand(edi, 0)); - __ sub(edi, Immediate(kPointerSize)); - __ cmp(eax, ebx); - __ j(less, ©); - // eax now contains the expected number of arguments. - __ jmp(&invoke); - } - - { // Too few parameters: Actual < expected. - __ bind(&too_few); - EnterArgumentsAdaptorFrame(masm); - // edi is used as a scratch register. It should be restored from the frame - // when needed. - Generate_StackOverflowCheck(masm, ebx, ecx, edi, &stack_overflow); - - // Remember expected arguments in ecx. - __ mov(ecx, ebx); - - // Copy receiver and all actual arguments. - const int offset = StandardFrameConstants::kCallerSPOffset; - __ lea(edi, Operand(ebp, eax, times_4, offset)); - // ebx = expected - actual. - __ sub(ebx, eax); - // eax = -actual - 1 - __ neg(eax); - __ sub(eax, Immediate(1)); - - Label copy; - __ bind(©); - __ inc(eax); - __ push(Operand(edi, 0)); - __ sub(edi, Immediate(kPointerSize)); - __ test(eax, eax); - __ j(not_zero, ©); - - // Fill remaining expected arguments with undefined values. - Label fill; - __ bind(&fill); - __ inc(eax); - __ push(Immediate(masm->isolate()->factory()->undefined_value())); - __ cmp(eax, ebx); - __ j(less, &fill); - - // Restore expected arguments. - __ mov(eax, ecx); - } - - // Call the entry point. - __ bind(&invoke); - // Restore function pointer. - __ mov(edi, Operand(ebp, ArgumentsAdaptorFrameConstants::kFunctionOffset)); - // eax : expected number of arguments - // edx : new target (passed through to callee) - // edi : function (passed through to callee) - __ mov(ecx, FieldOperand(edi, JSFunction::kCodeEntryOffset)); - __ call(ecx); - - // Store offset of return address for deoptimizer. - masm->isolate()->heap()->SetArgumentsAdaptorDeoptPCOffset(masm->pc_offset()); - - // Leave frame and return. - LeaveArgumentsAdaptorFrame(masm); - __ ret(0); - - // ------------------------------------------- - // Dont adapt arguments. - // ------------------------------------------- - __ bind(&dont_adapt_arguments); - __ mov(ecx, FieldOperand(edi, JSFunction::kCodeEntryOffset)); - __ jmp(ecx); - - __ bind(&stack_overflow); - { - FrameScope frame(masm, StackFrame::MANUAL); - __ CallRuntime(Runtime::kThrowStackOverflow); - __ int3(); - } -} - -static void Generate_OnStackReplacementHelper(MacroAssembler* masm, - bool has_handler_frame) { - // Lookup the function in the JavaScript frame. - if (has_handler_frame) { - __ mov(eax, Operand(ebp, StandardFrameConstants::kCallerFPOffset)); - __ mov(eax, Operand(eax, JavaScriptFrameConstants::kFunctionOffset)); - } else { - __ mov(eax, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); - } - - { - FrameScope scope(masm, StackFrame::INTERNAL); - // Pass function as argument. - __ push(eax); - __ CallRuntime(Runtime::kCompileForOnStackReplacement); - } - - Label skip; - // If the code object is null, just return to the caller. - __ cmp(eax, Immediate(0)); - __ j(not_equal, &skip, Label::kNear); - __ ret(0); - - __ bind(&skip); - - // Drop any potential handler frame that is be sitting on top of the actual - // JavaScript frame. This is the case then OSR is triggered from bytecode. - if (has_handler_frame) { - __ leave(); - } - - // Load deoptimization data from the code object. - __ mov(ebx, Operand(eax, Code::kDeoptimizationDataOffset - kHeapObjectTag)); - - // Load the OSR entrypoint offset from the deoptimization data. - __ mov(ebx, Operand(ebx, FixedArray::OffsetOfElementAt( - DeoptimizationInputData::kOsrPcOffsetIndex) - - kHeapObjectTag)); - __ SmiUntag(ebx); - - // Compute the target address = code_obj + header_size + osr_offset - __ lea(eax, Operand(eax, ebx, times_1, Code::kHeaderSize - kHeapObjectTag)); - - // Overwrite the return address on the stack. - __ mov(Operand(esp, 0), eax); - - // And "return" to the OSR entry point of the function. - __ ret(0); -} - -void Builtins::Generate_OnStackReplacement(MacroAssembler* masm) { - Generate_OnStackReplacementHelper(masm, false); -} - -void Builtins::Generate_InterpreterOnStackReplacement(MacroAssembler* masm) { - Generate_OnStackReplacementHelper(masm, true); -} - -#undef __ -} // namespace internal -} // namespace v8 - -#endif // V8_TARGET_ARCH_X87 diff --git a/src/code-stubs.h b/src/code-stubs.h index d5e4c9666a..d57a35f8a5 100644 --- a/src/code-stubs.h +++ b/src/code-stubs.h @@ -514,8 +514,6 @@ class RuntimeCallHelper { #include "src/mips64/code-stubs-mips64.h" #elif V8_TARGET_ARCH_S390 #include "src/s390/code-stubs-s390.h" -#elif V8_TARGET_ARCH_X87 -#include "src/x87/code-stubs-x87.h" #else #error Unsupported target architecture. #endif diff --git a/src/codegen.h b/src/codegen.h index f51b80c25f..c906513358 100644 --- a/src/codegen.h +++ b/src/codegen.h @@ -59,8 +59,6 @@ #include "src/mips64/codegen-mips64.h" // NOLINT #elif V8_TARGET_ARCH_S390 #include "src/s390/codegen-s390.h" // NOLINT -#elif V8_TARGET_ARCH_X87 -#include "src/x87/codegen-x87.h" // NOLINT #else #error Unsupported target architecture. #endif diff --git a/src/compiler/c-linkage.cc b/src/compiler/c-linkage.cc index d8fc12624d..16a7ce8908 100644 --- a/src/compiler/c-linkage.cc +++ b/src/compiler/c-linkage.cc @@ -50,12 +50,6 @@ LinkageLocation regloc(Register reg, MachineType type) { rbx.bit() | r12.bit() | r13.bit() | r14.bit() | r15.bit() #endif -#elif V8_TARGET_ARCH_X87 -// =========================================================================== -// == x87 ==================================================================== -// =========================================================================== -#define CALLEE_SAVE_REGISTERS esi.bit() | edi.bit() | ebx.bit() - #elif V8_TARGET_ARCH_ARM // =========================================================================== // == arm ==================================================================== @@ -161,7 +155,7 @@ CallDescriptor* Linkage::GetSimplifiedCDescriptor( msig->parameter_count()); // Check the types of the signature. // Currently no floating point parameters or returns are allowed because - // on x87 and ia32, the FP top of stack is involved. + // on ia32, the FP top of stack is involved. for (size_t i = 0; i < msig->return_count(); i++) { MachineRepresentation rep = msig->GetReturn(i).representation(); CHECK_NE(MachineRepresentation::kFloat32, rep); diff --git a/src/compiler/instruction-codes.h b/src/compiler/instruction-codes.h index d4e0449ad9..df7a03163d 100644 --- a/src/compiler/instruction-codes.h +++ b/src/compiler/instruction-codes.h @@ -23,8 +23,6 @@ #include "src/compiler/ppc/instruction-codes-ppc.h" #elif V8_TARGET_ARCH_S390 #include "src/compiler/s390/instruction-codes-s390.h" -#elif V8_TARGET_ARCH_X87 -#include "src/compiler/x87/instruction-codes-x87.h" #else #define TARGET_ARCH_OPCODE_LIST(V) #define TARGET_ADDRESSING_MODE_LIST(V) diff --git a/src/compiler/wasm-linkage.cc b/src/compiler/wasm-linkage.cc index f9c7126b2f..e5130fb63a 100644 --- a/src/compiler/wasm-linkage.cc +++ b/src/compiler/wasm-linkage.cc @@ -69,14 +69,6 @@ LinkageLocation stackloc(int i, MachineType type) { #define FP_PARAM_REGISTERS xmm1, xmm2, xmm3, xmm4, xmm5, xmm6 #define FP_RETURN_REGISTERS xmm1, xmm2 -#elif V8_TARGET_ARCH_X87 -// =========================================================================== -// == x87 ==================================================================== -// =========================================================================== -#define GP_PARAM_REGISTERS eax, edx, ecx, ebx, esi -#define GP_RETURN_REGISTERS eax, edx -#define FP_RETURN_REGISTERS stX_0 - #elif V8_TARGET_ARCH_ARM // =========================================================================== // == arm ==================================================================== diff --git a/src/compiler/x87/OWNERS b/src/compiler/x87/OWNERS deleted file mode 100644 index 61245ae8e2..0000000000 --- a/src/compiler/x87/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -weiliang.lin@intel.com -chunyang.dai@intel.com diff --git a/src/compiler/x87/code-generator-x87.cc b/src/compiler/x87/code-generator-x87.cc deleted file mode 100644 index ab242c8ce3..0000000000 --- a/src/compiler/x87/code-generator-x87.cc +++ /dev/null @@ -1,2768 +0,0 @@ -// Copyright 2013 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. - -#include "src/compiler/code-generator.h" - -#include "src/compilation-info.h" -#include "src/compiler/code-generator-impl.h" -#include "src/compiler/gap-resolver.h" -#include "src/compiler/node-matchers.h" -#include "src/compiler/osr.h" -#include "src/frames.h" -#include "src/x87/assembler-x87.h" -#include "src/x87/frames-x87.h" -#include "src/x87/macro-assembler-x87.h" - -namespace v8 { -namespace internal { -namespace compiler { - -#define __ masm()-> - - -// Adds X87 specific methods for decoding operands. -class X87OperandConverter : public InstructionOperandConverter { - public: - X87OperandConverter(CodeGenerator* gen, Instruction* instr) - : InstructionOperandConverter(gen, instr) {} - - Operand InputOperand(size_t index, int extra = 0) { - return ToOperand(instr_->InputAt(index), extra); - } - - Immediate InputImmediate(size_t index) { - return ToImmediate(instr_->InputAt(index)); - } - - Operand OutputOperand() { return ToOperand(instr_->Output()); } - - Operand ToOperand(InstructionOperand* op, int extra = 0) { - if (op->IsRegister()) { - DCHECK(extra == 0); - return Operand(ToRegister(op)); - } - DCHECK(op->IsStackSlot() || op->IsFPStackSlot()); - return SlotToOperand(AllocatedOperand::cast(op)->index(), extra); - } - - Operand SlotToOperand(int slot, int extra = 0) { - FrameOffset offset = frame_access_state()->GetFrameOffset(slot); - return Operand(offset.from_stack_pointer() ? esp : ebp, - offset.offset() + extra); - } - - Operand HighOperand(InstructionOperand* op) { - DCHECK(op->IsFPStackSlot()); - return ToOperand(op, kPointerSize); - } - - Immediate ToImmediate(InstructionOperand* operand) { - Constant constant = ToConstant(operand); - if (constant.type() == Constant::kInt32 && - RelocInfo::IsWasmReference(constant.rmode())) { - return Immediate(reinterpret_cast
(constant.ToInt32()), - constant.rmode()); - } - switch (constant.type()) { - case Constant::kInt32: - return Immediate(constant.ToInt32()); - case Constant::kFloat32: - return Immediate( - isolate()->factory()->NewNumber(constant.ToFloat32(), TENURED)); - case Constant::kFloat64: - return Immediate(isolate()->factory()->NewNumber( - constant.ToFloat64(value()).value(), TENURED)); - case Constant::kExternalReference: - return Immediate(constant.ToExternalReference()); - case Constant::kHeapObject: - return Immediate(constant.ToHeapObject()); - case Constant::kInt64: - break; - case Constant::kRpoNumber: - return Immediate::CodeRelativeOffset(ToLabel(operand)); - } - UNREACHABLE(); - } - - static size_t NextOffset(size_t* offset) { - size_t i = *offset; - (*offset)++; - return i; - } - - static ScaleFactor ScaleFor(AddressingMode one, AddressingMode mode) { - STATIC_ASSERT(0 == static_cast(times_1)); - STATIC_ASSERT(1 == static_cast(times_2)); - STATIC_ASSERT(2 == static_cast(times_4)); - STATIC_ASSERT(3 == static_cast(times_8)); - int scale = static_cast(mode - one); - DCHECK(scale >= 0 && scale < 4); - return static_cast(scale); - } - - Operand MemoryOperand(size_t* offset) { - AddressingMode mode = AddressingModeField::decode(instr_->opcode()); - switch (mode) { - case kMode_MR: { - Register base = InputRegister(NextOffset(offset)); - int32_t disp = 0; - return Operand(base, disp); - } - case kMode_MRI: { - Register base = InputRegister(NextOffset(offset)); - Constant ctant = ToConstant(instr_->InputAt(NextOffset(offset))); - return Operand(base, ctant.ToInt32(), ctant.rmode()); - } - case kMode_MR1: - case kMode_MR2: - case kMode_MR4: - case kMode_MR8: { - Register base = InputRegister(NextOffset(offset)); - Register index = InputRegister(NextOffset(offset)); - ScaleFactor scale = ScaleFor(kMode_MR1, mode); - int32_t disp = 0; - return Operand(base, index, scale, disp); - } - case kMode_MR1I: - case kMode_MR2I: - case kMode_MR4I: - case kMode_MR8I: { - Register base = InputRegister(NextOffset(offset)); - Register index = InputRegister(NextOffset(offset)); - ScaleFactor scale = ScaleFor(kMode_MR1I, mode); - Constant ctant = ToConstant(instr_->InputAt(NextOffset(offset))); - return Operand(base, index, scale, ctant.ToInt32(), ctant.rmode()); - } - case kMode_M1: - case kMode_M2: - case kMode_M4: - case kMode_M8: { - Register index = InputRegister(NextOffset(offset)); - ScaleFactor scale = ScaleFor(kMode_M1, mode); - int32_t disp = 0; - return Operand(index, scale, disp); - } - case kMode_M1I: - case kMode_M2I: - case kMode_M4I: - case kMode_M8I: { - Register index = InputRegister(NextOffset(offset)); - ScaleFactor scale = ScaleFor(kMode_M1I, mode); - Constant ctant = ToConstant(instr_->InputAt(NextOffset(offset))); - return Operand(index, scale, ctant.ToInt32(), ctant.rmode()); - } - case kMode_MI: { - Constant ctant = ToConstant(instr_->InputAt(NextOffset(offset))); - return Operand(ctant.ToInt32(), ctant.rmode()); - } - case kMode_None: - UNREACHABLE(); - } - UNREACHABLE(); - } - - Operand MemoryOperand(size_t first_input = 0) { - return MemoryOperand(&first_input); - } -}; - - -namespace { - -bool HasImmediateInput(Instruction* instr, size_t index) { - return instr->InputAt(index)->IsImmediate(); -} - - -class OutOfLineLoadInteger final : public OutOfLineCode { - public: - OutOfLineLoadInteger(CodeGenerator* gen, Register result) - : OutOfLineCode(gen), result_(result) {} - - void Generate() final { __ xor_(result_, result_); } - - private: - Register const result_; -}; - -class OutOfLineLoadFloat32NaN final : public OutOfLineCode { - public: - OutOfLineLoadFloat32NaN(CodeGenerator* gen, X87Register result) - : OutOfLineCode(gen), result_(result) {} - - void Generate() final { - DCHECK(result_.code() == 0); - USE(result_); - __ fstp(0); - __ push(Immediate(0xffc00000)); - __ fld_s(MemOperand(esp, 0)); - __ lea(esp, Operand(esp, kFloatSize)); - } - - private: - X87Register const result_; -}; - -class OutOfLineLoadFloat64NaN final : public OutOfLineCode { - public: - OutOfLineLoadFloat64NaN(CodeGenerator* gen, X87Register result) - : OutOfLineCode(gen), result_(result) {} - - void Generate() final { - DCHECK(result_.code() == 0); - USE(result_); - __ fstp(0); - __ push(Immediate(0xfff80000)); - __ push(Immediate(0x00000000)); - __ fld_d(MemOperand(esp, 0)); - __ lea(esp, Operand(esp, kDoubleSize)); - } - - private: - X87Register const result_; -}; - -class OutOfLineTruncateDoubleToI final : public OutOfLineCode { - public: - OutOfLineTruncateDoubleToI(CodeGenerator* gen, Register result, - X87Register input) - : OutOfLineCode(gen), result_(result), input_(input) {} - - void Generate() final { - UNIMPLEMENTED(); - USE(result_); - USE(input_); - } - - private: - Register const result_; - X87Register const input_; -}; - - -class OutOfLineRecordWrite final : public OutOfLineCode { - public: - OutOfLineRecordWrite(CodeGenerator* gen, Register object, Operand operand, - Register value, Register scratch0, Register scratch1, - RecordWriteMode mode) - : OutOfLineCode(gen), - object_(object), - operand_(operand), - value_(value), - scratch0_(scratch0), - scratch1_(scratch1), - mode_(mode) {} - - void Generate() final { - if (mode_ > RecordWriteMode::kValueIsPointer) { - __ JumpIfSmi(value_, exit()); - } - __ CheckPageFlag(value_, scratch0_, - MemoryChunk::kPointersToHereAreInterestingMask, zero, - exit()); - RememberedSetAction const remembered_set_action = - mode_ > RecordWriteMode::kValueIsMap ? EMIT_REMEMBERED_SET - : OMIT_REMEMBERED_SET; - SaveFPRegsMode const save_fp_mode = - frame()->DidAllocateDoubleRegisters() ? kSaveFPRegs : kDontSaveFPRegs; - RecordWriteStub stub(isolate(), object_, scratch0_, scratch1_, - remembered_set_action, save_fp_mode); - __ lea(scratch1_, operand_); - __ CallStub(&stub); - } - - private: - Register const object_; - Operand const operand_; - Register const value_; - Register const scratch0_; - Register const scratch1_; - RecordWriteMode const mode_; -}; - -} // namespace - -#define ASSEMBLE_CHECKED_LOAD_FLOAT(asm_instr, OutOfLineLoadNaN) \ - do { \ - auto result = i.OutputDoubleRegister(); \ - auto offset = i.InputRegister(0); \ - DCHECK(result.code() == 0); \ - if (instr->InputAt(1)->IsRegister()) { \ - __ cmp(offset, i.InputRegister(1)); \ - } else { \ - __ cmp(offset, i.InputImmediate(1)); \ - } \ - OutOfLineCode* ool = new (zone()) OutOfLineLoadNaN(this, result); \ - __ j(above_equal, ool->entry()); \ - __ fstp(0); \ - __ asm_instr(i.MemoryOperand(2)); \ - __ bind(ool->exit()); \ - } while (false) - -#define ASSEMBLE_CHECKED_LOAD_INTEGER(asm_instr) \ - do { \ - auto result = i.OutputRegister(); \ - auto offset = i.InputRegister(0); \ - if (instr->InputAt(1)->IsRegister()) { \ - __ cmp(offset, i.InputRegister(1)); \ - } else { \ - __ cmp(offset, i.InputImmediate(1)); \ - } \ - OutOfLineCode* ool = new (zone()) OutOfLineLoadInteger(this, result); \ - __ j(above_equal, ool->entry()); \ - __ asm_instr(result, i.MemoryOperand(2)); \ - __ bind(ool->exit()); \ - } while (false) - - -#define ASSEMBLE_CHECKED_STORE_FLOAT(asm_instr) \ - do { \ - auto offset = i.InputRegister(0); \ - if (instr->InputAt(1)->IsRegister()) { \ - __ cmp(offset, i.InputRegister(1)); \ - } else { \ - __ cmp(offset, i.InputImmediate(1)); \ - } \ - Label done; \ - DCHECK(i.InputDoubleRegister(2).code() == 0); \ - __ j(above_equal, &done, Label::kNear); \ - __ asm_instr(i.MemoryOperand(3)); \ - __ bind(&done); \ - } while (false) - - -#define ASSEMBLE_CHECKED_STORE_INTEGER(asm_instr) \ - do { \ - auto offset = i.InputRegister(0); \ - if (instr->InputAt(1)->IsRegister()) { \ - __ cmp(offset, i.InputRegister(1)); \ - } else { \ - __ cmp(offset, i.InputImmediate(1)); \ - } \ - Label done; \ - __ j(above_equal, &done, Label::kNear); \ - if (instr->InputAt(2)->IsRegister()) { \ - __ asm_instr(i.MemoryOperand(3), i.InputRegister(2)); \ - } else { \ - __ asm_instr(i.MemoryOperand(3), i.InputImmediate(2)); \ - } \ - __ bind(&done); \ - } while (false) - -#define ASSEMBLE_COMPARE(asm_instr) \ - do { \ - if (AddressingModeField::decode(instr->opcode()) != kMode_None) { \ - size_t index = 0; \ - Operand left = i.MemoryOperand(&index); \ - if (HasImmediateInput(instr, index)) { \ - __ asm_instr(left, i.InputImmediate(index)); \ - } else { \ - __ asm_instr(left, i.InputRegister(index)); \ - } \ - } else { \ - if (HasImmediateInput(instr, 1)) { \ - if (instr->InputAt(0)->IsRegister()) { \ - __ asm_instr(i.InputRegister(0), i.InputImmediate(1)); \ - } else { \ - __ asm_instr(i.InputOperand(0), i.InputImmediate(1)); \ - } \ - } else { \ - if (instr->InputAt(1)->IsRegister()) { \ - __ asm_instr(i.InputRegister(0), i.InputRegister(1)); \ - } else { \ - __ asm_instr(i.InputRegister(0), i.InputOperand(1)); \ - } \ - } \ - } \ - } while (0) - -#define ASSEMBLE_IEEE754_BINOP(name) \ - do { \ - /* Saves the esp into ebx */ \ - __ push(ebx); \ - __ mov(ebx, esp); \ - /* Pass one double as argument on the stack. */ \ - __ PrepareCallCFunction(4, eax); \ - __ fstp(0); \ - /* Load first operand from original stack */ \ - __ fld_d(MemOperand(ebx, 4 + kDoubleSize)); \ - /* Put first operand into stack for function call */ \ - __ fstp_d(Operand(esp, 0 * kDoubleSize)); \ - /* Load second operand from original stack */ \ - __ fld_d(MemOperand(ebx, 4)); \ - /* Put second operand into stack for function call */ \ - __ fstp_d(Operand(esp, 1 * kDoubleSize)); \ - __ CallCFunction(ExternalReference::ieee754_##name##_function(isolate()), \ - 4); \ - /* Restore the ebx */ \ - __ pop(ebx); \ - /* Return value is in st(0) on x87. */ \ - __ lea(esp, Operand(esp, 2 * kDoubleSize)); \ - } while (false) - -#define ASSEMBLE_IEEE754_UNOP(name) \ - do { \ - /* Saves the esp into ebx */ \ - __ push(ebx); \ - __ mov(ebx, esp); \ - /* Pass one double as argument on the stack. */ \ - __ PrepareCallCFunction(2, eax); \ - __ fstp(0); \ - /* Load operand from original stack */ \ - __ fld_d(MemOperand(ebx, 4)); \ - /* Put operand into stack for function call */ \ - __ fstp_d(Operand(esp, 0)); \ - __ CallCFunction(ExternalReference::ieee754_##name##_function(isolate()), \ - 2); \ - /* Restore the ebx */ \ - __ pop(ebx); \ - /* Return value is in st(0) on x87. */ \ - __ lea(esp, Operand(esp, kDoubleSize)); \ - } while (false) - -void CodeGenerator::AssembleDeconstructFrame() { - __ mov(esp, ebp); - __ pop(ebp); -} - -void CodeGenerator::AssemblePrepareTailCall() { - if (frame_access_state()->has_frame()) { - __ mov(ebp, MemOperand(ebp, 0)); - } - frame_access_state()->SetFrameAccessToSP(); -} - -void CodeGenerator::AssemblePopArgumentsAdaptorFrame(Register args_reg, - Register, Register, - Register) { - // There are not enough temp registers left on ia32 for a call instruction - // so we pick some scratch registers and save/restore them manually here. - int scratch_count = 3; - Register scratch1 = ebx; - Register scratch2 = ecx; - Register scratch3 = edx; - DCHECK(!AreAliased(args_reg, scratch1, scratch2, scratch3)); - Label done; - - // Check if current frame is an arguments adaptor frame. - __ cmp(Operand(ebp, StandardFrameConstants::kContextOffset), - Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); - __ j(not_equal, &done, Label::kNear); - - __ push(scratch1); - __ push(scratch2); - __ push(scratch3); - - // Load arguments count from current arguments adaptor frame (note, it - // does not include receiver). - Register caller_args_count_reg = scratch1; - __ mov(caller_args_count_reg, - Operand(ebp, ArgumentsAdaptorFrameConstants::kLengthOffset)); - __ SmiUntag(caller_args_count_reg); - - ParameterCount callee_args_count(args_reg); - __ PrepareForTailCall(callee_args_count, caller_args_count_reg, scratch2, - scratch3, ReturnAddressState::kOnStack, scratch_count); - __ pop(scratch3); - __ pop(scratch2); - __ pop(scratch1); - - __ bind(&done); -} - -namespace { - -void AdjustStackPointerForTailCall(MacroAssembler* masm, - FrameAccessState* state, - int new_slot_above_sp, - bool allow_shrinkage = true) { - int current_sp_offset = state->GetSPToFPSlotCount() + - StandardFrameConstants::kFixedSlotCountAboveFp; - int stack_slot_delta = new_slot_above_sp - current_sp_offset; - if (stack_slot_delta > 0) { - masm->sub(esp, Immediate(stack_slot_delta * kPointerSize)); - state->IncreaseSPDelta(stack_slot_delta); - } else if (allow_shrinkage && stack_slot_delta < 0) { - masm->add(esp, Immediate(-stack_slot_delta * kPointerSize)); - state->IncreaseSPDelta(stack_slot_delta); - } -} - -} // namespace - -void CodeGenerator::AssembleTailCallBeforeGap(Instruction* instr, - int first_unused_stack_slot) { - CodeGenerator::PushTypeFlags flags(kImmediatePush | kScalarPush); - ZoneVector pushes(zone()); - GetPushCompatibleMoves(instr, flags, &pushes); - - if (!pushes.empty() && - (LocationOperand::cast(pushes.back()->destination()).index() + 1 == - first_unused_stack_slot)) { - X87OperandConverter g(this, instr); - for (auto move : pushes) { - LocationOperand destination_location( - LocationOperand::cast(move->destination())); - InstructionOperand source(move->source()); - AdjustStackPointerForTailCall(masm(), frame_access_state(), - destination_location.index()); - if (source.IsStackSlot()) { - LocationOperand source_location(LocationOperand::cast(source)); - __ push(g.SlotToOperand(source_location.index())); - } else if (source.IsRegister()) { - LocationOperand source_location(LocationOperand::cast(source)); - __ push(source_location.GetRegister()); - } else if (source.IsImmediate()) { - __ push(Immediate(ImmediateOperand::cast(source).inline_value())); - } else { - // Pushes of non-scalar data types is not supported. - UNIMPLEMENTED(); - } - frame_access_state()->IncreaseSPDelta(1); - move->Eliminate(); - } - } - AdjustStackPointerForTailCall(masm(), frame_access_state(), - first_unused_stack_slot, false); -} - -void CodeGenerator::AssembleTailCallAfterGap(Instruction* instr, - int first_unused_stack_slot) { - AdjustStackPointerForTailCall(masm(), frame_access_state(), - first_unused_stack_slot); -} - -// Assembles an instruction after register allocation, producing machine code. -CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction( - Instruction* instr) { - X87OperandConverter i(this, instr); - InstructionCode opcode = instr->opcode(); - ArchOpcode arch_opcode = ArchOpcodeField::decode(opcode); - - switch (arch_opcode) { - case kArchCallCodeObject: { - if (FLAG_debug_code && FLAG_enable_slow_asserts) { - __ VerifyX87StackDepth(1); - } - __ fstp(0); - EnsureSpaceForLazyDeopt(); - if (HasImmediateInput(instr, 0)) { - Handle code = Handle::cast(i.InputHeapObject(0)); - __ call(code, RelocInfo::CODE_TARGET); - } else { - Register reg = i.InputRegister(0); - __ add(reg, Immediate(Code::kHeaderSize - kHeapObjectTag)); - __ call(reg); - } - RecordCallPosition(instr); - bool double_result = - instr->HasOutput() && instr->Output()->IsFPRegister(); - if (double_result) { - __ lea(esp, Operand(esp, -kDoubleSize)); - __ fstp_d(Operand(esp, 0)); - } - __ fninit(); - if (double_result) { - __ fld_d(Operand(esp, 0)); - __ lea(esp, Operand(esp, kDoubleSize)); - } else { - __ fld1(); - } - frame_access_state()->ClearSPDelta(); - break; - } - case kArchTailCallCodeObjectFromJSFunction: - case kArchTailCallCodeObject: { - if (FLAG_debug_code && FLAG_enable_slow_asserts) { - __ VerifyX87StackDepth(1); - } - __ fstp(0); - if (arch_opcode == kArchTailCallCodeObjectFromJSFunction) { - AssemblePopArgumentsAdaptorFrame(kJavaScriptCallArgCountRegister, - no_reg, no_reg, no_reg); - } - if (HasImmediateInput(instr, 0)) { - Handle code = Handle::cast(i.InputHeapObject(0)); - __ jmp(code, RelocInfo::CODE_TARGET); - } else { - Register reg = i.InputRegister(0); - __ add(reg, Immediate(Code::kHeaderSize - kHeapObjectTag)); - __ jmp(reg); - } - frame_access_state()->ClearSPDelta(); - frame_access_state()->SetFrameAccessToDefault(); - break; - } - case kArchTailCallAddress: { - CHECK(!HasImmediateInput(instr, 0)); - Register reg = i.InputRegister(0); - __ jmp(reg); - frame_access_state()->ClearSPDelta(); - frame_access_state()->SetFrameAccessToDefault(); - break; - } - case kArchCallJSFunction: { - EnsureSpaceForLazyDeopt(); - Register func = i.InputRegister(0); - if (FLAG_debug_code) { - // Check the function's context matches the context argument. - __ cmp(esi, FieldOperand(func, JSFunction::kContextOffset)); - __ Assert(equal, kWrongFunctionContext); - } - if (FLAG_debug_code && FLAG_enable_slow_asserts) { - __ VerifyX87StackDepth(1); - } - __ fstp(0); - __ call(FieldOperand(func, JSFunction::kCodeEntryOffset)); - RecordCallPosition(instr); - bool double_result = - instr->HasOutput() && instr->Output()->IsFPRegister(); - if (double_result) { - __ lea(esp, Operand(esp, -kDoubleSize)); - __ fstp_d(Operand(esp, 0)); - } - __ fninit(); - if (double_result) { - __ fld_d(Operand(esp, 0)); - __ lea(esp, Operand(esp, kDoubleSize)); - } else { - __ fld1(); - } - frame_access_state()->ClearSPDelta(); - break; - } - case kArchTailCallJSFunctionFromJSFunction: { - Register func = i.InputRegister(0); - if (FLAG_debug_code) { - // Check the function's context matches the context argument. - __ cmp(esi, FieldOperand(func, JSFunction::kContextOffset)); - __ Assert(equal, kWrongFunctionContext); - } - if (FLAG_debug_code && FLAG_enable_slow_asserts) { - __ VerifyX87StackDepth(1); - } - __ fstp(0); - AssemblePopArgumentsAdaptorFrame(kJavaScriptCallArgCountRegister, no_reg, - no_reg, no_reg); - __ jmp(FieldOperand(func, JSFunction::kCodeEntryOffset)); - frame_access_state()->ClearSPDelta(); - frame_access_state()->SetFrameAccessToDefault(); - break; - } - case kArchPrepareCallCFunction: { - // Frame alignment requires using FP-relative frame addressing. - frame_access_state()->SetFrameAccessToFP(); - int const num_parameters = MiscField::decode(instr->opcode()); - __ PrepareCallCFunction(num_parameters, i.TempRegister(0)); - break; - } - case kArchPrepareTailCall: - AssemblePrepareTailCall(); - break; - case kArchCallCFunction: { - if (FLAG_debug_code && FLAG_enable_slow_asserts) { - __ VerifyX87StackDepth(1); - } - __ fstp(0); - int const num_parameters = MiscField::decode(instr->opcode()); - if (HasImmediateInput(instr, 0)) { - ExternalReference ref = i.InputExternalReference(0); - __ CallCFunction(ref, num_parameters); - } else { - Register func = i.InputRegister(0); - __ CallCFunction(func, num_parameters); - } - bool double_result = - instr->HasOutput() && instr->Output()->IsFPRegister(); - if (double_result) { - __ lea(esp, Operand(esp, -kDoubleSize)); - __ fstp_d(Operand(esp, 0)); - } - __ fninit(); - if (double_result) { - __ fld_d(Operand(esp, 0)); - __ lea(esp, Operand(esp, kDoubleSize)); - } else { - __ fld1(); - } - frame_access_state()->SetFrameAccessToDefault(); - frame_access_state()->ClearSPDelta(); - break; - } - case kArchJmp: - AssembleArchJump(i.InputRpo(0)); - break; - case kArchLookupSwitch: - AssembleArchLookupSwitch(instr); - break; - case kArchTableSwitch: - AssembleArchTableSwitch(instr); - break; - case kArchComment: { - Address comment_string = i.InputExternalReference(0).address(); - __ RecordComment(reinterpret_cast(comment_string)); - break; - } - case kArchDebugBreak: - __ int3(); - break; - case kArchNop: - case kArchThrowTerminator: - // don't emit code for nops. - break; - case kArchDeoptimize: { - int deopt_state_id = - BuildTranslation(instr, -1, 0, OutputFrameStateCombine::Ignore()); - int double_register_param_count = 0; - int x87_layout = 0; - for (size_t i = 0; i < instr->InputCount(); i++) { - if (instr->InputAt(i)->IsFPRegister()) { - double_register_param_count++; - } - } - // Currently we use only one X87 register. If double_register_param_count - // is bigger than 1, it means duplicated double register is added to input - // of this instruction. - if (double_register_param_count > 0) { - x87_layout = (0 << 3) | 1; - } - // The layout of x87 register stack is loaded on the top of FPU register - // stack for deoptimization. - __ push(Immediate(x87_layout)); - __ fild_s(MemOperand(esp, 0)); - __ lea(esp, Operand(esp, kPointerSize)); - - CodeGenResult result = - AssembleDeoptimizerCall(deopt_state_id, current_source_position_); - if (result != kSuccess) return result; - break; - } - case kArchRet: - AssembleReturn(instr->InputAt(0)); - break; - case kArchFramePointer: - __ mov(i.OutputRegister(), ebp); - break; - case kArchStackPointer: - __ mov(i.OutputRegister(), esp); - break; - case kArchParentFramePointer: - if (frame_access_state()->has_frame()) { - __ mov(i.OutputRegister(), Operand(ebp, 0)); - } else { - __ mov(i.OutputRegister(), ebp); - } - break; - case kArchTruncateDoubleToI: { - if (!instr->InputAt(0)->IsFPRegister()) { - __ fld_d(i.InputOperand(0)); - } - __ TruncateX87TOSToI(i.OutputRegister()); - if (!instr->InputAt(0)->IsFPRegister()) { - __ fstp(0); - } - break; - } - case kArchStoreWithWriteBarrier: { - RecordWriteMode mode = - static_cast(MiscField::decode(instr->opcode())); - Register object = i.InputRegister(0); - size_t index = 0; - Operand operand = i.MemoryOperand(&index); - Register value = i.InputRegister(index); - Register scratch0 = i.TempRegister(0); - Register scratch1 = i.TempRegister(1); - auto ool = new (zone()) OutOfLineRecordWrite(this, object, operand, value, - scratch0, scratch1, mode); - __ mov(operand, value); - __ CheckPageFlag(object, scratch0, - MemoryChunk::kPointersFromHereAreInterestingMask, - not_zero, ool->entry()); - __ bind(ool->exit()); - break; - } - case kArchStackSlot: { - FrameOffset offset = - frame_access_state()->GetFrameOffset(i.InputInt32(0)); - Register base; - if (offset.from_stack_pointer()) { - base = esp; - } else { - base = ebp; - } - __ lea(i.OutputRegister(), Operand(base, offset.offset())); - break; - } - case kIeee754Float64Acos: - ASSEMBLE_IEEE754_UNOP(acos); - break; - case kIeee754Float64Acosh: - ASSEMBLE_IEEE754_UNOP(acosh); - break; - case kIeee754Float64Asin: - ASSEMBLE_IEEE754_UNOP(asin); - break; - case kIeee754Float64Asinh: - ASSEMBLE_IEEE754_UNOP(asinh); - break; - case kIeee754Float64Atan: - ASSEMBLE_IEEE754_UNOP(atan); - break; - case kIeee754Float64Atanh: - ASSEMBLE_IEEE754_UNOP(atanh); - break; - case kIeee754Float64Atan2: - ASSEMBLE_IEEE754_BINOP(atan2); - break; - case kIeee754Float64Cbrt: - ASSEMBLE_IEEE754_UNOP(cbrt); - break; - case kIeee754Float64Cos: - __ X87SetFPUCW(0x027F); - ASSEMBLE_IEEE754_UNOP(cos); - __ X87SetFPUCW(0x037F); - break; - case kIeee754Float64Cosh: - ASSEMBLE_IEEE754_UNOP(cosh); - break; - case kIeee754Float64Expm1: - __ X87SetFPUCW(0x027F); - ASSEMBLE_IEEE754_UNOP(expm1); - __ X87SetFPUCW(0x037F); - break; - case kIeee754Float64Exp: - ASSEMBLE_IEEE754_UNOP(exp); - break; - case kIeee754Float64Log: - ASSEMBLE_IEEE754_UNOP(log); - break; - case kIeee754Float64Log1p: - ASSEMBLE_IEEE754_UNOP(log1p); - break; - case kIeee754Float64Log2: - ASSEMBLE_IEEE754_UNOP(log2); - break; - case kIeee754Float64Log10: - ASSEMBLE_IEEE754_UNOP(log10); - break; - case kIeee754Float64Pow: { - // Keep the x87 FPU stack empty before calling stub code - __ fstp(0); - // Call the MathStub and put return value in stX_0 - MathPowStub stub(isolate(), MathPowStub::DOUBLE); - __ CallStub(&stub); - /* Return value is in st(0) on x87. */ - __ lea(esp, Operand(esp, 2 * kDoubleSize)); - break; - } - case kIeee754Float64Sin: - __ X87SetFPUCW(0x027F); - ASSEMBLE_IEEE754_UNOP(sin); - __ X87SetFPUCW(0x037F); - break; - case kIeee754Float64Sinh: - ASSEMBLE_IEEE754_UNOP(sinh); - break; - case kIeee754Float64Tan: - __ X87SetFPUCW(0x027F); - ASSEMBLE_IEEE754_UNOP(tan); - __ X87SetFPUCW(0x037F); - break; - case kIeee754Float64Tanh: - ASSEMBLE_IEEE754_UNOP(tanh); - break; - case kX87Add: - if (HasImmediateInput(instr, 1)) { - __ add(i.InputOperand(0), i.InputImmediate(1)); - } else { - __ add(i.InputRegister(0), i.InputOperand(1)); - } - break; - case kX87And: - if (HasImmediateInput(instr, 1)) { - __ and_(i.InputOperand(0), i.InputImmediate(1)); - } else { - __ and_(i.InputRegister(0), i.InputOperand(1)); - } - break; - case kX87Cmp: - ASSEMBLE_COMPARE(cmp); - break; - case kX87Cmp16: - ASSEMBLE_COMPARE(cmpw); - break; - case kX87Cmp8: - ASSEMBLE_COMPARE(cmpb); - break; - case kX87Test: - ASSEMBLE_COMPARE(test); - break; - case kX87Test16: - ASSEMBLE_COMPARE(test_w); - break; - case kX87Test8: - ASSEMBLE_COMPARE(test_b); - break; - case kX87Imul: - if (HasImmediateInput(instr, 1)) { - __ imul(i.OutputRegister(), i.InputOperand(0), i.InputInt32(1)); - } else { - __ imul(i.OutputRegister(), i.InputOperand(1)); - } - break; - case kX87ImulHigh: - __ imul(i.InputRegister(1)); - break; - case kX87UmulHigh: - __ mul(i.InputRegister(1)); - break; - case kX87Idiv: - __ cdq(); - __ idiv(i.InputOperand(1)); - break; - case kX87Udiv: - __ Move(edx, Immediate(0)); - __ div(i.InputOperand(1)); - break; - case kX87Not: - __ not_(i.OutputOperand()); - break; - case kX87Neg: - __ neg(i.OutputOperand()); - break; - case kX87Or: - if (HasImmediateInput(instr, 1)) { - __ or_(i.InputOperand(0), i.InputImmediate(1)); - } else { - __ or_(i.InputRegister(0), i.InputOperand(1)); - } - break; - case kX87Xor: - if (HasImmediateInput(instr, 1)) { - __ xor_(i.InputOperand(0), i.InputImmediate(1)); - } else { - __ xor_(i.InputRegister(0), i.InputOperand(1)); - } - break; - case kX87Sub: - if (HasImmediateInput(instr, 1)) { - __ sub(i.InputOperand(0), i.InputImmediate(1)); - } else { - __ sub(i.InputRegister(0), i.InputOperand(1)); - } - break; - case kX87Shl: - if (HasImmediateInput(instr, 1)) { - __ shl(i.OutputOperand(), i.InputInt5(1)); - } else { - __ shl_cl(i.OutputOperand()); - } - break; - case kX87Shr: - if (HasImmediateInput(instr, 1)) { - __ shr(i.OutputOperand(), i.InputInt5(1)); - } else { - __ shr_cl(i.OutputOperand()); - } - break; - case kX87Sar: - if (HasImmediateInput(instr, 1)) { - __ sar(i.OutputOperand(), i.InputInt5(1)); - } else { - __ sar_cl(i.OutputOperand()); - } - break; - case kX87AddPair: { - // i.OutputRegister(0) == i.InputRegister(0) ... left low word. - // i.InputRegister(1) ... left high word. - // i.InputRegister(2) ... right low word. - // i.InputRegister(3) ... right high word. - bool use_temp = false; - if (i.OutputRegister(0).code() == i.InputRegister(1).code() || - i.OutputRegister(0).code() == i.InputRegister(3).code()) { - // We cannot write to the output register directly, because it would - // overwrite an input for adc. We have to use the temp register. - use_temp = true; - __ Move(i.TempRegister(0), i.InputRegister(0)); - __ add(i.TempRegister(0), i.InputRegister(2)); - } else { - __ add(i.OutputRegister(0), i.InputRegister(2)); - } - if (i.OutputRegister(1).code() != i.InputRegister(1).code()) { - __ Move(i.OutputRegister(1), i.InputRegister(1)); - } - __ adc(i.OutputRegister(1), Operand(i.InputRegister(3))); - if (use_temp) { - __ Move(i.OutputRegister(0), i.TempRegister(0)); - } - break; - } - case kX87SubPair: { - // i.OutputRegister(0) == i.InputRegister(0) ... left low word. - // i.InputRegister(1) ... left high word. - // i.InputRegister(2) ... right low word. - // i.InputRegister(3) ... right high word. - bool use_temp = false; - if (i.OutputRegister(0).code() == i.InputRegister(1).code() || - i.OutputRegister(0).code() == i.InputRegister(3).code()) { - // We cannot write to the output register directly, because it would - // overwrite an input for adc. We have to use the temp register. - use_temp = true; - __ Move(i.TempRegister(0), i.InputRegister(0)); - __ sub(i.TempRegister(0), i.InputRegister(2)); - } else { - __ sub(i.OutputRegister(0), i.InputRegister(2)); - } - if (i.OutputRegister(1).code() != i.InputRegister(1).code()) { - __ Move(i.OutputRegister(1), i.InputRegister(1)); - } - __ sbb(i.OutputRegister(1), Operand(i.InputRegister(3))); - if (use_temp) { - __ Move(i.OutputRegister(0), i.TempRegister(0)); - } - break; - } - case kX87MulPair: { - __ imul(i.OutputRegister(1), i.InputOperand(0)); - __ mov(i.TempRegister(0), i.InputOperand(1)); - __ imul(i.TempRegister(0), i.InputOperand(2)); - __ add(i.OutputRegister(1), i.TempRegister(0)); - __ mov(i.OutputRegister(0), i.InputOperand(0)); - // Multiplies the low words and stores them in eax and edx. - __ mul(i.InputRegister(2)); - __ add(i.OutputRegister(1), i.TempRegister(0)); - - break; - } - case kX87ShlPair: - if (HasImmediateInput(instr, 2)) { - __ ShlPair(i.InputRegister(1), i.InputRegister(0), i.InputInt6(2)); - } else { - // Shift has been loaded into CL by the register allocator. - __ ShlPair_cl(i.InputRegister(1), i.InputRegister(0)); - } - break; - case kX87ShrPair: - if (HasImmediateInput(instr, 2)) { - __ ShrPair(i.InputRegister(1), i.InputRegister(0), i.InputInt6(2)); - } else { - // Shift has been loaded into CL by the register allocator. - __ ShrPair_cl(i.InputRegister(1), i.InputRegister(0)); - } - break; - case kX87SarPair: - if (HasImmediateInput(instr, 2)) { - __ SarPair(i.InputRegister(1), i.InputRegister(0), i.InputInt6(2)); - } else { - // Shift has been loaded into CL by the register allocator. - __ SarPair_cl(i.InputRegister(1), i.InputRegister(0)); - } - break; - case kX87Ror: - if (HasImmediateInput(instr, 1)) { - __ ror(i.OutputOperand(), i.InputInt5(1)); - } else { - __ ror_cl(i.OutputOperand()); - } - break; - case kX87Lzcnt: - __ Lzcnt(i.OutputRegister(), i.InputOperand(0)); - break; - case kX87Popcnt: - __ Popcnt(i.OutputRegister(), i.InputOperand(0)); - break; - case kX87LoadFloat64Constant: { - InstructionOperand* source = instr->InputAt(0); - InstructionOperand* destination = instr->Output(); - DCHECK(source->IsConstant()); - X87OperandConverter g(this, nullptr); - Constant src_constant = g.ToConstant(source); - - DCHECK_EQ(Constant::kFloat64, src_constant.type()); - uint64_t src = src_constant.ToFloat64().AsUint64(); - uint32_t lower = static_cast(src); - uint32_t upper = static_cast(src >> 32); - if (destination->IsFPRegister()) { - __ sub(esp, Immediate(kDoubleSize)); - __ mov(MemOperand(esp, 0), Immediate(lower)); - __ mov(MemOperand(esp, kInt32Size), Immediate(upper)); - __ fstp(0); - __ fld_d(MemOperand(esp, 0)); - __ add(esp, Immediate(kDoubleSize)); - } else { - UNREACHABLE(); - } - break; - } - case kX87Float32Cmp: { - __ fld_s(MemOperand(esp, kFloatSize)); - __ fld_s(MemOperand(esp, 0)); - __ FCmp(); - __ lea(esp, Operand(esp, 2 * kFloatSize)); - break; - } - case kX87Float32Add: { - if (FLAG_debug_code && FLAG_enable_slow_asserts) { - __ VerifyX87StackDepth(1); - } - __ X87SetFPUCW(0x027F); - __ fstp(0); - __ fld_s(MemOperand(esp, 0)); - __ fld_s(MemOperand(esp, kFloatSize)); - __ faddp(); - // Clear stack. - __ lea(esp, Operand(esp, 2 * kFloatSize)); - // Restore the default value of control word. - __ X87SetFPUCW(0x037F); - break; - } - case kX87Float32Sub: { - if (FLAG_debug_code && FLAG_enable_slow_asserts) { - __ VerifyX87StackDepth(1); - } - __ X87SetFPUCW(0x027F); - __ fstp(0); - __ fld_s(MemOperand(esp, kFloatSize)); - __ fld_s(MemOperand(esp, 0)); - __ fsubp(); - // Clear stack. - __ lea(esp, Operand(esp, 2 * kFloatSize)); - // Restore the default value of control word. - __ X87SetFPUCW(0x037F); - break; - } - case kX87Float32Mul: { - if (FLAG_debug_code && FLAG_enable_slow_asserts) { - __ VerifyX87StackDepth(1); - } - __ X87SetFPUCW(0x027F); - __ fstp(0); - __ fld_s(MemOperand(esp, kFloatSize)); - __ fld_s(MemOperand(esp, 0)); - __ fmulp(); - // Clear stack. - __ lea(esp, Operand(esp, 2 * kFloatSize)); - // Restore the default value of control word. - __ X87SetFPUCW(0x037F); - break; - } - case kX87Float32Div: { - if (FLAG_debug_code && FLAG_enable_slow_asserts) { - __ VerifyX87StackDepth(1); - } - __ X87SetFPUCW(0x027F); - __ fstp(0); - __ fld_s(MemOperand(esp, kFloatSize)); - __ fld_s(MemOperand(esp, 0)); - __ fdivp(); - // Clear stack. - __ lea(esp, Operand(esp, 2 * kFloatSize)); - // Restore the default value of control word. - __ X87SetFPUCW(0x037F); - break; - } - - case kX87Float32Sqrt: { - if (FLAG_debug_code && FLAG_enable_slow_asserts) { - __ VerifyX87StackDepth(1); - } - __ fstp(0); - __ fld_s(MemOperand(esp, 0)); - __ fsqrt(); - __ lea(esp, Operand(esp, kFloatSize)); - break; - } - case kX87Float32Abs: { - if (FLAG_debug_code && FLAG_enable_slow_asserts) { - __ VerifyX87StackDepth(1); - } - __ fstp(0); - __ fld_s(MemOperand(esp, 0)); - __ fabs(); - __ lea(esp, Operand(esp, kFloatSize)); - break; - } - case kX87Float32Neg: { - if (FLAG_debug_code && FLAG_enable_slow_asserts) { - __ VerifyX87StackDepth(1); - } - __ fstp(0); - __ fld_s(MemOperand(esp, 0)); - __ fchs(); - __ lea(esp, Operand(esp, kFloatSize)); - break; - } - case kX87Float32Round: { - RoundingMode mode = - static_cast(MiscField::decode(instr->opcode())); - // Set the correct round mode in x87 control register - __ X87SetRC((mode << 10)); - - if (!instr->InputAt(0)->IsFPRegister()) { - InstructionOperand* input = instr->InputAt(0); - USE(input); - DCHECK(input->IsFPStackSlot()); - if (FLAG_debug_code && FLAG_enable_slow_asserts) { - __ VerifyX87StackDepth(1); - } - __ fstp(0); - __ fld_s(i.InputOperand(0)); - } - __ frndint(); - __ X87SetRC(0x0000); - break; - } - case kX87Float64Add: { - if (FLAG_debug_code && FLAG_enable_slow_asserts) { - __ VerifyX87StackDepth(1); - } - __ X87SetFPUCW(0x027F); - __ fstp(0); - __ fld_d(MemOperand(esp, 0)); - __ fld_d(MemOperand(esp, kDoubleSize)); - __ faddp(); - // Clear stack. - __ lea(esp, Operand(esp, 2 * kDoubleSize)); - // Restore the default value of control word. - __ X87SetFPUCW(0x037F); - break; - } - case kX87Float64Sub: { - if (FLAG_debug_code && FLAG_enable_slow_asserts) { - __ VerifyX87StackDepth(1); - } - __ X87SetFPUCW(0x027F); - __ fstp(0); - __ fld_d(MemOperand(esp, kDoubleSize)); - __ fsub_d(MemOperand(esp, 0)); - // Clear stack. - __ lea(esp, Operand(esp, 2 * kDoubleSize)); - // Restore the default value of control word. - __ X87SetFPUCW(0x037F); - break; - } - case kX87Float64Mul: { - if (FLAG_debug_code && FLAG_enable_slow_asserts) { - __ VerifyX87StackDepth(1); - } - __ X87SetFPUCW(0x027F); - __ fstp(0); - __ fld_d(MemOperand(esp, kDoubleSize)); - __ fmul_d(MemOperand(esp, 0)); - // Clear stack. - __ lea(esp, Operand(esp, 2 * kDoubleSize)); - // Restore the default value of control word. - __ X87SetFPUCW(0x037F); - break; - } - case kX87Float64Div: { - if (FLAG_debug_code && FLAG_enable_slow_asserts) { - __ VerifyX87StackDepth(1); - } - __ X87SetFPUCW(0x027F); - __ fstp(0); - __ fld_d(MemOperand(esp, kDoubleSize)); - __ fdiv_d(MemOperand(esp, 0)); - // Clear stack. - __ lea(esp, Operand(esp, 2 * kDoubleSize)); - // Restore the default value of control word. - __ X87SetFPUCW(0x037F); - break; - } - case kX87Float64Mod: { - FrameScope frame_scope(&masm_, StackFrame::MANUAL); - if (FLAG_debug_code && FLAG_enable_slow_asserts) { - __ VerifyX87StackDepth(1); - } - __ mov(eax, esp); - __ PrepareCallCFunction(4, eax); - __ fstp(0); - __ fld_d(MemOperand(eax, 0)); - __ fstp_d(Operand(esp, 1 * kDoubleSize)); - __ fld_d(MemOperand(eax, kDoubleSize)); - __ fstp_d(Operand(esp, 0)); - __ CallCFunction(ExternalReference::mod_two_doubles_operation(isolate()), - 4); - __ lea(esp, Operand(esp, 2 * kDoubleSize)); - break; - } - case kX87Float32Max: { - Label compare_swap, done_compare; - if (FLAG_debug_code && FLAG_enable_slow_asserts) { - __ VerifyX87StackDepth(1); - } - __ fstp(0); - __ fld_s(MemOperand(esp, kFloatSize)); - __ fld_s(MemOperand(esp, 0)); - __ fld(1); - __ fld(1); - __ FCmp(); - - auto ool = - new (zone()) OutOfLineLoadFloat32NaN(this, i.OutputDoubleRegister()); - __ j(parity_even, ool->entry()); - __ j(below, &done_compare, Label::kNear); - __ j(above, &compare_swap, Label::kNear); - __ push(eax); - __ lea(esp, Operand(esp, -kFloatSize)); - __ fld(1); - __ fstp_s(Operand(esp, 0)); - __ mov(eax, MemOperand(esp, 0)); - __ and_(eax, Immediate(0x80000000)); - __ lea(esp, Operand(esp, kFloatSize)); - __ pop(eax); - __ j(zero, &done_compare, Label::kNear); - - __ bind(&compare_swap); - __ bind(ool->exit()); - __ fxch(1); - - __ bind(&done_compare); - __ fstp(0); - __ lea(esp, Operand(esp, 2 * kFloatSize)); - break; - } - case kX87Float64Max: { - Label compare_swap, done_compare; - if (FLAG_debug_code && FLAG_enable_slow_asserts) { - __ VerifyX87StackDepth(1); - } - __ fstp(0); - __ fld_d(MemOperand(esp, kDoubleSize)); - __ fld_d(MemOperand(esp, 0)); - __ fld(1); - __ fld(1); - __ FCmp(); - - auto ool = - new (zone()) OutOfLineLoadFloat64NaN(this, i.OutputDoubleRegister()); - __ j(parity_even, ool->entry()); - __ j(below, &done_compare, Label::kNear); - __ j(above, &compare_swap, Label::kNear); - __ push(eax); - __ lea(esp, Operand(esp, -kDoubleSize)); - __ fld(1); - __ fstp_d(Operand(esp, 0)); - __ mov(eax, MemOperand(esp, 4)); - __ and_(eax, Immediate(0x80000000)); - __ lea(esp, Operand(esp, kDoubleSize)); - __ pop(eax); - __ j(zero, &done_compare, Label::kNear); - - __ bind(&compare_swap); - __ bind(ool->exit()); - __ fxch(1); - - __ bind(&done_compare); - __ fstp(0); - __ lea(esp, Operand(esp, 2 * kDoubleSize)); - break; - } - case kX87Float32Min: { - Label compare_swap, done_compare; - if (FLAG_debug_code && FLAG_enable_slow_asserts) { - __ VerifyX87StackDepth(1); - } - __ fstp(0); - __ fld_s(MemOperand(esp, kFloatSize)); - __ fld_s(MemOperand(esp, 0)); - __ fld(1); - __ fld(1); - __ FCmp(); - - auto ool = - new (zone()) OutOfLineLoadFloat32NaN(this, i.OutputDoubleRegister()); - __ j(parity_even, ool->entry()); - __ j(above, &done_compare, Label::kNear); - __ j(below, &compare_swap, Label::kNear); - __ push(eax); - __ lea(esp, Operand(esp, -kFloatSize)); - __ fld(0); - __ fstp_s(Operand(esp, 0)); - __ mov(eax, MemOperand(esp, 0)); - __ and_(eax, Immediate(0x80000000)); - __ lea(esp, Operand(esp, kFloatSize)); - __ pop(eax); - __ j(zero, &done_compare, Label::kNear); - - __ bind(&compare_swap); - __ bind(ool->exit()); - __ fxch(1); - - __ bind(&done_compare); - __ fstp(0); - __ lea(esp, Operand(esp, 2 * kFloatSize)); - break; - } - case kX87Float64Min: { - Label compare_swap, done_compare; - if (FLAG_debug_code && FLAG_enable_slow_asserts) { - __ VerifyX87StackDepth(1); - } - __ fstp(0); - __ fld_d(MemOperand(esp, kDoubleSize)); - __ fld_d(MemOperand(esp, 0)); - __ fld(1); - __ fld(1); - __ FCmp(); - - auto ool = - new (zone()) OutOfLineLoadFloat64NaN(this, i.OutputDoubleRegister()); - __ j(parity_even, ool->entry()); - __ j(above, &done_compare, Label::kNear); - __ j(below, &compare_swap, Label::kNear); - __ push(eax); - __ lea(esp, Operand(esp, -kDoubleSize)); - __ fld(0); - __ fstp_d(Operand(esp, 0)); - __ mov(eax, MemOperand(esp, 4)); - __ and_(eax, Immediate(0x80000000)); - __ lea(esp, Operand(esp, kDoubleSize)); - __ pop(eax); - __ j(zero, &done_compare, Label::kNear); - - __ bind(&compare_swap); - __ bind(ool->exit()); - __ fxch(1); - - __ bind(&done_compare); - __ fstp(0); - __ lea(esp, Operand(esp, 2 * kDoubleSize)); - break; - } - case kX87Float64Abs: { - if (FLAG_debug_code && FLAG_enable_slow_asserts) { - __ VerifyX87StackDepth(1); - } - __ fstp(0); - __ fld_d(MemOperand(esp, 0)); - __ fabs(); - __ lea(esp, Operand(esp, kDoubleSize)); - break; - } - case kX87Float64Neg: { - if (FLAG_debug_code && FLAG_enable_slow_asserts) { - __ VerifyX87StackDepth(1); - } - __ fstp(0); - __ fld_d(MemOperand(esp, 0)); - __ fchs(); - __ lea(esp, Operand(esp, kDoubleSize)); - break; - } - case kX87Int32ToFloat32: { - InstructionOperand* input = instr->InputAt(0); - DCHECK(input->IsRegister() || input->IsStackSlot()); - if (FLAG_debug_code && FLAG_enable_slow_asserts) { - __ VerifyX87StackDepth(1); - } - __ fstp(0); - if (input->IsRegister()) { - Register input_reg = i.InputRegister(0); - __ push(input_reg); - __ fild_s(Operand(esp, 0)); - __ pop(input_reg); - } else { - __ fild_s(i.InputOperand(0)); - } - break; - } - case kX87Uint32ToFloat32: { - InstructionOperand* input = instr->InputAt(0); - DCHECK(input->IsRegister() || input->IsStackSlot()); - if (FLAG_debug_code && FLAG_enable_slow_asserts) { - __ VerifyX87StackDepth(1); - } - __ fstp(0); - Label msb_set_src; - Label jmp_return; - // Put input integer into eax(tmporarilly) - __ push(eax); - if (input->IsRegister()) - __ mov(eax, i.InputRegister(0)); - else - __ mov(eax, i.InputOperand(0)); - - __ test(eax, eax); - __ j(sign, &msb_set_src, Label::kNear); - __ push(eax); - __ fild_s(Operand(esp, 0)); - __ pop(eax); - - __ jmp(&jmp_return, Label::kNear); - __ bind(&msb_set_src); - // Need another temp reg - __ push(ebx); - __ mov(ebx, eax); - __ shr(eax, 1); - // Recover the least significant bit to avoid rounding errors. - __ and_(ebx, Immediate(1)); - __ or_(eax, ebx); - __ push(eax); - __ fild_s(Operand(esp, 0)); - __ pop(eax); - __ fld(0); - __ faddp(); - // Restore the ebx - __ pop(ebx); - __ bind(&jmp_return); - // Restore the eax - __ pop(eax); - break; - } - case kX87Int32ToFloat64: { - InstructionOperand* input = instr->InputAt(0); - DCHECK(input->IsRegister() || input->IsStackSlot()); - if (FLAG_debug_code && FLAG_enable_slow_asserts) { - __ VerifyX87StackDepth(1); - } - __ fstp(0); - if (input->IsRegister()) { - Register input_reg = i.InputRegister(0); - __ push(input_reg); - __ fild_s(Operand(esp, 0)); - __ pop(input_reg); - } else { - __ fild_s(i.InputOperand(0)); - } - break; - } - case kX87Float32ToFloat64: { - InstructionOperand* input = instr->InputAt(0); - if (input->IsFPRegister()) { - __ sub(esp, Immediate(kDoubleSize)); - __ fstp_s(MemOperand(esp, 0)); - __ fld_s(MemOperand(esp, 0)); - __ add(esp, Immediate(kDoubleSize)); - } else { - DCHECK(input->IsFPStackSlot()); - if (FLAG_debug_code && FLAG_enable_slow_asserts) { - __ VerifyX87StackDepth(1); - } - __ fstp(0); - __ fld_s(i.InputOperand(0)); - } - break; - } - case kX87Uint32ToFloat64: { - if (FLAG_debug_code && FLAG_enable_slow_asserts) { - __ VerifyX87StackDepth(1); - } - __ fstp(0); - __ LoadUint32NoSSE2(i.InputRegister(0)); - break; - } - case kX87Float32ToInt32: { - if (!instr->InputAt(0)->IsFPRegister()) { - __ fld_s(i.InputOperand(0)); - } - __ TruncateX87TOSToI(i.OutputRegister(0)); - if (!instr->InputAt(0)->IsFPRegister()) { - __ fstp(0); - } - break; - } - case kX87Float32ToUint32: { - if (!instr->InputAt(0)->IsFPRegister()) { - __ fld_s(i.InputOperand(0)); - } - Label success; - __ TruncateX87TOSToI(i.OutputRegister(0)); - __ test(i.OutputRegister(0), i.OutputRegister(0)); - __ j(positive, &success); - // Need to reserve the input float32 data. - __ fld(0); - __ push(Immediate(INT32_MIN)); - __ fild_s(Operand(esp, 0)); - __ lea(esp, Operand(esp, kPointerSize)); - __ faddp(); - __ TruncateX87TOSToI(i.OutputRegister(0)); - __ or_(i.OutputRegister(0), Immediate(0x80000000)); - // Only keep input float32 data in x87 stack when return. - __ fstp(0); - __ bind(&success); - if (!instr->InputAt(0)->IsFPRegister()) { - __ fstp(0); - } - break; - } - case kX87Float64ToInt32: { - if (!instr->InputAt(0)->IsFPRegister()) { - __ fld_d(i.InputOperand(0)); - } - __ TruncateX87TOSToI(i.OutputRegister(0)); - if (!instr->InputAt(0)->IsFPRegister()) { - __ fstp(0); - } - break; - } - case kX87Float64ToFloat32: { - InstructionOperand* input = instr->InputAt(0); - if (input->IsFPRegister()) { - __ sub(esp, Immediate(kDoubleSize)); - __ fstp_s(MemOperand(esp, 0)); - __ fld_s(MemOperand(esp, 0)); - __ add(esp, Immediate(kDoubleSize)); - } else { - DCHECK(input->IsFPStackSlot()); - if (FLAG_debug_code && FLAG_enable_slow_asserts) { - __ VerifyX87StackDepth(1); - } - __ fstp(0); - __ fld_d(i.InputOperand(0)); - __ sub(esp, Immediate(kDoubleSize)); - __ fstp_s(MemOperand(esp, 0)); - __ fld_s(MemOperand(esp, 0)); - __ add(esp, Immediate(kDoubleSize)); - } - break; - } - case kX87Float64ToUint32: { - __ push_imm32(-2147483648); - if (!instr->InputAt(0)->IsFPRegister()) { - __ fld_d(i.InputOperand(0)); - } - __ fild_s(Operand(esp, 0)); - __ fld(1); - __ faddp(); - __ TruncateX87TOSToI(i.OutputRegister(0)); - __ add(esp, Immediate(kInt32Size)); - __ add(i.OutputRegister(), Immediate(0x80000000)); - __ fstp(0); - if (!instr->InputAt(0)->IsFPRegister()) { - __ fstp(0); - } - break; - } - case kX87Float64ExtractHighWord32: { - if (instr->InputAt(0)->IsFPRegister()) { - __ sub(esp, Immediate(kDoubleSize)); - __ fst_d(MemOperand(esp, 0)); - __ mov(i.OutputRegister(), MemOperand(esp, kDoubleSize / 2)); - __ add(esp, Immediate(kDoubleSize)); - } else { - InstructionOperand* input = instr->InputAt(0); - USE(input); - DCHECK(input->IsFPStackSlot()); - __ mov(i.OutputRegister(), i.InputOperand(0, kDoubleSize / 2)); - } - break; - } - case kX87Float64ExtractLowWord32: { - if (instr->InputAt(0)->IsFPRegister()) { - __ sub(esp, Immediate(kDoubleSize)); - __ fst_d(MemOperand(esp, 0)); - __ mov(i.OutputRegister(), MemOperand(esp, 0)); - __ add(esp, Immediate(kDoubleSize)); - } else { - InstructionOperand* input = instr->InputAt(0); - USE(input); - DCHECK(input->IsFPStackSlot()); - __ mov(i.OutputRegister(), i.InputOperand(0)); - } - break; - } - case kX87Float64InsertHighWord32: { - __ sub(esp, Immediate(kDoubleSize)); - __ fstp_d(MemOperand(esp, 0)); - __ mov(MemOperand(esp, kDoubleSize / 2), i.InputRegister(1)); - __ fld_d(MemOperand(esp, 0)); - __ add(esp, Immediate(kDoubleSize)); - break; - } - case kX87Float64InsertLowWord32: { - __ sub(esp, Immediate(kDoubleSize)); - __ fstp_d(MemOperand(esp, 0)); - __ mov(MemOperand(esp, 0), i.InputRegister(1)); - __ fld_d(MemOperand(esp, 0)); - __ add(esp, Immediate(kDoubleSize)); - break; - } - case kX87Float64Sqrt: { - if (FLAG_debug_code && FLAG_enable_slow_asserts) { - __ VerifyX87StackDepth(1); - } - __ X87SetFPUCW(0x027F); - __ fstp(0); - __ fld_d(MemOperand(esp, 0)); - __ fsqrt(); - __ lea(esp, Operand(esp, kDoubleSize)); - __ X87SetFPUCW(0x037F); - break; - } - case kX87Float64Round: { - RoundingMode mode = - static_cast(MiscField::decode(instr->opcode())); - // Set the correct round mode in x87 control register - __ X87SetRC((mode << 10)); - - if (!instr->InputAt(0)->IsFPRegister()) { - InstructionOperand* input = instr->InputAt(0); - USE(input); - DCHECK(input->IsFPStackSlot()); - if (FLAG_debug_code && FLAG_enable_slow_asserts) { - __ VerifyX87StackDepth(1); - } - __ fstp(0); - __ fld_d(i.InputOperand(0)); - } - __ frndint(); - __ X87SetRC(0x0000); - break; - } - case kX87Float64Cmp: { - __ fld_d(MemOperand(esp, kDoubleSize)); - __ fld_d(MemOperand(esp, 0)); - __ FCmp(); - __ lea(esp, Operand(esp, 2 * kDoubleSize)); - break; - } - case kX87Float64SilenceNaN: { - Label end, return_qnan; - __ fstp(0); - __ push(ebx); - // Load Half word of HoleNan(SNaN) into ebx - __ mov(ebx, MemOperand(esp, 2 * kInt32Size)); - __ cmp(ebx, Immediate(kHoleNanUpper32)); - // Check input is HoleNaN(SNaN)? - __ j(equal, &return_qnan, Label::kNear); - // If input isn't HoleNaN(SNaN), just load it and return - __ fld_d(MemOperand(esp, 1 * kInt32Size)); - __ jmp(&end); - __ bind(&return_qnan); - // If input is HoleNaN(SNaN), Return QNaN - __ push(Immediate(0xffffffff)); - __ push(Immediate(0xfff7ffff)); - __ fld_d(MemOperand(esp, 0)); - __ lea(esp, Operand(esp, kDoubleSize)); - __ bind(&end); - __ pop(ebx); - // Clear stack. - __ lea(esp, Operand(esp, 1 * kDoubleSize)); - break; - } - case kX87Movsxbl: - __ movsx_b(i.OutputRegister(), i.MemoryOperand()); - break; - case kX87Movzxbl: - __ movzx_b(i.OutputRegister(), i.MemoryOperand()); - break; - case kX87Movb: { - size_t index = 0; - Operand operand = i.MemoryOperand(&index); - if (HasImmediateInput(instr, index)) { - __ mov_b(operand, i.InputInt8(index)); - } else { - __ mov_b(operand, i.InputRegister(index)); - } - break; - } - case kX87Movsxwl: - __ movsx_w(i.OutputRegister(), i.MemoryOperand()); - break; - case kX87Movzxwl: - __ movzx_w(i.OutputRegister(), i.MemoryOperand()); - break; - case kX87Movw: { - size_t index = 0; - Operand operand = i.MemoryOperand(&index); - if (HasImmediateInput(instr, index)) { - __ mov_w(operand, i.InputInt16(index)); - } else { - __ mov_w(operand, i.InputRegister(index)); - } - break; - } - case kX87Movl: - if (instr->HasOutput()) { - __ mov(i.OutputRegister(), i.MemoryOperand()); - } else { - size_t index = 0; - Operand operand = i.MemoryOperand(&index); - if (HasImmediateInput(instr, index)) { - __ mov(operand, i.InputImmediate(index)); - } else { - __ mov(operand, i.InputRegister(index)); - } - } - break; - case kX87Movsd: { - if (instr->HasOutput()) { - X87Register output = i.OutputDoubleRegister(); - USE(output); - DCHECK(output.code() == 0); - if (FLAG_debug_code && FLAG_enable_slow_asserts) { - __ VerifyX87StackDepth(1); - } - __ fstp(0); - __ fld_d(i.MemoryOperand()); - } else { - size_t index = 0; - Operand operand = i.MemoryOperand(&index); - __ fst_d(operand); - } - break; - } - case kX87Movss: { - if (instr->HasOutput()) { - X87Register output = i.OutputDoubleRegister(); - USE(output); - DCHECK(output.code() == 0); - if (FLAG_debug_code && FLAG_enable_slow_asserts) { - __ VerifyX87StackDepth(1); - } - __ fstp(0); - __ fld_s(i.MemoryOperand()); - } else { - size_t index = 0; - Operand operand = i.MemoryOperand(&index); - __ fst_s(operand); - } - break; - } - case kX87BitcastFI: { - __ mov(i.OutputRegister(), MemOperand(esp, 0)); - __ lea(esp, Operand(esp, kFloatSize)); - break; - } - case kX87BitcastIF: { - if (FLAG_debug_code && FLAG_enable_slow_asserts) { - __ VerifyX87StackDepth(1); - } - __ fstp(0); - if (instr->InputAt(0)->IsRegister()) { - __ lea(esp, Operand(esp, -kFloatSize)); - __ mov(MemOperand(esp, 0), i.InputRegister(0)); - __ fld_s(MemOperand(esp, 0)); - __ lea(esp, Operand(esp, kFloatSize)); - } else { - __ fld_s(i.InputOperand(0)); - } - break; - } - case kX87Lea: { - AddressingMode mode = AddressingModeField::decode(instr->opcode()); - // Shorten "leal" to "addl", "subl" or "shll" if the register allocation - // and addressing mode just happens to work out. The "addl"/"subl" forms - // in these cases are faster based on measurements. - if (mode == kMode_MI) { - __ Move(i.OutputRegister(), Immediate(i.InputInt32(0))); - } else if (i.InputRegister(0).is(i.OutputRegister())) { - if (mode == kMode_MRI) { - int32_t constant_summand = i.InputInt32(1); - if (constant_summand > 0) { - __ add(i.OutputRegister(), Immediate(constant_summand)); - } else if (constant_summand < 0) { - __ sub(i.OutputRegister(), Immediate(-constant_summand)); - } - } else if (mode == kMode_MR1) { - if (i.InputRegister(1).is(i.OutputRegister())) { - __ shl(i.OutputRegister(), 1); - } else { - __ add(i.OutputRegister(), i.InputRegister(1)); - } - } else if (mode == kMode_M2) { - __ shl(i.OutputRegister(), 1); - } else if (mode == kMode_M4) { - __ shl(i.OutputRegister(), 2); - } else if (mode == kMode_M8) { - __ shl(i.OutputRegister(), 3); - } else { - __ lea(i.OutputRegister(), i.MemoryOperand()); - } - } else if (mode == kMode_MR1 && - i.InputRegister(1).is(i.OutputRegister())) { - __ add(i.OutputRegister(), i.InputRegister(0)); - } else { - __ lea(i.OutputRegister(), i.MemoryOperand()); - } - break; - } - case kX87Push: - if (instr->InputAt(0)->IsFPRegister()) { - auto allocated = AllocatedOperand::cast(*instr->InputAt(0)); - if (allocated.representation() == MachineRepresentation::kFloat32) { - __ sub(esp, Immediate(kFloatSize)); - __ fst_s(Operand(esp, 0)); - frame_access_state()->IncreaseSPDelta(kFloatSize / kPointerSize); - } else { - DCHECK(allocated.representation() == MachineRepresentation::kFloat64); - __ sub(esp, Immediate(kDoubleSize)); - __ fst_d(Operand(esp, 0)); - frame_access_state()->IncreaseSPDelta(kDoubleSize / kPointerSize); - } - } else if (instr->InputAt(0)->IsFPStackSlot()) { - auto allocated = AllocatedOperand::cast(*instr->InputAt(0)); - if (allocated.representation() == MachineRepresentation::kFloat32) { - __ sub(esp, Immediate(kFloatSize)); - __ fld_s(i.InputOperand(0)); - __ fstp_s(MemOperand(esp, 0)); - frame_access_state()->IncreaseSPDelta(kFloatSize / kPointerSize); - } else { - DCHECK(allocated.representation() == MachineRepresentation::kFloat64); - __ sub(esp, Immediate(kDoubleSize)); - __ fld_d(i.InputOperand(0)); - __ fstp_d(MemOperand(esp, 0)); - frame_access_state()->IncreaseSPDelta(kDoubleSize / kPointerSize); - } - } else if (HasImmediateInput(instr, 0)) { - __ push(i.InputImmediate(0)); - frame_access_state()->IncreaseSPDelta(1); - } else { - __ push(i.InputOperand(0)); - frame_access_state()->IncreaseSPDelta(1); - } - break; - case kX87Poke: { - int const slot = MiscField::decode(instr->opcode()); - if (HasImmediateInput(instr, 0)) { - __ mov(Operand(esp, slot * kPointerSize), i.InputImmediate(0)); - } else { - __ mov(Operand(esp, slot * kPointerSize), i.InputRegister(0)); - } - break; - } - case kX87Xchgb: { - size_t index = 0; - Operand operand = i.MemoryOperand(&index); - __ xchg_b(i.InputRegister(index), operand); - break; - } - case kX87Xchgw: { - size_t index = 0; - Operand operand = i.MemoryOperand(&index); - __ xchg_w(i.InputRegister(index), operand); - break; - } - case kX87Xchgl: { - size_t index = 0; - Operand operand = i.MemoryOperand(&index); - __ xchg(i.InputRegister(index), operand); - break; - } - case kX87PushFloat32: - __ lea(esp, Operand(esp, -kFloatSize)); - if (instr->InputAt(0)->IsFPStackSlot()) { - __ fld_s(i.InputOperand(0)); - __ fstp_s(MemOperand(esp, 0)); - } else if (instr->InputAt(0)->IsFPRegister()) { - __ fst_s(MemOperand(esp, 0)); - } else { - UNREACHABLE(); - } - break; - case kX87PushFloat64: - __ lea(esp, Operand(esp, -kDoubleSize)); - if (instr->InputAt(0)->IsFPStackSlot()) { - __ fld_d(i.InputOperand(0)); - __ fstp_d(MemOperand(esp, 0)); - } else if (instr->InputAt(0)->IsFPRegister()) { - __ fst_d(MemOperand(esp, 0)); - } else { - UNREACHABLE(); - } - break; - case kCheckedLoadInt8: - ASSEMBLE_CHECKED_LOAD_INTEGER(movsx_b); - break; - case kCheckedLoadUint8: - ASSEMBLE_CHECKED_LOAD_INTEGER(movzx_b); - break; - case kCheckedLoadInt16: - ASSEMBLE_CHECKED_LOAD_INTEGER(movsx_w); - break; - case kCheckedLoadUint16: - ASSEMBLE_CHECKED_LOAD_INTEGER(movzx_w); - break; - case kCheckedLoadWord32: - ASSEMBLE_CHECKED_LOAD_INTEGER(mov); - break; - case kCheckedLoadFloat32: - ASSEMBLE_CHECKED_LOAD_FLOAT(fld_s, OutOfLineLoadFloat32NaN); - break; - case kCheckedLoadFloat64: - ASSEMBLE_CHECKED_LOAD_FLOAT(fld_d, OutOfLineLoadFloat64NaN); - break; - case kCheckedStoreWord8: - ASSEMBLE_CHECKED_STORE_INTEGER(mov_b); - break; - case kCheckedStoreWord16: - ASSEMBLE_CHECKED_STORE_INTEGER(mov_w); - break; - case kCheckedStoreWord32: - ASSEMBLE_CHECKED_STORE_INTEGER(mov); - break; - case kCheckedStoreFloat32: - ASSEMBLE_CHECKED_STORE_FLOAT(fst_s); - break; - case kCheckedStoreFloat64: - ASSEMBLE_CHECKED_STORE_FLOAT(fst_d); - break; - case kX87StackCheck: { - ExternalReference const stack_limit = - ExternalReference::address_of_stack_limit(isolate()); - __ cmp(esp, Operand::StaticVariable(stack_limit)); - break; - } - case kCheckedLoadWord64: - case kCheckedStoreWord64: - UNREACHABLE(); // currently unsupported checked int64 load/store. - break; - case kAtomicLoadInt8: - case kAtomicLoadUint8: - case kAtomicLoadInt16: - case kAtomicLoadUint16: - case kAtomicLoadWord32: - case kAtomicStoreWord8: - case kAtomicStoreWord16: - case kAtomicStoreWord32: - UNREACHABLE(); // Won't be generated by instruction selector. - break; - } - return kSuccess; -} // NOLINT(readability/fn_size) - -static Condition FlagsConditionToCondition(FlagsCondition condition) { - switch (condition) { - case kUnorderedEqual: - case kEqual: - return equal; - break; - case kUnorderedNotEqual: - case kNotEqual: - return not_equal; - break; - case kSignedLessThan: - return less; - break; - case kSignedGreaterThanOrEqual: - return greater_equal; - break; - case kSignedLessThanOrEqual: - return less_equal; - break; - case kSignedGreaterThan: - return greater; - break; - case kUnsignedLessThan: - return below; - break; - case kUnsignedGreaterThanOrEqual: - return above_equal; - break; - case kUnsignedLessThanOrEqual: - return below_equal; - break; - case kUnsignedGreaterThan: - return above; - break; - case kOverflow: - return overflow; - break; - case kNotOverflow: - return no_overflow; - break; - default: - UNREACHABLE(); - break; - } -} - -// Assembles a branch after an instruction. -void CodeGenerator::AssembleArchBranch(Instruction* instr, BranchInfo* branch) { - Label::Distance flabel_distance = - branch->fallthru ? Label::kNear : Label::kFar; - - Label done; - Label tlabel_tmp; - Label flabel_tmp; - Label* tlabel = &tlabel_tmp; - Label* flabel = &flabel_tmp; - - Label* tlabel_dst = branch->true_label; - Label* flabel_dst = branch->false_label; - - if (branch->condition == kUnorderedEqual) { - __ j(parity_even, flabel, flabel_distance); - } else if (branch->condition == kUnorderedNotEqual) { - __ j(parity_even, tlabel); - } - __ j(FlagsConditionToCondition(branch->condition), tlabel); - - // Add a jump if not falling through to the next block. - if (!branch->fallthru) __ jmp(flabel); - - __ jmp(&done); - __ bind(&tlabel_tmp); - FlagsMode mode = FlagsModeField::decode(instr->opcode()); - if (mode == kFlags_deoptimize) { - int double_register_param_count = 0; - int x87_layout = 0; - for (size_t i = 0; i < instr->InputCount(); i++) { - if (instr->InputAt(i)->IsFPRegister()) { - double_register_param_count++; - } - } - // Currently we use only one X87 register. If double_register_param_count - // is bigger than 1, it means duplicated double register is added to input - // of this instruction. - if (double_register_param_count > 0) { - x87_layout = (0 << 3) | 1; - } - // The layout of x87 register stack is loaded on the top of FPU register - // stack for deoptimization. - __ push(Immediate(x87_layout)); - __ fild_s(MemOperand(esp, 0)); - __ lea(esp, Operand(esp, kPointerSize)); - } - __ jmp(tlabel_dst); - __ bind(&flabel_tmp); - __ jmp(flabel_dst); - __ bind(&done); -} - - -void CodeGenerator::AssembleArchJump(RpoNumber target) { - if (!IsNextInAssemblyOrder(target)) __ jmp(GetLabel(target)); -} - -void CodeGenerator::AssembleArchTrap(Instruction* instr, - FlagsCondition condition) { - class OutOfLineTrap final : public OutOfLineCode { - public: - OutOfLineTrap(CodeGenerator* gen, bool frame_elided, Instruction* instr) - : OutOfLineCode(gen), - frame_elided_(frame_elided), - instr_(instr), - gen_(gen) {} - - void Generate() final { - X87OperandConverter i(gen_, instr_); - - Runtime::FunctionId trap_id = static_cast( - i.InputInt32(instr_->InputCount() - 1)); - bool old_has_frame = __ has_frame(); - if (frame_elided_) { - __ set_has_frame(true); - __ EnterFrame(StackFrame::WASM_COMPILED); - } - GenerateCallToTrap(trap_id); - if (frame_elided_) { - ReferenceMap* reference_map = - new (gen_->zone()) ReferenceMap(gen_->zone()); - gen_->RecordSafepoint(reference_map, Safepoint::kSimple, 0, - Safepoint::kNoLazyDeopt); - __ set_has_frame(old_has_frame); - } - if (FLAG_debug_code) { - __ ud2(); - } - } - - private: - void GenerateCallToTrap(Runtime::FunctionId trap_id) { - if (trap_id == Runtime::kNumFunctions) { - // We cannot test calls to the runtime in cctest/test-run-wasm. - // Therefore we emit a call to C here instead of a call to the runtime. - __ PrepareCallCFunction(0, esi); - __ CallCFunction( - ExternalReference::wasm_call_trap_callback_for_testing(isolate()), - 0); - } else { - __ Move(esi, isolate()->native_context()); - gen_->AssembleSourcePosition(instr_); - __ CallRuntime(trap_id); - } - } - - bool frame_elided_; - Instruction* instr_; - CodeGenerator* gen_; - }; - bool frame_elided = !frame_access_state()->has_frame(); - auto ool = new (zone()) OutOfLineTrap(this, frame_elided, instr); - Label* tlabel = ool->entry(); - Label end; - if (condition == kUnorderedEqual) { - __ j(parity_even, &end); - } else if (condition == kUnorderedNotEqual) { - __ j(parity_even, tlabel); - } - __ j(FlagsConditionToCondition(condition), tlabel); - __ bind(&end); -} - -// Assembles boolean materializations after an instruction. -void CodeGenerator::AssembleArchBoolean(Instruction* instr, - FlagsCondition condition) { - X87OperandConverter i(this, instr); - Label done; - - // Materialize a full 32-bit 1 or 0 value. The result register is always the - // last output of the instruction. - Label check; - DCHECK_NE(0u, instr->OutputCount()); - Register reg = i.OutputRegister(instr->OutputCount() - 1); - if (condition == kUnorderedEqual) { - __ j(parity_odd, &check, Label::kNear); - __ Move(reg, Immediate(0)); - __ jmp(&done, Label::kNear); - } else if (condition == kUnorderedNotEqual) { - __ j(parity_odd, &check, Label::kNear); - __ mov(reg, Immediate(1)); - __ jmp(&done, Label::kNear); - } - Condition cc = FlagsConditionToCondition(condition); - - __ bind(&check); - if (reg.is_byte_register()) { - // setcc for byte registers (al, bl, cl, dl). - __ setcc(cc, reg); - __ movzx_b(reg, reg); - } else { - // Emit a branch to set a register to either 1 or 0. - Label set; - __ j(cc, &set, Label::kNear); - __ Move(reg, Immediate(0)); - __ jmp(&done, Label::kNear); - __ bind(&set); - __ mov(reg, Immediate(1)); - } - __ bind(&done); -} - - -void CodeGenerator::AssembleArchLookupSwitch(Instruction* instr) { - X87OperandConverter i(this, instr); - Register input = i.InputRegister(0); - for (size_t index = 2; index < instr->InputCount(); index += 2) { - __ cmp(input, Immediate(i.InputInt32(index + 0))); - __ j(equal, GetLabel(i.InputRpo(index + 1))); - } - AssembleArchJump(i.InputRpo(1)); -} - - -void CodeGenerator::AssembleArchTableSwitch(Instruction* instr) { - X87OperandConverter i(this, instr); - Register input = i.InputRegister(0); - size_t const case_count = instr->InputCount() - 2; - Label** cases = zone()->NewArray(case_count); - for (size_t index = 0; index < case_count; ++index) { - cases[index] = GetLabel(i.InputRpo(index + 2)); - } - Label* const table = AddJumpTable(cases, case_count); - __ cmp(input, Immediate(case_count)); - __ j(above_equal, GetLabel(i.InputRpo(1))); - __ jmp(Operand::JumpTable(input, times_4, table)); -} - -CodeGenerator::CodeGenResult CodeGenerator::AssembleDeoptimizerCall( - int deoptimization_id, SourcePosition pos) { - DeoptimizeKind deoptimization_kind = GetDeoptimizationKind(deoptimization_id); - DeoptimizeReason deoptimization_reason = - GetDeoptimizationReason(deoptimization_id); - Deoptimizer::BailoutType bailout_type = - deoptimization_kind == DeoptimizeKind::kSoft ? Deoptimizer::SOFT - : Deoptimizer::EAGER; - Address deopt_entry = Deoptimizer::GetDeoptimizationEntry( - isolate(), deoptimization_id, bailout_type); - if (deopt_entry == nullptr) return kTooManyDeoptimizationBailouts; - __ RecordDeoptReason(deoptimization_reason, pos, deoptimization_id); - __ call(deopt_entry, RelocInfo::RUNTIME_ENTRY); - return kSuccess; -} - - -// The calling convention for JSFunctions on X87 passes arguments on the -// stack and the JSFunction and context in EDI and ESI, respectively, thus -// the steps of the call look as follows: - -// --{ before the call instruction }-------------------------------------------- -// | caller frame | -// ^ esp ^ ebp - -// --{ push arguments and setup ESI, EDI }-------------------------------------- -// | args + receiver | caller frame | -// ^ esp ^ ebp -// [edi = JSFunction, esi = context] - -// --{ call [edi + kCodeEntryOffset] }------------------------------------------ -// | RET | args + receiver | caller frame | -// ^ esp ^ ebp - -// =={ prologue of called function }============================================ -// --{ push ebp }--------------------------------------------------------------- -// | FP | RET | args + receiver | caller frame | -// ^ esp ^ ebp - -// --{ mov ebp, esp }----------------------------------------------------------- -// | FP | RET | args + receiver | caller frame | -// ^ ebp,esp - -// --{ push esi }--------------------------------------------------------------- -// | CTX | FP | RET | args + receiver | caller frame | -// ^esp ^ ebp - -// --{ push edi }--------------------------------------------------------------- -// | FNC | CTX | FP | RET | args + receiver | caller frame | -// ^esp ^ ebp - -// --{ subi esp, #N }----------------------------------------------------------- -// | callee frame | FNC | CTX | FP | RET | args + receiver | caller frame | -// ^esp ^ ebp - -// =={ body of called function }================================================ - -// =={ epilogue of called function }============================================ -// --{ mov esp, ebp }----------------------------------------------------------- -// | FP | RET | args + receiver | caller frame | -// ^ esp,ebp - -// --{ pop ebp }----------------------------------------------------------- -// | | RET | args + receiver | caller frame | -// ^ esp ^ ebp - -// --{ ret #A+1 }----------------------------------------------------------- -// | | caller frame | -// ^ esp ^ ebp - - -// Runtime function calls are accomplished by doing a stub call to the -// CEntryStub (a real code object). On X87 passes arguments on the -// stack, the number of arguments in EAX, the address of the runtime function -// in EBX, and the context in ESI. - -// --{ before the call instruction }-------------------------------------------- -// | caller frame | -// ^ esp ^ ebp - -// --{ push arguments and setup EAX, EBX, and ESI }----------------------------- -// | args + receiver | caller frame | -// ^ esp ^ ebp -// [eax = #args, ebx = runtime function, esi = context] - -// --{ call #CEntryStub }------------------------------------------------------- -// | RET | args + receiver | caller frame | -// ^ esp ^ ebp - -// =={ body of runtime function }=============================================== - -// --{ runtime returns }-------------------------------------------------------- -// | caller frame | -// ^ esp ^ ebp - -// Other custom linkages (e.g. for calling directly into and out of C++) may -// need to save callee-saved registers on the stack, which is done in the -// function prologue of generated code. - -// --{ before the call instruction }-------------------------------------------- -// | caller frame | -// ^ esp ^ ebp - -// --{ set up arguments in registers on stack }--------------------------------- -// | args | caller frame | -// ^ esp ^ ebp -// [r0 = arg0, r1 = arg1, ...] - -// --{ call code }-------------------------------------------------------------- -// | RET | args | caller frame | -// ^ esp ^ ebp - -// =={ prologue of called function }============================================ -// --{ push ebp }--------------------------------------------------------------- -// | FP | RET | args | caller frame | -// ^ esp ^ ebp - -// --{ mov ebp, esp }----------------------------------------------------------- -// | FP | RET | args | caller frame | -// ^ ebp,esp - -// --{ save registers }--------------------------------------------------------- -// | regs | FP | RET | args | caller frame | -// ^ esp ^ ebp - -// --{ subi esp, #N }----------------------------------------------------------- -// | callee frame | regs | FP | RET | args | caller frame | -// ^esp ^ ebp - -// =={ body of called function }================================================ - -// =={ epilogue of called function }============================================ -// --{ restore registers }------------------------------------------------------ -// | regs | FP | RET | args | caller frame | -// ^ esp ^ ebp - -// --{ mov esp, ebp }----------------------------------------------------------- -// | FP | RET | args | caller frame | -// ^ esp,ebp - -// --{ pop ebp }---------------------------------------------------------------- -// | RET | args | caller frame | -// ^ esp ^ ebp - -void CodeGenerator::FinishFrame(Frame* frame) { - CallDescriptor* descriptor = linkage()->GetIncomingDescriptor(); - const RegList saves = descriptor->CalleeSavedRegisters(); - if (saves != 0) { // Save callee-saved registers. - DCHECK(!info()->is_osr()); - int pushed = 0; - for (int i = Register::kNumRegisters - 1; i >= 0; i--) { - if (!((1 << i) & saves)) continue; - ++pushed; - } - frame->AllocateSavedCalleeRegisterSlots(pushed); - } - - // Initailize FPU state. - __ fninit(); - __ fld1(); -} - -void CodeGenerator::AssembleConstructFrame() { - CallDescriptor* descriptor = linkage()->GetIncomingDescriptor(); - if (frame_access_state()->has_frame()) { - if (descriptor->IsCFunctionCall()) { - __ push(ebp); - __ mov(ebp, esp); - } else if (descriptor->IsJSFunctionCall()) { - __ Prologue(this->info()->GeneratePreagedPrologue()); - if (descriptor->PushArgumentCount()) { - __ push(kJavaScriptCallArgCountRegister); - } - } else { - __ StubPrologue(info()->GetOutputStackFrameType()); - } - } - - int shrink_slots = - frame()->GetTotalFrameSlotCount() - descriptor->CalculateFixedFrameSize(); - - if (info()->is_osr()) { - // TurboFan OSR-compiled functions cannot be entered directly. - __ Abort(kShouldNotDirectlyEnterOsrFunction); - - // Unoptimized code jumps directly to this entrypoint while the unoptimized - // frame is still on the stack. Optimized code uses OSR values directly from - // the unoptimized frame. Thus, all that needs to be done is to allocate the - // remaining stack slots. - if (FLAG_code_comments) __ RecordComment("-- OSR entrypoint --"); - osr_pc_offset_ = __ pc_offset(); - shrink_slots -= osr_helper()->UnoptimizedFrameSlots(); - - // Initailize FPU state. - __ fninit(); - __ fld1(); - } - - const RegList saves = descriptor->CalleeSavedRegisters(); - if (shrink_slots > 0) { - __ sub(esp, Immediate(shrink_slots * kPointerSize)); - } - - if (saves != 0) { // Save callee-saved registers. - DCHECK(!info()->is_osr()); - int pushed = 0; - for (int i = Register::kNumRegisters - 1; i >= 0; i--) { - if (!((1 << i) & saves)) continue; - __ push(Register::from_code(i)); - ++pushed; - } - } -} - -void CodeGenerator::AssembleReturn(InstructionOperand* pop) { - CallDescriptor* descriptor = linkage()->GetIncomingDescriptor(); - - // Clear the FPU stack only if there is no return value in the stack. - if (FLAG_debug_code && FLAG_enable_slow_asserts) { - __ VerifyX87StackDepth(1); - } - bool clear_stack = true; - for (size_t i = 0; i < descriptor->ReturnCount(); i++) { - MachineRepresentation rep = descriptor->GetReturnType(i).representation(); - LinkageLocation loc = descriptor->GetReturnLocation(i); - if (IsFloatingPoint(rep) && loc == LinkageLocation::ForRegister(0)) { - clear_stack = false; - break; - } - } - if (clear_stack) __ fstp(0); - - const RegList saves = descriptor->CalleeSavedRegisters(); - // Restore registers. - if (saves != 0) { - for (int i = 0; i < Register::kNumRegisters; i++) { - if (!((1 << i) & saves)) continue; - __ pop(Register::from_code(i)); - } - } - - // Might need ecx for scratch if pop_size is too big or if there is a variable - // pop count. - DCHECK_EQ(0u, descriptor->CalleeSavedRegisters() & ecx.bit()); - size_t pop_size = descriptor->StackParameterCount() * kPointerSize; - X87OperandConverter g(this, nullptr); - if (descriptor->IsCFunctionCall()) { - AssembleDeconstructFrame(); - } else if (frame_access_state()->has_frame()) { - // Canonicalize JSFunction return sites for now if they always have the same - // number of return args. - if (pop->IsImmediate() && g.ToConstant(pop).ToInt32() == 0) { - if (return_label_.is_bound()) { - __ jmp(&return_label_); - return; - } else { - __ bind(&return_label_); - AssembleDeconstructFrame(); - } - } else { - AssembleDeconstructFrame(); - } - } - DCHECK_EQ(0u, descriptor->CalleeSavedRegisters() & edx.bit()); - DCHECK_EQ(0u, descriptor->CalleeSavedRegisters() & ecx.bit()); - if (pop->IsImmediate()) { - DCHECK_EQ(Constant::kInt32, g.ToConstant(pop).type()); - pop_size += g.ToConstant(pop).ToInt32() * kPointerSize; - __ Ret(static_cast(pop_size), ecx); - } else { - Register pop_reg = g.ToRegister(pop); - Register scratch_reg = pop_reg.is(ecx) ? edx : ecx; - __ pop(scratch_reg); - __ lea(esp, Operand(esp, pop_reg, times_4, static_cast(pop_size))); - __ jmp(scratch_reg); - } -} - -void CodeGenerator::FinishCode() {} - -void CodeGenerator::AssembleMove(InstructionOperand* source, - InstructionOperand* destination) { - X87OperandConverter g(this, nullptr); - // Dispatch on the source and destination operand kinds. Not all - // combinations are possible. - if (source->IsRegister()) { - DCHECK(destination->IsRegister() || destination->IsStackSlot()); - Register src = g.ToRegister(source); - Operand dst = g.ToOperand(destination); - __ mov(dst, src); - } else if (source->IsStackSlot()) { - DCHECK(destination->IsRegister() || destination->IsStackSlot()); - Operand src = g.ToOperand(source); - if (destination->IsRegister()) { - Register dst = g.ToRegister(destination); - __ mov(dst, src); - } else { - Operand dst = g.ToOperand(destination); - __ push(src); - __ pop(dst); - } - } else if (source->IsConstant()) { - Constant src_constant = g.ToConstant(source); - if (src_constant.type() == Constant::kHeapObject) { - Handle src = src_constant.ToHeapObject(); - if (destination->IsRegister()) { - Register dst = g.ToRegister(destination); - __ LoadHeapObject(dst, src); - } else { - DCHECK(destination->IsStackSlot()); - Operand dst = g.ToOperand(destination); - AllowDeferredHandleDereference embedding_raw_address; - if (isolate()->heap()->InNewSpace(*src)) { - __ PushHeapObject(src); - __ pop(dst); - } else { - __ mov(dst, src); - } - } - } else if (destination->IsRegister()) { - Register dst = g.ToRegister(destination); - __ Move(dst, g.ToImmediate(source)); - } else if (destination->IsStackSlot()) { - Operand dst = g.ToOperand(destination); - __ Move(dst, g.ToImmediate(source)); - } else if (src_constant.type() == Constant::kFloat32) { - // TODO(turbofan): Can we do better here? - uint32_t src = src_constant.ToFloat32AsInt(); - if (destination->IsFPRegister()) { - __ sub(esp, Immediate(kInt32Size)); - __ mov(MemOperand(esp, 0), Immediate(src)); - // always only push one value into the x87 stack. - __ fstp(0); - __ fld_s(MemOperand(esp, 0)); - __ add(esp, Immediate(kInt32Size)); - } else { - DCHECK(destination->IsFPStackSlot()); - Operand dst = g.ToOperand(destination); - __ Move(dst, Immediate(src)); - } - } else { - DCHECK_EQ(Constant::kFloat64, src_constant.type()); - uint64_t src = src_constant.ToFloat64().AsUint64(); - uint32_t lower = static_cast(src); - uint32_t upper = static_cast(src >> 32); - if (destination->IsFPRegister()) { - __ sub(esp, Immediate(kDoubleSize)); - __ mov(MemOperand(esp, 0), Immediate(lower)); - __ mov(MemOperand(esp, kInt32Size), Immediate(upper)); - // always only push one value into the x87 stack. - __ fstp(0); - __ fld_d(MemOperand(esp, 0)); - __ add(esp, Immediate(kDoubleSize)); - } else { - DCHECK(destination->IsFPStackSlot()); - Operand dst0 = g.ToOperand(destination); - Operand dst1 = g.HighOperand(destination); - __ Move(dst0, Immediate(lower)); - __ Move(dst1, Immediate(upper)); - } - } - } else if (source->IsFPRegister()) { - DCHECK(destination->IsFPStackSlot()); - Operand dst = g.ToOperand(destination); - auto allocated = AllocatedOperand::cast(*source); - switch (allocated.representation()) { - case MachineRepresentation::kFloat32: - __ fst_s(dst); - break; - case MachineRepresentation::kFloat64: - __ fst_d(dst); - break; - default: - UNREACHABLE(); - } - } else if (source->IsFPStackSlot()) { - DCHECK(destination->IsFPRegister() || destination->IsFPStackSlot()); - Operand src = g.ToOperand(source); - auto allocated = AllocatedOperand::cast(*source); - if (destination->IsFPRegister()) { - // always only push one value into the x87 stack. - __ fstp(0); - switch (allocated.representation()) { - case MachineRepresentation::kFloat32: - __ fld_s(src); - break; - case MachineRepresentation::kFloat64: - __ fld_d(src); - break; - default: - UNREACHABLE(); - } - } else { - Operand dst = g.ToOperand(destination); - switch (allocated.representation()) { - case MachineRepresentation::kFloat32: - __ fld_s(src); - __ fstp_s(dst); - break; - case MachineRepresentation::kFloat64: - __ fld_d(src); - __ fstp_d(dst); - break; - default: - UNREACHABLE(); - } - } - } else { - UNREACHABLE(); - } -} - - -void CodeGenerator::AssembleSwap(InstructionOperand* source, - InstructionOperand* destination) { - X87OperandConverter g(this, nullptr); - // Dispatch on the source and destination operand kinds. Not all - // combinations are possible. - if (source->IsRegister() && destination->IsRegister()) { - // Register-register. - Register src = g.ToRegister(source); - Register dst = g.ToRegister(destination); - __ xchg(dst, src); - } else if (source->IsRegister() && destination->IsStackSlot()) { - // Register-memory. - __ xchg(g.ToRegister(source), g.ToOperand(destination)); - } else if (source->IsStackSlot() && destination->IsStackSlot()) { - // Memory-memory. - Operand dst1 = g.ToOperand(destination); - __ push(dst1); - frame_access_state()->IncreaseSPDelta(1); - Operand src1 = g.ToOperand(source); - __ push(src1); - Operand dst2 = g.ToOperand(destination); - __ pop(dst2); - frame_access_state()->IncreaseSPDelta(-1); - Operand src2 = g.ToOperand(source); - __ pop(src2); - } else if (source->IsFPRegister() && destination->IsFPRegister()) { - UNREACHABLE(); - } else if (source->IsFPRegister() && destination->IsFPStackSlot()) { - auto allocated = AllocatedOperand::cast(*source); - switch (allocated.representation()) { - case MachineRepresentation::kFloat32: - __ fld_s(g.ToOperand(destination)); - __ fxch(); - __ fstp_s(g.ToOperand(destination)); - break; - case MachineRepresentation::kFloat64: - __ fld_d(g.ToOperand(destination)); - __ fxch(); - __ fstp_d(g.ToOperand(destination)); - break; - default: - UNREACHABLE(); - } - } else if (source->IsFPStackSlot() && destination->IsFPStackSlot()) { - auto allocated = AllocatedOperand::cast(*source); - switch (allocated.representation()) { - case MachineRepresentation::kFloat32: - __ fld_s(g.ToOperand(source)); - __ fld_s(g.ToOperand(destination)); - __ fstp_s(g.ToOperand(source)); - __ fstp_s(g.ToOperand(destination)); - break; - case MachineRepresentation::kFloat64: - __ fld_d(g.ToOperand(source)); - __ fld_d(g.ToOperand(destination)); - __ fstp_d(g.ToOperand(source)); - __ fstp_d(g.ToOperand(destination)); - break; - default: - UNREACHABLE(); - } - } else { - // No other combinations are possible. - UNREACHABLE(); - } -} - - -void CodeGenerator::AssembleJumpTable(Label** targets, size_t target_count) { - for (size_t index = 0; index < target_count; ++index) { - __ dd(targets[index]); - } -} - - -void CodeGenerator::EnsureSpaceForLazyDeopt() { - if (!info()->ShouldEnsureSpaceForLazyDeopt()) { - return; - } - - int space_needed = Deoptimizer::patch_size(); - // Ensure that we have enough space after the previous lazy-bailout - // instruction for patching the code here. - int current_pc = masm()->pc_offset(); - if (current_pc < last_lazy_deopt_pc_ + space_needed) { - int padding_size = last_lazy_deopt_pc_ + space_needed - current_pc; - __ Nop(padding_size); - } -} - -#undef __ - -} // namespace compiler -} // namespace internal -} // namespace v8 diff --git a/src/compiler/x87/instruction-codes-x87.h b/src/compiler/x87/instruction-codes-x87.h deleted file mode 100644 index 5f527fd43f..0000000000 --- a/src/compiler/x87/instruction-codes-x87.h +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2014 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. - -#ifndef V8_COMPILER_X87_INSTRUCTION_CODES_X87_H_ -#define V8_COMPILER_X87_INSTRUCTION_CODES_X87_H_ - -#include "src/compiler/instruction.h" -#include "src/compiler/instruction-codes.h" -namespace v8 { -namespace internal { -namespace compiler { - -// X87-specific opcodes that specify which assembly sequence to emit. -// Most opcodes specify a single instruction. -#define TARGET_ARCH_OPCODE_LIST(V) \ - V(X87Add) \ - V(X87And) \ - V(X87Cmp) \ - V(X87Cmp16) \ - V(X87Cmp8) \ - V(X87Test) \ - V(X87Test16) \ - V(X87Test8) \ - V(X87Or) \ - V(X87Xor) \ - V(X87Sub) \ - V(X87Imul) \ - V(X87ImulHigh) \ - V(X87UmulHigh) \ - V(X87Idiv) \ - V(X87Udiv) \ - V(X87Not) \ - V(X87Neg) \ - V(X87Shl) \ - V(X87Shr) \ - V(X87Sar) \ - V(X87AddPair) \ - V(X87SubPair) \ - V(X87MulPair) \ - V(X87ShlPair) \ - V(X87ShrPair) \ - V(X87SarPair) \ - V(X87Ror) \ - V(X87Lzcnt) \ - V(X87Popcnt) \ - V(X87Float32Cmp) \ - V(X87Float32Add) \ - V(X87Float32Sub) \ - V(X87Float32Mul) \ - V(X87Float32Div) \ - V(X87Float32Abs) \ - V(X87Float32Neg) \ - V(X87Float32Sqrt) \ - V(X87Float32Round) \ - V(X87LoadFloat64Constant) \ - V(X87Float64Add) \ - V(X87Float64Sub) \ - V(X87Float64Mul) \ - V(X87Float64Div) \ - V(X87Float64Mod) \ - V(X87Float32Max) \ - V(X87Float64Max) \ - V(X87Float32Min) \ - V(X87Float64Min) \ - V(X87Float64Abs) \ - V(X87Float64Neg) \ - V(X87Int32ToFloat32) \ - V(X87Uint32ToFloat32) \ - V(X87Int32ToFloat64) \ - V(X87Float32ToFloat64) \ - V(X87Uint32ToFloat64) \ - V(X87Float64ToInt32) \ - V(X87Float32ToInt32) \ - V(X87Float32ToUint32) \ - V(X87Float64ToFloat32) \ - V(X87Float64ToUint32) \ - V(X87Float64ExtractHighWord32) \ - V(X87Float64ExtractLowWord32) \ - V(X87Float64InsertHighWord32) \ - V(X87Float64InsertLowWord32) \ - V(X87Float64Sqrt) \ - V(X87Float64Round) \ - V(X87Float64Cmp) \ - V(X87Float64SilenceNaN) \ - V(X87Movsxbl) \ - V(X87Movzxbl) \ - V(X87Movb) \ - V(X87Movsxwl) \ - V(X87Movzxwl) \ - V(X87Movw) \ - V(X87Movl) \ - V(X87Movss) \ - V(X87Movsd) \ - V(X87Lea) \ - V(X87BitcastFI) \ - V(X87BitcastIF) \ - V(X87Push) \ - V(X87PushFloat64) \ - V(X87PushFloat32) \ - V(X87Poke) \ - V(X87StackCheck) \ - V(X87Xchgb) \ - V(X87Xchgw) \ - V(X87Xchgl) - -// Addressing modes represent the "shape" of inputs to an instruction. -// Many instructions support multiple addressing modes. Addressing modes -// are encoded into the InstructionCode of the instruction and tell the -// code generator after register allocation which assembler method to call. -// -// We use the following local notation for addressing modes: -// -// M = memory operand -// R = base register -// N = index register * N for N in {1, 2, 4, 8} -// I = immediate displacement (int32_t) - -#define TARGET_ADDRESSING_MODE_LIST(V) \ - V(MR) /* [%r1 ] */ \ - V(MRI) /* [%r1 + K] */ \ - V(MR1) /* [%r1 + %r2*1 ] */ \ - V(MR2) /* [%r1 + %r2*2 ] */ \ - V(MR4) /* [%r1 + %r2*4 ] */ \ - V(MR8) /* [%r1 + %r2*8 ] */ \ - V(MR1I) /* [%r1 + %r2*1 + K] */ \ - V(MR2I) /* [%r1 + %r2*2 + K] */ \ - V(MR4I) /* [%r1 + %r2*3 + K] */ \ - V(MR8I) /* [%r1 + %r2*4 + K] */ \ - V(M1) /* [ %r2*1 ] */ \ - V(M2) /* [ %r2*2 ] */ \ - V(M4) /* [ %r2*4 ] */ \ - V(M8) /* [ %r2*8 ] */ \ - V(M1I) /* [ %r2*1 + K] */ \ - V(M2I) /* [ %r2*2 + K] */ \ - V(M4I) /* [ %r2*4 + K] */ \ - V(M8I) /* [ %r2*8 + K] */ \ - V(MI) /* [ K] */ - -} // namespace compiler -} // namespace internal -} // namespace v8 - -#endif // V8_COMPILER_X87_INSTRUCTION_CODES_X87_H_ diff --git a/src/compiler/x87/instruction-scheduler-x87.cc b/src/compiler/x87/instruction-scheduler-x87.cc deleted file mode 100644 index af86a87ad7..0000000000 --- a/src/compiler/x87/instruction-scheduler-x87.cc +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2015 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. - -#include "src/compiler/instruction-scheduler.h" - -namespace v8 { -namespace internal { -namespace compiler { - -bool InstructionScheduler::SchedulerSupported() { return false; } - - -int InstructionScheduler::GetTargetInstructionFlags( - const Instruction* instr) const { - UNIMPLEMENTED(); -} - - -int InstructionScheduler::GetInstructionLatency(const Instruction* instr) { - UNIMPLEMENTED(); -} - -} // namespace compiler -} // namespace internal -} // namespace v8 diff --git a/src/compiler/x87/instruction-selector-x87.cc b/src/compiler/x87/instruction-selector-x87.cc deleted file mode 100644 index 51af7db20f..0000000000 --- a/src/compiler/x87/instruction-selector-x87.cc +++ /dev/null @@ -1,1871 +0,0 @@ -// Copyright 2014 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. - -#include "src/base/adapters.h" -#include "src/compiler/instruction-selector-impl.h" -#include "src/compiler/node-matchers.h" -#include "src/compiler/node-properties.h" - -namespace v8 { -namespace internal { -namespace compiler { - -// Adds X87-specific methods for generating operands. -class X87OperandGenerator final : public OperandGenerator { - public: - explicit X87OperandGenerator(InstructionSelector* selector) - : OperandGenerator(selector) {} - - InstructionOperand UseByteRegister(Node* node) { - // TODO(titzer): encode byte register use constraints. - return UseFixed(node, edx); - } - - InstructionOperand DefineAsByteRegister(Node* node) { - // TODO(titzer): encode byte register def constraints. - return DefineAsRegister(node); - } - - bool CanBeMemoryOperand(InstructionCode opcode, Node* node, Node* input, - int effect_level) { - if (input->opcode() != IrOpcode::kLoad || - !selector()->CanCover(node, input)) { - return false; - } - if (effect_level != selector()->GetEffectLevel(input)) { - return false; - } - MachineRepresentation rep = - LoadRepresentationOf(input->op()).representation(); - switch (opcode) { - case kX87Cmp: - case kX87Test: - return rep == MachineRepresentation::kWord32 || - rep == MachineRepresentation::kTagged; - case kX87Cmp16: - case kX87Test16: - return rep == MachineRepresentation::kWord16; - case kX87Cmp8: - case kX87Test8: - return rep == MachineRepresentation::kWord8; - default: - break; - } - return false; - } - - InstructionOperand CreateImmediate(int imm) { - return sequence()->AddImmediate(Constant(imm)); - } - - bool CanBeImmediate(Node* node) { - switch (node->opcode()) { - case IrOpcode::kInt32Constant: - case IrOpcode::kNumberConstant: - case IrOpcode::kExternalConstant: - case IrOpcode::kRelocatableInt32Constant: - case IrOpcode::kRelocatableInt64Constant: - return true; - case IrOpcode::kHeapConstant: { -// TODO(bmeurer): We must not dereference handles concurrently. If we -// really have to this here, then we need to find a way to put this -// information on the HeapConstant node already. -#if 0 - // Constants in new space cannot be used as immediates in V8 because - // the GC does not scan code objects when collecting the new generation. - Handle value = OpParameter>(node); - Isolate* isolate = value->GetIsolate(); - return !isolate->heap()->InNewSpace(*value); -#endif - } - default: - return false; - } - } - - AddressingMode GenerateMemoryOperandInputs(Node* index, int scale, Node* base, - Node* displacement_node, - DisplacementMode displacement_mode, - InstructionOperand inputs[], - size_t* input_count) { - AddressingMode mode = kMode_MRI; - int32_t displacement = (displacement_node == nullptr) - ? 0 - : OpParameter(displacement_node); - if (displacement_mode == kNegativeDisplacement) { - displacement = -displacement; - } - if (base != nullptr) { - if (base->opcode() == IrOpcode::kInt32Constant) { - displacement += OpParameter(base); - base = nullptr; - } - } - if (base != nullptr) { - inputs[(*input_count)++] = UseRegister(base); - if (index != nullptr) { - DCHECK(scale >= 0 && scale <= 3); - inputs[(*input_count)++] = UseRegister(index); - if (displacement != 0) { - inputs[(*input_count)++] = TempImmediate(displacement); - static const AddressingMode kMRnI_modes[] = {kMode_MR1I, kMode_MR2I, - kMode_MR4I, kMode_MR8I}; - mode = kMRnI_modes[scale]; - } else { - static const AddressingMode kMRn_modes[] = {kMode_MR1, kMode_MR2, - kMode_MR4, kMode_MR8}; - mode = kMRn_modes[scale]; - } - } else { - if (displacement == 0) { - mode = kMode_MR; - } else { - inputs[(*input_count)++] = TempImmediate(displacement); - mode = kMode_MRI; - } - } - } else { - DCHECK(scale >= 0 && scale <= 3); - if (index != nullptr) { - inputs[(*input_count)++] = UseRegister(index); - if (displacement != 0) { - inputs[(*input_count)++] = TempImmediate(displacement); - static const AddressingMode kMnI_modes[] = {kMode_MRI, kMode_M2I, - kMode_M4I, kMode_M8I}; - mode = kMnI_modes[scale]; - } else { - static const AddressingMode kMn_modes[] = {kMode_MR, kMode_M2, - kMode_M4, kMode_M8}; - mode = kMn_modes[scale]; - } - } else { - inputs[(*input_count)++] = TempImmediate(displacement); - return kMode_MI; - } - } - return mode; - } - - AddressingMode GetEffectiveAddressMemoryOperand(Node* node, - InstructionOperand inputs[], - size_t* input_count) { - BaseWithIndexAndDisplacement32Matcher m(node, AddressOption::kAllowAll); - DCHECK(m.matches()); - if ((m.displacement() == nullptr || CanBeImmediate(m.displacement()))) { - return GenerateMemoryOperandInputs( - m.index(), m.scale(), m.base(), m.displacement(), - m.displacement_mode(), inputs, input_count); - } else { - inputs[(*input_count)++] = UseRegister(node->InputAt(0)); - inputs[(*input_count)++] = UseRegister(node->InputAt(1)); - return kMode_MR1; - } - } - - bool CanBeBetterLeftOperand(Node* node) const { - return !selector()->IsLive(node); - } -}; - -void InstructionSelector::VisitStackSlot(Node* node) { - StackSlotRepresentation rep = StackSlotRepresentationOf(node->op()); - int slot = frame_->AllocateSpillSlot(rep.size()); - OperandGenerator g(this); - - Emit(kArchStackSlot, g.DefineAsRegister(node), - sequence()->AddImmediate(Constant(slot)), 0, nullptr); -} - -void InstructionSelector::VisitLoad(Node* node) { - LoadRepresentation load_rep = LoadRepresentationOf(node->op()); - - ArchOpcode opcode = kArchNop; - switch (load_rep.representation()) { - case MachineRepresentation::kFloat32: - opcode = kX87Movss; - break; - case MachineRepresentation::kFloat64: - opcode = kX87Movsd; - break; - case MachineRepresentation::kBit: // Fall through. - case MachineRepresentation::kWord8: - opcode = load_rep.IsSigned() ? kX87Movsxbl : kX87Movzxbl; - break; - case MachineRepresentation::kWord16: - opcode = load_rep.IsSigned() ? kX87Movsxwl : kX87Movzxwl; - break; - case MachineRepresentation::kTaggedSigned: // Fall through. - case MachineRepresentation::kTaggedPointer: // Fall through. - case MachineRepresentation::kTagged: // Fall through. - case MachineRepresentation::kWord32: - opcode = kX87Movl; - break; - case MachineRepresentation::kWord64: // Fall through. - case MachineRepresentation::kSimd128: // Fall through. - case MachineRepresentation::kNone: - UNREACHABLE(); - return; - } - - X87OperandGenerator g(this); - InstructionOperand outputs[1]; - outputs[0] = g.DefineAsRegister(node); - InstructionOperand inputs[3]; - size_t input_count = 0; - AddressingMode mode = - g.GetEffectiveAddressMemoryOperand(node, inputs, &input_count); - InstructionCode code = opcode | AddressingModeField::encode(mode); - Emit(code, 1, outputs, input_count, inputs); -} - -void InstructionSelector::VisitProtectedLoad(Node* node) { - // TODO(eholk) - UNIMPLEMENTED(); -} - -void InstructionSelector::VisitStore(Node* node) { - X87OperandGenerator g(this); - Node* base = node->InputAt(0); - Node* index = node->InputAt(1); - Node* value = node->InputAt(2); - - StoreRepresentation store_rep = StoreRepresentationOf(node->op()); - WriteBarrierKind write_barrier_kind = store_rep.write_barrier_kind(); - MachineRepresentation rep = store_rep.representation(); - - if (write_barrier_kind != kNoWriteBarrier) { - DCHECK(CanBeTaggedPointer(rep)); - AddressingMode addressing_mode; - InstructionOperand inputs[3]; - size_t input_count = 0; - inputs[input_count++] = g.UseUniqueRegister(base); - if (g.CanBeImmediate(index)) { - inputs[input_count++] = g.UseImmediate(index); - addressing_mode = kMode_MRI; - } else { - inputs[input_count++] = g.UseUniqueRegister(index); - addressing_mode = kMode_MR1; - } - inputs[input_count++] = g.UseUniqueRegister(value); - RecordWriteMode record_write_mode = RecordWriteMode::kValueIsAny; - switch (write_barrier_kind) { - case kNoWriteBarrier: - UNREACHABLE(); - break; - case kMapWriteBarrier: - record_write_mode = RecordWriteMode::kValueIsMap; - break; - case kPointerWriteBarrier: - record_write_mode = RecordWriteMode::kValueIsPointer; - break; - case kFullWriteBarrier: - record_write_mode = RecordWriteMode::kValueIsAny; - break; - } - InstructionOperand temps[] = {g.TempRegister(), g.TempRegister()}; - size_t const temp_count = arraysize(temps); - InstructionCode code = kArchStoreWithWriteBarrier; - code |= AddressingModeField::encode(addressing_mode); - code |= MiscField::encode(static_cast(record_write_mode)); - Emit(code, 0, nullptr, input_count, inputs, temp_count, temps); - } else { - ArchOpcode opcode = kArchNop; - switch (rep) { - case MachineRepresentation::kFloat32: - opcode = kX87Movss; - break; - case MachineRepresentation::kFloat64: - opcode = kX87Movsd; - break; - case MachineRepresentation::kBit: // Fall through. - case MachineRepresentation::kWord8: - opcode = kX87Movb; - break; - case MachineRepresentation::kWord16: - opcode = kX87Movw; - break; - case MachineRepresentation::kTaggedSigned: // Fall through. - case MachineRepresentation::kTaggedPointer: // Fall through. - case MachineRepresentation::kTagged: // Fall through. - case MachineRepresentation::kWord32: - opcode = kX87Movl; - break; - case MachineRepresentation::kWord64: // Fall through. - case MachineRepresentation::kSimd128: // Fall through. - case MachineRepresentation::kNone: - UNREACHABLE(); - return; - } - - InstructionOperand val; - if (g.CanBeImmediate(value)) { - val = g.UseImmediate(value); - } else if (rep == MachineRepresentation::kWord8 || - rep == MachineRepresentation::kBit) { - val = g.UseByteRegister(value); - } else { - val = g.UseRegister(value); - } - - InstructionOperand inputs[4]; - size_t input_count = 0; - AddressingMode addressing_mode = - g.GetEffectiveAddressMemoryOperand(node, inputs, &input_count); - InstructionCode code = - opcode | AddressingModeField::encode(addressing_mode); - inputs[input_count++] = val; - Emit(code, 0, static_cast(nullptr), input_count, - inputs); - } -} - -void InstructionSelector::VisitProtectedStore(Node* node) { - // TODO(eholk) - UNIMPLEMENTED(); -} - -// Architecture supports unaligned access, therefore VisitLoad is used instead -void InstructionSelector::VisitUnalignedLoad(Node* node) { UNREACHABLE(); } - -// Architecture supports unaligned access, therefore VisitStore is used instead -void InstructionSelector::VisitUnalignedStore(Node* node) { UNREACHABLE(); } - -void InstructionSelector::VisitCheckedLoad(Node* node) { - CheckedLoadRepresentation load_rep = CheckedLoadRepresentationOf(node->op()); - X87OperandGenerator g(this); - Node* const buffer = node->InputAt(0); - Node* const offset = node->InputAt(1); - Node* const length = node->InputAt(2); - ArchOpcode opcode = kArchNop; - switch (load_rep.representation()) { - case MachineRepresentation::kWord8: - opcode = load_rep.IsSigned() ? kCheckedLoadInt8 : kCheckedLoadUint8; - break; - case MachineRepresentation::kWord16: - opcode = load_rep.IsSigned() ? kCheckedLoadInt16 : kCheckedLoadUint16; - break; - case MachineRepresentation::kWord32: - opcode = kCheckedLoadWord32; - break; - case MachineRepresentation::kFloat32: - opcode = kCheckedLoadFloat32; - break; - case MachineRepresentation::kFloat64: - opcode = kCheckedLoadFloat64; - break; - case MachineRepresentation::kBit: // Fall through. - case MachineRepresentation::kTaggedSigned: // Fall through. - case MachineRepresentation::kTaggedPointer: // Fall through. - case MachineRepresentation::kTagged: // Fall through. - case MachineRepresentation::kWord64: // Fall through. - case MachineRepresentation::kSimd128: // Fall through. - case MachineRepresentation::kNone: - UNREACHABLE(); - return; - } - InstructionOperand offset_operand = g.UseRegister(offset); - InstructionOperand length_operand = - g.CanBeImmediate(length) ? g.UseImmediate(length) : g.UseRegister(length); - if (g.CanBeImmediate(buffer)) { - Emit(opcode | AddressingModeField::encode(kMode_MRI), - g.DefineAsRegister(node), offset_operand, length_operand, - offset_operand, g.UseImmediate(buffer)); - } else { - Emit(opcode | AddressingModeField::encode(kMode_MR1), - g.DefineAsRegister(node), offset_operand, length_operand, - g.UseRegister(buffer), offset_operand); - } -} - - -void InstructionSelector::VisitCheckedStore(Node* node) { - MachineRepresentation rep = CheckedStoreRepresentationOf(node->op()); - X87OperandGenerator g(this); - Node* const buffer = node->InputAt(0); - Node* const offset = node->InputAt(1); - Node* const length = node->InputAt(2); - Node* const value = node->InputAt(3); - ArchOpcode opcode = kArchNop; - switch (rep) { - case MachineRepresentation::kWord8: - opcode = kCheckedStoreWord8; - break; - case MachineRepresentation::kWord16: - opcode = kCheckedStoreWord16; - break; - case MachineRepresentation::kWord32: - opcode = kCheckedStoreWord32; - break; - case MachineRepresentation::kFloat32: - opcode = kCheckedStoreFloat32; - break; - case MachineRepresentation::kFloat64: - opcode = kCheckedStoreFloat64; - break; - case MachineRepresentation::kBit: // Fall through. - case MachineRepresentation::kTaggedSigned: // Fall through. - case MachineRepresentation::kTaggedPointer: // Fall through. - case MachineRepresentation::kTagged: // Fall through. - case MachineRepresentation::kWord64: // Fall through. - case MachineRepresentation::kSimd128: // Fall through. - case MachineRepresentation::kNone: - UNREACHABLE(); - return; - } - InstructionOperand value_operand = - g.CanBeImmediate(value) ? g.UseImmediate(value) - : ((rep == MachineRepresentation::kWord8 || - rep == MachineRepresentation::kBit) - ? g.UseByteRegister(value) - : g.UseRegister(value)); - InstructionOperand offset_operand = g.UseRegister(offset); - InstructionOperand length_operand = - g.CanBeImmediate(length) ? g.UseImmediate(length) : g.UseRegister(length); - if (g.CanBeImmediate(buffer)) { - Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(), - offset_operand, length_operand, value_operand, offset_operand, - g.UseImmediate(buffer)); - } else { - Emit(opcode | AddressingModeField::encode(kMode_MR1), g.NoOutput(), - offset_operand, length_operand, value_operand, g.UseRegister(buffer), - offset_operand); - } -} - -namespace { - -// Shared routine for multiple binary operations. -void VisitBinop(InstructionSelector* selector, Node* node, - InstructionCode opcode, FlagsContinuation* cont) { - X87OperandGenerator g(selector); - Int32BinopMatcher m(node); - Node* left = m.left().node(); - Node* right = m.right().node(); - InstructionOperand inputs[4]; - size_t input_count = 0; - InstructionOperand outputs[2]; - size_t output_count = 0; - - // TODO(turbofan): match complex addressing modes. - if (left == right) { - // If both inputs refer to the same operand, enforce allocating a register - // for both of them to ensure that we don't end up generating code like - // this: - // - // mov eax, [ebp-0x10] - // add eax, [ebp-0x10] - // jo label - InstructionOperand const input = g.UseRegister(left); - inputs[input_count++] = input; - inputs[input_count++] = input; - } else if (g.CanBeImmediate(right)) { - inputs[input_count++] = g.UseRegister(left); - inputs[input_count++] = g.UseImmediate(right); - } else { - if (node->op()->HasProperty(Operator::kCommutative) && - g.CanBeBetterLeftOperand(right)) { - std::swap(left, right); - } - inputs[input_count++] = g.UseRegister(left); - inputs[input_count++] = g.Use(right); - } - - if (cont->IsBranch()) { - inputs[input_count++] = g.Label(cont->true_block()); - inputs[input_count++] = g.Label(cont->false_block()); - } - - outputs[output_count++] = g.DefineSameAsFirst(node); - if (cont->IsSet()) { - outputs[output_count++] = g.DefineAsRegister(cont->result()); - } - - DCHECK_NE(0u, input_count); - DCHECK_NE(0u, output_count); - DCHECK_GE(arraysize(inputs), input_count); - DCHECK_GE(arraysize(outputs), output_count); - - opcode = cont->Encode(opcode); - if (cont->IsDeoptimize()) { - selector->EmitDeoptimize(opcode, output_count, outputs, input_count, inputs, - cont->kind(), cont->reason(), cont->frame_state()); - } else { - selector->Emit(opcode, output_count, outputs, input_count, inputs); - } -} - - -// Shared routine for multiple binary operations. -void VisitBinop(InstructionSelector* selector, Node* node, - InstructionCode opcode) { - FlagsContinuation cont; - VisitBinop(selector, node, opcode, &cont); -} - -} // namespace - -void InstructionSelector::VisitWord32And(Node* node) { - VisitBinop(this, node, kX87And); -} - - -void InstructionSelector::VisitWord32Or(Node* node) { - VisitBinop(this, node, kX87Or); -} - - -void InstructionSelector::VisitWord32Xor(Node* node) { - X87OperandGenerator g(this); - Int32BinopMatcher m(node); - if (m.right().Is(-1)) { - Emit(kX87Not, g.DefineSameAsFirst(node), g.UseRegister(m.left().node())); - } else { - VisitBinop(this, node, kX87Xor); - } -} - - -// Shared routine for multiple shift operations. -static inline void VisitShift(InstructionSelector* selector, Node* node, - ArchOpcode opcode) { - X87OperandGenerator g(selector); - Node* left = node->InputAt(0); - Node* right = node->InputAt(1); - - if (g.CanBeImmediate(right)) { - selector->Emit(opcode, g.DefineSameAsFirst(node), g.UseRegister(left), - g.UseImmediate(right)); - } else { - selector->Emit(opcode, g.DefineSameAsFirst(node), g.UseRegister(left), - g.UseFixed(right, ecx)); - } -} - - -namespace { - -void VisitMulHigh(InstructionSelector* selector, Node* node, - ArchOpcode opcode) { - X87OperandGenerator g(selector); - InstructionOperand temps[] = {g.TempRegister(eax)}; - selector->Emit( - opcode, g.DefineAsFixed(node, edx), g.UseFixed(node->InputAt(0), eax), - g.UseUniqueRegister(node->InputAt(1)), arraysize(temps), temps); -} - - -void VisitDiv(InstructionSelector* selector, Node* node, ArchOpcode opcode) { - X87OperandGenerator g(selector); - InstructionOperand temps[] = {g.TempRegister(edx)}; - selector->Emit(opcode, g.DefineAsFixed(node, eax), - g.UseFixed(node->InputAt(0), eax), - g.UseUnique(node->InputAt(1)), arraysize(temps), temps); -} - - -void VisitMod(InstructionSelector* selector, Node* node, ArchOpcode opcode) { - X87OperandGenerator g(selector); - InstructionOperand temps[] = {g.TempRegister(eax)}; - selector->Emit(opcode, g.DefineAsFixed(node, edx), - g.UseFixed(node->InputAt(0), eax), - g.UseUnique(node->InputAt(1)), arraysize(temps), temps); -} - -void EmitLea(InstructionSelector* selector, Node* result, Node* index, - int scale, Node* base, Node* displacement, - DisplacementMode displacement_mode) { - X87OperandGenerator g(selector); - InstructionOperand inputs[4]; - size_t input_count = 0; - AddressingMode mode = - g.GenerateMemoryOperandInputs(index, scale, base, displacement, - displacement_mode, inputs, &input_count); - - DCHECK_NE(0u, input_count); - DCHECK_GE(arraysize(inputs), input_count); - - InstructionOperand outputs[1]; - outputs[0] = g.DefineAsRegister(result); - - InstructionCode opcode = AddressingModeField::encode(mode) | kX87Lea; - - selector->Emit(opcode, 1, outputs, input_count, inputs); -} - -} // namespace - - -void InstructionSelector::VisitWord32Shl(Node* node) { - Int32ScaleMatcher m(node, true); - if (m.matches()) { - Node* index = node->InputAt(0); - Node* base = m.power_of_two_plus_one() ? index : nullptr; - EmitLea(this, node, index, m.scale(), base, nullptr, kPositiveDisplacement); - return; - } - VisitShift(this, node, kX87Shl); -} - - -void InstructionSelector::VisitWord32Shr(Node* node) { - VisitShift(this, node, kX87Shr); -} - - -void InstructionSelector::VisitWord32Sar(Node* node) { - VisitShift(this, node, kX87Sar); -} - -void InstructionSelector::VisitInt32PairAdd(Node* node) { - X87OperandGenerator g(this); - - Node* projection1 = NodeProperties::FindProjection(node, 1); - if (projection1) { - // We use UseUniqueRegister here to avoid register sharing with the temp - // register. - InstructionOperand inputs[] = { - g.UseRegister(node->InputAt(0)), g.UseUniqueRegister(node->InputAt(1)), - g.UseRegister(node->InputAt(2)), g.UseUniqueRegister(node->InputAt(3))}; - - InstructionOperand outputs[] = {g.DefineSameAsFirst(node), - g.DefineAsRegister(projection1)}; - - InstructionOperand temps[] = {g.TempRegister()}; - - Emit(kX87AddPair, 2, outputs, 4, inputs, 1, temps); - } else { - // The high word of the result is not used, so we emit the standard 32 bit - // instruction. - Emit(kX87Add, g.DefineSameAsFirst(node), g.UseRegister(node->InputAt(0)), - g.Use(node->InputAt(2))); - } -} - -void InstructionSelector::VisitInt32PairSub(Node* node) { - X87OperandGenerator g(this); - - Node* projection1 = NodeProperties::FindProjection(node, 1); - if (projection1) { - // We use UseUniqueRegister here to avoid register sharing with the temp - // register. - InstructionOperand inputs[] = { - g.UseRegister(node->InputAt(0)), g.UseUniqueRegister(node->InputAt(1)), - g.UseRegister(node->InputAt(2)), g.UseUniqueRegister(node->InputAt(3))}; - - InstructionOperand outputs[] = {g.DefineSameAsFirst(node), - g.DefineAsRegister(projection1)}; - - InstructionOperand temps[] = {g.TempRegister()}; - - Emit(kX87SubPair, 2, outputs, 4, inputs, 1, temps); - } else { - // The high word of the result is not used, so we emit the standard 32 bit - // instruction. - Emit(kX87Sub, g.DefineSameAsFirst(node), g.UseRegister(node->InputAt(0)), - g.Use(node->InputAt(2))); - } -} - -void InstructionSelector::VisitInt32PairMul(Node* node) { - X87OperandGenerator g(this); - - Node* projection1 = NodeProperties::FindProjection(node, 1); - if (projection1) { - // InputAt(3) explicitly shares ecx with OutputRegister(1) to save one - // register and one mov instruction. - InstructionOperand inputs[] = {g.UseUnique(node->InputAt(0)), - g.UseUnique(node->InputAt(1)), - g.UseUniqueRegister(node->InputAt(2)), - g.UseFixed(node->InputAt(3), ecx)}; - - InstructionOperand outputs[] = { - g.DefineAsFixed(node, eax), - g.DefineAsFixed(NodeProperties::FindProjection(node, 1), ecx)}; - - InstructionOperand temps[] = {g.TempRegister(edx)}; - - Emit(kX87MulPair, 2, outputs, 4, inputs, 1, temps); - } else { - // The high word of the result is not used, so we emit the standard 32 bit - // instruction. - Emit(kX87Imul, g.DefineSameAsFirst(node), g.UseRegister(node->InputAt(0)), - g.Use(node->InputAt(2))); - } -} - -void VisitWord32PairShift(InstructionSelector* selector, InstructionCode opcode, - Node* node) { - X87OperandGenerator g(selector); - - Node* shift = node->InputAt(2); - InstructionOperand shift_operand; - if (g.CanBeImmediate(shift)) { - shift_operand = g.UseImmediate(shift); - } else { - shift_operand = g.UseFixed(shift, ecx); - } - InstructionOperand inputs[] = {g.UseFixed(node->InputAt(0), eax), - g.UseFixed(node->InputAt(1), edx), - shift_operand}; - - InstructionOperand outputs[2]; - InstructionOperand temps[1]; - int32_t output_count = 0; - int32_t temp_count = 0; - outputs[output_count++] = g.DefineAsFixed(node, eax); - Node* projection1 = NodeProperties::FindProjection(node, 1); - if (projection1) { - outputs[output_count++] = g.DefineAsFixed(projection1, edx); - } else { - temps[temp_count++] = g.TempRegister(edx); - } - - selector->Emit(opcode, output_count, outputs, 3, inputs, temp_count, temps); -} - -void InstructionSelector::VisitWord32PairShl(Node* node) { - VisitWord32PairShift(this, kX87ShlPair, node); -} - -void InstructionSelector::VisitWord32PairShr(Node* node) { - VisitWord32PairShift(this, kX87ShrPair, node); -} - -void InstructionSelector::VisitWord32PairSar(Node* node) { - VisitWord32PairShift(this, kX87SarPair, node); -} - -void InstructionSelector::VisitWord32Ror(Node* node) { - VisitShift(this, node, kX87Ror); -} - - -void InstructionSelector::VisitWord32Clz(Node* node) { - X87OperandGenerator g(this); - Emit(kX87Lzcnt, g.DefineAsRegister(node), g.Use(node->InputAt(0))); -} - - -void InstructionSelector::VisitWord32Ctz(Node* node) { UNREACHABLE(); } - - -void InstructionSelector::VisitWord32ReverseBits(Node* node) { UNREACHABLE(); } - -void InstructionSelector::VisitWord64ReverseBytes(Node* node) { UNREACHABLE(); } - -void InstructionSelector::VisitWord32ReverseBytes(Node* node) { UNREACHABLE(); } - -void InstructionSelector::VisitWord32Popcnt(Node* node) { - X87OperandGenerator g(this); - Emit(kX87Popcnt, g.DefineAsRegister(node), g.Use(node->InputAt(0))); -} - - -void InstructionSelector::VisitInt32Add(Node* node) { - X87OperandGenerator g(this); - - // Try to match the Add to a lea pattern - BaseWithIndexAndDisplacement32Matcher m(node); - if (m.matches() && - (m.displacement() == nullptr || g.CanBeImmediate(m.displacement()))) { - InstructionOperand inputs[4]; - size_t input_count = 0; - AddressingMode mode = g.GenerateMemoryOperandInputs( - m.index(), m.scale(), m.base(), m.displacement(), m.displacement_mode(), - inputs, &input_count); - - DCHECK_NE(0u, input_count); - DCHECK_GE(arraysize(inputs), input_count); - - InstructionOperand outputs[1]; - outputs[0] = g.DefineAsRegister(node); - - InstructionCode opcode = AddressingModeField::encode(mode) | kX87Lea; - Emit(opcode, 1, outputs, input_count, inputs); - return; - } - - // No lea pattern match, use add - VisitBinop(this, node, kX87Add); -} - - -void InstructionSelector::VisitInt32Sub(Node* node) { - X87OperandGenerator g(this); - Int32BinopMatcher m(node); - if (m.left().Is(0)) { - Emit(kX87Neg, g.DefineSameAsFirst(node), g.Use(m.right().node())); - } else { - VisitBinop(this, node, kX87Sub); - } -} - - -void InstructionSelector::VisitInt32Mul(Node* node) { - Int32ScaleMatcher m(node, true); - if (m.matches()) { - Node* index = node->InputAt(0); - Node* base = m.power_of_two_plus_one() ? index : nullptr; - EmitLea(this, node, index, m.scale(), base, nullptr, kPositiveDisplacement); - return; - } - X87OperandGenerator g(this); - Node* left = node->InputAt(0); - Node* right = node->InputAt(1); - if (g.CanBeImmediate(right)) { - Emit(kX87Imul, g.DefineAsRegister(node), g.Use(left), - g.UseImmediate(right)); - } else { - if (g.CanBeBetterLeftOperand(right)) { - std::swap(left, right); - } - Emit(kX87Imul, g.DefineSameAsFirst(node), g.UseRegister(left), - g.Use(right)); - } -} - - -void InstructionSelector::VisitInt32MulHigh(Node* node) { - VisitMulHigh(this, node, kX87ImulHigh); -} - - -void InstructionSelector::VisitUint32MulHigh(Node* node) { - VisitMulHigh(this, node, kX87UmulHigh); -} - - -void InstructionSelector::VisitInt32Div(Node* node) { - VisitDiv(this, node, kX87Idiv); -} - - -void InstructionSelector::VisitUint32Div(Node* node) { - VisitDiv(this, node, kX87Udiv); -} - - -void InstructionSelector::VisitInt32Mod(Node* node) { - VisitMod(this, node, kX87Idiv); -} - - -void InstructionSelector::VisitUint32Mod(Node* node) { - VisitMod(this, node, kX87Udiv); -} - - -void InstructionSelector::VisitChangeFloat32ToFloat64(Node* node) { - X87OperandGenerator g(this); - Emit(kX87Float32ToFloat64, g.DefineAsFixed(node, stX_0), - g.Use(node->InputAt(0))); -} - - -void InstructionSelector::VisitRoundInt32ToFloat32(Node* node) { - X87OperandGenerator g(this); - Emit(kX87Int32ToFloat32, g.DefineAsFixed(node, stX_0), - g.Use(node->InputAt(0))); -} - - -void InstructionSelector::VisitRoundUint32ToFloat32(Node* node) { - X87OperandGenerator g(this); - Emit(kX87Uint32ToFloat32, g.DefineAsFixed(node, stX_0), - g.Use(node->InputAt(0))); -} - - -void InstructionSelector::VisitChangeInt32ToFloat64(Node* node) { - X87OperandGenerator g(this); - Emit(kX87Int32ToFloat64, g.DefineAsFixed(node, stX_0), - g.Use(node->InputAt(0))); -} - - -void InstructionSelector::VisitChangeUint32ToFloat64(Node* node) { - X87OperandGenerator g(this); - Emit(kX87Uint32ToFloat64, g.DefineAsFixed(node, stX_0), - g.UseRegister(node->InputAt(0))); -} - - -void InstructionSelector::VisitTruncateFloat32ToInt32(Node* node) { - X87OperandGenerator g(this); - Emit(kX87Float32ToInt32, g.DefineAsRegister(node), g.Use(node->InputAt(0))); -} - - -void InstructionSelector::VisitTruncateFloat32ToUint32(Node* node) { - X87OperandGenerator g(this); - Emit(kX87Float32ToUint32, g.DefineAsRegister(node), g.Use(node->InputAt(0))); -} - - -void InstructionSelector::VisitChangeFloat64ToInt32(Node* node) { - X87OperandGenerator g(this); - Emit(kX87Float64ToInt32, g.DefineAsRegister(node), g.Use(node->InputAt(0))); -} - - -void InstructionSelector::VisitChangeFloat64ToUint32(Node* node) { - X87OperandGenerator g(this); - Emit(kX87Float64ToUint32, g.DefineAsRegister(node), g.Use(node->InputAt(0))); -} - -void InstructionSelector::VisitTruncateFloat64ToUint32(Node* node) { - X87OperandGenerator g(this); - Emit(kX87Float64ToUint32, g.DefineAsRegister(node), g.Use(node->InputAt(0))); -} - -void InstructionSelector::VisitTruncateFloat64ToFloat32(Node* node) { - X87OperandGenerator g(this); - Emit(kX87Float64ToFloat32, g.DefineAsFixed(node, stX_0), - g.Use(node->InputAt(0))); -} - -void InstructionSelector::VisitTruncateFloat64ToWord32(Node* node) { - X87OperandGenerator g(this); - Emit(kArchTruncateDoubleToI, g.DefineAsRegister(node), - g.Use(node->InputAt(0))); -} - -void InstructionSelector::VisitRoundFloat64ToInt32(Node* node) { - X87OperandGenerator g(this); - Emit(kX87Float64ToInt32, g.DefineAsRegister(node), g.Use(node->InputAt(0))); -} - - -void InstructionSelector::VisitBitcastFloat32ToInt32(Node* node) { - X87OperandGenerator g(this); - Emit(kX87PushFloat32, g.NoOutput(), g.Use(node->InputAt(0))); - Emit(kX87BitcastFI, g.DefineAsRegister(node), 0, nullptr); -} - - -void InstructionSelector::VisitBitcastInt32ToFloat32(Node* node) { - X87OperandGenerator g(this); - Emit(kX87BitcastIF, g.DefineAsFixed(node, stX_0), g.Use(node->InputAt(0))); -} - - -void InstructionSelector::VisitFloat32Add(Node* node) { - X87OperandGenerator g(this); - Emit(kX87PushFloat32, g.NoOutput(), g.Use(node->InputAt(0))); - Emit(kX87PushFloat32, g.NoOutput(), g.Use(node->InputAt(1))); - Emit(kX87Float32Add, g.DefineAsFixed(node, stX_0), 0, nullptr); -} - - -void InstructionSelector::VisitFloat64Add(Node* node) { - X87OperandGenerator g(this); - Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(0))); - Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(1))); - Emit(kX87Float64Add, g.DefineAsFixed(node, stX_0), 0, nullptr); -} - - -void InstructionSelector::VisitFloat32Sub(Node* node) { - X87OperandGenerator g(this); - Emit(kX87PushFloat32, g.NoOutput(), g.Use(node->InputAt(0))); - Emit(kX87PushFloat32, g.NoOutput(), g.Use(node->InputAt(1))); - Emit(kX87Float32Sub, g.DefineAsFixed(node, stX_0), 0, nullptr); -} - -void InstructionSelector::VisitFloat64Sub(Node* node) { - X87OperandGenerator g(this); - Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(0))); - Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(1))); - Emit(kX87Float64Sub, g.DefineAsFixed(node, stX_0), 0, nullptr); -} - -void InstructionSelector::VisitFloat32Mul(Node* node) { - X87OperandGenerator g(this); - Emit(kX87PushFloat32, g.NoOutput(), g.Use(node->InputAt(0))); - Emit(kX87PushFloat32, g.NoOutput(), g.Use(node->InputAt(1))); - Emit(kX87Float32Mul, g.DefineAsFixed(node, stX_0), 0, nullptr); -} - - -void InstructionSelector::VisitFloat64Mul(Node* node) { - X87OperandGenerator g(this); - Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(0))); - Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(1))); - Emit(kX87Float64Mul, g.DefineAsFixed(node, stX_0), 0, nullptr); -} - - -void InstructionSelector::VisitFloat32Div(Node* node) { - X87OperandGenerator g(this); - Emit(kX87PushFloat32, g.NoOutput(), g.Use(node->InputAt(0))); - Emit(kX87PushFloat32, g.NoOutput(), g.Use(node->InputAt(1))); - Emit(kX87Float32Div, g.DefineAsFixed(node, stX_0), 0, nullptr); -} - - -void InstructionSelector::VisitFloat64Div(Node* node) { - X87OperandGenerator g(this); - Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(0))); - Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(1))); - Emit(kX87Float64Div, g.DefineAsFixed(node, stX_0), 0, nullptr); -} - - -void InstructionSelector::VisitFloat64Mod(Node* node) { - X87OperandGenerator g(this); - InstructionOperand temps[] = {g.TempRegister(eax)}; - Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(0))); - Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(1))); - Emit(kX87Float64Mod, g.DefineAsFixed(node, stX_0), 1, temps)->MarkAsCall(); -} - -void InstructionSelector::VisitFloat32Max(Node* node) { - X87OperandGenerator g(this); - Emit(kX87PushFloat32, g.NoOutput(), g.Use(node->InputAt(0))); - Emit(kX87PushFloat32, g.NoOutput(), g.Use(node->InputAt(1))); - Emit(kX87Float32Max, g.DefineAsFixed(node, stX_0), 0, nullptr); -} - -void InstructionSelector::VisitFloat64Max(Node* node) { - X87OperandGenerator g(this); - Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(0))); - Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(1))); - Emit(kX87Float64Max, g.DefineAsFixed(node, stX_0), 0, nullptr); -} - -void InstructionSelector::VisitFloat32Min(Node* node) { - X87OperandGenerator g(this); - Emit(kX87PushFloat32, g.NoOutput(), g.Use(node->InputAt(0))); - Emit(kX87PushFloat32, g.NoOutput(), g.Use(node->InputAt(1))); - Emit(kX87Float32Min, g.DefineAsFixed(node, stX_0), 0, nullptr); -} - -void InstructionSelector::VisitFloat64Min(Node* node) { - X87OperandGenerator g(this); - Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(0))); - Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(1))); - Emit(kX87Float64Min, g.DefineAsFixed(node, stX_0), 0, nullptr); -} - - -void InstructionSelector::VisitFloat32Abs(Node* node) { - X87OperandGenerator g(this); - Emit(kX87PushFloat32, g.NoOutput(), g.Use(node->InputAt(0))); - Emit(kX87Float32Abs, g.DefineAsFixed(node, stX_0), 0, nullptr); -} - - -void InstructionSelector::VisitFloat64Abs(Node* node) { - X87OperandGenerator g(this); - Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(0))); - Emit(kX87Float64Abs, g.DefineAsFixed(node, stX_0), 0, nullptr); -} - -void InstructionSelector::VisitFloat32Sqrt(Node* node) { - X87OperandGenerator g(this); - Emit(kX87PushFloat32, g.NoOutput(), g.Use(node->InputAt(0))); - Emit(kX87Float32Sqrt, g.DefineAsFixed(node, stX_0), 0, nullptr); -} - - -void InstructionSelector::VisitFloat64Sqrt(Node* node) { - X87OperandGenerator g(this); - Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(0))); - Emit(kX87Float64Sqrt, g.DefineAsFixed(node, stX_0), 0, nullptr); -} - - -void InstructionSelector::VisitFloat32RoundDown(Node* node) { - X87OperandGenerator g(this); - Emit(kX87Float32Round | MiscField::encode(kRoundDown), - g.UseFixed(node, stX_0), g.Use(node->InputAt(0))); -} - - -void InstructionSelector::VisitFloat64RoundDown(Node* node) { - X87OperandGenerator g(this); - Emit(kX87Float64Round | MiscField::encode(kRoundDown), - g.UseFixed(node, stX_0), g.Use(node->InputAt(0))); -} - - -void InstructionSelector::VisitFloat32RoundUp(Node* node) { - X87OperandGenerator g(this); - Emit(kX87Float32Round | MiscField::encode(kRoundUp), g.UseFixed(node, stX_0), - g.Use(node->InputAt(0))); -} - - -void InstructionSelector::VisitFloat64RoundUp(Node* node) { - X87OperandGenerator g(this); - Emit(kX87Float64Round | MiscField::encode(kRoundUp), g.UseFixed(node, stX_0), - g.Use(node->InputAt(0))); -} - - -void InstructionSelector::VisitFloat32RoundTruncate(Node* node) { - X87OperandGenerator g(this); - Emit(kX87Float32Round | MiscField::encode(kRoundToZero), - g.UseFixed(node, stX_0), g.Use(node->InputAt(0))); -} - - -void InstructionSelector::VisitFloat64RoundTruncate(Node* node) { - X87OperandGenerator g(this); - Emit(kX87Float64Round | MiscField::encode(kRoundToZero), - g.UseFixed(node, stX_0), g.Use(node->InputAt(0))); -} - - -void InstructionSelector::VisitFloat64RoundTiesAway(Node* node) { - UNREACHABLE(); -} - - -void InstructionSelector::VisitFloat32RoundTiesEven(Node* node) { - X87OperandGenerator g(this); - Emit(kX87Float32Round | MiscField::encode(kRoundToNearest), - g.UseFixed(node, stX_0), g.Use(node->InputAt(0))); -} - - -void InstructionSelector::VisitFloat64RoundTiesEven(Node* node) { - X87OperandGenerator g(this); - Emit(kX87Float64Round | MiscField::encode(kRoundToNearest), - g.UseFixed(node, stX_0), g.Use(node->InputAt(0))); -} - -void InstructionSelector::VisitFloat32Neg(Node* node) { - X87OperandGenerator g(this); - Emit(kX87PushFloat32, g.NoOutput(), g.Use(node->InputAt(0))); - Emit(kX87Float32Neg, g.DefineAsFixed(node, stX_0), 0, nullptr); -} - -void InstructionSelector::VisitFloat64Neg(Node* node) { - X87OperandGenerator g(this); - Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(0))); - Emit(kX87Float64Neg, g.DefineAsFixed(node, stX_0), 0, nullptr); -} - -void InstructionSelector::VisitFloat64Ieee754Binop(Node* node, - InstructionCode opcode) { - X87OperandGenerator g(this); - Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(0))); - Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(1))); - Emit(opcode, g.DefineAsFixed(node, stX_0), 0, nullptr)->MarkAsCall(); -} - -void InstructionSelector::VisitFloat64Ieee754Unop(Node* node, - InstructionCode opcode) { - X87OperandGenerator g(this); - Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(0))); - Emit(opcode, g.DefineAsFixed(node, stX_0), 0, nullptr)->MarkAsCall(); -} - -void InstructionSelector::EmitPrepareArguments( - ZoneVector* arguments, const CallDescriptor* descriptor, - Node* node) { - X87OperandGenerator g(this); - - // Prepare for C function call. - if (descriptor->IsCFunctionCall()) { - InstructionOperand temps[] = {g.TempRegister()}; - size_t const temp_count = arraysize(temps); - Emit(kArchPrepareCallCFunction | - MiscField::encode(static_cast(descriptor->ParameterCount())), - 0, nullptr, 0, nullptr, temp_count, temps); - - // Poke any stack arguments. - for (size_t n = 0; n < arguments->size(); ++n) { - PushParameter input = (*arguments)[n]; - if (input.node()) { - int const slot = static_cast(n); - InstructionOperand value = g.CanBeImmediate(input.node()) - ? g.UseImmediate(input.node()) - : g.UseRegister(input.node()); - Emit(kX87Poke | MiscField::encode(slot), g.NoOutput(), value); - } - } - } else { - // Push any stack arguments. - for (PushParameter input : base::Reversed(*arguments)) { - // TODO(titzer): handle pushing double parameters. - if (input.node() == nullptr) continue; - InstructionOperand value = - g.CanBeImmediate(input.node()) - ? g.UseImmediate(input.node()) - : IsSupported(ATOM) || - sequence()->IsFP(GetVirtualRegister(input.node())) - ? g.UseRegister(input.node()) - : g.Use(input.node()); - Emit(kX87Push, g.NoOutput(), value); - } - } -} - - -bool InstructionSelector::IsTailCallAddressImmediate() { return true; } - -int InstructionSelector::GetTempsCountForTailCallFromJSFunction() { return 0; } - -namespace { - -void VisitCompareWithMemoryOperand(InstructionSelector* selector, - InstructionCode opcode, Node* left, - InstructionOperand right, - FlagsContinuation* cont) { - DCHECK(left->opcode() == IrOpcode::kLoad); - X87OperandGenerator g(selector); - size_t input_count = 0; - InstructionOperand inputs[6]; - AddressingMode addressing_mode = - g.GetEffectiveAddressMemoryOperand(left, inputs, &input_count); - opcode |= AddressingModeField::encode(addressing_mode); - opcode = cont->Encode(opcode); - inputs[input_count++] = right; - - if (cont->IsBranch()) { - inputs[input_count++] = g.Label(cont->true_block()); - inputs[input_count++] = g.Label(cont->false_block()); - selector->Emit(opcode, 0, nullptr, input_count, inputs); - } else if (cont->IsDeoptimize()) { - selector->EmitDeoptimize(opcode, 0, nullptr, input_count, inputs, - cont->kind(), cont->reason(), cont->frame_state()); - } else if (cont->IsSet()) { - InstructionOperand output = g.DefineAsRegister(cont->result()); - selector->Emit(opcode, 1, &output, input_count, inputs); - } else { - DCHECK(cont->IsTrap()); - inputs[input_count++] = g.UseImmediate(cont->trap_id()); - selector->Emit(opcode, 0, nullptr, input_count, inputs); - } -} - -// Shared routine for multiple compare operations. -void VisitCompare(InstructionSelector* selector, InstructionCode opcode, - InstructionOperand left, InstructionOperand right, - FlagsContinuation* cont) { - X87OperandGenerator g(selector); - opcode = cont->Encode(opcode); - if (cont->IsBranch()) { - selector->Emit(opcode, g.NoOutput(), left, right, - g.Label(cont->true_block()), g.Label(cont->false_block())); - } else if (cont->IsDeoptimize()) { - selector->EmitDeoptimize(opcode, g.NoOutput(), left, right, cont->kind(), - cont->reason(), cont->frame_state()); - } else if (cont->IsSet()) { - selector->Emit(opcode, g.DefineAsByteRegister(cont->result()), left, right); - } else { - DCHECK(cont->IsTrap()); - selector->Emit(opcode, g.NoOutput(), left, right, - g.UseImmediate(cont->trap_id())); - } -} - - -// Shared routine for multiple compare operations. -void VisitCompare(InstructionSelector* selector, InstructionCode opcode, - Node* left, Node* right, FlagsContinuation* cont, - bool commutative) { - X87OperandGenerator g(selector); - if (commutative && g.CanBeBetterLeftOperand(right)) { - std::swap(left, right); - } - VisitCompare(selector, opcode, g.UseRegister(left), g.Use(right), cont); -} - -MachineType MachineTypeForNarrow(Node* node, Node* hint_node) { - if (hint_node->opcode() == IrOpcode::kLoad) { - MachineType hint = LoadRepresentationOf(hint_node->op()); - if (node->opcode() == IrOpcode::kInt32Constant || - node->opcode() == IrOpcode::kInt64Constant) { - int64_t constant = node->opcode() == IrOpcode::kInt32Constant - ? OpParameter(node) - : OpParameter(node); - if (hint == MachineType::Int8()) { - if (constant >= std::numeric_limits::min() && - constant <= std::numeric_limits::max()) { - return hint; - } - } else if (hint == MachineType::Uint8()) { - if (constant >= std::numeric_limits::min() && - constant <= std::numeric_limits::max()) { - return hint; - } - } else if (hint == MachineType::Int16()) { - if (constant >= std::numeric_limits::min() && - constant <= std::numeric_limits::max()) { - return hint; - } - } else if (hint == MachineType::Uint16()) { - if (constant >= std::numeric_limits::min() && - constant <= std::numeric_limits::max()) { - return hint; - } - } else if (hint == MachineType::Int32()) { - return hint; - } else if (hint == MachineType::Uint32()) { - if (constant >= 0) return hint; - } - } - } - return node->opcode() == IrOpcode::kLoad ? LoadRepresentationOf(node->op()) - : MachineType::None(); -} - -// Tries to match the size of the given opcode to that of the operands, if -// possible. -InstructionCode TryNarrowOpcodeSize(InstructionCode opcode, Node* left, - Node* right, FlagsContinuation* cont) { - // TODO(epertoso): we can probably get some size information out of phi nodes. - // If the load representations don't match, both operands will be - // zero/sign-extended to 32bit. - MachineType left_type = MachineTypeForNarrow(left, right); - MachineType right_type = MachineTypeForNarrow(right, left); - if (left_type == right_type) { - switch (left_type.representation()) { - case MachineRepresentation::kBit: - case MachineRepresentation::kWord8: { - if (opcode == kX87Test) return kX87Test8; - if (opcode == kX87Cmp) { - if (left_type.semantic() == MachineSemantic::kUint32) { - cont->OverwriteUnsignedIfSigned(); - } else { - CHECK_EQ(MachineSemantic::kInt32, left_type.semantic()); - } - return kX87Cmp8; - } - break; - } - case MachineRepresentation::kWord16: - if (opcode == kX87Test) return kX87Test16; - if (opcode == kX87Cmp) { - if (left_type.semantic() == MachineSemantic::kUint32) { - cont->OverwriteUnsignedIfSigned(); - } else { - CHECK_EQ(MachineSemantic::kInt32, left_type.semantic()); - } - return kX87Cmp16; - } - break; - default: - break; - } - } - return opcode; -} - -// Shared routine for multiple float32 compare operations (inputs commuted). -void VisitFloat32Compare(InstructionSelector* selector, Node* node, - FlagsContinuation* cont) { - X87OperandGenerator g(selector); - selector->Emit(kX87PushFloat32, g.NoOutput(), g.Use(node->InputAt(0))); - selector->Emit(kX87PushFloat32, g.NoOutput(), g.Use(node->InputAt(1))); - if (cont->IsBranch()) { - selector->Emit(cont->Encode(kX87Float32Cmp), g.NoOutput(), - g.Label(cont->true_block()), g.Label(cont->false_block())); - } else if (cont->IsDeoptimize()) { - selector->EmitDeoptimize(cont->Encode(kX87Float32Cmp), g.NoOutput(), - g.Use(node->InputAt(0)), g.Use(node->InputAt(1)), - cont->kind(), cont->reason(), cont->frame_state()); - } else if (cont->IsSet()) { - selector->Emit(cont->Encode(kX87Float32Cmp), - g.DefineAsByteRegister(cont->result())); - } else { - DCHECK(cont->IsTrap()); - selector->Emit(cont->Encode(kX87Float32Cmp), g.NoOutput(), - g.UseImmediate(cont->trap_id())); - } -} - - -// Shared routine for multiple float64 compare operations (inputs commuted). -void VisitFloat64Compare(InstructionSelector* selector, Node* node, - FlagsContinuation* cont) { - X87OperandGenerator g(selector); - selector->Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(0))); - selector->Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(1))); - if (cont->IsBranch()) { - selector->Emit(cont->Encode(kX87Float64Cmp), g.NoOutput(), - g.Label(cont->true_block()), g.Label(cont->false_block())); - } else if (cont->IsDeoptimize()) { - selector->EmitDeoptimize(cont->Encode(kX87Float64Cmp), g.NoOutput(), - g.Use(node->InputAt(0)), g.Use(node->InputAt(1)), - cont->kind(), cont->reason(), cont->frame_state()); - } else if (cont->IsSet()) { - selector->Emit(cont->Encode(kX87Float64Cmp), - g.DefineAsByteRegister(cont->result())); - } else { - DCHECK(cont->IsTrap()); - selector->Emit(cont->Encode(kX87Float64Cmp), g.NoOutput(), - g.UseImmediate(cont->trap_id())); - } -} - -// Shared routine for multiple word compare operations. -void VisitWordCompare(InstructionSelector* selector, Node* node, - InstructionCode opcode, FlagsContinuation* cont) { - X87OperandGenerator g(selector); - Node* left = node->InputAt(0); - Node* right = node->InputAt(1); - - InstructionCode narrowed_opcode = - TryNarrowOpcodeSize(opcode, left, right, cont); - - int effect_level = selector->GetEffectLevel(node); - if (cont->IsBranch()) { - effect_level = selector->GetEffectLevel( - cont->true_block()->PredecessorAt(0)->control_input()); - } - - // If one of the two inputs is an immediate, make sure it's on the right, or - // if one of the two inputs is a memory operand, make sure it's on the left. - if ((!g.CanBeImmediate(right) && g.CanBeImmediate(left)) || - (g.CanBeMemoryOperand(narrowed_opcode, node, right, effect_level) && - !g.CanBeMemoryOperand(narrowed_opcode, node, left, effect_level))) { - if (!node->op()->HasProperty(Operator::kCommutative)) cont->Commute(); - std::swap(left, right); - } - - // Match immediates on right side of comparison. - if (g.CanBeImmediate(right)) { - if (g.CanBeMemoryOperand(narrowed_opcode, node, left, effect_level)) { - return VisitCompareWithMemoryOperand(selector, narrowed_opcode, left, - g.UseImmediate(right), cont); - } - return VisitCompare(selector, opcode, g.Use(left), g.UseImmediate(right), - cont); - } - - // Match memory operands on left side of comparison. - if (g.CanBeMemoryOperand(narrowed_opcode, node, left, effect_level)) { - bool needs_byte_register = - narrowed_opcode == kX87Test8 || narrowed_opcode == kX87Cmp8; - return VisitCompareWithMemoryOperand( - selector, narrowed_opcode, left, - needs_byte_register ? g.UseByteRegister(right) : g.UseRegister(right), - cont); - } - - if (g.CanBeBetterLeftOperand(right)) { - if (!node->op()->HasProperty(Operator::kCommutative)) cont->Commute(); - std::swap(left, right); - } - - return VisitCompare(selector, opcode, left, right, cont, - node->op()->HasProperty(Operator::kCommutative)); -} - -void VisitWordCompare(InstructionSelector* selector, Node* node, - FlagsContinuation* cont) { - X87OperandGenerator g(selector); - Int32BinopMatcher m(node); - if (m.left().IsLoad() && m.right().IsLoadStackPointer()) { - LoadMatcher mleft(m.left().node()); - ExternalReference js_stack_limit = - ExternalReference::address_of_stack_limit(selector->isolate()); - if (mleft.object().Is(js_stack_limit) && mleft.index().Is(0)) { - // Compare(Load(js_stack_limit), LoadStackPointer) - if (!node->op()->HasProperty(Operator::kCommutative)) cont->Commute(); - InstructionCode opcode = cont->Encode(kX87StackCheck); - if (cont->IsBranch()) { - selector->Emit(opcode, g.NoOutput(), g.Label(cont->true_block()), - g.Label(cont->false_block())); - } else if (cont->IsDeoptimize()) { - selector->EmitDeoptimize(opcode, 0, nullptr, 0, nullptr, cont->kind(), - cont->reason(), cont->frame_state()); - } else { - DCHECK(cont->IsSet()); - selector->Emit(opcode, g.DefineAsRegister(cont->result())); - } - return; - } - } - VisitWordCompare(selector, node, kX87Cmp, cont); -} - - -// Shared routine for word comparison with zero. -void VisitWordCompareZero(InstructionSelector* selector, Node* user, - Node* value, FlagsContinuation* cont) { - // Try to combine with comparisons against 0 by simply inverting the branch. - while (value->opcode() == IrOpcode::kWord32Equal && - selector->CanCover(user, value)) { - Int32BinopMatcher m(value); - if (!m.right().Is(0)) break; - - user = value; - value = m.left().node(); - cont->Negate(); - } - - if (selector->CanCover(user, value)) { - switch (value->opcode()) { - case IrOpcode::kWord32Equal: - cont->OverwriteAndNegateIfEqual(kEqual); - return VisitWordCompare(selector, value, cont); - case IrOpcode::kInt32LessThan: - cont->OverwriteAndNegateIfEqual(kSignedLessThan); - return VisitWordCompare(selector, value, cont); - case IrOpcode::kInt32LessThanOrEqual: - cont->OverwriteAndNegateIfEqual(kSignedLessThanOrEqual); - return VisitWordCompare(selector, value, cont); - case IrOpcode::kUint32LessThan: - cont->OverwriteAndNegateIfEqual(kUnsignedLessThan); - return VisitWordCompare(selector, value, cont); - case IrOpcode::kUint32LessThanOrEqual: - cont->OverwriteAndNegateIfEqual(kUnsignedLessThanOrEqual); - return VisitWordCompare(selector, value, cont); - case IrOpcode::kFloat32Equal: - cont->OverwriteAndNegateIfEqual(kUnorderedEqual); - return VisitFloat32Compare(selector, value, cont); - case IrOpcode::kFloat32LessThan: - cont->OverwriteAndNegateIfEqual(kUnsignedGreaterThan); - return VisitFloat32Compare(selector, value, cont); - case IrOpcode::kFloat32LessThanOrEqual: - cont->OverwriteAndNegateIfEqual(kUnsignedGreaterThanOrEqual); - return VisitFloat32Compare(selector, value, cont); - case IrOpcode::kFloat64Equal: - cont->OverwriteAndNegateIfEqual(kUnorderedEqual); - return VisitFloat64Compare(selector, value, cont); - case IrOpcode::kFloat64LessThan: - cont->OverwriteAndNegateIfEqual(kUnsignedGreaterThan); - return VisitFloat64Compare(selector, value, cont); - case IrOpcode::kFloat64LessThanOrEqual: - cont->OverwriteAndNegateIfEqual(kUnsignedGreaterThanOrEqual); - return VisitFloat64Compare(selector, value, cont); - case IrOpcode::kProjection: - // Check if this is the overflow output projection of an - // WithOverflow node. - if (ProjectionIndexOf(value->op()) == 1u) { - // We cannot combine the WithOverflow with this branch - // unless the 0th projection (the use of the actual value of the - // is either nullptr, which means there's no use of the - // actual value, or was already defined, which means it is scheduled - // *AFTER* this branch). - Node* const node = value->InputAt(0); - Node* const result = NodeProperties::FindProjection(node, 0); - if (result == nullptr || selector->IsDefined(result)) { - switch (node->opcode()) { - case IrOpcode::kInt32AddWithOverflow: - cont->OverwriteAndNegateIfEqual(kOverflow); - return VisitBinop(selector, node, kX87Add, cont); - case IrOpcode::kInt32SubWithOverflow: - cont->OverwriteAndNegateIfEqual(kOverflow); - return VisitBinop(selector, node, kX87Sub, cont); - case IrOpcode::kInt32MulWithOverflow: - cont->OverwriteAndNegateIfEqual(kOverflow); - return VisitBinop(selector, node, kX87Imul, cont); - default: - break; - } - } - } - break; - case IrOpcode::kInt32Sub: - return VisitWordCompare(selector, value, cont); - case IrOpcode::kWord32And: - return VisitWordCompare(selector, value, kX87Test, cont); - default: - break; - } - } - - // Continuation could not be combined with a compare, emit compare against 0. - X87OperandGenerator g(selector); - VisitCompare(selector, kX87Cmp, g.Use(value), g.TempImmediate(0), cont); -} - -} // namespace - - -void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch, - BasicBlock* fbranch) { - FlagsContinuation cont(kNotEqual, tbranch, fbranch); - VisitWordCompareZero(this, branch, branch->InputAt(0), &cont); -} - -void InstructionSelector::VisitDeoptimizeIf(Node* node) { - DeoptimizeParameters p = DeoptimizeParametersOf(node->op()); - FlagsContinuation cont = FlagsContinuation::ForDeoptimize( - kNotEqual, p.kind(), p.reason(), node->InputAt(1)); - VisitWordCompareZero(this, node, node->InputAt(0), &cont); -} - -void InstructionSelector::VisitDeoptimizeUnless(Node* node) { - DeoptimizeParameters p = DeoptimizeParametersOf(node->op()); - FlagsContinuation cont = FlagsContinuation::ForDeoptimize( - kEqual, p.kind(), p.reason(), node->InputAt(1)); - VisitWordCompareZero(this, node, node->InputAt(0), &cont); -} - -void InstructionSelector::VisitTrapIf(Node* node, Runtime::FunctionId func_id) { - FlagsContinuation cont = - FlagsContinuation::ForTrap(kNotEqual, func_id, node->InputAt(1)); - VisitWordCompareZero(this, node, node->InputAt(0), &cont); -} - -void InstructionSelector::VisitTrapUnless(Node* node, - Runtime::FunctionId func_id) { - FlagsContinuation cont = - FlagsContinuation::ForTrap(kEqual, func_id, node->InputAt(1)); - VisitWordCompareZero(this, node, node->InputAt(0), &cont); -} - -void InstructionSelector::VisitSwitch(Node* node, const SwitchInfo& sw) { - X87OperandGenerator g(this); - InstructionOperand value_operand = g.UseRegister(node->InputAt(0)); - - // Emit either ArchTableSwitch or ArchLookupSwitch. - static const size_t kMaxTableSwitchValueRange = 2 << 16; - size_t table_space_cost = 4 + sw.value_range; - size_t table_time_cost = 3; - size_t lookup_space_cost = 3 + 2 * sw.case_count; - size_t lookup_time_cost = sw.case_count; - if (sw.case_count > 4 && - table_space_cost + 3 * table_time_cost <= - lookup_space_cost + 3 * lookup_time_cost && - sw.min_value > std::numeric_limits::min() && - sw.value_range <= kMaxTableSwitchValueRange) { - InstructionOperand index_operand = value_operand; - if (sw.min_value) { - index_operand = g.TempRegister(); - Emit(kX87Lea | AddressingModeField::encode(kMode_MRI), index_operand, - value_operand, g.TempImmediate(-sw.min_value)); - } - // Generate a table lookup. - return EmitTableSwitch(sw, index_operand); - } - - // Generate a sequence of conditional jumps. - return EmitLookupSwitch(sw, value_operand); -} - - -void InstructionSelector::VisitWord32Equal(Node* const node) { - FlagsContinuation cont = FlagsContinuation::ForSet(kEqual, node); - Int32BinopMatcher m(node); - if (m.right().Is(0)) { - return VisitWordCompareZero(this, m.node(), m.left().node(), &cont); - } - VisitWordCompare(this, node, &cont); -} - - -void InstructionSelector::VisitInt32LessThan(Node* node) { - FlagsContinuation cont = FlagsContinuation::ForSet(kSignedLessThan, node); - VisitWordCompare(this, node, &cont); -} - - -void InstructionSelector::VisitInt32LessThanOrEqual(Node* node) { - FlagsContinuation cont = - FlagsContinuation::ForSet(kSignedLessThanOrEqual, node); - VisitWordCompare(this, node, &cont); -} - - -void InstructionSelector::VisitUint32LessThan(Node* node) { - FlagsContinuation cont = FlagsContinuation::ForSet(kUnsignedLessThan, node); - VisitWordCompare(this, node, &cont); -} - - -void InstructionSelector::VisitUint32LessThanOrEqual(Node* node) { - FlagsContinuation cont = - FlagsContinuation::ForSet(kUnsignedLessThanOrEqual, node); - VisitWordCompare(this, node, &cont); -} - - -void InstructionSelector::VisitInt32AddWithOverflow(Node* node) { - if (Node* ovf = NodeProperties::FindProjection(node, 1)) { - FlagsContinuation cont = FlagsContinuation::ForSet(kOverflow, ovf); - return VisitBinop(this, node, kX87Add, &cont); - } - FlagsContinuation cont; - VisitBinop(this, node, kX87Add, &cont); -} - - -void InstructionSelector::VisitInt32SubWithOverflow(Node* node) { - if (Node* ovf = NodeProperties::FindProjection(node, 1)) { - FlagsContinuation cont = FlagsContinuation::ForSet(kOverflow, ovf); - return VisitBinop(this, node, kX87Sub, &cont); - } - FlagsContinuation cont; - VisitBinop(this, node, kX87Sub, &cont); -} - -void InstructionSelector::VisitInt32MulWithOverflow(Node* node) { - if (Node* ovf = NodeProperties::FindProjection(node, 1)) { - FlagsContinuation cont = FlagsContinuation::ForSet(kOverflow, ovf); - return VisitBinop(this, node, kX87Imul, &cont); - } - FlagsContinuation cont; - VisitBinop(this, node, kX87Imul, &cont); -} - -void InstructionSelector::VisitFloat32Equal(Node* node) { - FlagsContinuation cont = FlagsContinuation::ForSet(kUnorderedEqual, node); - VisitFloat32Compare(this, node, &cont); -} - - -void InstructionSelector::VisitFloat32LessThan(Node* node) { - FlagsContinuation cont = - FlagsContinuation::ForSet(kUnsignedGreaterThan, node); - VisitFloat32Compare(this, node, &cont); -} - - -void InstructionSelector::VisitFloat32LessThanOrEqual(Node* node) { - FlagsContinuation cont = - FlagsContinuation::ForSet(kUnsignedGreaterThanOrEqual, node); - VisitFloat32Compare(this, node, &cont); -} - - -void InstructionSelector::VisitFloat64Equal(Node* node) { - FlagsContinuation cont = FlagsContinuation::ForSet(kUnorderedEqual, node); - VisitFloat64Compare(this, node, &cont); -} - - -void InstructionSelector::VisitFloat64LessThan(Node* node) { - FlagsContinuation cont = - FlagsContinuation::ForSet(kUnsignedGreaterThan, node); - VisitFloat64Compare(this, node, &cont); -} - - -void InstructionSelector::VisitFloat64LessThanOrEqual(Node* node) { - FlagsContinuation cont = - FlagsContinuation::ForSet(kUnsignedGreaterThanOrEqual, node); - VisitFloat64Compare(this, node, &cont); -} - - -void InstructionSelector::VisitFloat64ExtractLowWord32(Node* node) { - X87OperandGenerator g(this); - Emit(kX87Float64ExtractLowWord32, g.DefineAsRegister(node), - g.Use(node->InputAt(0))); -} - - -void InstructionSelector::VisitFloat64ExtractHighWord32(Node* node) { - X87OperandGenerator g(this); - Emit(kX87Float64ExtractHighWord32, g.DefineAsRegister(node), - g.Use(node->InputAt(0))); -} - - -void InstructionSelector::VisitFloat64InsertLowWord32(Node* node) { - X87OperandGenerator g(this); - Node* left = node->InputAt(0); - Node* right = node->InputAt(1); - Emit(kX87Float64InsertLowWord32, g.UseFixed(node, stX_0), g.UseRegister(left), - g.UseRegister(right)); -} - - -void InstructionSelector::VisitFloat64InsertHighWord32(Node* node) { - X87OperandGenerator g(this); - Node* left = node->InputAt(0); - Node* right = node->InputAt(1); - Emit(kX87Float64InsertHighWord32, g.UseFixed(node, stX_0), - g.UseRegister(left), g.UseRegister(right)); -} - -void InstructionSelector::VisitFloat64SilenceNaN(Node* node) { - X87OperandGenerator g(this); - Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(0))); - Emit(kX87Float64SilenceNaN, g.DefineAsFixed(node, stX_0), 0, nullptr); -} - -void InstructionSelector::VisitAtomicLoad(Node* node) { - LoadRepresentation load_rep = LoadRepresentationOf(node->op()); - DCHECK(load_rep.representation() == MachineRepresentation::kWord8 || - load_rep.representation() == MachineRepresentation::kWord16 || - load_rep.representation() == MachineRepresentation::kWord32); - USE(load_rep); - VisitLoad(node); -} - -void InstructionSelector::VisitAtomicStore(Node* node) { - X87OperandGenerator g(this); - Node* base = node->InputAt(0); - Node* index = node->InputAt(1); - Node* value = node->InputAt(2); - - MachineRepresentation rep = AtomicStoreRepresentationOf(node->op()); - ArchOpcode opcode = kArchNop; - switch (rep) { - case MachineRepresentation::kWord8: - opcode = kX87Xchgb; - break; - case MachineRepresentation::kWord16: - opcode = kX87Xchgw; - break; - case MachineRepresentation::kWord32: - opcode = kX87Xchgl; - break; - default: - UNREACHABLE(); - break; - } - AddressingMode addressing_mode; - InstructionOperand inputs[4]; - size_t input_count = 0; - inputs[input_count++] = g.UseUniqueRegister(base); - if (g.CanBeImmediate(index)) { - inputs[input_count++] = g.UseImmediate(index); - addressing_mode = kMode_MRI; - } else { - inputs[input_count++] = g.UseUniqueRegister(index); - addressing_mode = kMode_MR1; - } - inputs[input_count++] = g.UseUniqueRegister(value); - InstructionCode code = opcode | AddressingModeField::encode(addressing_mode); - Emit(code, 0, nullptr, input_count, inputs); -} - -void InstructionSelector::VisitInt32AbsWithOverflow(Node* node) { - UNREACHABLE(); -} - -void InstructionSelector::VisitInt64AbsWithOverflow(Node* node) { - UNREACHABLE(); -} - -// static -MachineOperatorBuilder::Flags -InstructionSelector::SupportedMachineOperatorFlags() { - MachineOperatorBuilder::Flags flags = - MachineOperatorBuilder::kWord32ShiftIsSafe; - if (CpuFeatures::IsSupported(POPCNT)) { - flags |= MachineOperatorBuilder::kWord32Popcnt; - } - - flags |= MachineOperatorBuilder::kFloat32RoundDown | - MachineOperatorBuilder::kFloat64RoundDown | - MachineOperatorBuilder::kFloat32RoundUp | - MachineOperatorBuilder::kFloat64RoundUp | - MachineOperatorBuilder::kFloat32RoundTruncate | - MachineOperatorBuilder::kFloat64RoundTruncate | - MachineOperatorBuilder::kFloat32RoundTiesEven | - MachineOperatorBuilder::kFloat64RoundTiesEven; - return flags; -} - -// static -MachineOperatorBuilder::AlignmentRequirements -InstructionSelector::AlignmentRequirements() { - return MachineOperatorBuilder::AlignmentRequirements:: - FullUnalignedAccessSupport(); -} - -} // namespace compiler -} // namespace internal -} // namespace v8 diff --git a/src/debug/x87/OWNERS b/src/debug/x87/OWNERS deleted file mode 100644 index 61245ae8e2..0000000000 --- a/src/debug/x87/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -weiliang.lin@intel.com -chunyang.dai@intel.com diff --git a/src/debug/x87/debug-x87.cc b/src/debug/x87/debug-x87.cc deleted file mode 100644 index 8810f01f42..0000000000 --- a/src/debug/x87/debug-x87.cc +++ /dev/null @@ -1,157 +0,0 @@ -// 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_X87 - -#include "src/debug/debug.h" - -#include "src/codegen.h" -#include "src/debug/liveedit.h" -#include "src/x87/frames-x87.h" - -namespace v8 { -namespace internal { - -#define __ ACCESS_MASM(masm) - - -void EmitDebugBreakSlot(MacroAssembler* masm) { - Label check_codesize; - __ bind(&check_codesize); - __ Nop(Assembler::kDebugBreakSlotLength); - DCHECK_EQ(Assembler::kDebugBreakSlotLength, - masm->SizeOfCodeGeneratedSince(&check_codesize)); -} - - -void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode) { - // Generate enough nop's to make space for a call instruction. - masm->RecordDebugBreakSlot(mode); - EmitDebugBreakSlot(masm); -} - - -void DebugCodegen::ClearDebugBreakSlot(Isolate* isolate, Address pc) { - CodePatcher patcher(isolate, pc, Assembler::kDebugBreakSlotLength); - EmitDebugBreakSlot(patcher.masm()); -} - - -void DebugCodegen::PatchDebugBreakSlot(Isolate* isolate, Address pc, - Handle code) { - DCHECK(code->is_debug_stub()); - static const int kSize = Assembler::kDebugBreakSlotLength; - CodePatcher patcher(isolate, pc, kSize); - - // Add a label for checking the size of the code used for returning. - Label check_codesize; - patcher.masm()->bind(&check_codesize); - patcher.masm()->call(code->entry(), RelocInfo::NONE32); - // Check that the size of the code generated is as expected. - DCHECK_EQ(kSize, patcher.masm()->SizeOfCodeGeneratedSince(&check_codesize)); -} - -bool DebugCodegen::DebugBreakSlotIsPatched(Address pc) { - return !Assembler::IsNop(pc); -} - -void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm, - DebugBreakCallHelperMode mode) { - __ RecordComment("Debug break"); - - // Enter an internal frame. - { - FrameScope scope(masm, StackFrame::INTERNAL); - - // Load padding words on stack. - for (int i = 0; i < LiveEdit::kFramePaddingInitialSize; i++) { - __ push(Immediate(Smi::FromInt(LiveEdit::kFramePaddingValue))); - } - __ push(Immediate(Smi::FromInt(LiveEdit::kFramePaddingInitialSize))); - - // Push arguments for DebugBreak call. - if (mode == SAVE_RESULT_REGISTER) { - // Break on return. - __ push(eax); - } else { - // Non-return breaks. - __ Push(masm->isolate()->factory()->the_hole_value()); - } - __ Move(eax, Immediate(1)); - __ mov(ebx, - Immediate(ExternalReference( - Runtime::FunctionForId(Runtime::kDebugBreak), masm->isolate()))); - - CEntryStub ceb(masm->isolate(), 1); - __ CallStub(&ceb); - - if (FLAG_debug_code) { - for (int i = 0; i < kNumJSCallerSaved; ++i) { - Register reg = {JSCallerSavedCode(i)}; - // Do not clobber eax if mode is SAVE_RESULT_REGISTER. It will - // contain return value of the function. - if (!(reg.is(eax) && (mode == SAVE_RESULT_REGISTER))) { - __ Move(reg, Immediate(kDebugZapValue)); - } - } - } - - __ pop(ebx); - // We divide stored value by 2 (untagging) and multiply it by word's size. - STATIC_ASSERT(kSmiTagSize == 1 && kSmiShiftSize == 0); - __ lea(esp, Operand(esp, ebx, times_half_pointer_size, 0)); - - // Get rid of the internal frame. - } - - // This call did not replace a call , so there will be an unwanted - // return address left on the stack. Here we get rid of that. - __ add(esp, Immediate(kPointerSize)); - - // Now that the break point has been handled, resume normal execution by - // jumping to the target address intended by the caller and that was - // overwritten by the address of DebugBreakXXX. - ExternalReference after_break_target = - ExternalReference::debug_after_break_target_address(masm->isolate()); - __ jmp(Operand::StaticVariable(after_break_target)); -} - - -void DebugCodegen::GenerateFrameDropperLiveEdit(MacroAssembler* masm) { - // We do not know our frame height, but set esp based on ebp. - __ lea(esp, Operand(ebp, FrameDropperFrameConstants::kFunctionOffset)); - __ pop(edi); // Function. - __ add(esp, Immediate(-FrameDropperFrameConstants::kCodeOffset)); // INTERNAL - // frame - // marker - // and code - __ pop(ebp); - - ParameterCount dummy(0); - __ CheckDebugHook(edi, no_reg, dummy, dummy); - - // Load context from the function. - __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); - - // Clear new.target register as a safety measure. - __ mov(edx, masm->isolate()->factory()->undefined_value()); - - // Get function code. - __ mov(ebx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); - __ mov(ebx, FieldOperand(ebx, SharedFunctionInfo::kCodeOffset)); - __ lea(ebx, FieldOperand(ebx, Code::kHeaderSize)); - - // Re-run JSFunction, edi is function, esi is context. - __ jmp(ebx); -} - - -const bool LiveEdit::kFrameDropperSupported = true; - -#undef __ - -} // namespace internal -} // namespace v8 - -#endif // V8_TARGET_ARCH_X87 diff --git a/src/frames-inl.h b/src/frames-inl.h index 7f409968c0..5a383144be 100644 --- a/src/frames-inl.h +++ b/src/frames-inl.h @@ -26,8 +26,6 @@ #include "src/mips64/frames-mips64.h" // NOLINT #elif V8_TARGET_ARCH_S390 #include "src/s390/frames-s390.h" // NOLINT -#elif V8_TARGET_ARCH_X87 -#include "src/x87/frames-x87.h" // NOLINT #else #error Unsupported target architecture. #endif diff --git a/src/full-codegen/full-codegen.h b/src/full-codegen/full-codegen.h index efe53301de..b576c6eda8 100644 --- a/src/full-codegen/full-codegen.h +++ b/src/full-codegen/full-codegen.h @@ -45,7 +45,7 @@ class FullCodeGenerator final : public AstVisitor { static const int kMaxBackEdgeWeight = 127; // Platform-specific code size multiplier. -#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X87 +#if V8_TARGET_ARCH_IA32 static const int kCodeSizeMultiplier = 105; #elif V8_TARGET_ARCH_X64 static const int kCodeSizeMultiplier = 165; diff --git a/src/full-codegen/x87/OWNERS b/src/full-codegen/x87/OWNERS deleted file mode 100644 index 61245ae8e2..0000000000 --- a/src/full-codegen/x87/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -weiliang.lin@intel.com -chunyang.dai@intel.com diff --git a/src/full-codegen/x87/full-codegen-x87.cc b/src/full-codegen/x87/full-codegen-x87.cc deleted file mode 100644 index dd77308eb7..0000000000 --- a/src/full-codegen/x87/full-codegen-x87.cc +++ /dev/null @@ -1,2425 +0,0 @@ -// 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_X87 - -#include "src/ast/compile-time-value.h" -#include "src/ast/scopes.h" -#include "src/builtins/builtins-constructor.h" -#include "src/code-factory.h" -#include "src/code-stubs.h" -#include "src/codegen.h" -#include "src/compilation-info.h" -#include "src/compiler.h" -#include "src/debug/debug.h" -#include "src/full-codegen/full-codegen.h" -#include "src/ic/ic.h" -#include "src/x87/frames-x87.h" - -namespace v8 { -namespace internal { - -#define __ ACCESS_MASM(masm()) - -class JumpPatchSite BASE_EMBEDDED { - public: - explicit JumpPatchSite(MacroAssembler* masm) : masm_(masm) { -#ifdef DEBUG - info_emitted_ = false; -#endif - } - - ~JumpPatchSite() { - DCHECK(patch_site_.is_bound() == info_emitted_); - } - - void EmitJumpIfNotSmi(Register reg, - Label* target, - Label::Distance distance = Label::kFar) { - __ test(reg, Immediate(kSmiTagMask)); - EmitJump(not_carry, target, distance); // Always taken before patched. - } - - void EmitJumpIfSmi(Register reg, - Label* target, - Label::Distance distance = Label::kFar) { - __ test(reg, Immediate(kSmiTagMask)); - EmitJump(carry, target, distance); // Never taken before patched. - } - - void EmitPatchInfo() { - if (patch_site_.is_bound()) { - int delta_to_patch_site = masm_->SizeOfCodeGeneratedSince(&patch_site_); - DCHECK(is_uint8(delta_to_patch_site)); - __ test(eax, Immediate(delta_to_patch_site)); -#ifdef DEBUG - info_emitted_ = true; -#endif - } else { - __ nop(); // Signals no inlined code. - } - } - - private: - // jc will be patched with jz, jnc will become jnz. - void EmitJump(Condition cc, Label* target, Label::Distance distance) { - DCHECK(!patch_site_.is_bound() && !info_emitted_); - DCHECK(cc == carry || cc == not_carry); - __ bind(&patch_site_); - __ j(cc, target, distance); - } - - MacroAssembler* masm() { return masm_; } - MacroAssembler* masm_; - Label patch_site_; -#ifdef DEBUG - bool info_emitted_; -#endif -}; - - -// Generate code for a JS function. On entry to the function the receiver -// and arguments have been pushed on the stack left to right, with the -// return address on top of them. The actual argument count matches the -// formal parameter count expected by the function. -// -// The live registers are: -// o edi: the JS function object being called (i.e. ourselves) -// o edx: the new target value -// o esi: our context -// o ebp: our caller's frame pointer -// o esp: stack pointer (pointing to return address) -// -// The function builds a JS frame. Please see JavaScriptFrameConstants in -// frames-x87.h for its layout. -void FullCodeGenerator::Generate() { - CompilationInfo* info = info_; - profiling_counter_ = isolate()->factory()->NewCell( - Handle(Smi::FromInt(FLAG_interrupt_budget), isolate())); - SetFunctionPosition(literal()); - Comment cmnt(masm_, "[ function compiled by full code generator"); - - ProfileEntryHookStub::MaybeCallEntryHook(masm_); - - if (FLAG_debug_code && info->ExpectsJSReceiverAsReceiver()) { - int receiver_offset = (info->scope()->num_parameters() + 1) * kPointerSize; - __ mov(ecx, Operand(esp, receiver_offset)); - __ AssertNotSmi(ecx); - __ CmpObjectType(ecx, FIRST_JS_RECEIVER_TYPE, ecx); - __ Assert(above_equal, kSloppyFunctionExpectsJSReceiverReceiver); - } - - // Open a frame scope to indicate that there is a frame on the stack. The - // MANUAL indicates that the scope shouldn't actually generate code to set up - // the frame (that is done below). - FrameScope frame_scope(masm_, StackFrame::MANUAL); - - info->set_prologue_offset(masm_->pc_offset()); - __ Prologue(info->GeneratePreagedPrologue()); - - // Increment invocation count for the function. - { - Comment cmnt(masm_, "[ Increment invocation count"); - __ mov(ecx, FieldOperand(edi, JSFunction::kFeedbackVectorOffset)); - __ mov(ecx, FieldOperand(ecx, Cell::kValueOffset)); - __ add( - FieldOperand(ecx, FeedbackVector::kInvocationCountIndex * kPointerSize + - FeedbackVector::kHeaderSize), - Immediate(Smi::FromInt(1))); - } - - { Comment cmnt(masm_, "[ Allocate locals"); - int locals_count = info->scope()->num_stack_slots(); - OperandStackDepthIncrement(locals_count); - if (locals_count == 1) { - __ push(Immediate(isolate()->factory()->undefined_value())); - } else if (locals_count > 1) { - if (locals_count >= 128) { - Label ok; - __ mov(ecx, esp); - __ sub(ecx, Immediate(locals_count * kPointerSize)); - ExternalReference stack_limit = - ExternalReference::address_of_real_stack_limit(isolate()); - __ cmp(ecx, Operand::StaticVariable(stack_limit)); - __ j(above_equal, &ok, Label::kNear); - __ CallRuntime(Runtime::kThrowStackOverflow); - __ bind(&ok); - } - __ mov(eax, Immediate(isolate()->factory()->undefined_value())); - const int kMaxPushes = 32; - if (locals_count >= kMaxPushes) { - int loop_iterations = locals_count / kMaxPushes; - __ mov(ecx, loop_iterations); - Label loop_header; - __ bind(&loop_header); - // Do pushes. - for (int i = 0; i < kMaxPushes; i++) { - __ push(eax); - } - __ dec(ecx); - __ j(not_zero, &loop_header, Label::kNear); - } - int remaining = locals_count % kMaxPushes; - // Emit the remaining pushes. - for (int i = 0; i < remaining; i++) { - __ push(eax); - } - } - } - - bool function_in_register = true; - - // Possibly allocate a local context. - if (info->scope()->NeedsContext()) { - Comment cmnt(masm_, "[ Allocate context"); - bool need_write_barrier = true; - int slots = info->scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS; - // Argument to NewContext is the function, which is still in edi. - if (info->scope()->is_script_scope()) { - __ push(edi); - __ Push(info->scope()->scope_info()); - __ CallRuntime(Runtime::kNewScriptContext); - // The new target value is not used, clobbering is safe. - DCHECK_NULL(info->scope()->new_target_var()); - } else { - if (info->scope()->new_target_var() != nullptr) { - __ push(edx); // Preserve new target. - } - if (slots <= ConstructorBuiltins::MaximumFunctionContextSlots()) { - Callable callable = CodeFactory::FastNewFunctionContext( - isolate(), info->scope()->scope_type()); - __ mov(FastNewFunctionContextDescriptor::SlotsRegister(), - Immediate(slots)); - __ Call(callable.code(), RelocInfo::CODE_TARGET); - // Result of the FastNewFunctionContext builtin is always in new space. - need_write_barrier = false; - } else { - __ push(edi); - __ Push(Smi::FromInt(info->scope()->scope_type())); - __ CallRuntime(Runtime::kNewFunctionContext); - } - if (info->scope()->new_target_var() != nullptr) { - __ pop(edx); // Restore new target. - } - } - function_in_register = false; - // Context is returned in eax. It replaces the context passed to us. - // It's saved in the stack and kept live in esi. - __ mov(esi, eax); - __ mov(Operand(ebp, StandardFrameConstants::kContextOffset), eax); - - // Copy parameters into context if necessary. - int num_parameters = info->scope()->num_parameters(); - int first_parameter = info->scope()->has_this_declaration() ? -1 : 0; - for (int i = first_parameter; i < num_parameters; i++) { - Variable* var = - (i == -1) ? info->scope()->receiver() : info->scope()->parameter(i); - if (var->IsContextSlot()) { - int parameter_offset = StandardFrameConstants::kCallerSPOffset + - (num_parameters - 1 - i) * kPointerSize; - // Load parameter from stack. - __ mov(eax, Operand(ebp, parameter_offset)); - // Store it in the context. - int context_offset = Context::SlotOffset(var->index()); - __ mov(Operand(esi, context_offset), eax); - // Update the write barrier. This clobbers eax and ebx. - if (need_write_barrier) { - __ RecordWriteContextSlot(esi, context_offset, eax, ebx, - kDontSaveFPRegs); - } else if (FLAG_debug_code) { - Label done; - __ JumpIfInNewSpace(esi, eax, &done, Label::kNear); - __ Abort(kExpectedNewSpaceObject); - __ bind(&done); - } - } - } - } - - // We don't support new.target and rest parameters here. - DCHECK_NULL(info->scope()->new_target_var()); - DCHECK_NULL(info->scope()->rest_parameter()); - DCHECK_NULL(info->scope()->this_function_var()); - - Variable* arguments = info->scope()->arguments(); - if (arguments != NULL) { - // Arguments object must be allocated after the context object, in - // case the "arguments" or ".arguments" variables are in the context. - Comment cmnt(masm_, "[ Allocate arguments object"); - if (!function_in_register) { - __ mov(edi, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); - } - if (is_strict(language_mode()) || !has_simple_parameters()) { - FastNewStrictArgumentsStub stub(isolate()); - __ CallStub(&stub); - } else if (literal()->has_duplicate_parameters()) { - __ Push(edi); - __ CallRuntime(Runtime::kNewSloppyArguments_Generic); - } else { - FastNewSloppyArgumentsStub stub(isolate()); - __ CallStub(&stub); - } - - SetVar(arguments, eax, ebx, edx); - } - - if (FLAG_trace) { - __ CallRuntime(Runtime::kTraceEnter); - } - - // Visit the declarations and body. - { - Comment cmnt(masm_, "[ Declarations"); - VisitDeclarations(scope()->declarations()); - } - - // Assert that the declarations do not use ICs. Otherwise the debugger - // won't be able to redirect a PC at an IC to the correct IC in newly - // recompiled code. - DCHECK_EQ(0, ic_total_count_); - - { - Comment cmnt(masm_, "[ Stack check"); - Label ok; - ExternalReference stack_limit = - ExternalReference::address_of_stack_limit(isolate()); - __ cmp(esp, Operand::StaticVariable(stack_limit)); - __ j(above_equal, &ok, Label::kNear); - __ call(isolate()->builtins()->StackCheck(), RelocInfo::CODE_TARGET); - __ bind(&ok); - } - - { - Comment cmnt(masm_, "[ Body"); - DCHECK(loop_depth() == 0); - VisitStatements(literal()->body()); - DCHECK(loop_depth() == 0); - } - - // Always emit a 'return undefined' in case control fell off the end of - // the body. - { Comment cmnt(masm_, "[ return ;"); - __ mov(eax, isolate()->factory()->undefined_value()); - EmitReturnSequence(); - } -} - - -void FullCodeGenerator::ClearAccumulator() { - __ Move(eax, Immediate(Smi::kZero)); -} - - -void FullCodeGenerator::EmitProfilingCounterDecrement(int delta) { - __ mov(ebx, Immediate(profiling_counter_)); - __ sub(FieldOperand(ebx, Cell::kValueOffset), - Immediate(Smi::FromInt(delta))); -} - - -void FullCodeGenerator::EmitProfilingCounterReset() { - int reset_value = FLAG_interrupt_budget; - __ mov(ebx, Immediate(profiling_counter_)); - __ mov(FieldOperand(ebx, Cell::kValueOffset), - Immediate(Smi::FromInt(reset_value))); -} - - -void FullCodeGenerator::EmitBackEdgeBookkeeping(IterationStatement* stmt, - Label* back_edge_target) { - Comment cmnt(masm_, "[ Back edge bookkeeping"); - Label ok; - - DCHECK(back_edge_target->is_bound()); - int distance = masm_->SizeOfCodeGeneratedSince(back_edge_target); - int weight = Min(kMaxBackEdgeWeight, - Max(1, distance / kCodeSizeMultiplier)); - EmitProfilingCounterDecrement(weight); - __ j(positive, &ok, Label::kNear); - __ call(isolate()->builtins()->InterruptCheck(), RelocInfo::CODE_TARGET); - - // Record a mapping of this PC offset to the OSR id. This is used to find - // the AST id from the unoptimized code in order to use it as a key into - // the deoptimization input data found in the optimized code. - RecordBackEdge(stmt->OsrEntryId()); - - EmitProfilingCounterReset(); - - __ bind(&ok); -} - -void FullCodeGenerator::EmitProfilingCounterHandlingForReturnSequence( - bool is_tail_call) { - // Pretend that the exit is a backwards jump to the entry. - int weight = 1; - if (info_->ShouldSelfOptimize()) { - weight = FLAG_interrupt_budget / FLAG_self_opt_count; - } else { - int distance = masm_->pc_offset(); - weight = Min(kMaxBackEdgeWeight, Max(1, distance / kCodeSizeMultiplier)); - } - EmitProfilingCounterDecrement(weight); - Label ok; - __ j(positive, &ok, Label::kNear); - // Don't need to save result register if we are going to do a tail call. - if (!is_tail_call) { - __ push(eax); - } - __ call(isolate()->builtins()->InterruptCheck(), RelocInfo::CODE_TARGET); - if (!is_tail_call) { - __ pop(eax); - } - EmitProfilingCounterReset(); - __ bind(&ok); -} - -void FullCodeGenerator::EmitReturnSequence() { - Comment cmnt(masm_, "[ Return sequence"); - if (return_label_.is_bound()) { - __ jmp(&return_label_); - } else { - // Common return label - __ bind(&return_label_); - if (FLAG_trace) { - __ push(eax); - __ CallRuntime(Runtime::kTraceExit); - } - EmitProfilingCounterHandlingForReturnSequence(false); - - SetReturnPosition(literal()); - __ leave(); - - int arg_count = info_->scope()->num_parameters() + 1; - int arguments_bytes = arg_count * kPointerSize; - __ Ret(arguments_bytes, ecx); - } -} - -void FullCodeGenerator::RestoreContext() { - __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); -} - -void FullCodeGenerator::StackValueContext::Plug(Variable* var) const { - DCHECK(var->IsStackAllocated() || var->IsContextSlot()); - MemOperand operand = codegen()->VarOperand(var, result_register()); - // Memory operands can be pushed directly. - codegen()->PushOperand(operand); -} - - -void FullCodeGenerator::EffectContext::Plug(Heap::RootListIndex index) const { - UNREACHABLE(); // Not used on X87. -} - - -void FullCodeGenerator::AccumulatorValueContext::Plug( - Heap::RootListIndex index) const { - UNREACHABLE(); // Not used on X87. -} - - -void FullCodeGenerator::StackValueContext::Plug( - Heap::RootListIndex index) const { - UNREACHABLE(); // Not used on X87. -} - - -void FullCodeGenerator::TestContext::Plug(Heap::RootListIndex index) const { - UNREACHABLE(); // Not used on X87. -} - - -void FullCodeGenerator::EffectContext::Plug(Handle lit) const { -} - - -void FullCodeGenerator::AccumulatorValueContext::Plug( - Handle lit) const { - if (lit->IsSmi()) { - __ SafeMove(result_register(), Immediate(lit)); - } else { - __ Move(result_register(), Immediate(lit)); - } -} - - -void FullCodeGenerator::StackValueContext::Plug(Handle lit) const { - codegen()->OperandStackDepthIncrement(1); - if (lit->IsSmi()) { - __ SafePush(Immediate(lit)); - } else { - __ push(Immediate(lit)); - } -} - - -void FullCodeGenerator::TestContext::Plug(Handle lit) const { - DCHECK(lit->IsNullOrUndefined(isolate()) || !lit->IsUndetectable()); - if (lit->IsNullOrUndefined(isolate()) || lit->IsFalse(isolate())) { - if (false_label_ != fall_through_) __ jmp(false_label_); - } else if (lit->IsTrue(isolate()) || lit->IsJSObject()) { - if (true_label_ != fall_through_) __ jmp(true_label_); - } else if (lit->IsString()) { - if (String::cast(*lit)->length() == 0) { - if (false_label_ != fall_through_) __ jmp(false_label_); - } else { - if (true_label_ != fall_through_) __ jmp(true_label_); - } - } else if (lit->IsSmi()) { - if (Smi::ToInt(*lit) == 0) { - if (false_label_ != fall_through_) __ jmp(false_label_); - } else { - if (true_label_ != fall_through_) __ jmp(true_label_); - } - } else { - // For simplicity we always test the accumulator register. - __ mov(result_register(), lit); - codegen()->DoTest(this); - } -} - - -void FullCodeGenerator::StackValueContext::DropAndPlug(int count, - Register reg) const { - DCHECK(count > 0); - if (count > 1) codegen()->DropOperands(count - 1); - __ mov(Operand(esp, 0), reg); -} - - -void FullCodeGenerator::EffectContext::Plug(Label* materialize_true, - Label* materialize_false) const { - DCHECK(materialize_true == materialize_false); - __ bind(materialize_true); -} - - -void FullCodeGenerator::AccumulatorValueContext::Plug( - Label* materialize_true, - Label* materialize_false) const { - Label done; - __ bind(materialize_true); - __ mov(result_register(), isolate()->factory()->true_value()); - __ jmp(&done, Label::kNear); - __ bind(materialize_false); - __ mov(result_register(), isolate()->factory()->false_value()); - __ bind(&done); -} - - -void FullCodeGenerator::StackValueContext::Plug( - Label* materialize_true, - Label* materialize_false) const { - codegen()->OperandStackDepthIncrement(1); - Label done; - __ bind(materialize_true); - __ push(Immediate(isolate()->factory()->true_value())); - __ jmp(&done, Label::kNear); - __ bind(materialize_false); - __ push(Immediate(isolate()->factory()->false_value())); - __ bind(&done); -} - - -void FullCodeGenerator::TestContext::Plug(Label* materialize_true, - Label* materialize_false) const { - DCHECK(materialize_true == true_label_); - DCHECK(materialize_false == false_label_); -} - - -void FullCodeGenerator::AccumulatorValueContext::Plug(bool flag) const { - Handle value = flag - ? isolate()->factory()->true_value() - : isolate()->factory()->false_value(); - __ mov(result_register(), value); -} - - -void FullCodeGenerator::StackValueContext::Plug(bool flag) const { - codegen()->OperandStackDepthIncrement(1); - Handle value = flag - ? isolate()->factory()->true_value() - : isolate()->factory()->false_value(); - __ push(Immediate(value)); -} - - -void FullCodeGenerator::TestContext::Plug(bool flag) const { - if (flag) { - if (true_label_ != fall_through_) __ jmp(true_label_); - } else { - if (false_label_ != fall_through_) __ jmp(false_label_); - } -} - - -void FullCodeGenerator::DoTest(Expression* condition, - Label* if_true, - Label* if_false, - Label* fall_through) { - Callable callable = Builtins::CallableFor(isolate(), Builtins::kToBoolean); - __ Call(callable.code(), RelocInfo::CODE_TARGET); - RestoreContext(); - __ CompareRoot(result_register(), Heap::kTrueValueRootIndex); - Split(equal, if_true, if_false, fall_through); -} - - -void FullCodeGenerator::Split(Condition cc, - Label* if_true, - Label* if_false, - Label* fall_through) { - if (if_false == fall_through) { - __ j(cc, if_true); - } else if (if_true == fall_through) { - __ j(NegateCondition(cc), if_false); - } else { - __ j(cc, if_true); - __ jmp(if_false); - } -} - - -MemOperand FullCodeGenerator::StackOperand(Variable* var) { - DCHECK(var->IsStackAllocated()); - // Offset is negative because higher indexes are at lower addresses. - int offset = -var->index() * kPointerSize; - // Adjust by a (parameter or local) base offset. - if (var->IsParameter()) { - offset += (info_->scope()->num_parameters() + 1) * kPointerSize; - } else { - offset += JavaScriptFrameConstants::kLocal0Offset; - } - return Operand(ebp, offset); -} - - -MemOperand FullCodeGenerator::VarOperand(Variable* var, Register scratch) { - DCHECK(var->IsContextSlot() || var->IsStackAllocated()); - if (var->IsContextSlot()) { - int context_chain_length = scope()->ContextChainLength(var->scope()); - __ LoadContext(scratch, context_chain_length); - return ContextOperand(scratch, var->index()); - } else { - return StackOperand(var); - } -} - - -void FullCodeGenerator::GetVar(Register dest, Variable* var) { - DCHECK(var->IsContextSlot() || var->IsStackAllocated()); - MemOperand location = VarOperand(var, dest); - __ mov(dest, location); -} - - -void FullCodeGenerator::SetVar(Variable* var, - Register src, - Register scratch0, - Register scratch1) { - DCHECK(var->IsContextSlot() || var->IsStackAllocated()); - DCHECK(!scratch0.is(src)); - DCHECK(!scratch0.is(scratch1)); - DCHECK(!scratch1.is(src)); - MemOperand location = VarOperand(var, scratch0); - __ mov(location, src); - - // Emit the write barrier code if the location is in the heap. - if (var->IsContextSlot()) { - int offset = Context::SlotOffset(var->index()); - DCHECK(!scratch0.is(esi) && !src.is(esi) && !scratch1.is(esi)); - __ RecordWriteContextSlot(scratch0, offset, src, scratch1, kDontSaveFPRegs); - } -} - - -void FullCodeGenerator::EmitDebugCheckDeclarationContext(Variable* variable) { - // The variable in the declaration always resides in the current context. - DCHECK_EQ(0, scope()->ContextChainLength(variable->scope())); - if (FLAG_debug_code) { - // Check that we're not inside a with or catch context. - __ mov(ebx, FieldOperand(esi, HeapObject::kMapOffset)); - __ cmp(ebx, isolate()->factory()->with_context_map()); - __ Check(not_equal, kDeclarationInWithContext); - __ cmp(ebx, isolate()->factory()->catch_context_map()); - __ Check(not_equal, kDeclarationInCatchContext); - } -} - - -void FullCodeGenerator::VisitVariableDeclaration( - VariableDeclaration* declaration) { - VariableProxy* proxy = declaration->proxy(); - Variable* variable = proxy->var(); - switch (variable->location()) { - case VariableLocation::UNALLOCATED: { - DCHECK(!variable->binding_needs_init()); - globals_->Add(variable->name(), zone()); - FeedbackSlot slot = proxy->VariableFeedbackSlot(); - DCHECK(!slot.IsInvalid()); - globals_->Add(handle(Smi::FromInt(slot.ToInt()), isolate()), zone()); - globals_->Add(isolate()->factory()->undefined_value(), zone()); - globals_->Add(isolate()->factory()->undefined_value(), zone()); - break; - } - case VariableLocation::PARAMETER: - case VariableLocation::LOCAL: - if (variable->binding_needs_init()) { - Comment cmnt(masm_, "[ VariableDeclaration"); - __ mov(StackOperand(variable), - Immediate(isolate()->factory()->the_hole_value())); - } - break; - - case VariableLocation::CONTEXT: - if (variable->binding_needs_init()) { - Comment cmnt(masm_, "[ VariableDeclaration"); - EmitDebugCheckDeclarationContext(variable); - __ mov(ContextOperand(esi, variable->index()), - Immediate(isolate()->factory()->the_hole_value())); - // No write barrier since the hole value is in old space. - } - break; - - case VariableLocation::LOOKUP: - case VariableLocation::MODULE: - UNREACHABLE(); - } -} - -void FullCodeGenerator::VisitFunctionDeclaration( - FunctionDeclaration* declaration) { - VariableProxy* proxy = declaration->proxy(); - Variable* variable = proxy->var(); - switch (variable->location()) { - case VariableLocation::UNALLOCATED: { - globals_->Add(variable->name(), zone()); - FeedbackSlot slot = proxy->VariableFeedbackSlot(); - DCHECK(!slot.IsInvalid()); - globals_->Add(handle(Smi::FromInt(slot.ToInt()), isolate()), zone()); - - // We need the slot where the literals array lives, too. - slot = declaration->fun()->LiteralFeedbackSlot(); - DCHECK(!slot.IsInvalid()); - globals_->Add(handle(Smi::FromInt(slot.ToInt()), isolate()), zone()); - - Handle function = - Compiler::GetSharedFunctionInfo(declaration->fun(), script(), info_); - // Check for stack-overflow exception. - if (function.is_null()) return SetStackOverflow(); - globals_->Add(function, zone()); - break; - } - - case VariableLocation::PARAMETER: - case VariableLocation::LOCAL: { - Comment cmnt(masm_, "[ FunctionDeclaration"); - VisitForAccumulatorValue(declaration->fun()); - __ mov(StackOperand(variable), result_register()); - break; - } - - case VariableLocation::CONTEXT: { - Comment cmnt(masm_, "[ FunctionDeclaration"); - EmitDebugCheckDeclarationContext(variable); - VisitForAccumulatorValue(declaration->fun()); - __ mov(ContextOperand(esi, variable->index()), result_register()); - // We know that we have written a function, which is not a smi. - __ RecordWriteContextSlot(esi, Context::SlotOffset(variable->index()), - result_register(), ecx, kDontSaveFPRegs, - EMIT_REMEMBERED_SET, OMIT_SMI_CHECK); - break; - } - - case VariableLocation::LOOKUP: - case VariableLocation::MODULE: - UNREACHABLE(); - } -} - - -void FullCodeGenerator::DeclareGlobals(Handle pairs) { - // Call the runtime to declare the globals. - __ Push(pairs); - __ Push(Smi::FromInt(DeclareGlobalsFlags())); - __ EmitLoadFeedbackVector(eax); - __ Push(eax); - __ CallRuntime(Runtime::kDeclareGlobals); - // Return value is ignored. -} - - -void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { - Comment cmnt(masm_, "[ SwitchStatement"); - Breakable nested_statement(this, stmt); - SetStatementPosition(stmt); - - // Keep the switch value on the stack until a case matches. - VisitForStackValue(stmt->tag()); - - ZoneList* clauses = stmt->cases(); - CaseClause* default_clause = NULL; // Can occur anywhere in the list. - - Label next_test; // Recycled for each test. - // Compile all the tests with branches to their bodies. - for (int i = 0; i < clauses->length(); i++) { - CaseClause* clause = clauses->at(i); - clause->body_target()->Unuse(); - - // The default is not a test, but remember it as final fall through. - if (clause->is_default()) { - default_clause = clause; - continue; - } - - Comment cmnt(masm_, "[ Case comparison"); - __ bind(&next_test); - next_test.Unuse(); - - // Compile the label expression. - VisitForAccumulatorValue(clause->label()); - - // Perform the comparison as if via '==='. - __ mov(edx, Operand(esp, 0)); // Switch value. - bool inline_smi_code = ShouldInlineSmiCase(Token::EQ_STRICT); - JumpPatchSite patch_site(masm_); - if (inline_smi_code) { - Label slow_case; - __ mov(ecx, edx); - __ or_(ecx, eax); - patch_site.EmitJumpIfNotSmi(ecx, &slow_case, Label::kNear); - - __ cmp(edx, eax); - __ j(not_equal, &next_test); - __ Drop(1); // Switch value is no longer needed. - __ jmp(clause->body_target()); - __ bind(&slow_case); - } - - SetExpressionPosition(clause); - Handle ic = - CodeFactory::CompareIC(isolate(), Token::EQ_STRICT).code(); - CallIC(ic); - patch_site.EmitPatchInfo(); - - Label skip; - __ jmp(&skip, Label::kNear); - __ cmp(eax, isolate()->factory()->true_value()); - __ j(not_equal, &next_test); - __ Drop(1); - __ jmp(clause->body_target()); - __ bind(&skip); - - __ test(eax, eax); - __ j(not_equal, &next_test); - __ Drop(1); // Switch value is no longer needed. - __ jmp(clause->body_target()); - } - - // Discard the test value and jump to the default if present, otherwise to - // the end of the statement. - __ bind(&next_test); - DropOperands(1); // Switch value is no longer needed. - if (default_clause == NULL) { - __ jmp(nested_statement.break_label()); - } else { - __ jmp(default_clause->body_target()); - } - - // Compile all the case bodies. - for (int i = 0; i < clauses->length(); i++) { - Comment cmnt(masm_, "[ Case body"); - CaseClause* clause = clauses->at(i); - __ bind(clause->body_target()); - VisitStatements(clause->statements()); - } - - __ bind(nested_statement.break_label()); -} - - -void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { - Comment cmnt(masm_, "[ ForInStatement"); - SetStatementPosition(stmt, SKIP_BREAK); - - FeedbackSlot slot = stmt->ForInFeedbackSlot(); - - // Get the object to enumerate over. - SetExpressionAsStatementPosition(stmt->enumerable()); - VisitForAccumulatorValue(stmt->enumerable()); - OperandStackDepthIncrement(5); - - Label loop, exit; - Iteration loop_statement(this, stmt); - increment_loop_depth(); - - // If the object is null or undefined, skip over the loop, otherwise convert - // it to a JS receiver. See ECMA-262 version 5, section 12.6.4. - Label convert, done_convert; - __ JumpIfSmi(eax, &convert, Label::kNear); - __ CmpObjectType(eax, FIRST_JS_RECEIVER_TYPE, ecx); - __ j(above_equal, &done_convert, Label::kNear); - __ cmp(eax, isolate()->factory()->undefined_value()); - __ j(equal, &exit); - __ cmp(eax, isolate()->factory()->null_value()); - __ j(equal, &exit); - __ bind(&convert); - __ Call(isolate()->builtins()->ToObject(), RelocInfo::CODE_TARGET); - RestoreContext(); - __ bind(&done_convert); - __ push(eax); - - // Check cache validity in generated code. If we cannot guarantee cache - // validity, call the runtime system to check cache validity or get the - // property names in a fixed array. Note: Proxies never have an enum cache, - // so will always take the slow path. - Label call_runtime, use_cache, fixed_array; - __ CheckEnumCache(&call_runtime); - - __ mov(eax, FieldOperand(eax, HeapObject::kMapOffset)); - __ jmp(&use_cache, Label::kNear); - - // Get the set of properties to enumerate. - __ bind(&call_runtime); - __ push(eax); - __ CallRuntime(Runtime::kForInEnumerate); - __ cmp(FieldOperand(eax, HeapObject::kMapOffset), - isolate()->factory()->meta_map()); - __ j(not_equal, &fixed_array); - - - // We got a map in register eax. Get the enumeration cache from it. - Label no_descriptors; - __ bind(&use_cache); - - __ EnumLength(edx, eax); - __ cmp(edx, Immediate(Smi::kZero)); - __ j(equal, &no_descriptors); - - __ LoadInstanceDescriptors(eax, ecx); - __ mov(ecx, FieldOperand(ecx, DescriptorArray::kEnumCacheBridgeOffset)); - __ mov(ecx, FieldOperand(ecx, DescriptorArray::kEnumCacheBridgeCacheOffset)); - - // Set up the four remaining stack slots. - __ push(eax); // Map. - __ push(ecx); // Enumeration cache. - __ push(edx); // Number of valid entries for the map in the enum cache. - __ push(Immediate(Smi::kZero)); // Initial index. - __ jmp(&loop); - - __ bind(&no_descriptors); - __ add(esp, Immediate(kPointerSize)); - __ jmp(&exit); - - // We got a fixed array in register eax. Iterate through that. - __ bind(&fixed_array); - - __ push(Immediate(Smi::FromInt(1))); // Smi(1) indicates slow check - __ push(eax); // Array - __ mov(eax, FieldOperand(eax, FixedArray::kLengthOffset)); - __ push(eax); // Fixed array length (as smi). - __ push(Immediate(Smi::kZero)); // Initial index. - - // Generate code for doing the condition check. - __ bind(&loop); - SetExpressionAsStatementPosition(stmt->each()); - - __ mov(eax, Operand(esp, 0 * kPointerSize)); // Get the current index. - __ cmp(eax, Operand(esp, 1 * kPointerSize)); // Compare to the array length. - __ j(above_equal, loop_statement.break_label()); - - // Get the current entry of the array into register eax. - __ mov(ebx, Operand(esp, 2 * kPointerSize)); - __ mov(eax, FieldOperand(ebx, eax, times_2, FixedArray::kHeaderSize)); - - // Get the expected map from the stack or a smi in the - // permanent slow case into register edx. - __ mov(edx, Operand(esp, 3 * kPointerSize)); - - // Check if the expected map still matches that of the enumerable. - // If not, we may have to filter the key. - Label update_each; - __ mov(ebx, Operand(esp, 4 * kPointerSize)); - __ cmp(edx, FieldOperand(ebx, HeapObject::kMapOffset)); - __ j(equal, &update_each, Label::kNear); - - // We need to filter the key, record slow-path here. - int const vector_index = SmiFromSlot(slot)->value(); - __ EmitLoadFeedbackVector(edx); - __ mov(FieldOperand(edx, FixedArray::OffsetOfElementAt(vector_index)), - Immediate(FeedbackVector::MegamorphicSentinel(isolate()))); - - // eax contains the key. The receiver in ebx is the second argument to the - // ForInFilter. ForInFilter returns undefined if the receiver doesn't - // have the key or returns the name-converted key. - __ Call(isolate()->builtins()->ForInFilter(), RelocInfo::CODE_TARGET); - RestoreContext(); - __ JumpIfRoot(result_register(), Heap::kUndefinedValueRootIndex, - loop_statement.continue_label()); - - // Update the 'each' property or variable from the possibly filtered - // entry in register eax. - __ bind(&update_each); - // Perform the assignment as if via '='. - { EffectContext context(this); - EmitAssignment(stmt->each(), stmt->EachFeedbackSlot()); - } - - // Generate code for the body of the loop. - Visit(stmt->body()); - - // Generate code for going to the next element by incrementing the - // index (smi) stored on top of the stack. - __ bind(loop_statement.continue_label()); - __ add(Operand(esp, 0 * kPointerSize), Immediate(Smi::FromInt(1))); - - EmitBackEdgeBookkeeping(stmt, &loop); - __ jmp(&loop); - - // Remove the pointers stored on the stack. - __ bind(loop_statement.break_label()); - DropOperands(5); - - // Exit and decrement the loop depth. - __ bind(&exit); - decrement_loop_depth(); -} - -void FullCodeGenerator::EmitSetHomeObject(Expression* initializer, int offset, - FeedbackSlot slot) { - DCHECK(NeedsHomeObject(initializer)); - __ mov(StoreDescriptor::ReceiverRegister(), Operand(esp, 0)); - __ mov(StoreDescriptor::ValueRegister(), Operand(esp, offset * kPointerSize)); - CallStoreIC(slot, isolate()->factory()->home_object_symbol()); -} - -void FullCodeGenerator::EmitSetHomeObjectAccumulator(Expression* initializer, - int offset, - FeedbackSlot slot) { - DCHECK(NeedsHomeObject(initializer)); - __ mov(StoreDescriptor::ReceiverRegister(), eax); - __ mov(StoreDescriptor::ValueRegister(), Operand(esp, offset * kPointerSize)); - CallStoreIC(slot, isolate()->factory()->home_object_symbol()); -} - -void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy, - TypeofMode typeof_mode) { - SetExpressionPosition(proxy); - Variable* var = proxy->var(); - - // Two cases: global variables and all other types of variables. - switch (var->location()) { - case VariableLocation::UNALLOCATED: { - Comment cmnt(masm_, "[ Global variable"); - EmitGlobalVariableLoad(proxy, typeof_mode); - context()->Plug(eax); - break; - } - - case VariableLocation::PARAMETER: - case VariableLocation::LOCAL: - case VariableLocation::CONTEXT: { - DCHECK_EQ(NOT_INSIDE_TYPEOF, typeof_mode); - Comment cmnt(masm_, var->IsContextSlot() ? "[ Context variable" - : "[ Stack variable"); - - if (proxy->hole_check_mode() == HoleCheckMode::kRequired) { - // Throw a reference error when using an uninitialized let/const - // binding in harmony mode. - Label done; - GetVar(eax, var); - __ cmp(eax, isolate()->factory()->the_hole_value()); - __ j(not_equal, &done, Label::kNear); - __ push(Immediate(var->name())); - __ CallRuntime(Runtime::kThrowReferenceError); - __ bind(&done); - context()->Plug(eax); - break; - } - context()->Plug(var); - break; - } - - case VariableLocation::LOOKUP: - case VariableLocation::MODULE: - UNREACHABLE(); - } -} - - -void FullCodeGenerator::EmitAccessor(ObjectLiteralProperty* property) { - Expression* expression = (property == NULL) ? NULL : property->value(); - if (expression == NULL) { - PushOperand(isolate()->factory()->null_value()); - } else { - VisitForStackValue(expression); - if (NeedsHomeObject(expression)) { - DCHECK(property->kind() == ObjectLiteral::Property::GETTER || - property->kind() == ObjectLiteral::Property::SETTER); - int offset = property->kind() == ObjectLiteral::Property::GETTER ? 2 : 3; - EmitSetHomeObject(expression, offset, property->GetSlot()); - } - } -} - - -void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { - Comment cmnt(masm_, "[ ObjectLiteral"); - - Handle constant_properties = - expr->GetOrBuildConstantProperties(isolate()); - int flags = expr->ComputeFlags(); - // If any of the keys would store to the elements array, then we shouldn't - // allow it. - if (MustCreateObjectLiteralWithRuntime(expr)) { - __ push(Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); - __ push(Immediate(Smi::FromInt(expr->literal_index()))); - __ push(Immediate(constant_properties)); - __ push(Immediate(Smi::FromInt(flags))); - __ CallRuntime(Runtime::kCreateObjectLiteral); - } else { - __ mov(eax, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); - __ mov(ebx, Immediate(Smi::FromInt(expr->literal_index()))); - __ mov(ecx, Immediate(constant_properties)); - __ mov(edx, Immediate(Smi::FromInt(flags))); - Callable callable = - Builtins::CallableFor(isolate(), Builtins::kFastCloneShallowObject); - __ Call(callable.code(), RelocInfo::CODE_TARGET); - RestoreContext(); - } - - // If result_saved is true the result is on top of the stack. If - // result_saved is false the result is in eax. - bool result_saved = false; - - AccessorTable accessor_table(zone()); - for (int i = 0; i < expr->properties()->length(); i++) { - ObjectLiteral::Property* property = expr->properties()->at(i); - DCHECK(!property->is_computed_name()); - if (property->IsCompileTimeValue()) continue; - - Literal* key = property->key()->AsLiteral(); - Expression* value = property->value(); - if (!result_saved) { - PushOperand(eax); // Save result on the stack - result_saved = true; - } - switch (property->kind()) { - case ObjectLiteral::Property::SPREAD: - case ObjectLiteral::Property::CONSTANT: - UNREACHABLE(); - case ObjectLiteral::Property::MATERIALIZED_LITERAL: - DCHECK(!CompileTimeValue::IsCompileTimeValue(value)); - // Fall through. - case ObjectLiteral::Property::COMPUTED: - // It is safe to use [[Put]] here because the boilerplate already - // contains computed properties with an uninitialized value. - if (key->IsStringLiteral()) { - DCHECK(key->IsPropertyName()); - if (property->emit_store()) { - VisitForAccumulatorValue(value); - DCHECK(StoreDescriptor::ValueRegister().is(eax)); - __ mov(StoreDescriptor::ReceiverRegister(), Operand(esp, 0)); - CallStoreIC(property->GetSlot(0), key->value(), kStoreOwn); - if (NeedsHomeObject(value)) { - EmitSetHomeObjectAccumulator(value, 0, property->GetSlot(1)); - } - } else { - VisitForEffect(value); - } - break; - } - PushOperand(Operand(esp, 0)); // Duplicate receiver. - VisitForStackValue(key); - VisitForStackValue(value); - if (property->emit_store()) { - if (NeedsHomeObject(value)) { - EmitSetHomeObject(value, 2, property->GetSlot()); - } - PushOperand(Smi::FromInt(SLOPPY)); // Language mode - CallRuntimeWithOperands(Runtime::kSetProperty); - } else { - DropOperands(3); - } - break; - case ObjectLiteral::Property::PROTOTYPE: - PushOperand(Operand(esp, 0)); // Duplicate receiver. - VisitForStackValue(value); - DCHECK(property->emit_store()); - CallRuntimeWithOperands(Runtime::kInternalSetPrototype); - break; - case ObjectLiteral::Property::GETTER: - if (property->emit_store()) { - AccessorTable::Iterator it = accessor_table.lookup(key); - it->second->getter = property; - } - break; - case ObjectLiteral::Property::SETTER: - if (property->emit_store()) { - AccessorTable::Iterator it = accessor_table.lookup(key); - it->second->setter = property; - } - break; - } - } - - // Emit code to define accessors, using only a single call to the runtime for - // each pair of corresponding getters and setters. - for (AccessorTable::Iterator it = accessor_table.begin(); - it != accessor_table.end(); - ++it) { - PushOperand(Operand(esp, 0)); // Duplicate receiver. - VisitForStackValue(it->first); - - EmitAccessor(it->second->getter); - EmitAccessor(it->second->setter); - - PushOperand(Smi::FromInt(NONE)); - CallRuntimeWithOperands(Runtime::kDefineAccessorPropertyUnchecked); - } - - if (result_saved) { - context()->PlugTOS(); - } else { - context()->Plug(eax); - } -} - - -void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { - Comment cmnt(masm_, "[ ArrayLiteral"); - - Handle constant_elements = - expr->GetOrBuildConstantElements(isolate()); - - if (MustCreateArrayLiteralWithRuntime(expr)) { - __ push(Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); - __ push(Immediate(Smi::FromInt(expr->literal_index()))); - __ push(Immediate(constant_elements)); - __ push(Immediate(Smi::FromInt(expr->ComputeFlags()))); - __ CallRuntime(Runtime::kCreateArrayLiteral); - } else { - __ mov(eax, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); - __ mov(ebx, Immediate(Smi::FromInt(expr->literal_index()))); - __ mov(ecx, Immediate(constant_elements)); - Callable callable = - CodeFactory::FastCloneShallowArray(isolate(), TRACK_ALLOCATION_SITE); - __ Call(callable.code(), RelocInfo::CODE_TARGET); - RestoreContext(); - } - - bool result_saved = false; // Is the result saved to the stack? - ZoneList* subexprs = expr->values(); - int length = subexprs->length(); - - // Emit code to evaluate all the non-constant subexpressions and to store - // them into the newly cloned array. - for (int array_index = 0; array_index < length; array_index++) { - Expression* subexpr = subexprs->at(array_index); - DCHECK(!subexpr->IsSpread()); - - // If the subexpression is a literal or a simple materialized literal it - // is already set in the cloned array. - if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue; - - if (!result_saved) { - PushOperand(eax); // array literal. - result_saved = true; - } - VisitForAccumulatorValue(subexpr); - - __ mov(StoreDescriptor::NameRegister(), - Immediate(Smi::FromInt(array_index))); - __ mov(StoreDescriptor::ReceiverRegister(), Operand(esp, 0)); - CallKeyedStoreIC(expr->LiteralFeedbackSlot()); - } - - if (result_saved) { - context()->PlugTOS(); - } else { - context()->Plug(eax); - } -} - - -void FullCodeGenerator::VisitAssignment(Assignment* expr) { - DCHECK(expr->target()->IsValidReferenceExpressionOrThis()); - - Comment cmnt(masm_, "[ Assignment"); - - Property* property = expr->target()->AsProperty(); - LhsKind assign_type = Property::GetAssignType(property); - - // Evaluate LHS expression. - switch (assign_type) { - case VARIABLE: - // Nothing to do here. - break; - case NAMED_PROPERTY: - if (expr->is_compound()) { - // We need the receiver both on the stack and in the register. - VisitForStackValue(property->obj()); - __ mov(LoadDescriptor::ReceiverRegister(), Operand(esp, 0)); - } else { - VisitForStackValue(property->obj()); - } - break; - case KEYED_PROPERTY: { - if (expr->is_compound()) { - VisitForStackValue(property->obj()); - VisitForStackValue(property->key()); - __ mov(LoadDescriptor::ReceiverRegister(), Operand(esp, kPointerSize)); - __ mov(LoadDescriptor::NameRegister(), Operand(esp, 0)); - } else { - VisitForStackValue(property->obj()); - VisitForStackValue(property->key()); - } - break; - } - case NAMED_SUPER_PROPERTY: - case KEYED_SUPER_PROPERTY: - UNREACHABLE(); - break; - } - - // For compound assignments we need another deoptimization point after the - // variable/property load. - if (expr->is_compound()) { - AccumulatorValueContext result_context(this); - { AccumulatorValueContext left_operand_context(this); - switch (assign_type) { - case VARIABLE: - EmitVariableLoad(expr->target()->AsVariableProxy()); - break; - case NAMED_PROPERTY: - EmitNamedPropertyLoad(property); - break; - case KEYED_PROPERTY: - EmitKeyedPropertyLoad(property); - break; - case NAMED_SUPER_PROPERTY: - case KEYED_SUPER_PROPERTY: - UNREACHABLE(); - break; - } - } - - Token::Value op = expr->binary_op(); - PushOperand(eax); // Left operand goes on the stack. - VisitForAccumulatorValue(expr->value()); - - EmitBinaryOp(expr->binary_operation(), op); - } else { - VisitForAccumulatorValue(expr->value()); - } - - SetExpressionPosition(expr); - - // Store the value. - switch (assign_type) { - case VARIABLE: { - VariableProxy* proxy = expr->target()->AsVariableProxy(); - EmitVariableAssignment(proxy->var(), expr->op(), expr->AssignmentSlot(), - proxy->hole_check_mode()); - context()->Plug(eax); - break; - } - case NAMED_PROPERTY: - EmitNamedPropertyAssignment(expr); - break; - case KEYED_PROPERTY: - EmitKeyedPropertyAssignment(expr); - break; - case NAMED_SUPER_PROPERTY: - case KEYED_SUPER_PROPERTY: - UNREACHABLE(); - break; - } -} - -void FullCodeGenerator::VisitSuspend(Suspend* expr) { - // Resumable functions are not supported. - UNREACHABLE(); -} - -void FullCodeGenerator::PushOperand(MemOperand operand) { - OperandStackDepthIncrement(1); - __ Push(operand); -} - -void FullCodeGenerator::EmitOperandStackDepthCheck() { - if (FLAG_debug_code) { - int expected_diff = StandardFrameConstants::kFixedFrameSizeFromFp + - operand_stack_depth_ * kPointerSize; - __ mov(eax, ebp); - __ sub(eax, esp); - __ cmp(eax, Immediate(expected_diff)); - __ Assert(equal, kUnexpectedStackDepth); - } -} - - -void FullCodeGenerator::EmitBinaryOp(BinaryOperation* expr, Token::Value op) { - PopOperand(edx); - Handle code = CodeFactory::BinaryOperation(isolate(), op).code(); - __ Call(code, RelocInfo::CODE_TARGET); - RestoreContext(); - context()->Plug(eax); -} - -void FullCodeGenerator::EmitAssignment(Expression* expr, FeedbackSlot slot) { - DCHECK(expr->IsValidReferenceExpressionOrThis()); - - Property* prop = expr->AsProperty(); - LhsKind assign_type = Property::GetAssignType(prop); - - switch (assign_type) { - case VARIABLE: { - VariableProxy* proxy = expr->AsVariableProxy(); - EffectContext context(this); - EmitVariableAssignment(proxy->var(), Token::ASSIGN, slot, - proxy->hole_check_mode()); - break; - } - case NAMED_PROPERTY: { - PushOperand(eax); // Preserve value. - VisitForAccumulatorValue(prop->obj()); - __ Move(StoreDescriptor::ReceiverRegister(), eax); - PopOperand(StoreDescriptor::ValueRegister()); // Restore value. - CallStoreIC(slot, prop->key()->AsLiteral()->value()); - break; - } - case KEYED_PROPERTY: { - PushOperand(eax); // Preserve value. - VisitForStackValue(prop->obj()); - VisitForAccumulatorValue(prop->key()); - __ Move(StoreDescriptor::NameRegister(), eax); - PopOperand(StoreDescriptor::ReceiverRegister()); // Receiver. - PopOperand(StoreDescriptor::ValueRegister()); // Restore value. - CallKeyedStoreIC(slot); - break; - } - case NAMED_SUPER_PROPERTY: - case KEYED_SUPER_PROPERTY: - UNREACHABLE(); - break; - } - context()->Plug(eax); -} - - -void FullCodeGenerator::EmitStoreToStackLocalOrContextSlot( - Variable* var, MemOperand location) { - __ mov(location, eax); - if (var->IsContextSlot()) { - __ mov(edx, eax); - int offset = Context::SlotOffset(var->index()); - __ RecordWriteContextSlot(ecx, offset, edx, ebx, kDontSaveFPRegs); - } -} - -void FullCodeGenerator::EmitVariableAssignment(Variable* var, Token::Value op, - FeedbackSlot slot, - HoleCheckMode hole_check_mode) { - if (var->IsUnallocated()) { - // Global var, const, or let. - __ mov(StoreDescriptor::ReceiverRegister(), NativeContextOperand()); - __ mov(StoreDescriptor::ReceiverRegister(), - ContextOperand(StoreDescriptor::ReceiverRegister(), - Context::EXTENSION_INDEX)); - CallStoreIC(slot, var->name(), kStoreGlobal); - - } else if (IsLexicalVariableMode(var->mode()) && op != Token::INIT) { - DCHECK(!var->IsLookupSlot()); - DCHECK(var->IsStackAllocated() || var->IsContextSlot()); - MemOperand location = VarOperand(var, ecx); - // Perform an initialization check for lexically declared variables. - if (hole_check_mode == HoleCheckMode::kRequired) { - Label assign; - __ mov(edx, location); - __ cmp(edx, isolate()->factory()->the_hole_value()); - __ j(not_equal, &assign, Label::kNear); - __ push(Immediate(var->name())); - __ CallRuntime(Runtime::kThrowReferenceError); - __ bind(&assign); - } - if (var->mode() != CONST) { - EmitStoreToStackLocalOrContextSlot(var, location); - } else if (var->throw_on_const_assignment(language_mode())) { - __ CallRuntime(Runtime::kThrowConstAssignError); - } - } else if (var->is_this() && var->mode() == CONST && op == Token::INIT) { - // Initializing assignment to const {this} needs a write barrier. - DCHECK(var->IsStackAllocated() || var->IsContextSlot()); - Label uninitialized_this; - MemOperand location = VarOperand(var, ecx); - __ mov(edx, location); - __ cmp(edx, isolate()->factory()->the_hole_value()); - __ j(equal, &uninitialized_this); - __ push(Immediate(var->name())); - __ CallRuntime(Runtime::kThrowReferenceError); - __ bind(&uninitialized_this); - EmitStoreToStackLocalOrContextSlot(var, location); - - } else { - DCHECK(var->mode() != CONST || op == Token::INIT); - DCHECK(var->IsStackAllocated() || var->IsContextSlot()); - DCHECK(!var->IsLookupSlot()); - // Assignment to var or initializing assignment to let/const in harmony - // mode. - MemOperand location = VarOperand(var, ecx); - EmitStoreToStackLocalOrContextSlot(var, location); - } -} - - -void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { - // Assignment to a property, using a named store IC. - // eax : value - // esp[0] : receiver - Property* prop = expr->target()->AsProperty(); - DCHECK(prop != NULL); - DCHECK(prop->key()->IsLiteral()); - - PopOperand(StoreDescriptor::ReceiverRegister()); - CallStoreIC(expr->AssignmentSlot(), prop->key()->AsLiteral()->value()); - context()->Plug(eax); -} - - -void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) { - // Assignment to a property, using a keyed store IC. - // eax : value - // esp[0] : key - // esp[kPointerSize] : receiver - - PopOperand(StoreDescriptor::NameRegister()); // Key. - PopOperand(StoreDescriptor::ReceiverRegister()); - DCHECK(StoreDescriptor::ValueRegister().is(eax)); - CallKeyedStoreIC(expr->AssignmentSlot()); - context()->Plug(eax); -} - -// Code common for calls using the IC. -void FullCodeGenerator::EmitCallWithLoadIC(Call* expr) { - Expression* callee = expr->expression(); - - // Get the target function. - ConvertReceiverMode convert_mode; - if (callee->IsVariableProxy()) { - { StackValueContext context(this); - EmitVariableLoad(callee->AsVariableProxy()); - } - // Push undefined as receiver. This is patched in the method prologue if it - // is a sloppy mode method. - PushOperand(isolate()->factory()->undefined_value()); - convert_mode = ConvertReceiverMode::kNullOrUndefined; - } else { - // Load the function from the receiver. - DCHECK(callee->IsProperty()); - DCHECK(!callee->AsProperty()->IsSuperAccess()); - __ mov(LoadDescriptor::ReceiverRegister(), Operand(esp, 0)); - EmitNamedPropertyLoad(callee->AsProperty()); - // Push the target function under the receiver. - PushOperand(Operand(esp, 0)); - __ mov(Operand(esp, kPointerSize), eax); - convert_mode = ConvertReceiverMode::kNotNullOrUndefined; - } - - EmitCall(expr, convert_mode); -} - - -// Code common for calls using the IC. -void FullCodeGenerator::EmitKeyedCallWithLoadIC(Call* expr, - Expression* key) { - // Load the key. - VisitForAccumulatorValue(key); - - Expression* callee = expr->expression(); - - // Load the function from the receiver. - DCHECK(callee->IsProperty()); - __ mov(LoadDescriptor::ReceiverRegister(), Operand(esp, 0)); - __ mov(LoadDescriptor::NameRegister(), eax); - EmitKeyedPropertyLoad(callee->AsProperty()); - - // Push the target function under the receiver. - PushOperand(Operand(esp, 0)); - __ mov(Operand(esp, kPointerSize), eax); - - EmitCall(expr, ConvertReceiverMode::kNotNullOrUndefined); -} - - -void FullCodeGenerator::EmitCall(Call* expr, ConvertReceiverMode mode) { - // Load the arguments. - ZoneList* args = expr->arguments(); - int arg_count = args->length(); - for (int i = 0; i < arg_count; i++) { - VisitForStackValue(args->at(i)); - } - - SetCallPosition(expr, expr->tail_call_mode()); - if (expr->tail_call_mode() == TailCallMode::kAllow) { - if (FLAG_trace) { - __ CallRuntime(Runtime::kTraceTailCall); - } - // Update profiling counters before the tail call since we will - // not return to this function. - EmitProfilingCounterHandlingForReturnSequence(true); - } - Handle code = - CodeFactory::CallICTrampoline(isolate(), mode, expr->tail_call_mode()) - .code(); - __ Move(edx, Immediate(SmiFromSlot(expr->CallFeedbackICSlot()))); - __ mov(edi, Operand(esp, (arg_count + 1) * kPointerSize)); - __ Move(eax, Immediate(arg_count)); - CallIC(code); - OperandStackDepthDecrement(arg_count + 1); - - RestoreContext(); - context()->DropAndPlug(1, eax); -} - -void FullCodeGenerator::VisitCallNew(CallNew* expr) { - Comment cmnt(masm_, "[ CallNew"); - // According to ECMA-262, section 11.2.2, page 44, the function - // expression in new calls must be evaluated before the - // arguments. - - // Push constructor on the stack. If it's not a function it's used as - // receiver for CALL_NON_FUNCTION, otherwise the value on the stack is - // ignored. - DCHECK(!expr->expression()->IsSuperPropertyReference()); - VisitForStackValue(expr->expression()); - - // Push the arguments ("left-to-right") on the stack. - ZoneList* args = expr->arguments(); - int arg_count = args->length(); - for (int i = 0; i < arg_count; i++) { - VisitForStackValue(args->at(i)); - } - - // Call the construct call builtin that handles allocation and - // constructor invocation. - SetConstructCallPosition(expr); - - // Load function and argument count into edi and eax. - __ Move(eax, Immediate(arg_count)); - __ mov(edi, Operand(esp, arg_count * kPointerSize)); - - // Record call targets in unoptimized code. - __ EmitLoadFeedbackVector(ebx); - __ mov(edx, Immediate(SmiFromSlot(expr->CallNewFeedbackSlot()))); - - CallConstructStub stub(isolate()); - CallIC(stub.GetCode()); - OperandStackDepthDecrement(arg_count + 1); - RestoreContext(); - context()->Plug(eax); -} - - -void FullCodeGenerator::EmitIsSmi(CallRuntime* expr) { - ZoneList* args = expr->arguments(); - DCHECK(args->length() == 1); - - VisitForAccumulatorValue(args->at(0)); - - Label materialize_true, materialize_false; - Label* if_true = NULL; - Label* if_false = NULL; - Label* fall_through = NULL; - context()->PrepareTest(&materialize_true, &materialize_false, - &if_true, &if_false, &fall_through); - - __ test(eax, Immediate(kSmiTagMask)); - Split(zero, if_true, if_false, fall_through); - - context()->Plug(if_true, if_false); -} - - -void FullCodeGenerator::EmitIsJSReceiver(CallRuntime* expr) { - ZoneList* args = expr->arguments(); - DCHECK(args->length() == 1); - - VisitForAccumulatorValue(args->at(0)); - - Label materialize_true, materialize_false; - Label* if_true = NULL; - Label* if_false = NULL; - Label* fall_through = NULL; - context()->PrepareTest(&materialize_true, &materialize_false, - &if_true, &if_false, &fall_through); - - __ JumpIfSmi(eax, if_false); - __ CmpObjectType(eax, FIRST_JS_RECEIVER_TYPE, ebx); - Split(above_equal, if_true, if_false, fall_through); - - context()->Plug(if_true, if_false); -} - - -void FullCodeGenerator::EmitIsArray(CallRuntime* expr) { - ZoneList* args = expr->arguments(); - DCHECK(args->length() == 1); - - VisitForAccumulatorValue(args->at(0)); - - Label materialize_true, materialize_false; - Label* if_true = NULL; - Label* if_false = NULL; - Label* fall_through = NULL; - context()->PrepareTest(&materialize_true, &materialize_false, - &if_true, &if_false, &fall_through); - - __ JumpIfSmi(eax, if_false); - __ CmpObjectType(eax, JS_ARRAY_TYPE, ebx); - Split(equal, if_true, if_false, fall_through); - - context()->Plug(if_true, if_false); -} - - -void FullCodeGenerator::EmitIsTypedArray(CallRuntime* expr) { - ZoneList* args = expr->arguments(); - DCHECK(args->length() == 1); - - VisitForAccumulatorValue(args->at(0)); - - Label materialize_true, materialize_false; - Label* if_true = NULL; - Label* if_false = NULL; - Label* fall_through = NULL; - context()->PrepareTest(&materialize_true, &materialize_false, &if_true, - &if_false, &fall_through); - - __ JumpIfSmi(eax, if_false); - __ CmpObjectType(eax, JS_TYPED_ARRAY_TYPE, ebx); - Split(equal, if_true, if_false, fall_through); - - context()->Plug(if_true, if_false); -} - - -void FullCodeGenerator::EmitIsJSProxy(CallRuntime* expr) { - ZoneList* args = expr->arguments(); - DCHECK(args->length() == 1); - - VisitForAccumulatorValue(args->at(0)); - - Label materialize_true, materialize_false; - Label* if_true = NULL; - Label* if_false = NULL; - Label* fall_through = NULL; - context()->PrepareTest(&materialize_true, &materialize_false, &if_true, - &if_false, &fall_through); - - __ JumpIfSmi(eax, if_false); - __ CmpObjectType(eax, JS_PROXY_TYPE, ebx); - Split(equal, if_true, if_false, fall_through); - - context()->Plug(if_true, if_false); -} - -void FullCodeGenerator::EmitClassOf(CallRuntime* expr) { - ZoneList* args = expr->arguments(); - DCHECK(args->length() == 1); - Label done, null, function, non_function_constructor; - - VisitForAccumulatorValue(args->at(0)); - - // If the object is not a JSReceiver, we return null. - __ JumpIfSmi(eax, &null, Label::kNear); - STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE); - __ CmpObjectType(eax, FIRST_JS_RECEIVER_TYPE, eax); - __ j(below, &null, Label::kNear); - - // Return 'Function' for JSFunction and JSBoundFunction objects. - __ CmpInstanceType(eax, FIRST_FUNCTION_TYPE); - STATIC_ASSERT(LAST_FUNCTION_TYPE == LAST_TYPE); - __ j(above_equal, &function, Label::kNear); - - // Check if the constructor in the map is a JS function. - __ GetMapConstructor(eax, eax, ebx); - __ CmpInstanceType(ebx, JS_FUNCTION_TYPE); - __ j(not_equal, &non_function_constructor, Label::kNear); - - // eax now contains the constructor function. Grab the - // instance class name from there. - __ mov(eax, FieldOperand(eax, JSFunction::kSharedFunctionInfoOffset)); - __ mov(eax, FieldOperand(eax, SharedFunctionInfo::kInstanceClassNameOffset)); - __ jmp(&done, Label::kNear); - - // Non-JS objects have class null. - __ bind(&null); - __ mov(eax, isolate()->factory()->null_value()); - __ jmp(&done, Label::kNear); - - // Functions have class 'Function'. - __ bind(&function); - __ mov(eax, isolate()->factory()->Function_string()); - __ jmp(&done, Label::kNear); - - // Objects with a non-function constructor have class 'Object'. - __ bind(&non_function_constructor); - __ mov(eax, isolate()->factory()->Object_string()); - - // All done. - __ bind(&done); - - context()->Plug(eax); -} - -void FullCodeGenerator::EmitStringCharCodeAt(CallRuntime* expr) { - ZoneList* args = expr->arguments(); - DCHECK(args->length() == 2); - - VisitForStackValue(args->at(0)); - VisitForAccumulatorValue(args->at(1)); - - Register object = ebx; - Register index = eax; - Register result = edx; - - PopOperand(object); - - Label need_conversion; - Label index_out_of_range; - Label done; - StringCharCodeAtGenerator generator(object, index, result, &need_conversion, - &need_conversion, &index_out_of_range); - generator.GenerateFast(masm_); - __ jmp(&done); - - __ bind(&index_out_of_range); - // When the index is out of range, the spec requires us to return - // NaN. - __ Move(result, Immediate(isolate()->factory()->nan_value())); - __ jmp(&done); - - __ bind(&need_conversion); - // Move the undefined value into the result register, which will - // trigger conversion. - __ Move(result, Immediate(isolate()->factory()->undefined_value())); - __ jmp(&done); - - NopRuntimeCallHelper call_helper; - generator.GenerateSlow(masm_, NOT_PART_OF_IC_HANDLER, call_helper); - - __ bind(&done); - context()->Plug(result); -} - - -void FullCodeGenerator::EmitCall(CallRuntime* expr) { - ZoneList* args = expr->arguments(); - DCHECK_LE(2, args->length()); - // Push target, receiver and arguments onto the stack. - for (Expression* const arg : *args) { - VisitForStackValue(arg); - } - // Move target to edi. - int const argc = args->length() - 2; - __ mov(edi, Operand(esp, (argc + 1) * kPointerSize)); - // Call the target. - __ mov(eax, Immediate(argc)); - __ Call(isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); - OperandStackDepthDecrement(argc + 1); - RestoreContext(); - // Discard the function left on TOS. - context()->DropAndPlug(1, eax); -} - -void FullCodeGenerator::EmitGetSuperConstructor(CallRuntime* expr) { - ZoneList* args = expr->arguments(); - DCHECK_EQ(1, args->length()); - VisitForAccumulatorValue(args->at(0)); - __ AssertFunction(eax); - __ mov(eax, FieldOperand(eax, HeapObject::kMapOffset)); - __ mov(eax, FieldOperand(eax, Map::kPrototypeOffset)); - context()->Plug(eax); -} - -void FullCodeGenerator::EmitDebugIsActive(CallRuntime* expr) { - DCHECK(expr->arguments()->length() == 0); - ExternalReference debug_is_active = - ExternalReference::debug_is_active_address(isolate()); - __ movzx_b(eax, Operand::StaticVariable(debug_is_active)); - __ SmiTag(eax); - context()->Plug(eax); -} - - -void FullCodeGenerator::EmitLoadJSRuntimeFunction(CallRuntime* expr) { - // Push function. - __ LoadGlobalFunction(expr->context_index(), eax); - PushOperand(eax); - - // Push undefined as receiver. - PushOperand(isolate()->factory()->undefined_value()); -} - - -void FullCodeGenerator::EmitCallJSRuntimeFunction(CallRuntime* expr) { - ZoneList* args = expr->arguments(); - int arg_count = args->length(); - - SetCallPosition(expr); - __ mov(edi, Operand(esp, (arg_count + 1) * kPointerSize)); - __ Set(eax, arg_count); - __ Call(isolate()->builtins()->Call(ConvertReceiverMode::kNullOrUndefined), - RelocInfo::CODE_TARGET); - OperandStackDepthDecrement(arg_count + 1); - RestoreContext(); -} - - -void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { - switch (expr->op()) { - case Token::DELETE: { - Comment cmnt(masm_, "[ UnaryOperation (DELETE)"); - Property* property = expr->expression()->AsProperty(); - VariableProxy* proxy = expr->expression()->AsVariableProxy(); - - if (property != NULL) { - VisitForStackValue(property->obj()); - VisitForStackValue(property->key()); - PushOperand(Smi::FromInt(language_mode())); - CallRuntimeWithOperands(Runtime::kDeleteProperty); - context()->Plug(eax); - } else if (proxy != NULL) { - Variable* var = proxy->var(); - // Delete of an unqualified identifier is disallowed in strict mode but - // "delete this" is allowed. - bool is_this = var->is_this(); - DCHECK(is_sloppy(language_mode()) || is_this); - if (var->IsUnallocated()) { - __ mov(eax, NativeContextOperand()); - __ push(ContextOperand(eax, Context::EXTENSION_INDEX)); - __ push(Immediate(var->name())); - __ Push(Smi::FromInt(SLOPPY)); - __ CallRuntime(Runtime::kDeleteProperty); - context()->Plug(eax); - } else { - DCHECK(!var->IsLookupSlot()); - DCHECK(var->IsStackAllocated() || var->IsContextSlot()); - // Result of deleting non-global variables is false. 'this' is - // not really a variable, though we implement it as one. The - // subexpression does not have side effects. - context()->Plug(is_this); - } - } else { - // Result of deleting non-property, non-variable reference is true. - // The subexpression may have side effects. - VisitForEffect(expr->expression()); - context()->Plug(true); - } - break; - } - - case Token::VOID: { - Comment cmnt(masm_, "[ UnaryOperation (VOID)"); - VisitForEffect(expr->expression()); - context()->Plug(isolate()->factory()->undefined_value()); - break; - } - - case Token::NOT: { - Comment cmnt(masm_, "[ UnaryOperation (NOT)"); - if (context()->IsEffect()) { - // Unary NOT has no side effects so it's only necessary to visit the - // subexpression. Match the optimizing compiler by not branching. - VisitForEffect(expr->expression()); - } else if (context()->IsTest()) { - const TestContext* test = TestContext::cast(context()); - // The labels are swapped for the recursive call. - VisitForControl(expr->expression(), - test->false_label(), - test->true_label(), - test->fall_through()); - context()->Plug(test->true_label(), test->false_label()); - } else { - // We handle value contexts explicitly rather than simply visiting - // for control and plugging the control flow into the context, - // because we need to prepare a pair of extra administrative AST ids - // for the optimizing compiler. - DCHECK(context()->IsAccumulatorValue() || context()->IsStackValue()); - Label materialize_true, materialize_false, done; - VisitForControl(expr->expression(), - &materialize_false, - &materialize_true, - &materialize_true); - if (!context()->IsAccumulatorValue()) OperandStackDepthIncrement(1); - __ bind(&materialize_true); - if (context()->IsAccumulatorValue()) { - __ mov(eax, isolate()->factory()->true_value()); - } else { - __ Push(isolate()->factory()->true_value()); - } - __ jmp(&done, Label::kNear); - __ bind(&materialize_false); - if (context()->IsAccumulatorValue()) { - __ mov(eax, isolate()->factory()->false_value()); - } else { - __ Push(isolate()->factory()->false_value()); - } - __ bind(&done); - } - break; - } - - case Token::TYPEOF: { - Comment cmnt(masm_, "[ UnaryOperation (TYPEOF)"); - { - AccumulatorValueContext context(this); - VisitForTypeofValue(expr->expression()); - } - __ mov(ebx, eax); - __ Call(isolate()->builtins()->Typeof(), RelocInfo::CODE_TARGET); - context()->Plug(eax); - break; - } - - default: - UNREACHABLE(); - } -} - - -void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { - DCHECK(expr->expression()->IsValidReferenceExpressionOrThis()); - - Comment cmnt(masm_, "[ CountOperation"); - - Property* prop = expr->expression()->AsProperty(); - LhsKind assign_type = Property::GetAssignType(prop); - - // Evaluate expression and get value. - if (assign_type == VARIABLE) { - DCHECK(expr->expression()->AsVariableProxy()->var() != NULL); - AccumulatorValueContext context(this); - EmitVariableLoad(expr->expression()->AsVariableProxy()); - } else { - // Reserve space for result of postfix operation. - if (expr->is_postfix() && !context()->IsEffect()) { - PushOperand(Smi::kZero); - } - switch (assign_type) { - case NAMED_PROPERTY: { - // Put the object both on the stack and in the register. - VisitForStackValue(prop->obj()); - __ mov(LoadDescriptor::ReceiverRegister(), Operand(esp, 0)); - EmitNamedPropertyLoad(prop); - break; - } - - case KEYED_PROPERTY: { - VisitForStackValue(prop->obj()); - VisitForStackValue(prop->key()); - __ mov(LoadDescriptor::ReceiverRegister(), - Operand(esp, kPointerSize)); // Object. - __ mov(LoadDescriptor::NameRegister(), Operand(esp, 0)); // Key. - EmitKeyedPropertyLoad(prop); - break; - } - - case NAMED_SUPER_PROPERTY: - case KEYED_SUPER_PROPERTY: - case VARIABLE: - UNREACHABLE(); - } - } - - // Convert old value into a number. - __ Call(isolate()->builtins()->ToNumber(), RelocInfo::CODE_TARGET); - RestoreContext(); - - // Save result for postfix expressions. - if (expr->is_postfix()) { - if (!context()->IsEffect()) { - // Save the result on the stack. If we have a named or keyed property - // we store the result under the receiver that is currently on top - // of the stack. - switch (assign_type) { - case VARIABLE: - PushOperand(eax); - break; - case NAMED_PROPERTY: - __ mov(Operand(esp, kPointerSize), eax); - break; - case KEYED_PROPERTY: - __ mov(Operand(esp, 2 * kPointerSize), eax); - break; - case NAMED_SUPER_PROPERTY: - case KEYED_SUPER_PROPERTY: - UNREACHABLE(); - break; - } - } - } - - SetExpressionPosition(expr); - - // Call stub for +1/-1. - __ mov(edx, eax); - __ mov(eax, Immediate(Smi::FromInt(1))); - Handle code = - CodeFactory::BinaryOperation(isolate(), expr->binary_op()).code(); - __ Call(code, RelocInfo::CODE_TARGET); - RestoreContext(); - - // Store the value returned in eax. - switch (assign_type) { - case VARIABLE: { - VariableProxy* proxy = expr->expression()->AsVariableProxy(); - if (expr->is_postfix()) { - // Perform the assignment as if via '='. - { EffectContext context(this); - EmitVariableAssignment(proxy->var(), Token::ASSIGN, expr->CountSlot(), - proxy->hole_check_mode()); - context.Plug(eax); - } - // For all contexts except EffectContext We have the result on - // top of the stack. - if (!context()->IsEffect()) { - context()->PlugTOS(); - } - } else { - // Perform the assignment as if via '='. - EmitVariableAssignment(proxy->var(), Token::ASSIGN, expr->CountSlot(), - proxy->hole_check_mode()); - context()->Plug(eax); - } - break; - } - case NAMED_PROPERTY: { - PopOperand(StoreDescriptor::ReceiverRegister()); - CallStoreIC(expr->CountSlot(), prop->key()->AsLiteral()->value()); - if (expr->is_postfix()) { - if (!context()->IsEffect()) { - context()->PlugTOS(); - } - } else { - context()->Plug(eax); - } - break; - } - case KEYED_PROPERTY: { - PopOperand(StoreDescriptor::NameRegister()); - PopOperand(StoreDescriptor::ReceiverRegister()); - CallKeyedStoreIC(expr->CountSlot()); - if (expr->is_postfix()) { - // Result is on the stack - if (!context()->IsEffect()) { - context()->PlugTOS(); - } - } else { - context()->Plug(eax); - } - break; - } - case NAMED_SUPER_PROPERTY: - case KEYED_SUPER_PROPERTY: - UNREACHABLE(); - break; - } -} - - -void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr, - Expression* sub_expr, - Handle check) { - Label materialize_true, materialize_false; - Label* if_true = NULL; - Label* if_false = NULL; - Label* fall_through = NULL; - context()->PrepareTest(&materialize_true, &materialize_false, - &if_true, &if_false, &fall_through); - - { AccumulatorValueContext context(this); - VisitForTypeofValue(sub_expr); - } - - Factory* factory = isolate()->factory(); - if (String::Equals(check, factory->number_string())) { - __ JumpIfSmi(eax, if_true); - __ cmp(FieldOperand(eax, HeapObject::kMapOffset), - isolate()->factory()->heap_number_map()); - Split(equal, if_true, if_false, fall_through); - } else if (String::Equals(check, factory->string_string())) { - __ JumpIfSmi(eax, if_false); - __ CmpObjectType(eax, FIRST_NONSTRING_TYPE, edx); - Split(below, if_true, if_false, fall_through); - } else if (String::Equals(check, factory->symbol_string())) { - __ JumpIfSmi(eax, if_false); - __ CmpObjectType(eax, SYMBOL_TYPE, edx); - Split(equal, if_true, if_false, fall_through); - } else if (String::Equals(check, factory->boolean_string())) { - __ cmp(eax, isolate()->factory()->true_value()); - __ j(equal, if_true); - __ cmp(eax, isolate()->factory()->false_value()); - Split(equal, if_true, if_false, fall_through); - } else if (String::Equals(check, factory->undefined_string())) { - __ cmp(eax, isolate()->factory()->null_value()); - __ j(equal, if_false); - __ JumpIfSmi(eax, if_false); - // Check for undetectable objects => true. - __ mov(edx, FieldOperand(eax, HeapObject::kMapOffset)); - __ test_b(FieldOperand(edx, Map::kBitFieldOffset), - Immediate(1 << Map::kIsUndetectable)); - Split(not_zero, if_true, if_false, fall_through); - } else if (String::Equals(check, factory->function_string())) { - __ JumpIfSmi(eax, if_false); - // Check for callable and not undetectable objects => true. - __ mov(edx, FieldOperand(eax, HeapObject::kMapOffset)); - __ movzx_b(ecx, FieldOperand(edx, Map::kBitFieldOffset)); - __ and_(ecx, (1 << Map::kIsCallable) | (1 << Map::kIsUndetectable)); - __ cmp(ecx, 1 << Map::kIsCallable); - Split(equal, if_true, if_false, fall_through); - } else if (String::Equals(check, factory->object_string())) { - __ JumpIfSmi(eax, if_false); - __ cmp(eax, isolate()->factory()->null_value()); - __ j(equal, if_true); - STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE); - __ CmpObjectType(eax, FIRST_JS_RECEIVER_TYPE, edx); - __ j(below, if_false); - // Check for callable or undetectable objects => false. - __ test_b(FieldOperand(edx, Map::kBitFieldOffset), - Immediate((1 << Map::kIsCallable) | (1 << Map::kIsUndetectable))); - Split(zero, if_true, if_false, fall_through); - } else { - if (if_false != fall_through) __ jmp(if_false); - } - context()->Plug(if_true, if_false); -} - - -void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { - Comment cmnt(masm_, "[ CompareOperation"); - - // First we try a fast inlined version of the compare when one of - // the operands is a literal. - if (TryLiteralCompare(expr)) return; - - // Always perform the comparison for its control flow. Pack the result - // into the expression's context after the comparison is performed. - Label materialize_true, materialize_false; - Label* if_true = NULL; - Label* if_false = NULL; - Label* fall_through = NULL; - context()->PrepareTest(&materialize_true, &materialize_false, - &if_true, &if_false, &fall_through); - - Token::Value op = expr->op(); - VisitForStackValue(expr->left()); - switch (op) { - case Token::IN: - VisitForStackValue(expr->right()); - SetExpressionPosition(expr); - EmitHasProperty(); - __ cmp(eax, isolate()->factory()->true_value()); - Split(equal, if_true, if_false, fall_through); - break; - - case Token::INSTANCEOF: { - VisitForAccumulatorValue(expr->right()); - SetExpressionPosition(expr); - PopOperand(edx); - __ Call(isolate()->builtins()->InstanceOf(), RelocInfo::CODE_TARGET); - RestoreContext(); - __ cmp(eax, isolate()->factory()->true_value()); - Split(equal, if_true, if_false, fall_through); - break; - } - - default: { - VisitForAccumulatorValue(expr->right()); - SetExpressionPosition(expr); - Condition cc = CompareIC::ComputeCondition(op); - PopOperand(edx); - - bool inline_smi_code = ShouldInlineSmiCase(op); - JumpPatchSite patch_site(masm_); - if (inline_smi_code) { - Label slow_case; - __ mov(ecx, edx); - __ or_(ecx, eax); - patch_site.EmitJumpIfNotSmi(ecx, &slow_case, Label::kNear); - __ cmp(edx, eax); - Split(cc, if_true, if_false, NULL); - __ bind(&slow_case); - } - - Handle ic = CodeFactory::CompareIC(isolate(), op).code(); - CallIC(ic); - patch_site.EmitPatchInfo(); - - __ test(eax, eax); - Split(cc, if_true, if_false, fall_through); - } - } - - // Convert the result of the comparison into one expected for this - // expression's context. - context()->Plug(if_true, if_false); -} - - -void FullCodeGenerator::EmitLiteralCompareNil(CompareOperation* expr, - Expression* sub_expr, - NilValue nil) { - Label materialize_true, materialize_false; - Label* if_true = NULL; - Label* if_false = NULL; - Label* fall_through = NULL; - context()->PrepareTest(&materialize_true, &materialize_false, - &if_true, &if_false, &fall_through); - - VisitForAccumulatorValue(sub_expr); - - Handle nil_value = nil == kNullValue - ? isolate()->factory()->null_value() - : isolate()->factory()->undefined_value(); - if (expr->op() == Token::EQ_STRICT) { - __ cmp(eax, nil_value); - Split(equal, if_true, if_false, fall_through); - } else { - __ JumpIfSmi(eax, if_false); - __ mov(eax, FieldOperand(eax, HeapObject::kMapOffset)); - __ test_b(FieldOperand(eax, Map::kBitFieldOffset), - Immediate(1 << Map::kIsUndetectable)); - Split(not_zero, if_true, if_false, fall_through); - } - context()->Plug(if_true, if_false); -} - - -Register FullCodeGenerator::result_register() { - return eax; -} - - -Register FullCodeGenerator::context_register() { - return esi; -} - -void FullCodeGenerator::LoadFromFrameField(int frame_offset, Register value) { - DCHECK_EQ(POINTER_SIZE_ALIGN(frame_offset), frame_offset); - __ mov(value, Operand(ebp, frame_offset)); -} - -void FullCodeGenerator::StoreToFrameField(int frame_offset, Register value) { - DCHECK_EQ(POINTER_SIZE_ALIGN(frame_offset), frame_offset); - __ mov(Operand(ebp, frame_offset), value); -} - - -void FullCodeGenerator::LoadContextField(Register dst, int context_index) { - __ mov(dst, ContextOperand(esi, context_index)); -} - - -void FullCodeGenerator::PushFunctionArgumentForContextAllocation() { - DeclarationScope* closure_scope = scope()->GetClosureScope(); - if (closure_scope->is_script_scope() || - closure_scope->is_module_scope()) { - // Contexts nested in the native context have a canonical empty function - // as their closure, not the anonymous closure containing the global - // code. - __ mov(eax, NativeContextOperand()); - PushOperand(ContextOperand(eax, Context::CLOSURE_INDEX)); - } else if (closure_scope->is_eval_scope()) { - // Contexts nested inside eval code have the same closure as the context - // calling eval, not the anonymous closure containing the eval code. - // Fetch it from the context. - PushOperand(ContextOperand(esi, Context::CLOSURE_INDEX)); - } else { - DCHECK(closure_scope->is_function_scope()); - PushOperand(Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); - } -} - - -#undef __ - - -static const byte kJnsInstruction = 0x79; -static const byte kJnsOffset = 0x11; -static const byte kNopByteOne = 0x66; -static const byte kNopByteTwo = 0x90; -#ifdef DEBUG -static const byte kCallInstruction = 0xe8; -#endif - - -void BackEdgeTable::PatchAt(Code* unoptimized_code, - Address pc, - BackEdgeState target_state, - Code* replacement_code) { - Address call_target_address = pc - kIntSize; - Address jns_instr_address = call_target_address - 3; - Address jns_offset_address = call_target_address - 2; - - switch (target_state) { - case INTERRUPT: - // sub , ;; Not changed - // jns ok - // call - // ok: - *jns_instr_address = kJnsInstruction; - *jns_offset_address = kJnsOffset; - break; - case ON_STACK_REPLACEMENT: - // sub , ;; Not changed - // nop - // nop - // call - // ok: - *jns_instr_address = kNopByteOne; - *jns_offset_address = kNopByteTwo; - break; - } - - Assembler::set_target_address_at(unoptimized_code->GetIsolate(), - call_target_address, unoptimized_code, - replacement_code->entry()); - unoptimized_code->GetHeap()->incremental_marking()->RecordCodeTargetPatch( - unoptimized_code, call_target_address, replacement_code); -} - - -BackEdgeTable::BackEdgeState BackEdgeTable::GetBackEdgeState( - Isolate* isolate, - Code* unoptimized_code, - Address pc) { - Address call_target_address = pc - kIntSize; - Address jns_instr_address = call_target_address - 3; - DCHECK_EQ(kCallInstruction, *(call_target_address - 1)); - - if (*jns_instr_address == kJnsInstruction) { - DCHECK_EQ(kJnsOffset, *(call_target_address - 2)); - DCHECK_EQ(isolate->builtins()->InterruptCheck()->entry(), - Assembler::target_address_at(call_target_address, - unoptimized_code)); - return INTERRUPT; - } - - DCHECK_EQ(kNopByteOne, *jns_instr_address); - DCHECK_EQ(kNopByteTwo, *(call_target_address - 2)); - - DCHECK_EQ( - isolate->builtins()->OnStackReplacement()->entry(), - Assembler::target_address_at(call_target_address, unoptimized_code)); - return ON_STACK_REPLACEMENT; -} - - -} // namespace internal -} // namespace v8 - -#endif // V8_TARGET_ARCH_X87 diff --git a/src/gdb-jit.cc b/src/gdb-jit.cc index 3a817893e0..d0395330c2 100644 --- a/src/gdb-jit.cc +++ b/src/gdb-jit.cc @@ -199,7 +199,7 @@ class DebugSectionBase : public ZoneObject { struct MachOSectionHeader { char sectname[16]; char segname[16]; -#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X87 +#if V8_TARGET_ARCH_IA32 uint32_t addr; uint32_t size; #else @@ -507,7 +507,7 @@ class MachO BASE_EMBEDDED { uint32_t cmd; uint32_t cmdsize; char segname[16]; -#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X87 +#if V8_TARGET_ARCH_IA32 uint32_t vmaddr; uint32_t vmsize; uint32_t fileoff; @@ -533,7 +533,7 @@ class MachO BASE_EMBEDDED { Writer::Slot WriteHeader(Writer* w) { DCHECK(w->position() == 0); Writer::Slot header = w->CreateSlotHere(); -#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X87 +#if V8_TARGET_ARCH_IA32 header->magic = 0xFEEDFACEu; header->cputype = 7; // i386 header->cpusubtype = 3; // CPU_SUBTYPE_I386_ALL @@ -558,7 +558,7 @@ class MachO BASE_EMBEDDED { uintptr_t code_size) { Writer::Slot cmd = w->CreateSlotHere(); -#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X87 +#if V8_TARGET_ARCH_IA32 cmd->cmd = LC_SEGMENT_32; #else cmd->cmd = LC_SEGMENT_64; @@ -646,7 +646,7 @@ class ELF BASE_EMBEDDED { void WriteHeader(Writer* w) { DCHECK(w->position() == 0); Writer::Slot header = w->CreateSlotHere(); -#if (V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_X87 || \ +#if (V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_ARM || \ (V8_TARGET_ARCH_X64 && V8_TARGET_ARCH_32_BIT)) const uint8_t ident[16] = { 0x7f, 'E', 'L', 'F', 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; @@ -668,7 +668,7 @@ class ELF BASE_EMBEDDED { #endif memcpy(header->ident, ident, 16); header->type = 1; -#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X87 +#if V8_TARGET_ARCH_IA32 header->machine = 3; #elif V8_TARGET_ARCH_X64 // Processor identification value for x64 is 62 as defined in @@ -783,8 +783,8 @@ class ELFSymbol BASE_EMBEDDED { Binding binding() const { return static_cast(info >> 4); } -#if (V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_X87 || \ - (V8_TARGET_ARCH_X64 && V8_TARGET_ARCH_32_BIT) || \ +#if (V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_ARM || \ + (V8_TARGET_ARCH_X64 && V8_TARGET_ARCH_32_BIT) || \ (V8_TARGET_ARCH_S390 && V8_TARGET_ARCH_32_BIT)) struct SerializedLayout { SerializedLayout(uint32_t name, @@ -1146,7 +1146,7 @@ class DebugInfoSection : public DebugSection { w->Write(desc_->CodeStart() + desc_->CodeSize()); Writer::Slot fb_block_size = w->CreateSlotHere(); uintptr_t fb_block_start = w->position(); -#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X87 +#if V8_TARGET_ARCH_IA32 w->Write(DW_OP_reg5); // The frame pointer's here on ia32 #elif V8_TARGET_ARCH_X64 w->Write(DW_OP_reg6); // and here on x64. diff --git a/src/globals.h b/src/globals.h index 0345547713..6a07802cd6 100644 --- a/src/globals.h +++ b/src/globals.h @@ -167,7 +167,7 @@ const int kRegisterSize = kPointerSize; const int kPCOnStackSize = kRegisterSize; const int kFPOnStackSize = kRegisterSize; -#if V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X87 +#if V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_IA32 const int kElidedFrameSlots = kPCOnStackSize / kPointerSize; #else const int kElidedFrameSlots = 0; @@ -912,16 +912,10 @@ enum AllocationSiteMode { }; // The mips architecture prior to revision 5 has inverted encoding for sNaN. -// The x87 FPU convert the sNaN to qNaN automatically when loading sNaN from -// memmory. -// Use mips sNaN which is a not used qNaN in x87 port as sNaN to workaround this -// issue -// for some test cases. #if (V8_TARGET_ARCH_MIPS && !defined(_MIPS_ARCH_MIPS32R6) && \ (!defined(USE_SIMULATOR) || !defined(_MIPS_TARGET_SIMULATOR))) || \ (V8_TARGET_ARCH_MIPS64 && !defined(_MIPS_ARCH_MIPS64R6) && \ - (!defined(USE_SIMULATOR) || !defined(_MIPS_TARGET_SIMULATOR))) || \ - (V8_TARGET_ARCH_X87) + (!defined(USE_SIMULATOR) || !defined(_MIPS_TARGET_SIMULATOR))) const uint32_t kHoleNanUpper32 = 0xFFFF7FFF; const uint32_t kHoleNanLower32 = 0xFFFF7FFF; #else diff --git a/src/ic/x87/OWNERS b/src/ic/x87/OWNERS deleted file mode 100644 index 61245ae8e2..0000000000 --- a/src/ic/x87/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -weiliang.lin@intel.com -chunyang.dai@intel.com diff --git a/src/ic/x87/access-compiler-x87.cc b/src/ic/x87/access-compiler-x87.cc deleted file mode 100644 index d1867553cd..0000000000 --- a/src/ic/x87/access-compiler-x87.cc +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2014 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_X87 - -#include "src/ic/access-compiler.h" - -namespace v8 { -namespace internal { - -#define __ ACCESS_MASM(masm) - -void PropertyAccessCompiler::GenerateTailCall(MacroAssembler* masm, - Handle code) { - __ jmp(code, RelocInfo::CODE_TARGET); -} - -void PropertyAccessCompiler::InitializePlatformSpecific( - AccessCompilerData* data) { - Register receiver = LoadDescriptor::ReceiverRegister(); - Register name = LoadDescriptor::NameRegister(); - - // Load calling convention. - // receiver, name, scratch1, scratch2, scratch3. - Register load_registers[] = {receiver, name, ebx, eax, edi}; - - // Store calling convention. - // receiver, name, scratch1, scratch2. - Register store_registers[] = {receiver, name, ebx, edi}; - - data->Initialize(arraysize(load_registers), load_registers, - arraysize(store_registers), store_registers); -} - -#undef __ -} // namespace internal -} // namespace v8 - -#endif // V8_TARGET_ARCH_X87 diff --git a/src/ic/x87/handler-compiler-x87.cc b/src/ic/x87/handler-compiler-x87.cc deleted file mode 100644 index 1c811bd90e..0000000000 --- a/src/ic/x87/handler-compiler-x87.cc +++ /dev/null @@ -1,450 +0,0 @@ -// 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_X87 - -#include "src/ic/handler-compiler.h" - -#include "src/api-arguments.h" -#include "src/field-type.h" -#include "src/ic/call-optimization.h" -#include "src/ic/ic.h" -#include "src/isolate-inl.h" - -namespace v8 { -namespace internal { - -#define __ ACCESS_MASM(masm) - -void NamedLoadHandlerCompiler::GenerateLoadViaGetterForDeopt( - MacroAssembler* masm) { - { - FrameScope scope(masm, StackFrame::INTERNAL); - // If we generate a global code snippet for deoptimization only, remember - // the place to continue after deoptimization. - masm->isolate()->heap()->SetGetterStubDeoptPCOffset(masm->pc_offset()); - // Restore context register. - __ pop(esi); - } - __ ret(0); -} - - -void PropertyHandlerCompiler::PushVectorAndSlot(Register vector, - Register slot) { - MacroAssembler* masm = this->masm(); - STATIC_ASSERT(LoadWithVectorDescriptor::kSlot < - LoadWithVectorDescriptor::kVector); - STATIC_ASSERT(StoreWithVectorDescriptor::kSlot < - StoreWithVectorDescriptor::kVector); - STATIC_ASSERT(StoreTransitionDescriptor::kSlot < - StoreTransitionDescriptor::kVector); - __ push(slot); - __ push(vector); -} - - -void PropertyHandlerCompiler::PopVectorAndSlot(Register vector, Register slot) { - MacroAssembler* masm = this->masm(); - __ pop(vector); - __ pop(slot); -} - - -void PropertyHandlerCompiler::DiscardVectorAndSlot() { - MacroAssembler* masm = this->masm(); - // Remove vector and slot. - __ add(esp, Immediate(2 * kPointerSize)); -} - -void PropertyHandlerCompiler::GenerateDictionaryNegativeLookup( - MacroAssembler* masm, Label* miss_label, Register receiver, - Handle name, Register scratch0, Register scratch1) { - DCHECK(name->IsUniqueName()); - DCHECK(!receiver.is(scratch0)); - Counters* counters = masm->isolate()->counters(); - __ IncrementCounter(counters->negative_lookups(), 1); - __ IncrementCounter(counters->negative_lookups_miss(), 1); - - __ mov(scratch0, FieldOperand(receiver, HeapObject::kMapOffset)); - - const int kInterceptorOrAccessCheckNeededMask = - (1 << Map::kHasNamedInterceptor) | (1 << Map::kIsAccessCheckNeeded); - - // Bail out if the receiver has a named interceptor or requires access checks. - __ test_b(FieldOperand(scratch0, Map::kBitFieldOffset), - Immediate(kInterceptorOrAccessCheckNeededMask)); - __ j(not_zero, miss_label); - - // Check that receiver is a JSObject. - __ CmpInstanceType(scratch0, FIRST_JS_RECEIVER_TYPE); - __ j(below, miss_label); - - // Load properties array. - Register properties = scratch0; - __ mov(properties, FieldOperand(receiver, JSObject::kPropertiesOrHashOffset)); - - // Check that the properties array is a dictionary. - __ cmp(FieldOperand(properties, HeapObject::kMapOffset), - Immediate(masm->isolate()->factory()->hash_table_map())); - __ j(not_equal, miss_label); - - Label done; - NameDictionaryLookupStub::GenerateNegativeLookup(masm, miss_label, &done, - properties, name, scratch1); - __ bind(&done); - __ DecrementCounter(counters->negative_lookups_miss(), 1); -} - -// Generate call to api function. -// This function uses push() to generate smaller, faster code than -// the version above. It is an optimization that should will be removed -// when api call ICs are generated in hydrogen. -void PropertyHandlerCompiler::GenerateApiAccessorCall( - MacroAssembler* masm, const CallOptimization& optimization, - Handle receiver_map, Register receiver, Register scratch, - bool is_store, Register store_parameter, Register accessor_holder, - int accessor_index) { - DCHECK(!accessor_holder.is(scratch)); - // Copy return value. - __ pop(scratch); - - if (is_store) { - // Discard stack arguments. - __ add(esp, Immediate(StoreWithVectorDescriptor::kStackArgumentsCount * - kPointerSize)); - } - // Write the receiver and arguments to stack frame. - __ push(receiver); - if (is_store) { - DCHECK(!AreAliased(receiver, scratch, store_parameter)); - __ push(store_parameter); - } - __ push(scratch); - // Stack now matches JSFunction abi. - DCHECK(optimization.is_simple_api_call()); - - // Abi for CallApiCallbackStub. - Register callee = edi; - Register data = ebx; - Register holder = ecx; - Register api_function_address = edx; - scratch = no_reg; - - // Put callee in place. - __ LoadAccessor(callee, accessor_holder, accessor_index, - is_store ? ACCESSOR_SETTER : ACCESSOR_GETTER); - - // Put holder in place. - CallOptimization::HolderLookup holder_lookup; - optimization.LookupHolderOfExpectedType(receiver_map, &holder_lookup); - switch (holder_lookup) { - case CallOptimization::kHolderIsReceiver: - __ Move(holder, receiver); - break; - case CallOptimization::kHolderFound: - __ mov(holder, FieldOperand(receiver, HeapObject::kMapOffset)); - __ mov(holder, FieldOperand(holder, Map::kPrototypeOffset)); - break; - case CallOptimization::kHolderNotFound: - UNREACHABLE(); - break; - } - - Isolate* isolate = masm->isolate(); - Handle api_call_info = optimization.api_call_info(); - bool call_data_undefined = false; - // Put call data in place. - if (api_call_info->data()->IsUndefined(isolate)) { - call_data_undefined = true; - __ mov(data, Immediate(isolate->factory()->undefined_value())); - } else { - if (optimization.is_constant_call()) { - __ mov(data, FieldOperand(callee, JSFunction::kSharedFunctionInfoOffset)); - __ mov(data, FieldOperand(data, SharedFunctionInfo::kFunctionDataOffset)); - __ mov(data, FieldOperand(data, FunctionTemplateInfo::kCallCodeOffset)); - } else { - __ mov(data, FieldOperand(callee, FunctionTemplateInfo::kCallCodeOffset)); - } - __ mov(data, FieldOperand(data, CallHandlerInfo::kDataOffset)); - } - - // Put api_function_address in place. - Address function_address = v8::ToCData
(api_call_info->callback()); - __ mov(api_function_address, Immediate(function_address)); - - // Jump to stub. - CallApiCallbackStub stub(isolate, is_store, call_data_undefined, - !optimization.is_constant_call()); - __ TailCallStub(&stub); -} - - -// Generate code to check that a global property cell is empty. Create -// the property cell at compilation time if no cell exists for the -// property. -void PropertyHandlerCompiler::GenerateCheckPropertyCell( - MacroAssembler* masm, Handle global, Handle name, - Register scratch, Label* miss) { - Handle cell = JSGlobalObject::EnsureEmptyPropertyCell( - global, name, PropertyCellType::kInvalidated); - Isolate* isolate = masm->isolate(); - DCHECK(cell->value()->IsTheHole(isolate)); - Handle weak_cell = isolate->factory()->NewWeakCell(cell); - __ LoadWeakValue(scratch, weak_cell, miss); - __ cmp(FieldOperand(scratch, PropertyCell::kValueOffset), - Immediate(isolate->factory()->the_hole_value())); - __ j(not_equal, miss); -} - - -void NamedStoreHandlerCompiler::GenerateStoreViaSetter( - MacroAssembler* masm, Handle map, Register receiver, Register holder, - int accessor_index, int expected_arguments, Register scratch) { - // ----------- S t a t e ------------- - // -- esp[12] : value - // -- esp[8] : slot - // -- esp[4] : vector - // -- esp[0] : return address - // ----------------------------------- - __ LoadParameterFromStack(value(), Descriptor::kValue); - - { - FrameScope scope(masm, StackFrame::INTERNAL); - - // Save context register - __ push(esi); - // Save value register, so we can restore it later. - __ push(value()); - - if (accessor_index >= 0) { - DCHECK(!holder.is(scratch)); - DCHECK(!receiver.is(scratch)); - DCHECK(!value().is(scratch)); - // Call the JavaScript setter with receiver and value on the stack. - if (map->IsJSGlobalObjectMap()) { - __ mov(scratch, - FieldOperand(receiver, JSGlobalObject::kGlobalProxyOffset)); - receiver = scratch; - } - __ push(receiver); - __ push(value()); - __ LoadAccessor(edi, holder, accessor_index, ACCESSOR_SETTER); - __ Set(eax, 1); - __ Call(masm->isolate()->builtins()->CallFunction( - ConvertReceiverMode::kNotNullOrUndefined), - RelocInfo::CODE_TARGET); - } else { - // If we generate a global code snippet for deoptimization only, remember - // the place to continue after deoptimization. - masm->isolate()->heap()->SetSetterStubDeoptPCOffset(masm->pc_offset()); - } - - // We have to return the passed value, not the return value of the setter. - __ pop(eax); - // Restore context register. - __ pop(esi); - } - if (accessor_index >= 0) { - __ ret(StoreWithVectorDescriptor::kStackArgumentsCount * kPointerSize); - } else { - // If we generate a global code snippet for deoptimization only, don't try - // to drop stack arguments for the StoreIC because they are not a part of - // expression stack and deoptimizer does not reconstruct them. - __ ret(0); - } -} - -#undef __ -#define __ ACCESS_MASM(masm()) - - -void NamedStoreHandlerCompiler::GenerateRestoreName(Label* label, - Handle name) { - if (!label->is_unused()) { - __ bind(label); - __ mov(this->name(), Immediate(name)); - } -} - -void PropertyHandlerCompiler::GenerateAccessCheck( - Handle native_context_cell, Register scratch1, Register scratch2, - Label* miss, bool compare_native_contexts_only) { - Label done; - // Load current native context. - __ mov(scratch1, NativeContextOperand()); - // Load expected native context. - __ LoadWeakValue(scratch2, native_context_cell, miss); - __ cmp(scratch1, scratch2); - - if (!compare_native_contexts_only) { - __ j(equal, &done); - - // Compare security tokens of current and expected native contexts. - __ mov(scratch1, ContextOperand(scratch1, Context::SECURITY_TOKEN_INDEX)); - __ mov(scratch2, ContextOperand(scratch2, Context::SECURITY_TOKEN_INDEX)); - __ cmp(scratch1, scratch2); - } - __ j(not_equal, miss); - - __ bind(&done); -} - -Register PropertyHandlerCompiler::CheckPrototypes( - Register object_reg, Register holder_reg, Register scratch1, - Register scratch2, Handle name, Label* miss) { - Handle receiver_map = map(); - - // Make sure there's no overlap between holder and object registers. - DCHECK(!scratch1.is(object_reg) && !scratch1.is(holder_reg)); - DCHECK(!scratch2.is(object_reg) && !scratch2.is(holder_reg) && - !scratch2.is(scratch1)); - - Handle validity_cell = - Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate()); - if (!validity_cell.is_null()) { - DCHECK_EQ(Smi::FromInt(Map::kPrototypeChainValid), validity_cell->value()); - // Operand::ForCell(...) points to the cell's payload! - __ cmp(Operand::ForCell(validity_cell), - Immediate(Smi::FromInt(Map::kPrototypeChainValid))); - __ j(not_equal, miss); - } - - // Keep track of the current object in register reg. - Register reg = object_reg; - int depth = 0; - - Handle current = Handle::null(); - if (receiver_map->IsJSGlobalObjectMap()) { - current = isolate()->global_object(); - } - - Handle current_map(receiver_map->GetPrototypeChainRootMap(isolate()), - isolate()); - Handle holder_map(holder()->map()); - // Traverse the prototype chain and check the maps in the prototype chain for - // fast and global objects or do negative lookup for normal objects. - while (!current_map.is_identical_to(holder_map)) { - ++depth; - - if (current_map->IsJSGlobalObjectMap()) { - GenerateCheckPropertyCell(masm(), Handle::cast(current), - name, scratch2, miss); - } else if (current_map->is_dictionary_map()) { - DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast. - DCHECK(name->IsUniqueName()); - DCHECK(current.is_null() || - current->property_dictionary()->FindEntry(name) == - NameDictionary::kNotFound); - - if (depth > 1) { - Handle weak_cell = - Map::GetOrCreatePrototypeWeakCell(current, isolate()); - __ LoadWeakValue(reg, weak_cell, miss); - } - GenerateDictionaryNegativeLookup(masm(), miss, reg, name, scratch1, - scratch2); - } - - reg = holder_reg; // From now on the object will be in holder_reg. - // Go to the next object in the prototype chain. - current = handle(JSObject::cast(current_map->prototype())); - current_map = handle(current->map()); - } - - DCHECK(!current_map->IsJSGlobalProxyMap()); - - // Log the check depth. - LOG(isolate(), IntEvent("check-maps-depth", depth + 1)); - - if (depth != 0) { - Handle weak_cell = - Map::GetOrCreatePrototypeWeakCell(current, isolate()); - __ LoadWeakValue(reg, weak_cell, miss); - } - - // Return the register containing the holder. - return reg; -} - - -void NamedLoadHandlerCompiler::FrontendFooter(Handle name, Label* miss) { - if (!miss->is_unused()) { - Label success; - __ jmp(&success); - __ bind(miss); - if (IC::ShouldPushPopSlotAndVector(kind())) { - DCHECK(kind() == Code::LOAD_IC); - PopVectorAndSlot(); - } - TailCallBuiltin(masm(), MissBuiltin(kind())); - __ bind(&success); - } -} - - -void NamedStoreHandlerCompiler::FrontendFooter(Handle name, Label* miss) { - if (!miss->is_unused()) { - Label success; - __ jmp(&success); - GenerateRestoreName(miss, name); - DCHECK(!IC::ShouldPushPopSlotAndVector(kind())); - TailCallBuiltin(masm(), MissBuiltin(kind())); - __ bind(&success); - } -} - -void NamedStoreHandlerCompiler::ZapStackArgumentsRegisterAliases() { - // Zap register aliases of the arguments passed on the stack to ensure they - // are properly loaded by the handler (debug-only). - STATIC_ASSERT(Descriptor::kPassLastArgsOnStack); - STATIC_ASSERT(Descriptor::kStackArgumentsCount == 3); - __ mov(Descriptor::ValueRegister(), Immediate(kDebugZapValue)); - __ mov(Descriptor::SlotRegister(), Immediate(kDebugZapValue)); - __ mov(Descriptor::VectorRegister(), Immediate(kDebugZapValue)); -} - -Handle NamedStoreHandlerCompiler::CompileStoreCallback( - Handle object, Handle name, Handle callback, - LanguageMode language_mode) { - Register holder_reg = Frontend(name); - __ LoadParameterFromStack(value(), Descriptor::kValue); - - __ pop(scratch1()); // remove the return address - // Discard stack arguments. - __ add(esp, Immediate(StoreWithVectorDescriptor::kStackArgumentsCount * - kPointerSize)); - __ push(receiver()); - __ push(holder_reg); - // If the callback cannot leak, then push the callback directly, - // otherwise wrap it in a weak cell. - if (callback->data()->IsUndefined(isolate()) || callback->data()->IsSmi()) { - __ Push(callback); - } else { - Handle cell = isolate()->factory()->NewWeakCell(callback); - __ Push(cell); - } - __ Push(name); - __ push(value()); - __ push(Immediate(Smi::FromInt(language_mode))); - __ push(scratch1()); // restore return address - - // Do tail-call to the runtime system. - __ TailCallRuntime(Runtime::kStoreCallbackProperty); - - // Return the generated code. - return GetCode(kind(), name); -} - - -Register NamedStoreHandlerCompiler::value() { - return StoreDescriptor::ValueRegister(); -} - - -#undef __ -} // namespace internal -} // namespace v8 - -#endif // V8_TARGET_ARCH_X87 diff --git a/src/ic/x87/ic-x87.cc b/src/ic/x87/ic-x87.cc deleted file mode 100644 index 5cc19ac4bd..0000000000 --- a/src/ic/x87/ic-x87.cc +++ /dev/null @@ -1,84 +0,0 @@ -// 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_X87 - -#include "src/codegen.h" -#include "src/ic/ic.h" -#include "src/ic/stub-cache.h" - -namespace v8 { -namespace internal { - - -Condition CompareIC::ComputeCondition(Token::Value op) { - switch (op) { - case Token::EQ_STRICT: - case Token::EQ: - return equal; - case Token::LT: - return less; - case Token::GT: - return greater; - case Token::LTE: - return less_equal; - case Token::GTE: - return greater_equal; - default: - UNREACHABLE(); - } -} - - -bool CompareIC::HasInlinedSmiCode(Address address) { - // The address of the instruction following the call. - Address test_instruction_address = - address + Assembler::kCallTargetAddressOffset; - - // If the instruction following the call is not a test al, nothing - // was inlined. - return *test_instruction_address == Assembler::kTestAlByte; -} - - -void PatchInlinedSmiCode(Isolate* isolate, Address address, - InlinedSmiCheck check) { - // The address of the instruction following the call. - Address test_instruction_address = - address + Assembler::kCallTargetAddressOffset; - - // If the instruction following the call is not a test al, nothing - // was inlined. - if (*test_instruction_address != Assembler::kTestAlByte) { - DCHECK(*test_instruction_address == Assembler::kNopByte); - return; - } - - Address delta_address = test_instruction_address + 1; - // The delta to the start of the map check instruction and the - // condition code uses at the patched jump. - uint8_t delta = *reinterpret_cast(delta_address); - if (FLAG_trace_ic) { - LOG(isolate, PatchIC(address, test_instruction_address, delta)); - } - - // Patch with a short conditional jump. Enabling means switching from a short - // jump-if-carry/not-carry to jump-if-zero/not-zero, whereas disabling is the - // reverse operation of that. - Address jmp_address = test_instruction_address - delta; - DCHECK((check == ENABLE_INLINED_SMI_CHECK) - ? (*jmp_address == Assembler::kJncShortOpcode || - *jmp_address == Assembler::kJcShortOpcode) - : (*jmp_address == Assembler::kJnzShortOpcode || - *jmp_address == Assembler::kJzShortOpcode)); - Condition cc = - (check == ENABLE_INLINED_SMI_CHECK) - ? (*jmp_address == Assembler::kJncShortOpcode ? not_zero : zero) - : (*jmp_address == Assembler::kJnzShortOpcode ? not_carry : carry); - *jmp_address = static_cast(Assembler::kJccShortPrefix | cc); -} -} // namespace internal -} // namespace v8 - -#endif // V8_TARGET_ARCH_X87 diff --git a/src/interface-descriptors.h b/src/interface-descriptors.h index bb302f79bc..0cd4cbd193 100644 --- a/src/interface-descriptors.h +++ b/src/interface-descriptors.h @@ -392,7 +392,7 @@ class StoreDescriptor : public CallInterfaceDescriptor { static const Register ValueRegister(); static const Register SlotRegister(); -#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X87 +#if V8_TARGET_ARCH_IA32 static const bool kPassLastArgsOnStack = true; #else static const bool kPassLastArgsOnStack = false; diff --git a/src/interpreter/interpreter-assembler.cc b/src/interpreter/interpreter-assembler.cc index e0cc5b974c..50489067b8 100644 --- a/src/interpreter/interpreter-assembler.cc +++ b/src/interpreter/interpreter-assembler.cc @@ -1367,9 +1367,8 @@ void InterpreterAssembler::TraceBytecodeDispatch(Node* target_bytecode) { bool InterpreterAssembler::TargetSupportsUnalignedAccess() { #if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 return false; -#elif V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_X87 || \ - V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_ARM64 || \ - V8_TARGET_ARCH_PPC +#elif V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_S390 || \ + V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_ARM64 || V8_TARGET_ARCH_PPC return true; #else #error "Unknown Architecture" diff --git a/src/log.cc b/src/log.cc index 35e6e54d1a..83ca726c59 100644 --- a/src/log.cc +++ b/src/log.cc @@ -370,8 +370,6 @@ void LowLevelLogger::LogCodeInfo() { const char arch[] = "ppc"; #elif V8_TARGET_ARCH_MIPS const char arch[] = "mips"; -#elif V8_TARGET_ARCH_X87 - const char arch[] = "x87"; #elif V8_TARGET_ARCH_ARM64 const char arch[] = "arm64"; #elif V8_TARGET_ARCH_S390 diff --git a/src/macro-assembler.h b/src/macro-assembler.h index 8b267e0ba2..c9600e03f6 100644 --- a/src/macro-assembler.h +++ b/src/macro-assembler.h @@ -52,8 +52,6 @@ enum AllocationFlags { #elif V8_TARGET_ARCH_S390 #include "src/s390/constants-s390.h" #include "src/s390/macro-assembler-s390.h" -#elif V8_TARGET_ARCH_X87 -#include "src/x87/macro-assembler-x87.h" #else #error Unsupported target architecture. #endif diff --git a/src/regexp/jsregexp.cc b/src/regexp/jsregexp.cc index e705053611..dc4727c38a 100644 --- a/src/regexp/jsregexp.cc +++ b/src/regexp/jsregexp.cc @@ -48,8 +48,6 @@ #include "src/regexp/mips/regexp-macro-assembler-mips.h" #elif V8_TARGET_ARCH_MIPS64 #include "src/regexp/mips64/regexp-macro-assembler-mips64.h" -#elif V8_TARGET_ARCH_X87 -#include "src/regexp/x87/regexp-macro-assembler-x87.h" #else #error Unsupported target architecture. #endif @@ -6762,9 +6760,6 @@ RegExpEngine::CompilationResult RegExpEngine::Compile( #elif V8_TARGET_ARCH_MIPS64 RegExpMacroAssemblerMIPS macro_assembler(isolate, zone, mode, (data->capture_count + 1) * 2); -#elif V8_TARGET_ARCH_X87 - RegExpMacroAssemblerX87 macro_assembler(isolate, zone, mode, - (data->capture_count + 1) * 2); #else #error "Unsupported architecture" #endif diff --git a/src/regexp/x87/OWNERS b/src/regexp/x87/OWNERS deleted file mode 100644 index 61245ae8e2..0000000000 --- a/src/regexp/x87/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -weiliang.lin@intel.com -chunyang.dai@intel.com diff --git a/src/regexp/x87/regexp-macro-assembler-x87.cc b/src/regexp/x87/regexp-macro-assembler-x87.cc deleted file mode 100644 index 622a36e021..0000000000 --- a/src/regexp/x87/regexp-macro-assembler-x87.cc +++ /dev/null @@ -1,1273 +0,0 @@ -// 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_X87 - -#include "src/regexp/x87/regexp-macro-assembler-x87.h" - -#include "src/log.h" -#include "src/macro-assembler.h" -#include "src/regexp/regexp-macro-assembler.h" -#include "src/regexp/regexp-stack.h" -#include "src/unicode.h" - -namespace v8 { -namespace internal { - -#ifndef V8_INTERPRETED_REGEXP -/* - * This assembler uses the following register assignment convention - * - edx : Current character. Must be loaded using LoadCurrentCharacter - * before using any of the dispatch methods. Temporarily stores the - * index of capture start after a matching pass for a global regexp. - * - edi : Current position in input, as negative offset from end of string. - * Please notice that this is the byte offset, not the character offset! - * - esi : end of input (points to byte after last character in input). - * - ebp : Frame pointer. Used to access arguments, local variables and - * RegExp registers. - * - esp : Points to tip of C stack. - * - ecx : Points to tip of backtrack stack - * - * The registers eax and ebx are free to use for computations. - * - * Each call to a public method should retain this convention. - * The stack will have the following structure: - * - Isolate* isolate (address of the current isolate) - * - direct_call (if 1, direct call from JavaScript code, if 0 - * call through the runtime system) - * - stack_area_base (high end of the memory area to use as - * backtracking stack) - * - capture array size (may fit multiple sets of matches) - * - int* capture_array (int[num_saved_registers_], for output). - * - end of input (address of end of string) - * - start of input (address of first character in string) - * - start index (character index of start) - * - String* input_string (location of a handle containing the string) - * --- frame alignment (if applicable) --- - * - return address - * ebp-> - old ebp - * - backup of caller esi - * - backup of caller edi - * - backup of caller ebx - * - success counter (only for global regexps to count matches). - * - Offset of location before start of input (effectively character - * string start - 1). Used to initialize capture registers to a - * non-position. - * - register 0 ebp[-4] (only positions must be stored in the first - * - register 1 ebp[-8] num_saved_registers_ registers) - * - ... - * - * The first num_saved_registers_ registers are initialized to point to - * "character -1" in the string (i.e., char_size() bytes before the first - * character of the string). The remaining registers starts out as garbage. - * - * The data up to the return address must be placed there by the calling - * code, by calling the code entry as cast to a function with the signature: - * int (*match)(String* input_string, - * int start_index, - * Address start, - * Address end, - * int* capture_output_array, - * int num_capture_registers, - * byte* stack_area_base, - * bool direct_call = false, - * Isolate* isolate); - */ - -#define __ ACCESS_MASM(masm_) - -RegExpMacroAssemblerX87::RegExpMacroAssemblerX87(Isolate* isolate, Zone* zone, - Mode mode, - int registers_to_save) - : NativeRegExpMacroAssembler(isolate, zone), - masm_(new MacroAssembler(isolate, NULL, kRegExpCodeSize, - CodeObjectRequired::kYes)), - mode_(mode), - num_registers_(registers_to_save), - num_saved_registers_(registers_to_save), - entry_label_(), - start_label_(), - success_label_(), - backtrack_label_(), - exit_label_() { - DCHECK_EQ(0, registers_to_save % 2); - __ jmp(&entry_label_); // We'll write the entry code later. - __ bind(&start_label_); // And then continue from here. -} - - -RegExpMacroAssemblerX87::~RegExpMacroAssemblerX87() { - delete masm_; - // Unuse labels in case we throw away the assembler without calling GetCode. - entry_label_.Unuse(); - start_label_.Unuse(); - success_label_.Unuse(); - backtrack_label_.Unuse(); - exit_label_.Unuse(); - check_preempt_label_.Unuse(); - stack_overflow_label_.Unuse(); -} - - -int RegExpMacroAssemblerX87::stack_limit_slack() { - return RegExpStack::kStackLimitSlack; -} - - -void RegExpMacroAssemblerX87::AdvanceCurrentPosition(int by) { - if (by != 0) { - __ add(edi, Immediate(by * char_size())); - } -} - - -void RegExpMacroAssemblerX87::AdvanceRegister(int reg, int by) { - DCHECK(reg >= 0); - DCHECK(reg < num_registers_); - if (by != 0) { - __ add(register_location(reg), Immediate(by)); - } -} - - -void RegExpMacroAssemblerX87::Backtrack() { - CheckPreemption(); - // Pop Code* offset from backtrack stack, add Code* and jump to location. - Pop(ebx); - __ add(ebx, Immediate(masm_->CodeObject())); - __ jmp(ebx); -} - - -void RegExpMacroAssemblerX87::Bind(Label* label) { - __ bind(label); -} - - -void RegExpMacroAssemblerX87::CheckCharacter(uint32_t c, Label* on_equal) { - __ cmp(current_character(), c); - BranchOrBacktrack(equal, on_equal); -} - - -void RegExpMacroAssemblerX87::CheckCharacterGT(uc16 limit, Label* on_greater) { - __ cmp(current_character(), limit); - BranchOrBacktrack(greater, on_greater); -} - - -void RegExpMacroAssemblerX87::CheckAtStart(Label* on_at_start) { - __ lea(eax, Operand(edi, -char_size())); - __ cmp(eax, Operand(ebp, kStringStartMinusOne)); - BranchOrBacktrack(equal, on_at_start); -} - - -void RegExpMacroAssemblerX87::CheckNotAtStart(int cp_offset, - Label* on_not_at_start) { - __ lea(eax, Operand(edi, -char_size() + cp_offset * char_size())); - __ cmp(eax, Operand(ebp, kStringStartMinusOne)); - BranchOrBacktrack(not_equal, on_not_at_start); -} - - -void RegExpMacroAssemblerX87::CheckCharacterLT(uc16 limit, Label* on_less) { - __ cmp(current_character(), limit); - BranchOrBacktrack(less, on_less); -} - - -void RegExpMacroAssemblerX87::CheckGreedyLoop(Label* on_equal) { - Label fallthrough; - __ cmp(edi, Operand(backtrack_stackpointer(), 0)); - __ j(not_equal, &fallthrough); - __ add(backtrack_stackpointer(), Immediate(kPointerSize)); // Pop. - BranchOrBacktrack(no_condition, on_equal); - __ bind(&fallthrough); -} - -void RegExpMacroAssemblerX87::CheckNotBackReferenceIgnoreCase( - int start_reg, bool read_backward, bool unicode, Label* on_no_match) { - Label fallthrough; - __ mov(edx, register_location(start_reg)); // Index of start of capture - __ mov(ebx, register_location(start_reg + 1)); // Index of end of capture - __ sub(ebx, edx); // Length of capture. - - // At this point, the capture registers are either both set or both cleared. - // If the capture length is zero, then the capture is either empty or cleared. - // Fall through in both cases. - __ j(equal, &fallthrough); - - // Check that there are sufficient characters left in the input. - if (read_backward) { - __ mov(eax, Operand(ebp, kStringStartMinusOne)); - __ add(eax, ebx); - __ cmp(edi, eax); - BranchOrBacktrack(less_equal, on_no_match); - } else { - __ mov(eax, edi); - __ add(eax, ebx); - BranchOrBacktrack(greater, on_no_match); - } - - if (mode_ == LATIN1) { - Label success; - Label fail; - Label loop_increment; - // Save register contents to make the registers available below. - __ push(edi); - __ push(backtrack_stackpointer()); - // After this, the eax, ecx, and edi registers are available. - - __ add(edx, esi); // Start of capture - __ add(edi, esi); // Start of text to match against capture. - if (read_backward) { - __ sub(edi, ebx); // Offset by length when matching backwards. - } - __ add(ebx, edi); // End of text to match against capture. - - Label loop; - __ bind(&loop); - __ movzx_b(eax, Operand(edi, 0)); - __ cmpb_al(Operand(edx, 0)); - __ j(equal, &loop_increment); - - // Mismatch, try case-insensitive match (converting letters to lower-case). - __ or_(eax, 0x20); // Convert match character to lower-case. - __ lea(ecx, Operand(eax, -'a')); - __ cmp(ecx, static_cast('z' - 'a')); // Is eax a lowercase letter? - Label convert_capture; - __ j(below_equal, &convert_capture); // In range 'a'-'z'. - // Latin-1: Check for values in range [224,254] but not 247. - __ sub(ecx, Immediate(224 - 'a')); - __ cmp(ecx, Immediate(254 - 224)); - __ j(above, &fail); // Weren't Latin-1 letters. - __ cmp(ecx, Immediate(247 - 224)); // Check for 247. - __ j(equal, &fail); - __ bind(&convert_capture); - // Also convert capture character. - __ movzx_b(ecx, Operand(edx, 0)); - __ or_(ecx, 0x20); - - __ cmp(eax, ecx); - __ j(not_equal, &fail); - - __ bind(&loop_increment); - // Increment pointers into match and capture strings. - __ add(edx, Immediate(1)); - __ add(edi, Immediate(1)); - // Compare to end of match, and loop if not done. - __ cmp(edi, ebx); - __ j(below, &loop); - __ jmp(&success); - - __ bind(&fail); - // Restore original values before failing. - __ pop(backtrack_stackpointer()); - __ pop(edi); - BranchOrBacktrack(no_condition, on_no_match); - - __ bind(&success); - // Restore original value before continuing. - __ pop(backtrack_stackpointer()); - // Drop original value of character position. - __ add(esp, Immediate(kPointerSize)); - // Compute new value of character position after the matched part. - __ sub(edi, esi); - if (read_backward) { - // Subtract match length if we matched backward. - __ add(edi, register_location(start_reg)); - __ sub(edi, register_location(start_reg + 1)); - } - } else { - DCHECK(mode_ == UC16); - // Save registers before calling C function. - __ push(esi); - __ push(edi); - __ push(backtrack_stackpointer()); - __ push(ebx); - - static const int argument_count = 4; - __ PrepareCallCFunction(argument_count, ecx); - // Put arguments into allocated stack area, last argument highest on stack. - // Parameters are - // Address byte_offset1 - Address captured substring's start. - // Address byte_offset2 - Address of current character position. - // size_t byte_length - length of capture in bytes(!) -// Isolate* isolate or 0 if unicode flag. - - // Set isolate. -#ifdef V8_INTL_SUPPORT - if (unicode) { - __ mov(Operand(esp, 3 * kPointerSize), Immediate(0)); - } else // NOLINT -#endif // V8_INTL_SUPPORT - { - __ mov(Operand(esp, 3 * kPointerSize), - Immediate(ExternalReference::isolate_address(isolate()))); - } - // Set byte_length. - __ mov(Operand(esp, 2 * kPointerSize), ebx); - // Set byte_offset2. - // Found by adding negative string-end offset of current position (edi) - // to end of string. - __ add(edi, esi); - if (read_backward) { - __ sub(edi, ebx); // Offset by length when matching backwards. - } - __ mov(Operand(esp, 1 * kPointerSize), edi); - // Set byte_offset1. - // Start of capture, where edx already holds string-end negative offset. - __ add(edx, esi); - __ mov(Operand(esp, 0 * kPointerSize), edx); - - { - AllowExternalCallThatCantCauseGC scope(masm_); - ExternalReference compare = - ExternalReference::re_case_insensitive_compare_uc16(isolate()); - __ CallCFunction(compare, argument_count); - } - // Pop original values before reacting on result value. - __ pop(ebx); - __ pop(backtrack_stackpointer()); - __ pop(edi); - __ pop(esi); - - // Check if function returned non-zero for success or zero for failure. - __ or_(eax, eax); - BranchOrBacktrack(zero, on_no_match); - // On success, advance position by length of capture. - if (read_backward) { - __ sub(edi, ebx); - } else { - __ add(edi, ebx); - } - } - __ bind(&fallthrough); -} - - -void RegExpMacroAssemblerX87::CheckNotBackReference(int start_reg, - bool read_backward, - Label* on_no_match) { - Label fallthrough; - Label success; - Label fail; - - // Find length of back-referenced capture. - __ mov(edx, register_location(start_reg)); - __ mov(eax, register_location(start_reg + 1)); - __ sub(eax, edx); // Length to check. - - // At this point, the capture registers are either both set or both cleared. - // If the capture length is zero, then the capture is either empty or cleared. - // Fall through in both cases. - __ j(equal, &fallthrough); - - // Check that there are sufficient characters left in the input. - if (read_backward) { - __ mov(ebx, Operand(ebp, kStringStartMinusOne)); - __ add(ebx, eax); - __ cmp(edi, ebx); - BranchOrBacktrack(less_equal, on_no_match); - } else { - __ mov(ebx, edi); - __ add(ebx, eax); - BranchOrBacktrack(greater, on_no_match); - } - - // Save register to make it available below. - __ push(backtrack_stackpointer()); - - // Compute pointers to match string and capture string - __ add(edx, esi); // Start of capture. - __ lea(ebx, Operand(esi, edi, times_1, 0)); // Start of match. - if (read_backward) { - __ sub(ebx, eax); // Offset by length when matching backwards. - } - __ lea(ecx, Operand(eax, ebx, times_1, 0)); // End of match - - Label loop; - __ bind(&loop); - if (mode_ == LATIN1) { - __ movzx_b(eax, Operand(edx, 0)); - __ cmpb_al(Operand(ebx, 0)); - } else { - DCHECK(mode_ == UC16); - __ movzx_w(eax, Operand(edx, 0)); - __ cmpw_ax(Operand(ebx, 0)); - } - __ j(not_equal, &fail); - // Increment pointers into capture and match string. - __ add(edx, Immediate(char_size())); - __ add(ebx, Immediate(char_size())); - // Check if we have reached end of match area. - __ cmp(ebx, ecx); - __ j(below, &loop); - __ jmp(&success); - - __ bind(&fail); - // Restore backtrack stackpointer. - __ pop(backtrack_stackpointer()); - BranchOrBacktrack(no_condition, on_no_match); - - __ bind(&success); - // Move current character position to position after match. - __ mov(edi, ecx); - __ sub(edi, esi); - if (read_backward) { - // Subtract match length if we matched backward. - __ add(edi, register_location(start_reg)); - __ sub(edi, register_location(start_reg + 1)); - } - // Restore backtrack stackpointer. - __ pop(backtrack_stackpointer()); - - __ bind(&fallthrough); -} - - -void RegExpMacroAssemblerX87::CheckNotCharacter(uint32_t c, - Label* on_not_equal) { - __ cmp(current_character(), c); - BranchOrBacktrack(not_equal, on_not_equal); -} - - -void RegExpMacroAssemblerX87::CheckCharacterAfterAnd(uint32_t c, - uint32_t mask, - Label* on_equal) { - if (c == 0) { - __ test(current_character(), Immediate(mask)); - } else { - __ mov(eax, mask); - __ and_(eax, current_character()); - __ cmp(eax, c); - } - BranchOrBacktrack(equal, on_equal); -} - - -void RegExpMacroAssemblerX87::CheckNotCharacterAfterAnd(uint32_t c, - uint32_t mask, - Label* on_not_equal) { - if (c == 0) { - __ test(current_character(), Immediate(mask)); - } else { - __ mov(eax, mask); - __ and_(eax, current_character()); - __ cmp(eax, c); - } - BranchOrBacktrack(not_equal, on_not_equal); -} - - -void RegExpMacroAssemblerX87::CheckNotCharacterAfterMinusAnd( - uc16 c, - uc16 minus, - uc16 mask, - Label* on_not_equal) { - DCHECK(minus < String::kMaxUtf16CodeUnit); - __ lea(eax, Operand(current_character(), -minus)); - if (c == 0) { - __ test(eax, Immediate(mask)); - } else { - __ and_(eax, mask); - __ cmp(eax, c); - } - BranchOrBacktrack(not_equal, on_not_equal); -} - - -void RegExpMacroAssemblerX87::CheckCharacterInRange( - uc16 from, - uc16 to, - Label* on_in_range) { - __ lea(eax, Operand(current_character(), -from)); - __ cmp(eax, to - from); - BranchOrBacktrack(below_equal, on_in_range); -} - - -void RegExpMacroAssemblerX87::CheckCharacterNotInRange( - uc16 from, - uc16 to, - Label* on_not_in_range) { - __ lea(eax, Operand(current_character(), -from)); - __ cmp(eax, to - from); - BranchOrBacktrack(above, on_not_in_range); -} - - -void RegExpMacroAssemblerX87::CheckBitInTable( - Handle table, - Label* on_bit_set) { - __ mov(eax, Immediate(table)); - Register index = current_character(); - if (mode_ != LATIN1 || kTableMask != String::kMaxOneByteCharCode) { - __ mov(ebx, kTableSize - 1); - __ and_(ebx, current_character()); - index = ebx; - } - __ cmpb(FieldOperand(eax, index, times_1, ByteArray::kHeaderSize), - Immediate(0)); - BranchOrBacktrack(not_equal, on_bit_set); -} - - -bool RegExpMacroAssemblerX87::CheckSpecialCharacterClass(uc16 type, - Label* on_no_match) { - // Range checks (c in min..max) are generally implemented by an unsigned - // (c - min) <= (max - min) check - switch (type) { - case 's': - // Match space-characters - if (mode_ == LATIN1) { - // One byte space characters are '\t'..'\r', ' ' and \u00a0. - Label success; - __ cmp(current_character(), ' '); - __ j(equal, &success, Label::kNear); - // Check range 0x09..0x0d - __ lea(eax, Operand(current_character(), -'\t')); - __ cmp(eax, '\r' - '\t'); - __ j(below_equal, &success, Label::kNear); - // \u00a0 (NBSP). - __ cmp(eax, 0x00a0 - '\t'); - BranchOrBacktrack(not_equal, on_no_match); - __ bind(&success); - return true; - } - return false; - case 'S': - // The emitted code for generic character classes is good enough. - return false; - case 'd': - // Match ASCII digits ('0'..'9') - __ lea(eax, Operand(current_character(), -'0')); - __ cmp(eax, '9' - '0'); - BranchOrBacktrack(above, on_no_match); - return true; - case 'D': - // Match non ASCII-digits - __ lea(eax, Operand(current_character(), -'0')); - __ cmp(eax, '9' - '0'); - BranchOrBacktrack(below_equal, on_no_match); - return true; - case '.': { - // Match non-newlines (not 0x0a('\n'), 0x0d('\r'), 0x2028 and 0x2029) - __ mov(eax, current_character()); - __ xor_(eax, Immediate(0x01)); - // See if current character is '\n'^1 or '\r'^1, i.e., 0x0b or 0x0c - __ sub(eax, Immediate(0x0b)); - __ cmp(eax, 0x0c - 0x0b); - BranchOrBacktrack(below_equal, on_no_match); - if (mode_ == UC16) { - // Compare original value to 0x2028 and 0x2029, using the already - // computed (current_char ^ 0x01 - 0x0b). I.e., check for - // 0x201d (0x2028 - 0x0b) or 0x201e. - __ sub(eax, Immediate(0x2028 - 0x0b)); - __ cmp(eax, 0x2029 - 0x2028); - BranchOrBacktrack(below_equal, on_no_match); - } - return true; - } - case 'w': { - if (mode_ != LATIN1) { - // Table is 256 entries, so all Latin1 characters can be tested. - __ cmp(current_character(), Immediate('z')); - BranchOrBacktrack(above, on_no_match); - } - DCHECK_EQ(0, word_character_map[0]); // Character '\0' is not a word char. - ExternalReference word_map = ExternalReference::re_word_character_map(); - __ test_b(current_character(), - Operand::StaticArray(current_character(), times_1, word_map)); - BranchOrBacktrack(zero, on_no_match); - return true; - } - case 'W': { - Label done; - if (mode_ != LATIN1) { - // Table is 256 entries, so all Latin1 characters can be tested. - __ cmp(current_character(), Immediate('z')); - __ j(above, &done); - } - DCHECK_EQ(0, word_character_map[0]); // Character '\0' is not a word char. - ExternalReference word_map = ExternalReference::re_word_character_map(); - __ test_b(current_character(), - Operand::StaticArray(current_character(), times_1, word_map)); - BranchOrBacktrack(not_zero, on_no_match); - if (mode_ != LATIN1) { - __ bind(&done); - } - return true; - } - // Non-standard classes (with no syntactic shorthand) used internally. - case '*': - // Match any character. - return true; - case 'n': { - // Match newlines (0x0a('\n'), 0x0d('\r'), 0x2028 or 0x2029). - // The opposite of '.'. - __ mov(eax, current_character()); - __ xor_(eax, Immediate(0x01)); - // See if current character is '\n'^1 or '\r'^1, i.e., 0x0b or 0x0c - __ sub(eax, Immediate(0x0b)); - __ cmp(eax, 0x0c - 0x0b); - if (mode_ == LATIN1) { - BranchOrBacktrack(above, on_no_match); - } else { - Label done; - BranchOrBacktrack(below_equal, &done); - DCHECK_EQ(UC16, mode_); - // Compare original value to 0x2028 and 0x2029, using the already - // computed (current_char ^ 0x01 - 0x0b). I.e., check for - // 0x201d (0x2028 - 0x0b) or 0x201e. - __ sub(eax, Immediate(0x2028 - 0x0b)); - __ cmp(eax, 1); - BranchOrBacktrack(above, on_no_match); - __ bind(&done); - } - return true; - } - // No custom implementation (yet): s(UC16), S(UC16). - default: - return false; - } -} - - -void RegExpMacroAssemblerX87::Fail() { - STATIC_ASSERT(FAILURE == 0); // Return value for failure is zero. - if (!global()) { - __ Move(eax, Immediate(FAILURE)); - } - __ jmp(&exit_label_); -} - - -Handle RegExpMacroAssemblerX87::GetCode(Handle source) { - Label return_eax; - // Finalize code - write the entry point code now we know how many - // registers we need. - - // Entry code: - __ bind(&entry_label_); - - // Tell the system that we have a stack frame. Because the type is MANUAL, no - // code is generated. - FrameScope scope(masm_, StackFrame::MANUAL); - - // Actually emit code to start a new stack frame. - __ push(ebp); - __ mov(ebp, esp); - // Save callee-save registers. Order here should correspond to order of - // kBackup_ebx etc. - __ push(esi); - __ push(edi); - __ push(ebx); // Callee-save on MacOS. - __ push(Immediate(0)); // Number of successful matches in a global regexp. - __ push(Immediate(0)); // Make room for "string start - 1" constant. - - // Check if we have space on the stack for registers. - Label stack_limit_hit; - Label stack_ok; - - ExternalReference stack_limit = - ExternalReference::address_of_stack_limit(isolate()); - __ mov(ecx, esp); - __ sub(ecx, Operand::StaticVariable(stack_limit)); - // Handle it if the stack pointer is already below the stack limit. - __ j(below_equal, &stack_limit_hit); - // Check if there is room for the variable number of registers above - // the stack limit. - __ cmp(ecx, num_registers_ * kPointerSize); - __ j(above_equal, &stack_ok); - // Exit with OutOfMemory exception. There is not enough space on the stack - // for our working registers. - __ mov(eax, EXCEPTION); - __ jmp(&return_eax); - - __ bind(&stack_limit_hit); - CallCheckStackGuardState(ebx); - __ or_(eax, eax); - // If returned value is non-zero, we exit with the returned value as result. - __ j(not_zero, &return_eax); - - __ bind(&stack_ok); - // Load start index for later use. - __ mov(ebx, Operand(ebp, kStartIndex)); - - // Allocate space on stack for registers. - __ sub(esp, Immediate(num_registers_ * kPointerSize)); - // Load string length. - __ mov(esi, Operand(ebp, kInputEnd)); - // Load input position. - __ mov(edi, Operand(ebp, kInputStart)); - // Set up edi to be negative offset from string end. - __ sub(edi, esi); - - // Set eax to address of char before start of the string. - // (effectively string position -1). - __ neg(ebx); - if (mode_ == UC16) { - __ lea(eax, Operand(edi, ebx, times_2, -char_size())); - } else { - __ lea(eax, Operand(edi, ebx, times_1, -char_size())); - } - // Store this value in a local variable, for use when clearing - // position registers. - __ mov(Operand(ebp, kStringStartMinusOne), eax); - -#if V8_OS_WIN - // Ensure that we write to each stack page, in order. Skipping a page - // on Windows can cause segmentation faults. Assuming page size is 4k. - const int kPageSize = 4096; - const int kRegistersPerPage = kPageSize / kPointerSize; - for (int i = num_saved_registers_ + kRegistersPerPage - 1; - i < num_registers_; - i += kRegistersPerPage) { - __ mov(register_location(i), eax); // One write every page. - } -#endif // V8_OS_WIN - - Label load_char_start_regexp, start_regexp; - // Load newline if index is at start, previous character otherwise. - __ cmp(Operand(ebp, kStartIndex), Immediate(0)); - __ j(not_equal, &load_char_start_regexp, Label::kNear); - __ mov(current_character(), '\n'); - __ jmp(&start_regexp, Label::kNear); - - // Global regexp restarts matching here. - __ bind(&load_char_start_regexp); - // Load previous char as initial value of current character register. - LoadCurrentCharacterUnchecked(-1, 1); - __ bind(&start_regexp); - - // Initialize on-stack registers. - if (num_saved_registers_ > 0) { // Always is, if generated from a regexp. - // Fill saved registers with initial value = start offset - 1 - // Fill in stack push order, to avoid accessing across an unwritten - // page (a problem on Windows). - if (num_saved_registers_ > 8) { - __ mov(ecx, kRegisterZero); - Label init_loop; - __ bind(&init_loop); - __ mov(Operand(ebp, ecx, times_1, 0), eax); - __ sub(ecx, Immediate(kPointerSize)); - __ cmp(ecx, kRegisterZero - num_saved_registers_ * kPointerSize); - __ j(greater, &init_loop); - } else { // Unroll the loop. - for (int i = 0; i < num_saved_registers_; i++) { - __ mov(register_location(i), eax); - } - } - } - - // Initialize backtrack stack pointer. - __ mov(backtrack_stackpointer(), Operand(ebp, kStackHighEnd)); - - __ jmp(&start_label_); - - // Exit code: - if (success_label_.is_linked()) { - // Save captures when successful. - __ bind(&success_label_); - if (num_saved_registers_ > 0) { - // copy captures to output - __ mov(ebx, Operand(ebp, kRegisterOutput)); - __ mov(ecx, Operand(ebp, kInputEnd)); - __ mov(edx, Operand(ebp, kStartIndex)); - __ sub(ecx, Operand(ebp, kInputStart)); - if (mode_ == UC16) { - __ lea(ecx, Operand(ecx, edx, times_2, 0)); - } else { - __ add(ecx, edx); - } - for (int i = 0; i < num_saved_registers_; i++) { - __ mov(eax, register_location(i)); - if (i == 0 && global_with_zero_length_check()) { - // Keep capture start in edx for the zero-length check later. - __ mov(edx, eax); - } - // Convert to index from start of string, not end. - __ add(eax, ecx); - if (mode_ == UC16) { - __ sar(eax, 1); // Convert byte index to character index. - } - __ mov(Operand(ebx, i * kPointerSize), eax); - } - } - - if (global()) { - // Restart matching if the regular expression is flagged as global. - // Increment success counter. - __ inc(Operand(ebp, kSuccessfulCaptures)); - // Capture results have been stored, so the number of remaining global - // output registers is reduced by the number of stored captures. - __ mov(ecx, Operand(ebp, kNumOutputRegisters)); - __ sub(ecx, Immediate(num_saved_registers_)); - // Check whether we have enough room for another set of capture results. - __ cmp(ecx, Immediate(num_saved_registers_)); - __ j(less, &exit_label_); - - __ mov(Operand(ebp, kNumOutputRegisters), ecx); - // Advance the location for output. - __ add(Operand(ebp, kRegisterOutput), - Immediate(num_saved_registers_ * kPointerSize)); - - // Prepare eax to initialize registers with its value in the next run. - __ mov(eax, Operand(ebp, kStringStartMinusOne)); - - if (global_with_zero_length_check()) { - // Special case for zero-length matches. - // edx: capture start index - __ cmp(edi, edx); - // Not a zero-length match, restart. - __ j(not_equal, &load_char_start_regexp); - // edi (offset from the end) is zero if we already reached the end. - __ test(edi, edi); - __ j(zero, &exit_label_, Label::kNear); - // Advance current position after a zero-length match. - Label advance; - __ bind(&advance); - if (mode_ == UC16) { - __ add(edi, Immediate(2)); - } else { - __ inc(edi); - } - if (global_unicode()) CheckNotInSurrogatePair(0, &advance); - } - __ jmp(&load_char_start_regexp); - } else { - __ mov(eax, Immediate(SUCCESS)); - } - } - - __ bind(&exit_label_); - if (global()) { - // Return the number of successful captures. - __ mov(eax, Operand(ebp, kSuccessfulCaptures)); - } - - __ bind(&return_eax); - // Skip esp past regexp registers. - __ lea(esp, Operand(ebp, kBackup_ebx)); - // Restore callee-save registers. - __ pop(ebx); - __ pop(edi); - __ pop(esi); - // Exit function frame, restore previous one. - __ pop(ebp); - __ ret(0); - - // Backtrack code (branch target for conditional backtracks). - if (backtrack_label_.is_linked()) { - __ bind(&backtrack_label_); - Backtrack(); - } - - Label exit_with_exception; - - // Preempt-code - if (check_preempt_label_.is_linked()) { - SafeCallTarget(&check_preempt_label_); - - __ push(backtrack_stackpointer()); - __ push(edi); - - CallCheckStackGuardState(ebx); - __ or_(eax, eax); - // If returning non-zero, we should end execution with the given - // result as return value. - __ j(not_zero, &return_eax); - - __ pop(edi); - __ pop(backtrack_stackpointer()); - // String might have moved: Reload esi from frame. - __ mov(esi, Operand(ebp, kInputEnd)); - SafeReturn(); - } - - // Backtrack stack overflow code. - if (stack_overflow_label_.is_linked()) { - SafeCallTarget(&stack_overflow_label_); - // Reached if the backtrack-stack limit has been hit. - - Label grow_failed; - // Save registers before calling C function - __ push(esi); - __ push(edi); - - // Call GrowStack(backtrack_stackpointer()) - static const int num_arguments = 3; - __ PrepareCallCFunction(num_arguments, ebx); - __ mov(Operand(esp, 2 * kPointerSize), - Immediate(ExternalReference::isolate_address(isolate()))); - __ lea(eax, Operand(ebp, kStackHighEnd)); - __ mov(Operand(esp, 1 * kPointerSize), eax); - __ mov(Operand(esp, 0 * kPointerSize), backtrack_stackpointer()); - ExternalReference grow_stack = - ExternalReference::re_grow_stack(isolate()); - __ CallCFunction(grow_stack, num_arguments); - // If return NULL, we have failed to grow the stack, and - // must exit with a stack-overflow exception. - __ or_(eax, eax); - __ j(equal, &exit_with_exception); - // Otherwise use return value as new stack pointer. - __ mov(backtrack_stackpointer(), eax); - // Restore saved registers and continue. - __ pop(edi); - __ pop(esi); - SafeReturn(); - } - - if (exit_with_exception.is_linked()) { - // If any of the code above needed to exit with an exception. - __ bind(&exit_with_exception); - // Exit with Result EXCEPTION(-1) to signal thrown exception. - __ mov(eax, EXCEPTION); - __ jmp(&return_eax); - } - - CodeDesc code_desc; - masm_->GetCode(&code_desc); - Handle code = - isolate()->factory()->NewCode(code_desc, - Code::ComputeFlags(Code::REGEXP), - masm_->CodeObject()); - PROFILE(masm_->isolate(), - RegExpCodeCreateEvent(AbstractCode::cast(*code), *source)); - return Handle::cast(code); -} - - -void RegExpMacroAssemblerX87::GoTo(Label* to) { - BranchOrBacktrack(no_condition, to); -} - - -void RegExpMacroAssemblerX87::IfRegisterGE(int reg, - int comparand, - Label* if_ge) { - __ cmp(register_location(reg), Immediate(comparand)); - BranchOrBacktrack(greater_equal, if_ge); -} - - -void RegExpMacroAssemblerX87::IfRegisterLT(int reg, - int comparand, - Label* if_lt) { - __ cmp(register_location(reg), Immediate(comparand)); - BranchOrBacktrack(less, if_lt); -} - - -void RegExpMacroAssemblerX87::IfRegisterEqPos(int reg, - Label* if_eq) { - __ cmp(edi, register_location(reg)); - BranchOrBacktrack(equal, if_eq); -} - - -RegExpMacroAssembler::IrregexpImplementation - RegExpMacroAssemblerX87::Implementation() { - return kX87Implementation; -} - - -void RegExpMacroAssemblerX87::LoadCurrentCharacter(int cp_offset, - Label* on_end_of_input, - bool check_bounds, - int characters) { - DCHECK(cp_offset < (1<<30)); // Be sane! (And ensure negation works) - if (check_bounds) { - if (cp_offset >= 0) { - CheckPosition(cp_offset + characters - 1, on_end_of_input); - } else { - CheckPosition(cp_offset, on_end_of_input); - } - } - LoadCurrentCharacterUnchecked(cp_offset, characters); -} - - -void RegExpMacroAssemblerX87::PopCurrentPosition() { - Pop(edi); -} - - -void RegExpMacroAssemblerX87::PopRegister(int register_index) { - Pop(eax); - __ mov(register_location(register_index), eax); -} - - -void RegExpMacroAssemblerX87::PushBacktrack(Label* label) { - Push(Immediate::CodeRelativeOffset(label)); - CheckStackLimit(); -} - - -void RegExpMacroAssemblerX87::PushCurrentPosition() { - Push(edi); -} - - -void RegExpMacroAssemblerX87::PushRegister(int register_index, - StackCheckFlag check_stack_limit) { - __ mov(eax, register_location(register_index)); - Push(eax); - if (check_stack_limit) CheckStackLimit(); -} - - -void RegExpMacroAssemblerX87::ReadCurrentPositionFromRegister(int reg) { - __ mov(edi, register_location(reg)); -} - - -void RegExpMacroAssemblerX87::ReadStackPointerFromRegister(int reg) { - __ mov(backtrack_stackpointer(), register_location(reg)); - __ add(backtrack_stackpointer(), Operand(ebp, kStackHighEnd)); -} - -void RegExpMacroAssemblerX87::SetCurrentPositionFromEnd(int by) { - Label after_position; - __ cmp(edi, -by * char_size()); - __ j(greater_equal, &after_position, Label::kNear); - __ mov(edi, -by * char_size()); - // On RegExp code entry (where this operation is used), the character before - // the current position is expected to be already loaded. - // We have advanced the position, so it's safe to read backwards. - LoadCurrentCharacterUnchecked(-1, 1); - __ bind(&after_position); -} - - -void RegExpMacroAssemblerX87::SetRegister(int register_index, int to) { - DCHECK(register_index >= num_saved_registers_); // Reserved for positions! - __ mov(register_location(register_index), Immediate(to)); -} - - -bool RegExpMacroAssemblerX87::Succeed() { - __ jmp(&success_label_); - return global(); -} - - -void RegExpMacroAssemblerX87::WriteCurrentPositionToRegister(int reg, - int cp_offset) { - if (cp_offset == 0) { - __ mov(register_location(reg), edi); - } else { - __ lea(eax, Operand(edi, cp_offset * char_size())); - __ mov(register_location(reg), eax); - } -} - - -void RegExpMacroAssemblerX87::ClearRegisters(int reg_from, int reg_to) { - DCHECK(reg_from <= reg_to); - __ mov(eax, Operand(ebp, kStringStartMinusOne)); - for (int reg = reg_from; reg <= reg_to; reg++) { - __ mov(register_location(reg), eax); - } -} - - -void RegExpMacroAssemblerX87::WriteStackPointerToRegister(int reg) { - __ mov(eax, backtrack_stackpointer()); - __ sub(eax, Operand(ebp, kStackHighEnd)); - __ mov(register_location(reg), eax); -} - - -// Private methods: - -void RegExpMacroAssemblerX87::CallCheckStackGuardState(Register scratch) { - static const int num_arguments = 3; - __ PrepareCallCFunction(num_arguments, scratch); - // RegExp code frame pointer. - __ mov(Operand(esp, 2 * kPointerSize), ebp); - // Code* of self. - __ mov(Operand(esp, 1 * kPointerSize), Immediate(masm_->CodeObject())); - // Next address on the stack (will be address of return address). - __ lea(eax, Operand(esp, -kPointerSize)); - __ mov(Operand(esp, 0 * kPointerSize), eax); - ExternalReference check_stack_guard = - ExternalReference::re_check_stack_guard_state(isolate()); - __ CallCFunction(check_stack_guard, num_arguments); -} - - -// Helper function for reading a value out of a stack frame. -template -static T& frame_entry(Address re_frame, int frame_offset) { - return reinterpret_cast(Memory::int32_at(re_frame + frame_offset)); -} - - -template -static T* frame_entry_address(Address re_frame, int frame_offset) { - return reinterpret_cast(re_frame + frame_offset); -} - - -int RegExpMacroAssemblerX87::CheckStackGuardState(Address* return_address, - Code* re_code, - Address re_frame) { - return NativeRegExpMacroAssembler::CheckStackGuardState( - frame_entry(re_frame, kIsolate), - frame_entry(re_frame, kStartIndex), - frame_entry(re_frame, kDirectCall) == 1, return_address, re_code, - frame_entry_address(re_frame, kInputString), - frame_entry_address(re_frame, kInputStart), - frame_entry_address(re_frame, kInputEnd)); -} - - -Operand RegExpMacroAssemblerX87::register_location(int register_index) { - DCHECK(register_index < (1<<30)); - if (num_registers_ <= register_index) { - num_registers_ = register_index + 1; - } - return Operand(ebp, kRegisterZero - register_index * kPointerSize); -} - - -void RegExpMacroAssemblerX87::CheckPosition(int cp_offset, - Label* on_outside_input) { - if (cp_offset >= 0) { - __ cmp(edi, -cp_offset * char_size()); - BranchOrBacktrack(greater_equal, on_outside_input); - } else { - __ lea(eax, Operand(edi, cp_offset * char_size())); - __ cmp(eax, Operand(ebp, kStringStartMinusOne)); - BranchOrBacktrack(less_equal, on_outside_input); - } -} - - -void RegExpMacroAssemblerX87::BranchOrBacktrack(Condition condition, - Label* to) { - if (condition < 0) { // No condition - if (to == NULL) { - Backtrack(); - return; - } - __ jmp(to); - return; - } - if (to == NULL) { - __ j(condition, &backtrack_label_); - return; - } - __ j(condition, to); -} - - -void RegExpMacroAssemblerX87::SafeCall(Label* to) { - Label return_to; - __ push(Immediate::CodeRelativeOffset(&return_to)); - __ jmp(to); - __ bind(&return_to); -} - - -void RegExpMacroAssemblerX87::SafeReturn() { - __ pop(ebx); - __ add(ebx, Immediate(masm_->CodeObject())); - __ jmp(ebx); -} - - -void RegExpMacroAssemblerX87::SafeCallTarget(Label* name) { - __ bind(name); -} - - -void RegExpMacroAssemblerX87::Push(Register source) { - DCHECK(!source.is(backtrack_stackpointer())); - // Notice: This updates flags, unlike normal Push. - __ sub(backtrack_stackpointer(), Immediate(kPointerSize)); - __ mov(Operand(backtrack_stackpointer(), 0), source); -} - - -void RegExpMacroAssemblerX87::Push(Immediate value) { - // Notice: This updates flags, unlike normal Push. - __ sub(backtrack_stackpointer(), Immediate(kPointerSize)); - __ mov(Operand(backtrack_stackpointer(), 0), value); -} - - -void RegExpMacroAssemblerX87::Pop(Register target) { - DCHECK(!target.is(backtrack_stackpointer())); - __ mov(target, Operand(backtrack_stackpointer(), 0)); - // Notice: This updates flags, unlike normal Pop. - __ add(backtrack_stackpointer(), Immediate(kPointerSize)); -} - - -void RegExpMacroAssemblerX87::CheckPreemption() { - // Check for preemption. - Label no_preempt; - ExternalReference stack_limit = - ExternalReference::address_of_stack_limit(isolate()); - __ cmp(esp, Operand::StaticVariable(stack_limit)); - __ j(above, &no_preempt); - - SafeCall(&check_preempt_label_); - - __ bind(&no_preempt); -} - - -void RegExpMacroAssemblerX87::CheckStackLimit() { - Label no_stack_overflow; - ExternalReference stack_limit = - ExternalReference::address_of_regexp_stack_limit(isolate()); - __ cmp(backtrack_stackpointer(), Operand::StaticVariable(stack_limit)); - __ j(above, &no_stack_overflow); - - SafeCall(&stack_overflow_label_); - - __ bind(&no_stack_overflow); -} - - -void RegExpMacroAssemblerX87::LoadCurrentCharacterUnchecked(int cp_offset, - int characters) { - if (mode_ == LATIN1) { - if (characters == 4) { - __ mov(current_character(), Operand(esi, edi, times_1, cp_offset)); - } else if (characters == 2) { - __ movzx_w(current_character(), Operand(esi, edi, times_1, cp_offset)); - } else { - DCHECK(characters == 1); - __ movzx_b(current_character(), Operand(esi, edi, times_1, cp_offset)); - } - } else { - DCHECK(mode_ == UC16); - if (characters == 2) { - __ mov(current_character(), - Operand(esi, edi, times_1, cp_offset * sizeof(uc16))); - } else { - DCHECK(characters == 1); - __ movzx_w(current_character(), - Operand(esi, edi, times_1, cp_offset * sizeof(uc16))); - } - } -} - - -#undef __ - -#endif // V8_INTERPRETED_REGEXP - -} // namespace internal -} // namespace v8 - -#endif // V8_TARGET_ARCH_X87 diff --git a/src/regexp/x87/regexp-macro-assembler-x87.h b/src/regexp/x87/regexp-macro-assembler-x87.h deleted file mode 100644 index 2f689612b7..0000000000 --- a/src/regexp/x87/regexp-macro-assembler-x87.h +++ /dev/null @@ -1,204 +0,0 @@ -// 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. - -#ifndef V8_REGEXP_X87_REGEXP_MACRO_ASSEMBLER_X87_H_ -#define V8_REGEXP_X87_REGEXP_MACRO_ASSEMBLER_X87_H_ - -#include "src/macro-assembler.h" -#include "src/regexp/regexp-macro-assembler.h" -#include "src/x87/assembler-x87.h" - -namespace v8 { -namespace internal { - -#ifndef V8_INTERPRETED_REGEXP -class RegExpMacroAssemblerX87: public NativeRegExpMacroAssembler { - public: - RegExpMacroAssemblerX87(Isolate* isolate, Zone* zone, Mode mode, - int registers_to_save); - virtual ~RegExpMacroAssemblerX87(); - virtual int stack_limit_slack(); - virtual void AdvanceCurrentPosition(int by); - virtual void AdvanceRegister(int reg, int by); - virtual void Backtrack(); - virtual void Bind(Label* label); - virtual void CheckAtStart(Label* on_at_start); - virtual void CheckCharacter(uint32_t c, Label* on_equal); - virtual void CheckCharacterAfterAnd(uint32_t c, - uint32_t mask, - Label* on_equal); - virtual void CheckCharacterGT(uc16 limit, Label* on_greater); - virtual void CheckCharacterLT(uc16 limit, Label* on_less); - // A "greedy loop" is a loop that is both greedy and with a simple - // body. It has a particularly simple implementation. - virtual void CheckGreedyLoop(Label* on_tos_equals_current_position); - virtual void CheckNotAtStart(int cp_offset, Label* on_not_at_start); - virtual void CheckNotBackReference(int start_reg, bool read_backward, - Label* on_no_match); - virtual void CheckNotBackReferenceIgnoreCase(int start_reg, - bool read_backward, bool unicode, - Label* on_no_match); - virtual void CheckNotCharacter(uint32_t c, Label* on_not_equal); - virtual void CheckNotCharacterAfterAnd(uint32_t c, - uint32_t mask, - Label* on_not_equal); - virtual void CheckNotCharacterAfterMinusAnd(uc16 c, - uc16 minus, - uc16 mask, - Label* on_not_equal); - virtual void CheckCharacterInRange(uc16 from, - uc16 to, - Label* on_in_range); - virtual void CheckCharacterNotInRange(uc16 from, - uc16 to, - Label* on_not_in_range); - virtual void CheckBitInTable(Handle table, Label* on_bit_set); - - // Checks whether the given offset from the current position is before - // the end of the string. - virtual void CheckPosition(int cp_offset, Label* on_outside_input); - virtual bool CheckSpecialCharacterClass(uc16 type, Label* on_no_match); - virtual void Fail(); - virtual Handle GetCode(Handle source); - virtual void GoTo(Label* label); - virtual void IfRegisterGE(int reg, int comparand, Label* if_ge); - virtual void IfRegisterLT(int reg, int comparand, Label* if_lt); - virtual void IfRegisterEqPos(int reg, Label* if_eq); - virtual IrregexpImplementation Implementation(); - virtual void LoadCurrentCharacter(int cp_offset, - Label* on_end_of_input, - bool check_bounds = true, - int characters = 1); - virtual void PopCurrentPosition(); - virtual void PopRegister(int register_index); - virtual void PushBacktrack(Label* label); - virtual void PushCurrentPosition(); - virtual void PushRegister(int register_index, - StackCheckFlag check_stack_limit); - virtual void ReadCurrentPositionFromRegister(int reg); - virtual void ReadStackPointerFromRegister(int reg); - virtual void SetCurrentPositionFromEnd(int by); - virtual void SetRegister(int register_index, int to); - virtual bool Succeed(); - virtual void WriteCurrentPositionToRegister(int reg, int cp_offset); - virtual void ClearRegisters(int reg_from, int reg_to); - virtual void WriteStackPointerToRegister(int reg); - - // Called from RegExp if the stack-guard is triggered. - // If the code object is relocated, the return address is fixed before - // returning. - static int CheckStackGuardState(Address* return_address, - Code* re_code, - Address re_frame); - - private: - // Offsets from ebp of function parameters and stored registers. - static const int kFramePointer = 0; - // Above the frame pointer - function parameters and return address. - static const int kReturn_eip = kFramePointer + kPointerSize; - static const int kFrameAlign = kReturn_eip + kPointerSize; - // Parameters. - static const int kInputString = kFrameAlign; - static const int kStartIndex = kInputString + kPointerSize; - static const int kInputStart = kStartIndex + kPointerSize; - static const int kInputEnd = kInputStart + kPointerSize; - static const int kRegisterOutput = kInputEnd + kPointerSize; - // For the case of global regular expression, we have room to store at least - // one set of capture results. For the case of non-global regexp, we ignore - // this value. - static const int kNumOutputRegisters = kRegisterOutput + kPointerSize; - static const int kStackHighEnd = kNumOutputRegisters + kPointerSize; - static const int kDirectCall = kStackHighEnd + kPointerSize; - static const int kIsolate = kDirectCall + kPointerSize; - // Below the frame pointer - local stack variables. - // When adding local variables remember to push space for them in - // the frame in GetCode. - static const int kBackup_esi = kFramePointer - kPointerSize; - static const int kBackup_edi = kBackup_esi - kPointerSize; - static const int kBackup_ebx = kBackup_edi - kPointerSize; - static const int kSuccessfulCaptures = kBackup_ebx - kPointerSize; - static const int kStringStartMinusOne = kSuccessfulCaptures - kPointerSize; - // First register address. Following registers are below it on the stack. - static const int kRegisterZero = kStringStartMinusOne - kPointerSize; - - // Initial size of code buffer. - static const size_t kRegExpCodeSize = 1024; - - // Load a number of characters at the given offset from the - // current position, into the current-character register. - void LoadCurrentCharacterUnchecked(int cp_offset, int character_count); - - // Check whether preemption has been requested. - void CheckPreemption(); - - // Check whether we are exceeding the stack limit on the backtrack stack. - void CheckStackLimit(); - - // Generate a call to CheckStackGuardState. - void CallCheckStackGuardState(Register scratch); - - // The ebp-relative location of a regexp register. - Operand register_location(int register_index); - - // The register containing the current character after LoadCurrentCharacter. - inline Register current_character() { return edx; } - - // The register containing the backtrack stack top. Provides a meaningful - // name to the register. - inline Register backtrack_stackpointer() { return ecx; } - - // Byte size of chars in the string to match (decided by the Mode argument) - inline int char_size() { return static_cast(mode_); } - - // Equivalent to a conditional branch to the label, unless the label - // is NULL, in which case it is a conditional Backtrack. - void BranchOrBacktrack(Condition condition, Label* to); - - // Call and return internally in the generated code in a way that - // is GC-safe (i.e., doesn't leave absolute code addresses on the stack) - inline void SafeCall(Label* to); - inline void SafeReturn(); - inline void SafeCallTarget(Label* name); - - // Pushes the value of a register on the backtrack stack. Decrements the - // stack pointer (ecx) by a word size and stores the register's value there. - inline void Push(Register source); - - // Pushes a value on the backtrack stack. Decrements the stack pointer (ecx) - // by a word size and stores the value there. - inline void Push(Immediate value); - - // Pops a value from the backtrack stack. Reads the word at the stack pointer - // (ecx) and increments it by a word size. - inline void Pop(Register target); - - Isolate* isolate() const { return masm_->isolate(); } - - MacroAssembler* masm_; - - // Which mode to generate code for (LATIN1 or UC16). - Mode mode_; - - // One greater than maximal register index actually used. - int num_registers_; - - // Number of registers to output at the end (the saved registers - // are always 0..num_saved_registers_-1) - int num_saved_registers_; - - // Labels used internally. - Label entry_label_; - Label start_label_; - Label success_label_; - Label backtrack_label_; - Label exit_label_; - Label check_preempt_label_; - Label stack_overflow_label_; -}; -#endif // V8_INTERPRETED_REGEXP - -} // namespace internal -} // namespace v8 - -#endif // V8_REGEXP_X87_REGEXP_MACRO_ASSEMBLER_X87_H_ diff --git a/src/register-configuration.cc b/src/register-configuration.cc index af35fd3b03..70f044ebae 100644 --- a/src/register-configuration.cc +++ b/src/register-configuration.cc @@ -74,9 +74,6 @@ class ArchDefaultRegisterConfiguration : public RegisterConfiguration { #if V8_TARGET_ARCH_IA32 kMaxAllocatableGeneralRegisterCount, kMaxAllocatableDoubleRegisterCount, -#elif V8_TARGET_ARCH_X87 - kMaxAllocatableGeneralRegisterCount, - compiler == TURBOFAN ? 1 : kMaxAllocatableDoubleRegisterCount, #elif V8_TARGET_ARCH_X64 kMaxAllocatableGeneralRegisterCount, kMaxAllocatableDoubleRegisterCount, diff --git a/src/register-configuration.h b/src/register-configuration.h index c59488444b..58b62db898 100644 --- a/src/register-configuration.h +++ b/src/register-configuration.h @@ -28,8 +28,7 @@ class V8_EXPORT_PRIVATE RegisterConfiguration { static const int kMaxFPRegisters = 32; // Default RegisterConfigurations for the target architecture. - // TODO(X87): This distinction in RegisterConfigurations is temporary - // until x87 TF supports all of the registers that Crankshaft does. + // TODO(mstarzinger): Crankshaft is gone. static const RegisterConfiguration* Crankshaft(); static const RegisterConfiguration* Turbofan(); diff --git a/src/simulator.h b/src/simulator.h index ca23889b90..6eab8cf976 100644 --- a/src/simulator.h +++ b/src/simulator.h @@ -21,8 +21,6 @@ #include "src/mips64/simulator-mips64.h" #elif V8_TARGET_ARCH_S390 #include "src/s390/simulator-s390.h" -#elif V8_TARGET_ARCH_X87 -#include "src/x87/simulator-x87.h" #else #error Unsupported target architecture. #endif diff --git a/src/strtod.cc b/src/strtod.cc index 6d6700698f..c98660b5bf 100644 --- a/src/strtod.cc +++ b/src/strtod.cc @@ -154,8 +154,7 @@ static void ReadDiyFp(Vector buffer, static bool DoubleStrtod(Vector trimmed, int exponent, double* result) { -#if (V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X87 || defined(USE_SIMULATOR)) && \ - !defined(_MSC_VER) +#if (V8_TARGET_ARCH_IA32 || defined(USE_SIMULATOR)) && !defined(_MSC_VER) // On x86 the floating-point stack can be 64 or 80 bits wide. If it is // 80 bits wide (as is the case on Linux) then double-rounding occurs and the // result is not accurate. diff --git a/src/utils.cc b/src/utils.cc index 96a7d2c9ee..9d166e06c6 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -356,8 +356,7 @@ void StringBuilder::AddFormattedList(const char* format, va_list list) { } } - -#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X87 +#if V8_TARGET_ARCH_IA32 static void MemMoveWrapper(void* dest, const void* src, size_t size) { memmove(dest, src, size); } @@ -411,7 +410,7 @@ static bool g_memcopy_functions_initialized = false; void init_memcopy_functions(Isolate* isolate) { if (g_memcopy_functions_initialized) return; g_memcopy_functions_initialized = true; -#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X87 +#if V8_TARGET_ARCH_IA32 MemMoveFunction generated_memmove = CreateMemMoveFunction(isolate); if (generated_memmove != NULL) { memmove_function = generated_memmove; diff --git a/src/utils.h b/src/utils.h index dc835fbc7c..6e9f1c01cb 100644 --- a/src/utils.h +++ b/src/utils.h @@ -431,7 +431,7 @@ inline uint32_t ComputePointerHash(void* ptr) { // Initializes the codegen support that depends on CPU features. void init_memcopy_functions(Isolate* isolate); -#if defined(V8_TARGET_ARCH_IA32) || defined(V8_TARGET_ARCH_X87) +#if defined(V8_TARGET_ARCH_IA32) // Limit below which the extra overhead of the MemCopy function is likely // to outweigh the benefits of faster copying. const int kMinComplexMemCopy = 64; diff --git a/src/v8.gyp b/src/v8.gyp index ba809758f8..e6e4567880 100644 --- a/src/v8.gyp +++ b/src/v8.gyp @@ -279,11 +279,6 @@ 'builtins/s390/builtins-s390.cc', ], }], - ['v8_target_arch=="x87"', { - 'sources': [ ### gcmole(arch:x87) ### - 'builtins/x87/builtins-x87.cc', - ], - }], ['v8_enable_i18n_support==0', { 'sources!': [ 'builtins/builtins-intl-gen.cc', @@ -1592,38 +1587,6 @@ 'regexp/ia32/regexp-macro-assembler-ia32.h', ], }], - ['v8_target_arch=="x87"', { - 'sources': [ ### gcmole(arch:x87) ### - 'x87/assembler-x87-inl.h', - 'x87/assembler-x87.cc', - 'x87/assembler-x87.h', - 'x87/code-stubs-x87.cc', - 'x87/code-stubs-x87.h', - 'x87/codegen-x87.cc', - 'x87/codegen-x87.h', - 'x87/cpu-x87.cc', - 'x87/deoptimizer-x87.cc', - 'x87/disasm-x87.cc', - 'x87/frames-x87.cc', - 'x87/frames-x87.h', - 'x87/interface-descriptors-x87.cc', - 'x87/macro-assembler-x87.cc', - 'x87/macro-assembler-x87.h', - 'x87/simulator-x87.cc', - 'x87/simulator-x87.h', - 'compiler/x87/code-generator-x87.cc', - 'compiler/x87/instruction-codes-x87.h', - 'compiler/x87/instruction-scheduler-x87.cc', - 'compiler/x87/instruction-selector-x87.cc', - 'debug/x87/debug-x87.cc', - 'full-codegen/x87/full-codegen-x87.cc', - 'ic/x87/access-compiler-x87.cc', - 'ic/x87/handler-compiler-x87.cc', - 'ic/x87/ic-x87.cc', - 'regexp/x87/regexp-macro-assembler-x87.cc', - 'regexp/x87/regexp-macro-assembler-x87.h', - ], - }], ['v8_target_arch=="mips" or v8_target_arch=="mipsel"', { 'sources': [ ### gcmole(arch:mipsel) ### 'mips/assembler-mips.cc', diff --git a/src/x87/OWNERS b/src/x87/OWNERS deleted file mode 100644 index 61245ae8e2..0000000000 --- a/src/x87/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -weiliang.lin@intel.com -chunyang.dai@intel.com diff --git a/src/x87/assembler-x87-inl.h b/src/x87/assembler-x87-inl.h deleted file mode 100644 index af9087df23..0000000000 --- a/src/x87/assembler-x87-inl.h +++ /dev/null @@ -1,546 +0,0 @@ -// Copyright (c) 1994-2006 Sun Microsystems Inc. -// 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. -// -// - Redistribution 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 Sun Microsystems or the names of 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. - -// The original source code covered by the above license above has been -// modified significantly by Google Inc. -// Copyright 2012 the V8 project authors. All rights reserved. - -// A light-weight IA32 Assembler. - -#ifndef V8_X87_ASSEMBLER_X87_INL_H_ -#define V8_X87_ASSEMBLER_X87_INL_H_ - -#include "src/x87/assembler-x87.h" - -#include "src/assembler.h" -#include "src/debug/debug.h" -#include "src/objects-inl.h" - -namespace v8 { -namespace internal { - -bool CpuFeatures::SupportsCrankshaft() { return true; } - -bool CpuFeatures::SupportsWasmSimd128() { return false; } - -static const byte kCallOpcode = 0xE8; -static const int kNoCodeAgeSequenceLength = 5; - - -// The modes possibly affected by apply must be in kApplyMask. -void RelocInfo::apply(intptr_t delta) { - if (IsRuntimeEntry(rmode_) || IsCodeTarget(rmode_)) { - int32_t* p = reinterpret_cast(pc_); - *p -= delta; // Relocate entry. - } else if (IsCodeAgeSequence(rmode_)) { - if (*pc_ == kCallOpcode) { - int32_t* p = reinterpret_cast(pc_ + 1); - *p -= delta; // Relocate entry. - } - } else if (IsDebugBreakSlot(rmode_) && IsPatchedDebugBreakSlotSequence()) { - // Special handling of a debug break slot when a break point is set (call - // instruction has been inserted). - int32_t* p = reinterpret_cast( - pc_ + Assembler::kPatchDebugBreakSlotAddressOffset); - *p -= delta; // Relocate entry. - } else if (IsInternalReference(rmode_)) { - // absolute code pointer inside code object moves with the code object. - int32_t* p = reinterpret_cast(pc_); - *p += delta; // Relocate entry. - } -} - - -Address RelocInfo::target_address() { - DCHECK(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)); - return Assembler::target_address_at(pc_, host_); -} - -Address RelocInfo::target_address_address() { - DCHECK(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_) - || rmode_ == EMBEDDED_OBJECT - || rmode_ == EXTERNAL_REFERENCE); - return reinterpret_cast
(pc_); -} - - -Address RelocInfo::constant_pool_entry_address() { - UNREACHABLE(); -} - - -int RelocInfo::target_address_size() { - return Assembler::kSpecialTargetSize; -} - -HeapObject* RelocInfo::target_object() { - DCHECK(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); - return HeapObject::cast(Memory::Object_at(pc_)); -} - -Handle RelocInfo::target_object_handle(Assembler* origin) { - DCHECK(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); - return Handle::cast(Memory::Object_Handle_at(pc_)); -} - -void RelocInfo::set_target_object(HeapObject* target, - WriteBarrierMode write_barrier_mode, - ICacheFlushMode icache_flush_mode) { - DCHECK(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); - Memory::Object_at(pc_) = target; - if (icache_flush_mode != SKIP_ICACHE_FLUSH) { - Assembler::FlushICache(target->GetIsolate(), pc_, sizeof(Address)); - } - if (write_barrier_mode == UPDATE_WRITE_BARRIER && host() != nullptr) { - host()->GetHeap()->RecordWriteIntoCode(host(), this, target); - host()->GetHeap()->incremental_marking()->RecordWriteIntoCode(host(), this, - target); - } -} - - -Address RelocInfo::target_external_reference() { - DCHECK(rmode_ == RelocInfo::EXTERNAL_REFERENCE); - return Memory::Address_at(pc_); -} - - -Address RelocInfo::target_internal_reference() { - DCHECK(rmode_ == INTERNAL_REFERENCE); - return Memory::Address_at(pc_); -} - - -Address RelocInfo::target_internal_reference_address() { - DCHECK(rmode_ == INTERNAL_REFERENCE); - return reinterpret_cast
(pc_); -} - - -Address RelocInfo::target_runtime_entry(Assembler* origin) { - DCHECK(IsRuntimeEntry(rmode_)); - return reinterpret_cast
(*reinterpret_cast(pc_)); -} - -void RelocInfo::set_target_runtime_entry(Isolate* isolate, Address target, - WriteBarrierMode write_barrier_mode, - ICacheFlushMode icache_flush_mode) { - DCHECK(IsRuntimeEntry(rmode_)); - if (target_address() != target) { - set_target_address(isolate, target, write_barrier_mode, icache_flush_mode); - } -} - - -Handle RelocInfo::target_cell_handle() { - DCHECK(rmode_ == RelocInfo::CELL); - Address address = Memory::Address_at(pc_); - return Handle(reinterpret_cast(address)); -} - - -Cell* RelocInfo::target_cell() { - DCHECK(rmode_ == RelocInfo::CELL); - return Cell::FromValueAddress(Memory::Address_at(pc_)); -} - - -void RelocInfo::set_target_cell(Cell* cell, - WriteBarrierMode write_barrier_mode, - ICacheFlushMode icache_flush_mode) { - DCHECK(cell->IsCell()); - DCHECK(rmode_ == RelocInfo::CELL); - Address address = cell->address() + Cell::kValueOffset; - Memory::Address_at(pc_) = address; - if (icache_flush_mode != SKIP_ICACHE_FLUSH) { - Assembler::FlushICache(cell->GetIsolate(), pc_, sizeof(Address)); - } - if (write_barrier_mode == UPDATE_WRITE_BARRIER && host() != NULL) { - host()->GetHeap()->incremental_marking()->RecordWriteIntoCode(host(), this, - cell); - } -} - -Handle RelocInfo::code_age_stub_handle(Assembler* origin) { - DCHECK(rmode_ == RelocInfo::CODE_AGE_SEQUENCE); - DCHECK(*pc_ == kCallOpcode); - return Handle::cast(Memory::Object_Handle_at(pc_ + 1)); -} - - -Code* RelocInfo::code_age_stub() { - DCHECK(rmode_ == RelocInfo::CODE_AGE_SEQUENCE); - DCHECK(*pc_ == kCallOpcode); - return Code::GetCodeFromTargetAddress( - Assembler::target_address_at(pc_ + 1, host_)); -} - - -void RelocInfo::set_code_age_stub(Code* stub, - ICacheFlushMode icache_flush_mode) { - DCHECK(*pc_ == kCallOpcode); - DCHECK(rmode_ == RelocInfo::CODE_AGE_SEQUENCE); - Assembler::set_target_address_at(stub->GetIsolate(), pc_ + 1, host_, - stub->instruction_start(), - icache_flush_mode); -} - - -Address RelocInfo::debug_call_address() { - DCHECK(IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()); - Address location = pc_ + Assembler::kPatchDebugBreakSlotAddressOffset; - return Assembler::target_address_at(location, host_); -} - -void RelocInfo::set_debug_call_address(Isolate* isolate, Address target) { - DCHECK(IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()); - Address location = pc_ + Assembler::kPatchDebugBreakSlotAddressOffset; - Assembler::set_target_address_at(isolate, location, host_, target); - if (host() != NULL) { - Code* target_code = Code::GetCodeFromTargetAddress(target); - host()->GetHeap()->incremental_marking()->RecordWriteIntoCode(host(), this, - target_code); - } -} - -void RelocInfo::WipeOut(Isolate* isolate) { - if (IsEmbeddedObject(rmode_) || IsExternalReference(rmode_) || - IsInternalReference(rmode_)) { - Memory::Address_at(pc_) = NULL; - } else if (IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)) { - // Effectively write zero into the relocation. - Assembler::set_target_address_at(isolate, pc_, host_, - pc_ + sizeof(int32_t)); - } else { - UNREACHABLE(); - } -} - -template -void RelocInfo::Visit(Isolate* isolate, ObjectVisitor* visitor) { - RelocInfo::Mode mode = rmode(); - if (mode == RelocInfo::EMBEDDED_OBJECT) { - visitor->VisitEmbeddedPointer(host(), this); - Assembler::FlushICache(isolate, pc_, sizeof(Address)); - } else if (RelocInfo::IsCodeTarget(mode)) { - visitor->VisitCodeTarget(host(), this); - } else if (mode == RelocInfo::CELL) { - visitor->VisitCellPointer(host(), this); - } else if (mode == RelocInfo::EXTERNAL_REFERENCE) { - visitor->VisitExternalReference(host(), this); - } else if (mode == RelocInfo::INTERNAL_REFERENCE) { - visitor->VisitInternalReference(host(), this); - } else if (RelocInfo::IsCodeAgeSequence(mode)) { - visitor->VisitCodeAgeSequence(host(), this); - } else if (RelocInfo::IsDebugBreakSlot(mode) && - IsPatchedDebugBreakSlotSequence()) { - visitor->VisitDebugTarget(host(), this); - } else if (IsRuntimeEntry(mode)) { - visitor->VisitRuntimeEntry(host(), this); - } -} - - -template -void RelocInfo::Visit(Heap* heap) { - RelocInfo::Mode mode = rmode(); - if (mode == RelocInfo::EMBEDDED_OBJECT) { - StaticVisitor::VisitEmbeddedPointer(heap, this); - Assembler::FlushICache(heap->isolate(), pc_, sizeof(Address)); - } else if (RelocInfo::IsCodeTarget(mode)) { - StaticVisitor::VisitCodeTarget(heap, this); - } else if (mode == RelocInfo::CELL) { - StaticVisitor::VisitCell(heap, this); - } else if (mode == RelocInfo::EXTERNAL_REFERENCE) { - StaticVisitor::VisitExternalReference(this); - } else if (mode == RelocInfo::INTERNAL_REFERENCE) { - StaticVisitor::VisitInternalReference(this); - } else if (RelocInfo::IsCodeAgeSequence(mode)) { - StaticVisitor::VisitCodeAgeSequence(heap, this); - } else if (RelocInfo::IsDebugBreakSlot(mode) && - IsPatchedDebugBreakSlotSequence()) { - StaticVisitor::VisitDebugTarget(heap, this); - } else if (IsRuntimeEntry(mode)) { - StaticVisitor::VisitRuntimeEntry(this); - } -} - - - -Immediate::Immediate(int x) { - x_ = x; - rmode_ = RelocInfo::NONE32; -} - -Immediate::Immediate(Address x, RelocInfo::Mode rmode) { - x_ = reinterpret_cast(x); - rmode_ = rmode; -} - -Immediate::Immediate(const ExternalReference& ext) { - x_ = reinterpret_cast(ext.address()); - rmode_ = RelocInfo::EXTERNAL_REFERENCE; -} - - -Immediate::Immediate(Label* internal_offset) { - x_ = reinterpret_cast(internal_offset); - rmode_ = RelocInfo::INTERNAL_REFERENCE; -} - - -Immediate::Immediate(Handle handle) { - AllowDeferredHandleDereference using_raw_address; - // Verify all Objects referred by code are NOT in new space. - Object* obj = *handle; - if (obj->IsHeapObject()) { - x_ = reinterpret_cast(handle.location()); - rmode_ = RelocInfo::EMBEDDED_OBJECT; - } else { - // no relocation needed - x_ = reinterpret_cast(obj); - rmode_ = RelocInfo::NONE32; - } -} - - -Immediate::Immediate(Smi* value) { - x_ = reinterpret_cast(value); - rmode_ = RelocInfo::NONE32; -} - - -Immediate::Immediate(Address addr) { - x_ = reinterpret_cast(addr); - rmode_ = RelocInfo::NONE32; -} - - -void Assembler::emit(uint32_t x) { - *reinterpret_cast(pc_) = x; - pc_ += sizeof(uint32_t); -} - - -void Assembler::emit_q(uint64_t x) { - *reinterpret_cast(pc_) = x; - pc_ += sizeof(uint64_t); -} - - -void Assembler::emit(Handle handle) { - AllowDeferredHandleDereference heap_object_check; - // Verify all Objects referred by code are NOT in new space. - Object* obj = *handle; - if (obj->IsHeapObject()) { - emit(reinterpret_cast(handle.location()), - RelocInfo::EMBEDDED_OBJECT); - } else { - // no relocation needed - emit(reinterpret_cast(obj)); - } -} - - -void Assembler::emit(uint32_t x, RelocInfo::Mode rmode, TypeFeedbackId id) { - if (rmode == RelocInfo::CODE_TARGET && !id.IsNone()) { - RecordRelocInfo(RelocInfo::CODE_TARGET_WITH_ID, id.ToInt()); - } else if (!RelocInfo::IsNone(rmode) - && rmode != RelocInfo::CODE_AGE_SEQUENCE) { - RecordRelocInfo(rmode); - } - emit(x); -} - - -void Assembler::emit(Handle code, - RelocInfo::Mode rmode, - TypeFeedbackId id) { - AllowDeferredHandleDereference embedding_raw_address; - emit(reinterpret_cast(code.location()), rmode, id); -} - - -void Assembler::emit(const Immediate& x) { - if (x.rmode_ == RelocInfo::INTERNAL_REFERENCE) { - Label* label = reinterpret_cast(x.x_); - emit_code_relative_offset(label); - return; - } - if (!RelocInfo::IsNone(x.rmode_)) RecordRelocInfo(x.rmode_); - emit(x.x_); -} - - -void Assembler::emit_code_relative_offset(Label* label) { - if (label->is_bound()) { - int32_t pos; - pos = label->pos() + Code::kHeaderSize - kHeapObjectTag; - emit(pos); - } else { - emit_disp(label, Displacement::CODE_RELATIVE); - } -} - -void Assembler::emit_b(Immediate x) { - DCHECK(x.is_int8() || x.is_uint8()); - uint8_t value = static_cast(x.x_); - *pc_++ = value; -} - -void Assembler::emit_w(const Immediate& x) { - DCHECK(RelocInfo::IsNone(x.rmode_)); - uint16_t value = static_cast(x.x_); - reinterpret_cast(pc_)[0] = value; - pc_ += sizeof(uint16_t); -} - - -Address Assembler::target_address_at(Address pc, Address constant_pool) { - return pc + sizeof(int32_t) + *reinterpret_cast(pc); -} - - -void Assembler::set_target_address_at(Isolate* isolate, Address pc, - Address constant_pool, Address target, - ICacheFlushMode icache_flush_mode) { - DCHECK_IMPLIES(isolate == nullptr, icache_flush_mode == SKIP_ICACHE_FLUSH); - int32_t* p = reinterpret_cast(pc); - *p = target - (pc + sizeof(int32_t)); - if (icache_flush_mode != SKIP_ICACHE_FLUSH) { - Assembler::FlushICache(isolate, p, sizeof(int32_t)); - } -} - -Address Assembler::target_address_at(Address pc, Code* code) { - Address constant_pool = code ? code->constant_pool() : NULL; - return target_address_at(pc, constant_pool); -} - -void Assembler::set_target_address_at(Isolate* isolate, Address pc, Code* code, - Address target, - ICacheFlushMode icache_flush_mode) { - Address constant_pool = code ? code->constant_pool() : NULL; - set_target_address_at(isolate, pc, constant_pool, target, icache_flush_mode); -} - -Address Assembler::target_address_from_return_address(Address pc) { - return pc - kCallTargetAddressOffset; -} - - -Displacement Assembler::disp_at(Label* L) { - return Displacement(long_at(L->pos())); -} - - -void Assembler::disp_at_put(Label* L, Displacement disp) { - long_at_put(L->pos(), disp.data()); -} - - -void Assembler::emit_disp(Label* L, Displacement::Type type) { - Displacement disp(L, type); - L->link_to(pc_offset()); - emit(static_cast(disp.data())); -} - - -void Assembler::emit_near_disp(Label* L) { - byte disp = 0x00; - if (L->is_near_linked()) { - int offset = L->near_link_pos() - pc_offset(); - DCHECK(is_int8(offset)); - disp = static_cast(offset & 0xFF); - } - L->link_to(pc_offset(), Label::kNear); - *pc_++ = disp; -} - - -void Assembler::deserialization_set_target_internal_reference_at( - Isolate* isolate, Address pc, Address target, RelocInfo::Mode mode) { - Memory::Address_at(pc) = target; -} - - -void Operand::set_modrm(int mod, Register rm) { - DCHECK((mod & -4) == 0); - buf_[0] = mod << 6 | rm.code(); - len_ = 1; -} - - -void Operand::set_sib(ScaleFactor scale, Register index, Register base) { - DCHECK(len_ == 1); - DCHECK((scale & -4) == 0); - // Use SIB with no index register only for base esp. - DCHECK(!index.is(esp) || base.is(esp)); - buf_[1] = scale << 6 | index.code() << 3 | base.code(); - len_ = 2; -} - - -void Operand::set_disp8(int8_t disp) { - DCHECK(len_ == 1 || len_ == 2); - *reinterpret_cast(&buf_[len_++]) = disp; -} - - -void Operand::set_dispr(int32_t disp, RelocInfo::Mode rmode) { - DCHECK(len_ == 1 || len_ == 2); - int32_t* p = reinterpret_cast(&buf_[len_]); - *p = disp; - len_ += sizeof(int32_t); - rmode_ = rmode; -} - -Operand::Operand(Register reg) { - // reg - set_modrm(3, reg); -} - - -Operand::Operand(int32_t disp, RelocInfo::Mode rmode) { - // [disp/r] - set_modrm(0, ebp); - set_dispr(disp, rmode); -} - - -Operand::Operand(Immediate imm) { - // [disp/r] - set_modrm(0, ebp); - set_dispr(imm.x_, imm.rmode_); -} -} // namespace internal -} // namespace v8 - -#endif // V8_X87_ASSEMBLER_X87_INL_H_ diff --git a/src/x87/assembler-x87.cc b/src/x87/assembler-x87.cc deleted file mode 100644 index 309a8ed96c..0000000000 --- a/src/x87/assembler-x87.cc +++ /dev/null @@ -1,2217 +0,0 @@ -// Copyright (c) 1994-2006 Sun Microsystems Inc. -// 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. -// -// - Redistribution 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 Sun Microsystems or the names of 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. - -// The original source code covered by the above license above has been modified -// significantly by Google Inc. -// Copyright 2012 the V8 project authors. All rights reserved. - -#include "src/x87/assembler-x87.h" - -#if V8_TARGET_ARCH_X87 - -#include "src/base/bits.h" -#include "src/base/cpu.h" -#include "src/disassembler.h" -#include "src/macro-assembler.h" -#include "src/v8.h" - -namespace v8 { -namespace internal { - -// ----------------------------------------------------------------------------- -// Implementation of CpuFeatures - -void CpuFeatures::ProbeImpl(bool cross_compile) { - base::CPU cpu; - - // Only use statically determined features for cross compile (snapshot). - if (cross_compile) return; -} - - -void CpuFeatures::PrintTarget() { } -void CpuFeatures::PrintFeatures() { } - - -// ----------------------------------------------------------------------------- -// Implementation of Displacement - -void Displacement::init(Label* L, Type type) { - DCHECK(!L->is_bound()); - int next = 0; - if (L->is_linked()) { - next = L->pos(); - DCHECK(next > 0); // Displacements must be at positions > 0 - } - // Ensure that we _never_ overflow the next field. - DCHECK(NextField::is_valid(Assembler::kMaximalBufferSize)); - data_ = NextField::encode(next) | TypeField::encode(type); -} - - -// ----------------------------------------------------------------------------- -// Implementation of RelocInfo - - -const int RelocInfo::kApplyMask = - RelocInfo::kCodeTargetMask | 1 << RelocInfo::RUNTIME_ENTRY | - 1 << RelocInfo::INTERNAL_REFERENCE | 1 << RelocInfo::CODE_AGE_SEQUENCE | - RelocInfo::kDebugBreakSlotMask; - - -bool RelocInfo::IsCodedSpecially() { - // The deserializer needs to know whether a pointer is specially coded. Being - // specially coded on IA32 means that it is a relative address, as used by - // branch instructions. These are also the ones that need changing when a - // code object moves. - return (1 << rmode_) & kApplyMask; -} - - -bool RelocInfo::IsInConstantPool() { - return false; -} - -Address RelocInfo::wasm_memory_reference() { - DCHECK(IsWasmMemoryReference(rmode_)); - return Memory::Address_at(pc_); -} - -Address RelocInfo::wasm_global_reference() { - DCHECK(IsWasmGlobalReference(rmode_)); - return Memory::Address_at(pc_); -} - -uint32_t RelocInfo::wasm_memory_size_reference() { - DCHECK(IsWasmMemorySizeReference(rmode_)); - return Memory::uint32_at(pc_); -} - -uint32_t RelocInfo::wasm_function_table_size_reference() { - DCHECK(IsWasmFunctionTableSizeReference(rmode_)); - return Memory::uint32_at(pc_); -} - -void RelocInfo::unchecked_update_wasm_memory_reference( - Isolate* isolate, Address address, ICacheFlushMode icache_flush_mode) { - Memory::Address_at(pc_) = address; - if (icache_flush_mode != SKIP_ICACHE_FLUSH) { - Assembler::FlushICache(isolate, pc_, sizeof(Address)); - } -} - -void RelocInfo::unchecked_update_wasm_size(Isolate* isolate, uint32_t size, - ICacheFlushMode icache_flush_mode) { - Memory::uint32_at(pc_) = size; - if (icache_flush_mode != SKIP_ICACHE_FLUSH) { - Assembler::FlushICache(isolate, pc_, sizeof(uint32_t)); - } -} - -// ----------------------------------------------------------------------------- -// Implementation of Operand - -Operand::Operand(Register base, int32_t disp, RelocInfo::Mode rmode) { - // [base + disp/r] - if (disp == 0 && RelocInfo::IsNone(rmode) && !base.is(ebp)) { - // [base] - set_modrm(0, base); - if (base.is(esp)) set_sib(times_1, esp, base); - } else if (is_int8(disp) && RelocInfo::IsNone(rmode)) { - // [base + disp8] - set_modrm(1, base); - if (base.is(esp)) set_sib(times_1, esp, base); - set_disp8(disp); - } else { - // [base + disp/r] - set_modrm(2, base); - if (base.is(esp)) set_sib(times_1, esp, base); - set_dispr(disp, rmode); - } -} - - -Operand::Operand(Register base, - Register index, - ScaleFactor scale, - int32_t disp, - RelocInfo::Mode rmode) { - DCHECK(!index.is(esp)); // illegal addressing mode - // [base + index*scale + disp/r] - if (disp == 0 && RelocInfo::IsNone(rmode) && !base.is(ebp)) { - // [base + index*scale] - set_modrm(0, esp); - set_sib(scale, index, base); - } else if (is_int8(disp) && RelocInfo::IsNone(rmode)) { - // [base + index*scale + disp8] - set_modrm(1, esp); - set_sib(scale, index, base); - set_disp8(disp); - } else { - // [base + index*scale + disp/r] - set_modrm(2, esp); - set_sib(scale, index, base); - set_dispr(disp, rmode); - } -} - - -Operand::Operand(Register index, - ScaleFactor scale, - int32_t disp, - RelocInfo::Mode rmode) { - DCHECK(!index.is(esp)); // illegal addressing mode - // [index*scale + disp/r] - set_modrm(0, esp); - set_sib(scale, index, ebp); - set_dispr(disp, rmode); -} - - -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. -} - - -bool Operand::is_reg_only() const { - return (buf_[0] & 0xF8) == 0xC0; // Addressing mode is register only. -} - - -Register Operand::reg() const { - DCHECK(is_reg_only()); - return Register::from_code(buf_[0] & 0x07); -} - - -// ----------------------------------------------------------------------------- -// Implementation of Assembler. - -// Emit a single byte. Must always be inlined. -#define EMIT(x) \ - *pc_++ = (x) - -Assembler::Assembler(IsolateData isolate_data, void* buffer, int buffer_size) - : AssemblerBase(isolate_data, buffer, buffer_size) { -// 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 - - reloc_info_writer.Reposition(buffer_ + buffer_size_, pc_); -} - - -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). - DCHECK(pc_ <= reloc_info_writer.pos()); // No overlap. - // Set up code descriptor. - 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; - desc->constant_pool_size = 0; - desc->unwinding_info_size = 0; - desc->unwinding_info = nullptr; -} - - -void Assembler::Align(int m) { - DCHECK(base::bits::IsPowerOfTwo(m)); - int mask = m - 1; - int addr = pc_offset(); - Nop((m - (addr & mask)) & mask); -} - - -bool Assembler::IsNop(Address addr) { - Address a = addr; - while (*a == 0x66) a++; - if (*a == 0x90) return true; - if (a[0] == 0xf && a[1] == 0x1f) return true; - return false; -} - - -void Assembler::Nop(int bytes) { - EnsureSpace ensure_space(this); - - // Older CPUs that do not support SSE2 may not support multibyte NOP - // instructions. - for (; bytes > 0; bytes--) { - EMIT(0x90); - } - return; -} - - -void Assembler::CodeTargetAlign() { - Align(16); // Preferred alignment of jump targets on ia32. -} - - -void Assembler::cpuid() { - EnsureSpace ensure_space(this); - EMIT(0x0F); - EMIT(0xA2); -} - - -void Assembler::pushad() { - EnsureSpace ensure_space(this); - EMIT(0x60); -} - - -void Assembler::popad() { - EnsureSpace ensure_space(this); - EMIT(0x61); -} - - -void Assembler::pushfd() { - EnsureSpace ensure_space(this); - EMIT(0x9C); -} - - -void Assembler::popfd() { - EnsureSpace ensure_space(this); - EMIT(0x9D); -} - - -void Assembler::push(const Immediate& x) { - EnsureSpace ensure_space(this); - if (x.is_int8()) { - EMIT(0x6a); - EMIT(x.x_); - } else { - EMIT(0x68); - emit(x); - } -} - - -void Assembler::push_imm32(int32_t imm32) { - EnsureSpace ensure_space(this); - EMIT(0x68); - emit(imm32); -} - - -void Assembler::push(Register src) { - EnsureSpace ensure_space(this); - EMIT(0x50 | src.code()); -} - - -void Assembler::push(const Operand& src) { - EnsureSpace ensure_space(this); - EMIT(0xFF); - emit_operand(esi, src); -} - - -void Assembler::pop(Register dst) { - DCHECK(reloc_info_writer.last_pc() != NULL); - EnsureSpace ensure_space(this); - EMIT(0x58 | dst.code()); -} - - -void Assembler::pop(const Operand& dst) { - EnsureSpace ensure_space(this); - EMIT(0x8F); - emit_operand(eax, dst); -} - - -void Assembler::enter(const Immediate& size) { - EnsureSpace ensure_space(this); - EMIT(0xC8); - emit_w(size); - EMIT(0); -} - - -void Assembler::leave() { - EnsureSpace ensure_space(this); - EMIT(0xC9); -} - - -void Assembler::mov_b(Register dst, const Operand& src) { - CHECK(dst.is_byte_register()); - EnsureSpace ensure_space(this); - EMIT(0x8A); - emit_operand(dst, src); -} - - -void Assembler::mov_b(const Operand& dst, const Immediate& src) { - EnsureSpace ensure_space(this); - EMIT(0xC6); - emit_operand(eax, dst); - EMIT(static_cast(src.x_)); -} - - -void Assembler::mov_b(const Operand& dst, int8_t imm8) { - EnsureSpace ensure_space(this); - EMIT(0xC6); - emit_operand(eax, dst); - EMIT(imm8); -} - - -void Assembler::mov_b(const Operand& dst, Register src) { - CHECK(src.is_byte_register()); - EnsureSpace ensure_space(this); - EMIT(0x88); - emit_operand(src, dst); -} - - -void Assembler::mov_w(Register dst, const Operand& src) { - EnsureSpace ensure_space(this); - EMIT(0x66); - EMIT(0x8B); - emit_operand(dst, src); -} - - -void Assembler::mov_w(const Operand& dst, Register src) { - EnsureSpace ensure_space(this); - EMIT(0x66); - EMIT(0x89); - emit_operand(src, dst); -} - - -void Assembler::mov_w(const Operand& dst, int16_t imm16) { - EnsureSpace ensure_space(this); - EMIT(0x66); - EMIT(0xC7); - emit_operand(eax, dst); - EMIT(static_cast(imm16 & 0xff)); - EMIT(static_cast(imm16 >> 8)); -} - - -void Assembler::mov_w(const Operand& dst, const Immediate& src) { - EnsureSpace ensure_space(this); - EMIT(0x66); - EMIT(0xC7); - emit_operand(eax, dst); - EMIT(static_cast(src.x_ & 0xff)); - EMIT(static_cast(src.x_ >> 8)); -} - - -void Assembler::mov(Register dst, int32_t imm32) { - EnsureSpace ensure_space(this); - EMIT(0xB8 | dst.code()); - emit(imm32); -} - - -void Assembler::mov(Register dst, const Immediate& x) { - EnsureSpace ensure_space(this); - EMIT(0xB8 | dst.code()); - emit(x); -} - - -void Assembler::mov(Register dst, Handle handle) { - EnsureSpace ensure_space(this); - EMIT(0xB8 | dst.code()); - emit(handle); -} - - -void Assembler::mov(Register dst, const Operand& src) { - EnsureSpace ensure_space(this); - EMIT(0x8B); - emit_operand(dst, src); -} - - -void Assembler::mov(Register dst, Register src) { - EnsureSpace ensure_space(this); - EMIT(0x89); - EMIT(0xC0 | src.code() << 3 | dst.code()); -} - - -void Assembler::mov(const Operand& dst, const Immediate& x) { - EnsureSpace ensure_space(this); - EMIT(0xC7); - emit_operand(eax, dst); - emit(x); -} - - -void Assembler::mov(const Operand& dst, Handle handle) { - EnsureSpace ensure_space(this); - EMIT(0xC7); - emit_operand(eax, dst); - emit(handle); -} - - -void Assembler::mov(const Operand& dst, Register src) { - EnsureSpace ensure_space(this); - EMIT(0x89); - emit_operand(src, dst); -} - - -void Assembler::movsx_b(Register dst, const Operand& src) { - EnsureSpace ensure_space(this); - EMIT(0x0F); - EMIT(0xBE); - emit_operand(dst, src); -} - - -void Assembler::movsx_w(Register dst, const Operand& src) { - EnsureSpace ensure_space(this); - EMIT(0x0F); - EMIT(0xBF); - emit_operand(dst, src); -} - - -void Assembler::movzx_b(Register dst, const Operand& src) { - EnsureSpace ensure_space(this); - EMIT(0x0F); - EMIT(0xB6); - emit_operand(dst, src); -} - - -void Assembler::movzx_w(Register dst, const Operand& src) { - EnsureSpace ensure_space(this); - EMIT(0x0F); - EMIT(0xB7); - emit_operand(dst, src); -} - - -void Assembler::cld() { - EnsureSpace ensure_space(this); - EMIT(0xFC); -} - - -void Assembler::rep_movs() { - EnsureSpace ensure_space(this); - EMIT(0xF3); - EMIT(0xA5); -} - - -void Assembler::rep_stos() { - EnsureSpace ensure_space(this); - EMIT(0xF3); - EMIT(0xAB); -} - - -void Assembler::stos() { - EnsureSpace ensure_space(this); - EMIT(0xAB); -} - - -void Assembler::xchg(Register dst, Register src) { - EnsureSpace ensure_space(this); - if (src.is(eax) || dst.is(eax)) { // Single-byte encoding. - EMIT(0x90 | (src.is(eax) ? dst.code() : src.code())); - } else { - EMIT(0x87); - EMIT(0xC0 | src.code() << 3 | dst.code()); - } -} - - -void Assembler::xchg(Register dst, const Operand& src) { - EnsureSpace ensure_space(this); - EMIT(0x87); - emit_operand(dst, src); -} - -void Assembler::xchg_b(Register reg, const Operand& op) { - EnsureSpace ensure_space(this); - EMIT(0x86); - emit_operand(reg, op); -} - -void Assembler::xchg_w(Register reg, const Operand& op) { - EnsureSpace ensure_space(this); - EMIT(0x66); - EMIT(0x87); - emit_operand(reg, op); -} - -void Assembler::lock() { - EnsureSpace ensure_space(this); - EMIT(0xF0); -} - -void Assembler::cmpxchg(const Operand& dst, Register src) { - EnsureSpace ensure_space(this); - EMIT(0x0F); - EMIT(0xB1); - emit_operand(src, dst); -} - -void Assembler::cmpxchg_b(const Operand& dst, Register src) { - EnsureSpace ensure_space(this); - EMIT(0x0F); - EMIT(0xB0); - emit_operand(src, dst); -} - -void Assembler::cmpxchg_w(const Operand& dst, Register src) { - EnsureSpace ensure_space(this); - EMIT(0x66); - EMIT(0x0F); - EMIT(0xB1); - emit_operand(src, dst); -} - -void Assembler::adc(Register dst, int32_t imm32) { - EnsureSpace ensure_space(this); - emit_arith(2, Operand(dst), Immediate(imm32)); -} - - -void Assembler::adc(Register dst, const Operand& src) { - EnsureSpace ensure_space(this); - EMIT(0x13); - emit_operand(dst, src); -} - - -void Assembler::add(Register dst, const Operand& src) { - EnsureSpace ensure_space(this); - EMIT(0x03); - emit_operand(dst, src); -} - - -void Assembler::add(const Operand& dst, Register src) { - EnsureSpace ensure_space(this); - EMIT(0x01); - emit_operand(src, dst); -} - - -void Assembler::add(const Operand& dst, const Immediate& x) { - DCHECK(reloc_info_writer.last_pc() != NULL); - EnsureSpace ensure_space(this); - emit_arith(0, dst, x); -} - - -void Assembler::and_(Register dst, int32_t imm32) { - and_(dst, Immediate(imm32)); -} - - -void Assembler::and_(Register dst, const Immediate& x) { - EnsureSpace ensure_space(this); - emit_arith(4, Operand(dst), x); -} - - -void Assembler::and_(Register dst, const Operand& src) { - EnsureSpace ensure_space(this); - EMIT(0x23); - emit_operand(dst, src); -} - - -void Assembler::and_(const Operand& dst, const Immediate& x) { - EnsureSpace ensure_space(this); - emit_arith(4, dst, x); -} - - -void Assembler::and_(const Operand& dst, Register src) { - EnsureSpace ensure_space(this); - EMIT(0x21); - emit_operand(src, dst); -} - -void Assembler::cmpb(const Operand& op, Immediate imm8) { - DCHECK(imm8.is_int8() || imm8.is_uint8()); - EnsureSpace ensure_space(this); - if (op.is_reg(eax)) { - EMIT(0x3C); - } else { - EMIT(0x80); - emit_operand(edi, op); // edi == 7 - } - emit_b(imm8); -} - - -void Assembler::cmpb(const Operand& op, Register reg) { - CHECK(reg.is_byte_register()); - EnsureSpace ensure_space(this); - EMIT(0x38); - emit_operand(reg, op); -} - - -void Assembler::cmpb(Register reg, const Operand& op) { - CHECK(reg.is_byte_register()); - EnsureSpace ensure_space(this); - EMIT(0x3A); - emit_operand(reg, op); -} - - -void Assembler::cmpw(const Operand& op, Immediate imm16) { - DCHECK(imm16.is_int16()); - EnsureSpace ensure_space(this); - EMIT(0x66); - EMIT(0x81); - emit_operand(edi, op); - emit_w(imm16); -} - -void Assembler::cmpw(Register reg, const Operand& op) { - EnsureSpace ensure_space(this); - EMIT(0x66); - EMIT(0x3B); - emit_operand(reg, op); -} - -void Assembler::cmpw(const Operand& op, Register reg) { - EnsureSpace ensure_space(this); - EMIT(0x66); - EMIT(0x39); - emit_operand(reg, op); -} - -void Assembler::cmp(Register reg, int32_t imm32) { - EnsureSpace ensure_space(this); - emit_arith(7, Operand(reg), Immediate(imm32)); -} - - -void Assembler::cmp(Register reg, Handle handle) { - EnsureSpace ensure_space(this); - emit_arith(7, Operand(reg), Immediate(handle)); -} - - -void Assembler::cmp(Register reg, const Operand& op) { - EnsureSpace ensure_space(this); - EMIT(0x3B); - emit_operand(reg, op); -} - -void Assembler::cmp(const Operand& op, Register reg) { - EnsureSpace ensure_space(this); - EMIT(0x39); - emit_operand(reg, op); -} - -void Assembler::cmp(const Operand& op, const Immediate& imm) { - EnsureSpace ensure_space(this); - emit_arith(7, op, imm); -} - - -void Assembler::cmp(const Operand& op, Handle handle) { - EnsureSpace ensure_space(this); - emit_arith(7, op, Immediate(handle)); -} - - -void Assembler::cmpb_al(const Operand& op) { - EnsureSpace ensure_space(this); - EMIT(0x38); // CMP r/m8, r8 - emit_operand(eax, op); // eax has same code as register al. -} - - -void Assembler::cmpw_ax(const Operand& op) { - EnsureSpace ensure_space(this); - EMIT(0x66); - EMIT(0x39); // CMP r/m16, r16 - emit_operand(eax, op); // eax has same code as register ax. -} - - -void Assembler::dec_b(Register dst) { - CHECK(dst.is_byte_register()); - EnsureSpace ensure_space(this); - EMIT(0xFE); - EMIT(0xC8 | dst.code()); -} - - -void Assembler::dec_b(const Operand& dst) { - EnsureSpace ensure_space(this); - EMIT(0xFE); - emit_operand(ecx, dst); -} - - -void Assembler::dec(Register dst) { - EnsureSpace ensure_space(this); - EMIT(0x48 | dst.code()); -} - - -void Assembler::dec(const Operand& dst) { - EnsureSpace ensure_space(this); - EMIT(0xFF); - emit_operand(ecx, dst); -} - - -void Assembler::cdq() { - EnsureSpace ensure_space(this); - EMIT(0x99); -} - - -void Assembler::idiv(const Operand& src) { - EnsureSpace ensure_space(this); - EMIT(0xF7); - emit_operand(edi, src); -} - - -void Assembler::div(const Operand& src) { - EnsureSpace ensure_space(this); - EMIT(0xF7); - emit_operand(esi, src); -} - - -void Assembler::imul(Register reg) { - EnsureSpace ensure_space(this); - EMIT(0xF7); - EMIT(0xE8 | reg.code()); -} - - -void Assembler::imul(Register dst, const Operand& src) { - EnsureSpace ensure_space(this); - EMIT(0x0F); - EMIT(0xAF); - emit_operand(dst, src); -} - - -void Assembler::imul(Register dst, Register src, int32_t imm32) { - imul(dst, Operand(src), imm32); -} - - -void Assembler::imul(Register dst, const Operand& src, int32_t imm32) { - EnsureSpace ensure_space(this); - if (is_int8(imm32)) { - EMIT(0x6B); - emit_operand(dst, src); - EMIT(imm32); - } else { - EMIT(0x69); - emit_operand(dst, src); - emit(imm32); - } -} - - -void Assembler::inc(Register dst) { - EnsureSpace ensure_space(this); - EMIT(0x40 | dst.code()); -} - - -void Assembler::inc(const Operand& dst) { - EnsureSpace ensure_space(this); - EMIT(0xFF); - emit_operand(eax, dst); -} - - -void Assembler::lea(Register dst, const Operand& src) { - EnsureSpace ensure_space(this); - EMIT(0x8D); - emit_operand(dst, src); -} - - -void Assembler::mul(Register src) { - EnsureSpace ensure_space(this); - EMIT(0xF7); - EMIT(0xE0 | src.code()); -} - - -void Assembler::neg(Register dst) { - EnsureSpace ensure_space(this); - EMIT(0xF7); - EMIT(0xD8 | dst.code()); -} - - -void Assembler::neg(const Operand& dst) { - EnsureSpace ensure_space(this); - EMIT(0xF7); - emit_operand(ebx, dst); -} - - -void Assembler::not_(Register dst) { - EnsureSpace ensure_space(this); - EMIT(0xF7); - EMIT(0xD0 | dst.code()); -} - - -void Assembler::not_(const Operand& dst) { - EnsureSpace ensure_space(this); - EMIT(0xF7); - emit_operand(edx, dst); -} - - -void Assembler::or_(Register dst, int32_t imm32) { - EnsureSpace ensure_space(this); - emit_arith(1, Operand(dst), Immediate(imm32)); -} - - -void Assembler::or_(Register dst, const Operand& src) { - EnsureSpace ensure_space(this); - EMIT(0x0B); - emit_operand(dst, src); -} - - -void Assembler::or_(const Operand& dst, const Immediate& x) { - EnsureSpace ensure_space(this); - emit_arith(1, dst, x); -} - - -void Assembler::or_(const Operand& dst, Register src) { - EnsureSpace ensure_space(this); - EMIT(0x09); - emit_operand(src, dst); -} - - -void Assembler::rcl(Register dst, uint8_t imm8) { - EnsureSpace ensure_space(this); - DCHECK(is_uint5(imm8)); // illegal shift count - if (imm8 == 1) { - EMIT(0xD1); - EMIT(0xD0 | dst.code()); - } else { - EMIT(0xC1); - EMIT(0xD0 | dst.code()); - EMIT(imm8); - } -} - - -void Assembler::rcr(Register dst, uint8_t imm8) { - EnsureSpace ensure_space(this); - DCHECK(is_uint5(imm8)); // illegal shift count - if (imm8 == 1) { - EMIT(0xD1); - EMIT(0xD8 | dst.code()); - } else { - EMIT(0xC1); - EMIT(0xD8 | dst.code()); - EMIT(imm8); - } -} - - -void Assembler::ror(const Operand& dst, uint8_t imm8) { - EnsureSpace ensure_space(this); - DCHECK(is_uint5(imm8)); // illegal shift count - if (imm8 == 1) { - EMIT(0xD1); - emit_operand(ecx, dst); - } else { - EMIT(0xC1); - emit_operand(ecx, dst); - EMIT(imm8); - } -} - - -void Assembler::ror_cl(const Operand& dst) { - EnsureSpace ensure_space(this); - EMIT(0xD3); - emit_operand(ecx, dst); -} - - -void Assembler::sar(const Operand& dst, uint8_t imm8) { - EnsureSpace ensure_space(this); - DCHECK(is_uint5(imm8)); // illegal shift count - if (imm8 == 1) { - EMIT(0xD1); - emit_operand(edi, dst); - } else { - EMIT(0xC1); - emit_operand(edi, dst); - EMIT(imm8); - } -} - - -void Assembler::sar_cl(const Operand& dst) { - EnsureSpace ensure_space(this); - EMIT(0xD3); - emit_operand(edi, dst); -} - -void Assembler::sbb(Register dst, const Operand& src) { - EnsureSpace ensure_space(this); - EMIT(0x1B); - emit_operand(dst, src); -} - -void Assembler::shld(Register dst, Register src, uint8_t shift) { - DCHECK(is_uint5(shift)); - EnsureSpace ensure_space(this); - EMIT(0x0F); - EMIT(0xA4); - emit_operand(src, Operand(dst)); - EMIT(shift); -} - -void Assembler::shld_cl(Register dst, Register src) { - EnsureSpace ensure_space(this); - EMIT(0x0F); - EMIT(0xA5); - emit_operand(src, Operand(dst)); -} - - -void Assembler::shl(const Operand& dst, uint8_t imm8) { - EnsureSpace ensure_space(this); - DCHECK(is_uint5(imm8)); // illegal shift count - if (imm8 == 1) { - EMIT(0xD1); - emit_operand(esp, dst); - } else { - EMIT(0xC1); - emit_operand(esp, dst); - EMIT(imm8); - } -} - - -void Assembler::shl_cl(const Operand& dst) { - EnsureSpace ensure_space(this); - EMIT(0xD3); - emit_operand(esp, dst); -} - -void Assembler::shr(const Operand& dst, uint8_t imm8) { - EnsureSpace ensure_space(this); - DCHECK(is_uint5(imm8)); // illegal shift count - if (imm8 == 1) { - EMIT(0xD1); - emit_operand(ebp, dst); - } else { - EMIT(0xC1); - emit_operand(ebp, dst); - EMIT(imm8); - } -} - - -void Assembler::shr_cl(const Operand& dst) { - EnsureSpace ensure_space(this); - EMIT(0xD3); - emit_operand(ebp, dst); -} - -void Assembler::shrd(Register dst, Register src, uint8_t shift) { - DCHECK(is_uint5(shift)); - EnsureSpace ensure_space(this); - EMIT(0x0F); - EMIT(0xAC); - emit_operand(dst, Operand(src)); - EMIT(shift); -} - -void Assembler::shrd_cl(const Operand& dst, Register src) { - EnsureSpace ensure_space(this); - EMIT(0x0F); - EMIT(0xAD); - emit_operand(src, dst); -} - -void Assembler::sub(const Operand& dst, const Immediate& x) { - EnsureSpace ensure_space(this); - emit_arith(5, dst, x); -} - - -void Assembler::sub(Register dst, const Operand& src) { - EnsureSpace ensure_space(this); - EMIT(0x2B); - emit_operand(dst, src); -} - - -void Assembler::sub(const Operand& dst, Register src) { - EnsureSpace ensure_space(this); - EMIT(0x29); - emit_operand(src, dst); -} - - -void Assembler::test(Register reg, const Immediate& imm) { - if (imm.is_uint8()) { - test_b(reg, imm); - return; - } - - EnsureSpace ensure_space(this); - // This is not using emit_arith because test doesn't support - // sign-extension of 8-bit operands. - if (reg.is(eax)) { - EMIT(0xA9); - } else { - EMIT(0xF7); - EMIT(0xC0 | reg.code()); - } - emit(imm); -} - - -void Assembler::test(Register reg, const Operand& op) { - EnsureSpace ensure_space(this); - EMIT(0x85); - emit_operand(reg, op); -} - - -void Assembler::test_b(Register reg, const Operand& op) { - CHECK(reg.is_byte_register()); - EnsureSpace ensure_space(this); - EMIT(0x84); - emit_operand(reg, op); -} - - -void Assembler::test(const Operand& op, const Immediate& imm) { - if (op.is_reg_only()) { - test(op.reg(), imm); - return; - } - if (imm.is_uint8()) { - return test_b(op, imm); - } - EnsureSpace ensure_space(this); - EMIT(0xF7); - emit_operand(eax, op); - emit(imm); -} - -void Assembler::test_b(Register reg, Immediate imm8) { - DCHECK(imm8.is_uint8()); - EnsureSpace ensure_space(this); - // Only use test against byte for registers that have a byte - // variant: eax, ebx, ecx, and edx. - if (reg.is(eax)) { - EMIT(0xA8); - emit_b(imm8); - } else if (reg.is_byte_register()) { - emit_arith_b(0xF6, 0xC0, reg, static_cast(imm8.x_)); - } else { - EMIT(0x66); - EMIT(0xF7); - EMIT(0xC0 | reg.code()); - emit_w(imm8); - } -} - -void Assembler::test_b(const Operand& op, Immediate imm8) { - if (op.is_reg_only()) { - test_b(op.reg(), imm8); - return; - } - EnsureSpace ensure_space(this); - EMIT(0xF6); - emit_operand(eax, op); - emit_b(imm8); -} - -void Assembler::test_w(Register reg, Immediate imm16) { - DCHECK(imm16.is_int16() || imm16.is_uint16()); - EnsureSpace ensure_space(this); - if (reg.is(eax)) { - EMIT(0xA9); - emit_w(imm16); - } else { - EMIT(0x66); - EMIT(0xF7); - EMIT(0xc0 | reg.code()); - emit_w(imm16); - } -} - -void Assembler::test_w(Register reg, const Operand& op) { - EnsureSpace ensure_space(this); - EMIT(0x66); - EMIT(0x85); - emit_operand(reg, op); -} - -void Assembler::test_w(const Operand& op, Immediate imm16) { - DCHECK(imm16.is_int16() || imm16.is_uint16()); - if (op.is_reg_only()) { - test_w(op.reg(), imm16); - return; - } - EnsureSpace ensure_space(this); - EMIT(0x66); - EMIT(0xF7); - emit_operand(eax, op); - emit_w(imm16); -} - -void Assembler::xor_(Register dst, int32_t imm32) { - EnsureSpace ensure_space(this); - emit_arith(6, Operand(dst), Immediate(imm32)); -} - - -void Assembler::xor_(Register dst, const Operand& src) { - EnsureSpace ensure_space(this); - EMIT(0x33); - emit_operand(dst, src); -} - - -void Assembler::xor_(const Operand& dst, Register src) { - EnsureSpace ensure_space(this); - EMIT(0x31); - emit_operand(src, dst); -} - - -void Assembler::xor_(const Operand& dst, const Immediate& x) { - EnsureSpace ensure_space(this); - emit_arith(6, dst, x); -} - - -void Assembler::bt(const Operand& dst, Register src) { - EnsureSpace ensure_space(this); - EMIT(0x0F); - EMIT(0xA3); - emit_operand(src, dst); -} - - -void Assembler::bts(const Operand& dst, Register src) { - EnsureSpace ensure_space(this); - EMIT(0x0F); - EMIT(0xAB); - emit_operand(src, dst); -} - - -void Assembler::bsr(Register dst, const Operand& src) { - EnsureSpace ensure_space(this); - EMIT(0x0F); - EMIT(0xBD); - emit_operand(dst, src); -} - - -void Assembler::bsf(Register dst, const Operand& src) { - EnsureSpace ensure_space(this); - EMIT(0x0F); - EMIT(0xBC); - emit_operand(dst, src); -} - - -void Assembler::hlt() { - EnsureSpace ensure_space(this); - EMIT(0xF4); -} - - -void Assembler::int3() { - EnsureSpace ensure_space(this); - EMIT(0xCC); -} - - -void Assembler::nop() { - EnsureSpace ensure_space(this); - EMIT(0x90); -} - - -void Assembler::ret(int imm16) { - EnsureSpace ensure_space(this); - DCHECK(is_uint16(imm16)); - if (imm16 == 0) { - EMIT(0xC3); - } else { - EMIT(0xC2); - EMIT(imm16 & 0xFF); - EMIT((imm16 >> 8) & 0xFF); - } -} - - -void Assembler::ud2() { - EnsureSpace ensure_space(this); - EMIT(0x0F); - EMIT(0x0B); -} - - -// Labels refer to positions in the (to be) generated code. -// There are bound, linked, and unused labels. -// -// Bound labels refer to known positions in the already -// generated code. pos() is the position the label refers to. -// -// Linked labels refer to unknown positions in the code -// to be generated; pos() is the position of the 32bit -// Displacement of the last instruction using the label. - - -void Assembler::print(Label* L) { - if (L->is_unused()) { - PrintF("unused label\n"); - } else if (L->is_bound()) { - PrintF("bound label to %d\n", L->pos()); - } else if (L->is_linked()) { - Label l = *L; - PrintF("unbound label"); - while (l.is_linked()) { - Displacement disp = disp_at(&l); - PrintF("@ %d ", l.pos()); - disp.print(); - PrintF("\n"); - disp.next(&l); - } - } else { - PrintF("label in inconsistent state (pos = %d)\n", L->pos_); - } -} - - -void Assembler::bind_to(Label* L, int pos) { - EnsureSpace ensure_space(this); - DCHECK(0 <= pos && pos <= pc_offset()); // must have a valid binding position - while (L->is_linked()) { - Displacement disp = disp_at(L); - int fixup_pos = L->pos(); - if (disp.type() == Displacement::CODE_ABSOLUTE) { - long_at_put(fixup_pos, reinterpret_cast(buffer_ + pos)); - internal_reference_positions_.push_back(fixup_pos); - } else if (disp.type() == Displacement::CODE_RELATIVE) { - // Relative to Code* heap object pointer. - long_at_put(fixup_pos, pos + Code::kHeaderSize - kHeapObjectTag); - } else { - if (disp.type() == Displacement::UNCONDITIONAL_JUMP) { - DCHECK(byte_at(fixup_pos - 1) == 0xE9); // jmp expected - } - // Relative address, relative to point after address. - int imm32 = pos - (fixup_pos + sizeof(int32_t)); - long_at_put(fixup_pos, imm32); - } - disp.next(L); - } - while (L->is_near_linked()) { - int fixup_pos = L->near_link_pos(); - int offset_to_next = - static_cast(*reinterpret_cast(addr_at(fixup_pos))); - DCHECK(offset_to_next <= 0); - // Relative address, relative to point after address. - int disp = pos - fixup_pos - sizeof(int8_t); - CHECK(0 <= disp && disp <= 127); - set_byte_at(fixup_pos, disp); - if (offset_to_next < 0) { - L->link_to(fixup_pos + offset_to_next, Label::kNear); - } else { - L->UnuseNear(); - } - } - L->bind_to(pos); -} - - -void Assembler::bind(Label* L) { - EnsureSpace ensure_space(this); - DCHECK(!L->is_bound()); // label can only be bound once - bind_to(L, pc_offset()); -} - - -void Assembler::call(Label* L) { - EnsureSpace ensure_space(this); - if (L->is_bound()) { - const int long_size = 5; - int offs = L->pos() - pc_offset(); - DCHECK(offs <= 0); - // 1110 1000 #32-bit disp. - EMIT(0xE8); - emit(offs - long_size); - } else { - // 1110 1000 #32-bit disp. - EMIT(0xE8); - emit_disp(L, Displacement::OTHER); - } -} - - -void Assembler::call(byte* entry, RelocInfo::Mode rmode) { - EnsureSpace ensure_space(this); - DCHECK(!RelocInfo::IsCodeTarget(rmode)); - EMIT(0xE8); - if (RelocInfo::IsRuntimeEntry(rmode)) { - emit(reinterpret_cast(entry), rmode); - } else { - emit(entry - (pc_ + sizeof(int32_t)), rmode); - } -} - - -int Assembler::CallSize(const Operand& adr) { - // Call size is 1 (opcode) + adr.len_ (operand). - return 1 + adr.len_; -} - - -void Assembler::call(const Operand& adr) { - EnsureSpace ensure_space(this); - EMIT(0xFF); - emit_operand(edx, adr); -} - - -int Assembler::CallSize(Handle code, RelocInfo::Mode rmode) { - return 1 /* EMIT */ + sizeof(uint32_t) /* emit */; -} - - -void Assembler::call(Handle code, - RelocInfo::Mode rmode, - TypeFeedbackId ast_id) { - EnsureSpace ensure_space(this); - DCHECK(RelocInfo::IsCodeTarget(rmode) - || rmode == RelocInfo::CODE_AGE_SEQUENCE); - EMIT(0xE8); - emit(code, rmode, ast_id); -} - - -void Assembler::jmp(Label* L, Label::Distance distance) { - EnsureSpace ensure_space(this); - if (L->is_bound()) { - const int short_size = 2; - const int long_size = 5; - int offs = L->pos() - pc_offset(); - DCHECK(offs <= 0); - if (is_int8(offs - short_size)) { - // 1110 1011 #8-bit disp. - EMIT(0xEB); - EMIT((offs - short_size) & 0xFF); - } else { - // 1110 1001 #32-bit disp. - EMIT(0xE9); - emit(offs - long_size); - } - } else if (distance == Label::kNear) { - EMIT(0xEB); - emit_near_disp(L); - } else { - // 1110 1001 #32-bit disp. - EMIT(0xE9); - emit_disp(L, Displacement::UNCONDITIONAL_JUMP); - } -} - - -void Assembler::jmp(byte* entry, RelocInfo::Mode rmode) { - EnsureSpace ensure_space(this); - DCHECK(!RelocInfo::IsCodeTarget(rmode)); - EMIT(0xE9); - if (RelocInfo::IsRuntimeEntry(rmode)) { - emit(reinterpret_cast(entry), rmode); - } else { - emit(entry - (pc_ + sizeof(int32_t)), rmode); - } -} - - -void Assembler::jmp(const Operand& adr) { - EnsureSpace ensure_space(this); - EMIT(0xFF); - emit_operand(esp, adr); -} - - -void Assembler::jmp(Handle code, RelocInfo::Mode rmode) { - EnsureSpace ensure_space(this); - DCHECK(RelocInfo::IsCodeTarget(rmode)); - EMIT(0xE9); - emit(code, rmode); -} - - -void Assembler::j(Condition cc, Label* L, Label::Distance distance) { - EnsureSpace ensure_space(this); - DCHECK(0 <= cc && static_cast(cc) < 16); - if (L->is_bound()) { - const int short_size = 2; - const int long_size = 6; - int offs = L->pos() - pc_offset(); - DCHECK(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); - emit(offs - long_size); - } - } else if (distance == Label::kNear) { - EMIT(0x70 | cc); - emit_near_disp(L); - } else { - // 0000 1111 1000 tttn #32-bit disp - // Note: could eliminate cond. jumps to this jump if condition - // is the same however, seems to be rather unlikely case. - EMIT(0x0F); - EMIT(0x80 | cc); - emit_disp(L, Displacement::OTHER); - } -} - - -void Assembler::j(Condition cc, byte* entry, RelocInfo::Mode rmode) { - EnsureSpace ensure_space(this); - DCHECK((0 <= cc) && (static_cast(cc) < 16)); - // 0000 1111 1000 tttn #32-bit disp. - EMIT(0x0F); - EMIT(0x80 | cc); - if (RelocInfo::IsRuntimeEntry(rmode)) { - emit(reinterpret_cast(entry), rmode); - } else { - emit(entry - (pc_ + sizeof(int32_t)), rmode); - } -} - - -void Assembler::j(Condition cc, Handle code, RelocInfo::Mode rmode) { - EnsureSpace ensure_space(this); - // 0000 1111 1000 tttn #32-bit disp - EMIT(0x0F); - EMIT(0x80 | cc); - emit(code, rmode); -} - - -// FPU instructions. - -void Assembler::fld(int i) { - EnsureSpace ensure_space(this); - emit_farith(0xD9, 0xC0, i); -} - - -void Assembler::fstp(int i) { - EnsureSpace ensure_space(this); - emit_farith(0xDD, 0xD8, i); -} - - -void Assembler::fld1() { - EnsureSpace ensure_space(this); - EMIT(0xD9); - EMIT(0xE8); -} - - -void Assembler::fldpi() { - EnsureSpace ensure_space(this); - EMIT(0xD9); - EMIT(0xEB); -} - - -void Assembler::fldz() { - EnsureSpace ensure_space(this); - EMIT(0xD9); - EMIT(0xEE); -} - - -void Assembler::fldln2() { - EnsureSpace ensure_space(this); - EMIT(0xD9); - EMIT(0xED); -} - - -void Assembler::fld_s(const Operand& adr) { - EnsureSpace ensure_space(this); - EMIT(0xD9); - emit_operand(eax, adr); -} - - -void Assembler::fld_d(const Operand& adr) { - EnsureSpace ensure_space(this); - EMIT(0xDD); - emit_operand(eax, adr); -} - - -void Assembler::fstp_s(const Operand& adr) { - EnsureSpace ensure_space(this); - EMIT(0xD9); - emit_operand(ebx, adr); -} - - -void Assembler::fst_s(const Operand& adr) { - EnsureSpace ensure_space(this); - EMIT(0xD9); - emit_operand(edx, adr); -} - - -void Assembler::fldcw(const Operand& adr) { - EnsureSpace ensure_space(this); - EMIT(0xD9); - emit_operand(ebp, adr); -} - - -void Assembler::fnstcw(const Operand& adr) { - EnsureSpace ensure_space(this); - EMIT(0xD9); - emit_operand(edi, adr); -} - - -void Assembler::fstp_d(const Operand& adr) { - EnsureSpace ensure_space(this); - EMIT(0xDD); - emit_operand(ebx, adr); -} - - -void Assembler::fst_d(const Operand& adr) { - EnsureSpace ensure_space(this); - EMIT(0xDD); - emit_operand(edx, adr); -} - - -void Assembler::fild_s(const Operand& adr) { - EnsureSpace ensure_space(this); - EMIT(0xDB); - emit_operand(eax, adr); -} - - -void Assembler::fild_d(const Operand& adr) { - EnsureSpace ensure_space(this); - EMIT(0xDF); - emit_operand(ebp, adr); -} - - -void Assembler::fistp_s(const Operand& adr) { - EnsureSpace ensure_space(this); - EMIT(0xDB); - emit_operand(ebx, adr); -} - - -void Assembler::fisttp_s(const Operand& adr) { - DCHECK(IsEnabled(SSE3)); - EnsureSpace ensure_space(this); - EMIT(0xDB); - emit_operand(ecx, adr); -} - - -void Assembler::fisttp_d(const Operand& adr) { - DCHECK(IsEnabled(SSE3)); - EnsureSpace ensure_space(this); - EMIT(0xDD); - emit_operand(ecx, adr); -} - - -void Assembler::fist_s(const Operand& adr) { - EnsureSpace ensure_space(this); - EMIT(0xDB); - emit_operand(edx, adr); -} - - -void Assembler::fistp_d(const Operand& adr) { - EnsureSpace ensure_space(this); - EMIT(0xDF); - emit_operand(edi, adr); -} - - -void Assembler::fabs() { - EnsureSpace ensure_space(this); - EMIT(0xD9); - EMIT(0xE1); -} - - -void Assembler::fchs() { - EnsureSpace ensure_space(this); - EMIT(0xD9); - EMIT(0xE0); -} - - -void Assembler::fsqrt() { - EnsureSpace ensure_space(this); - EMIT(0xD9); - EMIT(0xFA); -} - - -void Assembler::fcos() { - EnsureSpace ensure_space(this); - EMIT(0xD9); - EMIT(0xFF); -} - - -void Assembler::fsin() { - EnsureSpace ensure_space(this); - EMIT(0xD9); - EMIT(0xFE); -} - - -void Assembler::fptan() { - EnsureSpace ensure_space(this); - EMIT(0xD9); - EMIT(0xF2); -} - - -void Assembler::fyl2x() { - EnsureSpace ensure_space(this); - EMIT(0xD9); - EMIT(0xF1); -} - - -void Assembler::f2xm1() { - EnsureSpace ensure_space(this); - EMIT(0xD9); - EMIT(0xF0); -} - - -void Assembler::fscale() { - EnsureSpace ensure_space(this); - EMIT(0xD9); - EMIT(0xFD); -} - - -void Assembler::fninit() { - EnsureSpace ensure_space(this); - EMIT(0xDB); - EMIT(0xE3); -} - - -void Assembler::fadd(int i) { - EnsureSpace ensure_space(this); - emit_farith(0xDC, 0xC0, i); -} - - -void Assembler::fadd_i(int i) { - EnsureSpace ensure_space(this); - emit_farith(0xD8, 0xC0, i); -} - - -void Assembler::fadd_d(const Operand& adr) { - EnsureSpace ensure_space(this); - EMIT(0xDC); - emit_operand(eax, adr); -} - - -void Assembler::fsub(int i) { - EnsureSpace ensure_space(this); - emit_farith(0xDC, 0xE8, i); -} - - -void Assembler::fsub_i(int i) { - EnsureSpace ensure_space(this); - emit_farith(0xD8, 0xE0, i); -} - - -void Assembler::fsubr_d(const Operand& adr) { - EnsureSpace ensure_space(this); - EMIT(0xDC); - emit_operand(ebp, adr); -} - - -void Assembler::fsub_d(const Operand& adr) { - EnsureSpace ensure_space(this); - EMIT(0xDC); - emit_operand(esp, adr); -} - - -void Assembler::fisub_s(const Operand& adr) { - EnsureSpace ensure_space(this); - EMIT(0xDA); - emit_operand(esp, adr); -} - - -void Assembler::fmul_i(int i) { - EnsureSpace ensure_space(this); - emit_farith(0xD8, 0xC8, i); -} - - -void Assembler::fmul(int i) { - EnsureSpace ensure_space(this); - emit_farith(0xDC, 0xC8, i); -} - - -void Assembler::fmul_d(const Operand& adr) { - EnsureSpace ensure_space(this); - EMIT(0xDC); - emit_operand(ecx, adr); -} - - -void Assembler::fdiv(int i) { - EnsureSpace ensure_space(this); - emit_farith(0xDC, 0xF8, i); -} - - -void Assembler::fdiv_d(const Operand& adr) { - EnsureSpace ensure_space(this); - EMIT(0xDC); - emit_operand(esi, adr); -} - - -void Assembler::fdivr_d(const Operand& adr) { - EnsureSpace ensure_space(this); - EMIT(0xDC); - emit_operand(edi, adr); -} - - -void Assembler::fdiv_i(int i) { - EnsureSpace ensure_space(this); - emit_farith(0xD8, 0xF0, i); -} - - -void Assembler::faddp(int i) { - EnsureSpace ensure_space(this); - emit_farith(0xDE, 0xC0, i); -} - - -void Assembler::fsubp(int i) { - EnsureSpace ensure_space(this); - emit_farith(0xDE, 0xE8, i); -} - - -void Assembler::fsubrp(int i) { - EnsureSpace ensure_space(this); - emit_farith(0xDE, 0xE0, i); -} - - -void Assembler::fmulp(int i) { - EnsureSpace ensure_space(this); - emit_farith(0xDE, 0xC8, i); -} - - -void Assembler::fdivp(int i) { - EnsureSpace ensure_space(this); - emit_farith(0xDE, 0xF8, i); -} - - -void Assembler::fprem() { - EnsureSpace ensure_space(this); - EMIT(0xD9); - EMIT(0xF8); -} - - -void Assembler::fprem1() { - EnsureSpace ensure_space(this); - EMIT(0xD9); - EMIT(0xF5); -} - - -void Assembler::fxch(int i) { - EnsureSpace ensure_space(this); - emit_farith(0xD9, 0xC8, i); -} - - -void Assembler::fincstp() { - EnsureSpace ensure_space(this); - EMIT(0xD9); - EMIT(0xF7); -} - - -void Assembler::ffree(int i) { - EnsureSpace ensure_space(this); - emit_farith(0xDD, 0xC0, i); -} - - -void Assembler::ftst() { - EnsureSpace ensure_space(this); - EMIT(0xD9); - EMIT(0xE4); -} - - -void Assembler::fxam() { - EnsureSpace ensure_space(this); - EMIT(0xD9); - EMIT(0xE5); -} - - -void Assembler::fucomp(int i) { - EnsureSpace ensure_space(this); - emit_farith(0xDD, 0xE8, i); -} - - -void Assembler::fucompp() { - EnsureSpace ensure_space(this); - EMIT(0xDA); - EMIT(0xE9); -} - - -void Assembler::fucomi(int i) { - EnsureSpace ensure_space(this); - EMIT(0xDB); - EMIT(0xE8 + i); -} - - -void Assembler::fucomip() { - EnsureSpace ensure_space(this); - EMIT(0xDF); - EMIT(0xE9); -} - - -void Assembler::fcompp() { - EnsureSpace ensure_space(this); - EMIT(0xDE); - EMIT(0xD9); -} - - -void Assembler::fnstsw_ax() { - EnsureSpace ensure_space(this); - EMIT(0xDF); - EMIT(0xE0); -} - - -void Assembler::fwait() { - EnsureSpace ensure_space(this); - EMIT(0x9B); -} - - -void Assembler::frndint() { - EnsureSpace ensure_space(this); - EMIT(0xD9); - EMIT(0xFC); -} - - -void Assembler::fnclex() { - EnsureSpace ensure_space(this); - EMIT(0xDB); - EMIT(0xE2); -} - - -void Assembler::fnsave(const Operand& adr) { - EnsureSpace ensure_space(this); - EMIT(0xDD); - emit_operand(esi, adr); -} - - -void Assembler::frstor(const Operand& adr) { - EnsureSpace ensure_space(this); - EMIT(0xDD); - emit_operand(esp, adr); -} - - -void Assembler::sahf() { - EnsureSpace ensure_space(this); - EMIT(0x9E); -} - - -void Assembler::setcc(Condition cc, Register reg) { - DCHECK(reg.is_byte_register()); - EnsureSpace ensure_space(this); - EMIT(0x0F); - EMIT(0x90 | cc); - EMIT(0xC0 | reg.code()); -} - - -void Assembler::GrowBuffer() { - DCHECK(buffer_overflow()); - if (!own_buffer_) FATAL("external code buffer is too small"); - - // Compute new buffer size. - CodeDesc desc; // the new buffer - 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 || - static_cast(desc.buffer_size) > - isolate()->heap()->MaxOldGenerationSize()) { - V8::FatalProcessOutOfMemory("Assembler::GrowBuffer"); - } - - // Set up new buffer. - desc.buffer = NewArray(desc.buffer_size); - desc.origin = this; - 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); - - DeleteArray(buffer_); - buffer_ = desc.buffer; - buffer_size_ = desc.buffer_size; - pc_ += pc_delta; - reloc_info_writer.Reposition(reloc_info_writer.pos() + rc_delta, - reloc_info_writer.last_pc() + pc_delta); - - // Relocate internal references. - for (auto pos : internal_reference_positions_) { - int32_t* p = reinterpret_cast(buffer_ + pos); - *p += pc_delta; - } - - DCHECK(!buffer_overflow()); -} - - -void Assembler::emit_arith_b(int op1, int op2, Register dst, int imm8) { - DCHECK(is_uint8(op1) && is_uint8(op2)); // wrong opcode - DCHECK(is_uint8(imm8)); - DCHECK((op1 & 0x01) == 0); // should be 8bit operation - EMIT(op1); - EMIT(op2 | dst.code()); - EMIT(imm8); -} - - -void Assembler::emit_arith(int sel, Operand dst, const Immediate& x) { - DCHECK((0 <= sel) && (sel <= 7)); - Register ireg = { sel }; - if (x.is_int8()) { - EMIT(0x83); // using a sign-extended 8-bit immediate. - emit_operand(ireg, dst); - EMIT(x.x_ & 0xFF); - } else if (dst.is_reg(eax)) { - EMIT((sel << 3) | 0x05); // short form if the destination is eax. - emit(x); - } else { - EMIT(0x81); // using a literal 32-bit immediate. - emit_operand(ireg, dst); - emit(x); - } -} - - -void Assembler::emit_operand(Register reg, const Operand& adr) { - const unsigned length = adr.len_; - DCHECK(length > 0); - - // Emit updated ModRM byte containing the given register. - pc_[0] = (adr.buf_[0] & ~0x38) | (reg.code() << 3); - - // Emit the rest of the encoded operand. - for (unsigned i = 1; i < length; i++) pc_[i] = adr.buf_[i]; - pc_ += length; - - // Emit relocation information if necessary. - if (length >= sizeof(int32_t) && !RelocInfo::IsNone(adr.rmode_)) { - pc_ -= sizeof(int32_t); // pc_ must be *at* disp32 - RecordRelocInfo(adr.rmode_); - if (adr.rmode_ == RelocInfo::INTERNAL_REFERENCE) { // Fixup for labels - emit_label(*reinterpret_cast(pc_)); - } else { - pc_ += sizeof(int32_t); - } - } -} - - -void Assembler::emit_label(Label* label) { - if (label->is_bound()) { - internal_reference_positions_.push_back(pc_offset()); - emit(reinterpret_cast(buffer_ + label->pos())); - } else { - emit_disp(label, Displacement::CODE_ABSOLUTE); - } -} - - -void Assembler::emit_farith(int b1, int b2, int i) { - DCHECK(is_uint8(b1) && is_uint8(b2)); // wrong opcode - DCHECK(0 <= i && i < 8); // illegal stack offset - EMIT(b1); - EMIT(b2 + i); -} - - -void Assembler::db(uint8_t data) { - EnsureSpace ensure_space(this); - EMIT(data); -} - - -void Assembler::dd(uint32_t data) { - EnsureSpace ensure_space(this); - emit(data); -} - - -void Assembler::dq(uint64_t data) { - EnsureSpace ensure_space(this); - emit_q(data); -} - - -void Assembler::dd(Label* label) { - EnsureSpace ensure_space(this); - RecordRelocInfo(RelocInfo::INTERNAL_REFERENCE); - emit_label(label); -} - - -void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) { - DCHECK(!RelocInfo::IsNone(rmode)); - // Don't record external references unless the heap will be serialized. - if (rmode == RelocInfo::EXTERNAL_REFERENCE && - !serializer_enabled() && !emit_debug_code()) { - return; - } - RelocInfo rinfo(isolate(), pc_, rmode, data, NULL); - reloc_info_writer.Write(&rinfo); -} - -} // namespace internal -} // namespace v8 - -#endif // V8_TARGET_ARCH_X87 diff --git a/src/x87/assembler-x87.h b/src/x87/assembler-x87.h deleted file mode 100644 index 8b9cda4357..0000000000 --- a/src/x87/assembler-x87.h +++ /dev/null @@ -1,1107 +0,0 @@ -// Copyright (c) 1994-2006 Sun Microsystems Inc. -// 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. -// -// - Redistribution 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 Sun Microsystems or the names of 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. - -// The original source code covered by the above license above has been -// modified significantly by Google Inc. -// Copyright 2011 the V8 project authors. All rights reserved. - -// A light-weight IA32 Assembler. - -#ifndef V8_X87_ASSEMBLER_X87_H_ -#define V8_X87_ASSEMBLER_X87_H_ - -#include - -#include "src/assembler.h" -#include "src/isolate.h" -#include "src/utils.h" - -namespace v8 { -namespace internal { - -#define GENERAL_REGISTERS(V) \ - V(eax) \ - V(ecx) \ - V(edx) \ - V(ebx) \ - V(esp) \ - V(ebp) \ - V(esi) \ - V(edi) - -#define ALLOCATABLE_GENERAL_REGISTERS(V) \ - V(eax) \ - V(ecx) \ - V(edx) \ - V(ebx) \ - V(esi) \ - V(edi) - -#define DOUBLE_REGISTERS(V) \ - V(stX_0) \ - V(stX_1) \ - V(stX_2) \ - V(stX_3) \ - V(stX_4) \ - V(stX_5) \ - V(stX_6) \ - V(stX_7) - -#define FLOAT_REGISTERS DOUBLE_REGISTERS -#define SIMD128_REGISTERS DOUBLE_REGISTERS - -#define ALLOCATABLE_DOUBLE_REGISTERS(V) \ - V(stX_0) \ - V(stX_1) \ - V(stX_2) \ - V(stX_3) \ - V(stX_4) \ - V(stX_5) - -// CPU Registers. -// -// 1) We would prefer to use an enum, but enum values are assignment- -// compatible with int, which has caused code-generation bugs. -// -// 2) We would prefer to use a class instead of a struct but we don't like -// the register initialization to depend on the particular initialization -// order (which appears to be different on OS X, Linux, and Windows for the -// installed versions of C++ we tried). Using a struct permits C-style -// "initialization". Also, the Register objects cannot be const as this -// forces initialization stubs in MSVC, making us dependent on initialization -// order. -// -// 3) By not using an enum, we are possibly preventing the compiler from -// doing certain constant folds, which may significantly reduce the -// code generated for some assembly instructions (because they boil down -// to a few constants). If this is a problem, we could change the code -// such that we use an enum in optimized mode, and the struct in debug -// mode. This way we get the compile-time error checking in debug mode -// and best performance in optimized code. -// -struct Register { - enum Code { -#define REGISTER_CODE(R) kCode_##R, - GENERAL_REGISTERS(REGISTER_CODE) -#undef REGISTER_CODE - kAfterLast, - kCode_no_reg = -1 - }; - - static const int kNumRegisters = Code::kAfterLast; - - static Register from_code(int code) { - DCHECK(code >= 0); - DCHECK(code < kNumRegisters); - Register r = {code}; - return r; - } - bool is_valid() const { return 0 <= reg_code && reg_code < kNumRegisters; } - bool is(Register reg) const { return reg_code == reg.reg_code; } - int code() const { - DCHECK(is_valid()); - return reg_code; - } - int bit() const { - DCHECK(is_valid()); - return 1 << reg_code; - } - - bool is_byte_register() const { return reg_code <= 3; } - - // Unfortunately we can't make this private in a struct. - int reg_code; -}; - - -#define DECLARE_REGISTER(R) const Register R = {Register::kCode_##R}; -GENERAL_REGISTERS(DECLARE_REGISTER) -#undef DECLARE_REGISTER -const Register no_reg = {Register::kCode_no_reg}; - -static const bool kSimpleFPAliasing = true; -static const bool kSimdMaskRegisters = false; - -struct X87Register { - enum Code { -#define REGISTER_CODE(R) kCode_##R, - DOUBLE_REGISTERS(REGISTER_CODE) -#undef REGISTER_CODE - kAfterLast, - kCode_no_reg = -1 - }; - - static const int kMaxNumRegisters = Code::kAfterLast; - static const int kMaxNumAllocatableRegisters = 6; - - static X87Register from_code(int code) { - X87Register result = {code}; - return result; - } - - bool is_valid() const { return 0 <= reg_code && reg_code < kMaxNumRegisters; } - - int code() const { - DCHECK(is_valid()); - return reg_code; - } - - bool is(X87Register reg) const { return reg_code == reg.reg_code; } - - int reg_code; -}; - -typedef X87Register FloatRegister; - -typedef X87Register DoubleRegister; - -// TODO(x87) Define SIMD registers. -typedef X87Register Simd128Register; - -#define DECLARE_REGISTER(R) \ - const DoubleRegister R = {DoubleRegister::kCode_##R}; -DOUBLE_REGISTERS(DECLARE_REGISTER) -#undef DECLARE_REGISTER -const DoubleRegister no_double_reg = {DoubleRegister::kCode_no_reg}; - -enum Condition { - // any value < 0 is considered no_condition - no_condition = -1, - - overflow = 0, - no_overflow = 1, - below = 2, - above_equal = 3, - equal = 4, - not_equal = 5, - below_equal = 6, - above = 7, - negative = 8, - positive = 9, - parity_even = 10, - parity_odd = 11, - less = 12, - greater_equal = 13, - less_equal = 14, - greater = 15, - - // aliases - carry = below, - not_carry = above_equal, - zero = equal, - not_zero = not_equal, - sign = negative, - not_sign = positive -}; - - -// Returns the equivalent of !cc. -// Negation of the default no_condition (-1) results in a non-default -// no_condition value (-2). As long as tests for no_condition check -// for condition < 0, this will work as expected. -inline Condition NegateCondition(Condition cc) { - return static_cast(cc ^ 1); -} - - -// Commute a condition such that {a cond b == b cond' a}. -inline Condition CommuteCondition(Condition cc) { - switch (cc) { - case below: - return above; - case above: - return below; - case above_equal: - return below_equal; - case below_equal: - return above_equal; - case less: - return greater; - case greater: - return less; - case greater_equal: - return less_equal; - case less_equal: - return greater_equal; - default: - return cc; - } -} - - -enum RoundingMode { - kRoundToNearest = 0x0, - kRoundDown = 0x1, - kRoundUp = 0x2, - kRoundToZero = 0x3 -}; - - -// ----------------------------------------------------------------------------- -// Machine instruction Immediates - -class Immediate BASE_EMBEDDED { - public: - inline explicit Immediate(int x); - inline explicit Immediate(const ExternalReference& ext); - inline explicit Immediate(Handle handle); - inline explicit Immediate(Smi* value); - inline explicit Immediate(Address addr); - inline explicit Immediate(Address x, RelocInfo::Mode rmode); - - static Immediate CodeRelativeOffset(Label* label) { - return Immediate(label); - } - - bool is_zero() const { return x_ == 0 && RelocInfo::IsNone(rmode_); } - bool is_int8() const { - return -128 <= x_ && x_ < 128 && RelocInfo::IsNone(rmode_); - } - bool is_uint8() const { - return v8::internal::is_uint8(x_) && RelocInfo::IsNone(rmode_); - } - bool is_int16() const { - return -32768 <= x_ && x_ < 32768 && RelocInfo::IsNone(rmode_); - } - bool is_uint16() const { - return v8::internal::is_uint16(x_) && RelocInfo::IsNone(rmode_); - } - - private: - inline explicit Immediate(Label* value); - - int x_; - RelocInfo::Mode rmode_; - - friend class Operand; - friend class Assembler; - friend class MacroAssembler; -}; - - -// ----------------------------------------------------------------------------- -// Machine instruction Operands - -enum ScaleFactor { - times_1 = 0, - times_2 = 1, - times_4 = 2, - times_8 = 3, - times_int_size = times_4, - times_half_pointer_size = times_2, - times_pointer_size = times_4, - times_twice_pointer_size = times_8 -}; - - -class Operand BASE_EMBEDDED { - public: - // reg - INLINE(explicit Operand(Register reg)); - - // [disp/r] - INLINE(explicit Operand(int32_t disp, RelocInfo::Mode rmode)); - - // [disp/r] - INLINE(explicit Operand(Immediate imm)); - - // [base + disp/r] - explicit Operand(Register base, int32_t disp, - RelocInfo::Mode rmode = RelocInfo::NONE32); - - // [base + index*scale + disp/r] - explicit Operand(Register base, - Register index, - ScaleFactor scale, - int32_t disp, - RelocInfo::Mode rmode = RelocInfo::NONE32); - - // [index*scale + disp/r] - explicit Operand(Register index, - ScaleFactor scale, - int32_t disp, - RelocInfo::Mode rmode = RelocInfo::NONE32); - - static Operand JumpTable(Register index, ScaleFactor scale, Label* table) { - return Operand(index, scale, reinterpret_cast(table), - RelocInfo::INTERNAL_REFERENCE); - } - - static Operand StaticVariable(const ExternalReference& ext) { - return Operand(reinterpret_cast(ext.address()), - RelocInfo::EXTERNAL_REFERENCE); - } - - static Operand StaticArray(Register index, - ScaleFactor scale, - const ExternalReference& arr) { - return Operand(index, scale, reinterpret_cast(arr.address()), - RelocInfo::EXTERNAL_REFERENCE); - } - - static Operand ForCell(Handle cell) { - AllowDeferredHandleDereference embedding_raw_address; - return Operand(reinterpret_cast(cell.location()), - RelocInfo::CELL); - } - - static Operand ForRegisterPlusImmediate(Register base, Immediate imm) { - return Operand(base, imm.x_, imm.rmode_); - } - - // Returns true if this Operand is a wrapper for the specified register. - bool is_reg(Register reg) const; - - // Returns true if this Operand is a wrapper for one register. - bool is_reg_only() const; - - // Asserts that this Operand is a wrapper for one register and returns the - // register. - Register reg() const; - - private: - // Set the ModRM byte without an encoded 'reg' register. The - // register is encoded later as part of the emit_operand operation. - inline void set_modrm(int mod, Register rm); - - inline void set_sib(ScaleFactor scale, Register index, Register base); - inline void set_disp8(int8_t disp); - inline void set_dispr(int32_t disp, RelocInfo::Mode rmode); - - byte buf_[6]; - // The number of bytes in buf_. - unsigned int len_; - // Only valid if len_ > 4. - RelocInfo::Mode rmode_; - - friend class Assembler; - friend class MacroAssembler; -}; - - -// ----------------------------------------------------------------------------- -// 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, CODE_ABSOLUTE }; - - 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); } - - void print() { - PrintF("%s (%x) ", (type() == UNCONDITIONAL_JUMP ? "jmp" : "[other]"), - NextField::decode(data_)); - } - - private: - int data_; - - class TypeField: public BitField {}; - class NextField: public BitField {}; - - void init(Label* L, Type type); -}; - - -class Assembler : public AssemblerBase { - private: - // We check before assembling an instruction that there is sufficient - // space to write an instruction and its relocation information. - // The relocation writer's position must be kGap bytes above the end of - // the generated instructions. This leaves enough space for the - // longest possible ia32 instruction, 15 bytes, and the longest possible - // relocation information encoding, RelocInfoWriter::kMaxLength == 16. - // (There is a 15 byte limit on ia32 instruction length that rules out some - // otherwise valid instructions.) - // This allows for a single, fast space check per instruction. - static const int kGap = 32; - - public: - // Create an assembler. Instructions and relocation information are emitted - // into a buffer, with the instructions starting from the beginning and the - // relocation information starting from the end of the buffer. See CodeDesc - // for a detailed comment on the layout (globals.h). - // - // If the provided buffer is NULL, the assembler allocates and grows its own - // buffer, and buffer_size determines the initial buffer size. The buffer is - // owned by the assembler and deallocated upon destruction of the assembler. - // - // If the provided buffer is not NULL, the assembler uses the provided buffer - // for code generation and assumes its size to be buffer_size. If the buffer - // is too small, a fatal error occurs. No deallocation of the buffer is done - // upon destruction of the assembler. - Assembler(Isolate* isolate, void* buffer, int buffer_size) - : Assembler(IsolateData(isolate), buffer, buffer_size) {} - Assembler(IsolateData isolate_data, void* buffer, int buffer_size); - virtual ~Assembler() { } - - // GetCode emits any pending (non-emitted) code and fills the descriptor - // desc. GetCode() is idempotent; it returns the same result if no other - // Assembler functions are invoked in between GetCode() calls. - void GetCode(CodeDesc* desc); - - // Read/Modify the code target in the branch/call instruction at pc. - // The isolate argument is unused (and may be nullptr) when skipping flushing. - inline static Address target_address_at(Address pc, Address constant_pool); - inline static void set_target_address_at( - Isolate* isolate, Address pc, Address constant_pool, Address target, - ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED); - static inline Address target_address_at(Address pc, Code* code); - static inline void set_target_address_at( - Isolate* isolate, Address pc, Code* code, Address target, - ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED); - - // Return the code target address at a call site from the return address - // of that call in the instruction stream. - inline static Address target_address_from_return_address(Address pc); - - // This sets the branch destination (which is in the instruction on x86). - // This is for calls and branches within generated code. - inline static void deserialization_set_special_target_at( - Isolate* isolate, Address instruction_payload, Code* code, - Address target) { - set_target_address_at(isolate, instruction_payload, code, target); - } - - // This sets the internal reference at the pc. - inline static void deserialization_set_target_internal_reference_at( - Isolate* isolate, Address pc, Address target, - RelocInfo::Mode mode = RelocInfo::INTERNAL_REFERENCE); - - static const int kSpecialTargetSize = kPointerSize; - - // Distance between the address of the code target in the call instruction - // and the return address - static const int kCallTargetAddressOffset = kPointerSize; - - static const int kCallInstructionLength = 5; - - // The debug break slot must be able to contain a call instruction. - static const int kDebugBreakSlotLength = kCallInstructionLength; - - // Distance between start of patched debug break slot and the emitted address - // to jump to. - static const int kPatchDebugBreakSlotAddressOffset = 1; // JMP imm32. - - // One byte opcode for test al, 0xXX. - static const byte kTestAlByte = 0xA8; - // One byte opcode for nop. - static const byte kNopByte = 0x90; - - // One byte opcode for a short unconditional jump. - static const byte kJmpShortOpcode = 0xEB; - // One byte prefix for a short conditional jump. - static const byte kJccShortPrefix = 0x70; - static const byte kJncShortOpcode = kJccShortPrefix | not_carry; - static const byte kJcShortOpcode = kJccShortPrefix | carry; - static const byte kJnzShortOpcode = kJccShortPrefix | not_zero; - static const byte kJzShortOpcode = kJccShortPrefix | zero; - - - // --------------------------------------------------------------------------- - // 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. - - // Insert the smallest number of nop instructions - // possible to align the pc offset to a multiple - // of m. m must be a power of 2. - void Align(int m); - // Insert the smallest number of zero bytes possible to align the pc offset - // to a mulitple of m. m must be a power of 2 (>= 2). - void DataAlign(int m); - void Nop(int bytes = 1); - // Aligns code to something that's optimal for a jump target for the platform. - void CodeTargetAlign(); - - // Stack - void pushad(); - void popad(); - - void pushfd(); - void popfd(); - - void push(const Immediate& x); - void push_imm32(int32_t imm32); - void push(Register src); - void push(const Operand& src); - - void pop(Register dst); - void pop(const Operand& dst); - - void enter(const Immediate& size); - void leave(); - - // Moves - void mov_b(Register dst, Register src) { mov_b(dst, Operand(src)); } - void mov_b(Register dst, const Operand& src); - void mov_b(Register dst, int8_t imm8) { mov_b(Operand(dst), imm8); } - void mov_b(const Operand& dst, int8_t imm8); - void mov_b(const Operand& dst, const Immediate& src); - 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_w(const Operand& dst, int16_t imm16); - void mov_w(const Operand& dst, const Immediate& src); - - - void mov(Register dst, int32_t imm32); - void mov(Register dst, const Immediate& x); - void mov(Register dst, Handle 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 handle); - void mov(const Operand& dst, Register src); - - void movsx_b(Register dst, Register src) { movsx_b(dst, Operand(src)); } - void movsx_b(Register dst, const Operand& src); - - void movsx_w(Register dst, Register src) { movsx_w(dst, Operand(src)); } - void movsx_w(Register dst, const Operand& src); - - void movzx_b(Register dst, Register src) { movzx_b(dst, Operand(src)); } - void movzx_b(Register dst, const Operand& src); - - void movzx_w(Register dst, Register src) { movzx_w(dst, Operand(src)); } - void movzx_w(Register dst, const Operand& src); - - // Flag management. - void cld(); - - // Repetitive string instructions. - void rep_movs(); - void rep_stos(); - void stos(); - - // Exchange - void xchg(Register dst, Register src); - void xchg(Register dst, const Operand& src); - void xchg_b(Register reg, const Operand& op); - void xchg_w(Register reg, const Operand& op); - - // Lock prefix - void lock(); - - // CompareExchange - void cmpxchg(const Operand& dst, Register src); - void cmpxchg_b(const Operand& dst, Register src); - void cmpxchg_w(const Operand& dst, Register src); - - // Arithmetics - void adc(Register dst, int32_t imm32); - void adc(Register dst, const Operand& src); - - void add(Register dst, Register src) { add(dst, Operand(src)); } - void add(Register dst, const Operand& src); - void add(const Operand& dst, Register src); - void add(Register dst, const Immediate& imm) { add(Operand(dst), imm); } - void add(const Operand& dst, const Immediate& x); - - void and_(Register dst, int32_t imm32); - void and_(Register dst, const Immediate& x); - void and_(Register dst, Register src) { and_(dst, Operand(src)); } - void and_(Register dst, const Operand& src); - void and_(const Operand& dst, Register src); - void and_(const Operand& dst, const Immediate& x); - - void cmpb(Register reg, Immediate imm8) { cmpb(Operand(reg), imm8); } - void cmpb(const Operand& op, Immediate imm8); - void cmpb(Register reg, const Operand& op); - void cmpb(const Operand& op, Register reg); - void cmpb(Register dst, Register src) { cmpb(Operand(dst), src); } - void cmpb_al(const Operand& op); - void cmpw_ax(const Operand& op); - void cmpw(const Operand& dst, Immediate src); - void cmpw(Register dst, Immediate src) { cmpw(Operand(dst), src); } - void cmpw(Register dst, const Operand& src); - void cmpw(Register dst, Register src) { cmpw(Operand(dst), src); } - void cmpw(const Operand& dst, Register src); - void cmp(Register reg, int32_t imm32); - void cmp(Register reg, Handle handle); - void cmp(Register reg0, Register reg1) { cmp(reg0, Operand(reg1)); } - void cmp(Register reg, const Operand& op); - void cmp(Register reg, const Immediate& imm) { cmp(Operand(reg), imm); } - void cmp(const Operand& op, Register reg); - void cmp(const Operand& op, const Immediate& imm); - void cmp(const Operand& op, Handle handle); - - void dec_b(Register dst); - void dec_b(const Operand& dst); - - void dec(Register dst); - void dec(const Operand& dst); - - void cdq(); - - void idiv(Register src) { idiv(Operand(src)); } - void idiv(const Operand& src); - void div(Register src) { div(Operand(src)); } - void div(const Operand& src); - - // Signed multiply instructions. - void imul(Register src); // edx:eax = eax * src. - void imul(Register dst, Register src) { imul(dst, Operand(src)); } - void imul(Register dst, const Operand& src); // dst = dst * src. - void imul(Register dst, Register src, int32_t imm32); // dst = src * imm32. - void imul(Register dst, const Operand& src, int32_t imm32); - - void inc(Register dst); - void inc(const Operand& dst); - - void lea(Register dst, const Operand& src); - - // Unsigned multiply instruction. - void mul(Register src); // edx:eax = eax * reg. - - void neg(Register dst); - void neg(const Operand& dst); - - void not_(Register dst); - void not_(const Operand& dst); - - void or_(Register dst, int32_t imm32); - void or_(Register dst, Register src) { or_(dst, Operand(src)); } - void or_(Register dst, const Operand& src); - void or_(const Operand& dst, Register src); - void or_(Register dst, const Immediate& imm) { or_(Operand(dst), imm); } - void or_(const Operand& dst, const Immediate& x); - - void rcl(Register dst, uint8_t imm8); - void rcr(Register dst, uint8_t imm8); - - void ror(Register dst, uint8_t imm8) { ror(Operand(dst), imm8); } - void ror(const Operand& dst, uint8_t imm8); - void ror_cl(Register dst) { ror_cl(Operand(dst)); } - void ror_cl(const Operand& dst); - - void sar(Register dst, uint8_t imm8) { sar(Operand(dst), imm8); } - void sar(const Operand& dst, uint8_t imm8); - void sar_cl(Register dst) { sar_cl(Operand(dst)); } - void sar_cl(const Operand& dst); - - void sbb(Register dst, const Operand& src); - - void shl(Register dst, uint8_t imm8) { shl(Operand(dst), imm8); } - void shl(const Operand& dst, uint8_t imm8); - void shl_cl(Register dst) { shl_cl(Operand(dst)); } - void shl_cl(const Operand& dst); - void shld(Register dst, Register src, uint8_t shift); - void shld_cl(Register dst, Register src); - - void shr(Register dst, uint8_t imm8) { shr(Operand(dst), imm8); } - void shr(const Operand& dst, uint8_t imm8); - void shr_cl(Register dst) { shr_cl(Operand(dst)); } - void shr_cl(const Operand& dst); - void shrd(Register dst, Register src, uint8_t shift); - void shrd_cl(Register dst, Register src) { shrd_cl(Operand(dst), src); } - void shrd_cl(const Operand& dst, Register src); - - void sub(Register dst, const Immediate& imm) { sub(Operand(dst), imm); } - void sub(const Operand& dst, const Immediate& x); - void sub(Register dst, Register src) { sub(dst, Operand(src)); } - void sub(Register dst, const Operand& src); - void sub(const Operand& dst, Register src); - - void test(Register reg, const Immediate& imm); - void test(Register reg0, Register reg1) { test(reg0, Operand(reg1)); } - void test(Register reg, const Operand& op); - void test(const Operand& op, const Immediate& imm); - void test(const Operand& op, Register reg) { test(reg, op); } - void test_b(Register reg, const Operand& op); - void test_b(Register reg, Immediate imm8); - void test_b(const Operand& op, Immediate imm8); - void test_b(const Operand& op, Register reg) { test_b(reg, op); } - void test_b(Register dst, Register src) { test_b(dst, Operand(src)); } - void test_w(Register reg, const Operand& op); - void test_w(Register reg, Immediate imm16); - void test_w(const Operand& op, Immediate imm16); - void test_w(const Operand& op, Register reg) { test_w(reg, op); } - void test_w(Register dst, Register src) { test_w(dst, Operand(src)); } - - void xor_(Register dst, int32_t imm32); - void xor_(Register dst, Register src) { xor_(dst, Operand(src)); } - void xor_(Register dst, const Operand& src); - void xor_(const Operand& dst, Register src); - void xor_(Register dst, const Immediate& imm) { xor_(Operand(dst), imm); } - void xor_(const Operand& dst, const Immediate& x); - - // Bit operations. - void bt(const Operand& dst, Register src); - void bts(Register dst, Register src) { bts(Operand(dst), src); } - void bts(const Operand& dst, Register src); - void bsr(Register dst, Register src) { bsr(dst, Operand(src)); } - void bsr(Register dst, const Operand& src); - void bsf(Register dst, Register src) { bsf(dst, Operand(src)); } - void bsf(Register dst, const Operand& src); - - // Miscellaneous - void hlt(); - void int3(); - void nop(); - void ret(int imm16); - void ud2(); - - // Label operations & relative jumps (PPUM Appendix D) - // - // Takes a branch opcode (cc) and a label (L) and generates - // either a backward branch or a forward branch and links it - // to the label fixup chain. Usage: - // - // Label L; // unbound label - // j(cc, &L); // forward branch to unbound label - // bind(&L); // bind label to the current pc - // j(cc, &L); // backward branch to bound label - // bind(&L); // illegal: a label may be bound only once - // - // Note: The same Label can be used for forward and backward branches - // but it may be bound only once. - - void bind(Label* L); // binds an unbound label L to the current code position - - // Calls - void call(Label* L); - void call(byte* entry, RelocInfo::Mode rmode); - int CallSize(const Operand& adr); - void call(Register reg) { call(Operand(reg)); } - void call(const Operand& adr); - int CallSize(Handle code, RelocInfo::Mode mode); - void call(Handle code, - RelocInfo::Mode rmode, - TypeFeedbackId id = TypeFeedbackId::None()); - - // Jumps - // unconditional jump to L - void jmp(Label* L, Label::Distance distance = Label::kFar); - void jmp(byte* entry, RelocInfo::Mode rmode); - void jmp(Register reg) { jmp(Operand(reg)); } - void jmp(const Operand& adr); - void jmp(Handle code, RelocInfo::Mode rmode); - - // Conditional jumps - void j(Condition cc, - Label* L, - Label::Distance distance = Label::kFar); - void j(Condition cc, byte* entry, RelocInfo::Mode rmode); - void j(Condition cc, Handle code, - RelocInfo::Mode rmode = RelocInfo::CODE_TARGET); - - // Floating-point operations - void fld(int i); - void fstp(int i); - - void fld1(); - void fldz(); - void fldpi(); - void fldln2(); - - void fld_s(const Operand& adr); - void fld_d(const Operand& adr); - - void fstp_s(const Operand& adr); - void fst_s(const Operand& adr); - void fstp_d(const Operand& adr); - void fst_d(const Operand& adr); - - void fild_s(const Operand& adr); - void fild_d(const Operand& adr); - - void fist_s(const Operand& adr); - - void fistp_s(const Operand& adr); - void fistp_d(const Operand& adr); - - // The fisttp instructions require SSE3. - void fisttp_s(const Operand& adr); - void fisttp_d(const Operand& adr); - - void fabs(); - void fchs(); - void fsqrt(); - void fcos(); - void fsin(); - void fptan(); - void fyl2x(); - void f2xm1(); - void fscale(); - void fninit(); - - void fadd(int i); - void fadd_i(int i); - void fadd_d(const Operand& adr); - void fsub(int i); - void fsub_i(int i); - void fsub_d(const Operand& adr); - void fsubr_d(const Operand& adr); - void fmul(int i); - void fmul_d(const Operand& adr); - void fmul_i(int i); - void fdiv(int i); - void fdiv_d(const Operand& adr); - void fdivr_d(const Operand& adr); - void fdiv_i(int i); - - void fisub_s(const Operand& adr); - - void faddp(int i = 1); - void fsubp(int i = 1); - void fsubr(int i = 1); - void fsubrp(int i = 1); - void fmulp(int i = 1); - void fdivp(int i = 1); - void fprem(); - void fprem1(); - - void fxch(int i = 1); - void fincstp(); - void ffree(int i = 0); - - void ftst(); - void fxam(); - void fucomp(int i); - void fucompp(); - void fucomi(int i); - void fucomip(); - void fcompp(); - void fnstsw_ax(); - void fldcw(const Operand& adr); - void fnstcw(const Operand& adr); - void fwait(); - void fnclex(); - void fnsave(const Operand& adr); - void frstor(const Operand& adr); - - void frndint(); - - void sahf(); - void setcc(Condition cc, Register reg); - - void cpuid(); - - // TODO(lrn): Need SFENCE for movnt? - - // Check the code size generated from label to here. - int SizeOfCodeGeneratedSince(Label* label) { - return pc_offset() - label->pos(); - } - - // Mark address of a debug break slot. - void RecordDebugBreakSlot(RelocInfo::Mode mode); - - // Record a comment relocation entry that can be used by a disassembler. - // Use --code-comments to enable. - void RecordComment(const char* msg); - - // Record a deoptimization reason that can be used by a log or cpu profiler. - // Use --trace-deopt to enable. - void RecordDeoptReason(DeoptimizeReason reason, SourcePosition position, - int id); - - // Writes a single byte or word of data in the code stream. Used for - // inline tables, e.g., jump-tables. - void db(uint8_t data); - void dd(uint32_t data); - void dq(uint64_t data); - void dp(uintptr_t data) { dd(data); } - void dd(Label* label); - - // Check if there is less than kGap bytes available in the buffer. - // If this is the case, we need to grow the buffer before emitting - // an instruction or relocation information. - inline bool buffer_overflow() const { - return pc_ >= reloc_info_writer.pos() - kGap; - } - - // Get the number of bytes available in the buffer. - inline int available_space() const { return reloc_info_writer.pos() - pc_; } - - static bool IsNop(Address addr); - - int relocation_writer_size() { - return (buffer_ + buffer_size_) - reloc_info_writer.pos(); - } - - // Avoid overflows for displacements etc. - static const int kMaximalBufferSize = 512*MB; - - byte byte_at(int pos) { return buffer_[pos]; } - void set_byte_at(int pos, byte value) { buffer_[pos] = value; } - - void PatchConstantPoolAccessInstruction(int pc_offset, int offset, - ConstantPoolEntry::Access access, - ConstantPoolEntry::Type type) { - // No embedded constant pool support. - UNREACHABLE(); - } - - protected: - byte* addr_at(int pos) { return buffer_ + pos; } - - - private: - uint32_t long_at(int pos) { - return *reinterpret_cast(addr_at(pos)); - } - void long_at_put(int pos, uint32_t x) { - *reinterpret_cast(addr_at(pos)) = x; - } - - // code emission - void GrowBuffer(); - inline void emit(uint32_t x); - inline void emit(Handle handle); - inline void emit(uint32_t x, - RelocInfo::Mode rmode, - TypeFeedbackId id = TypeFeedbackId::None()); - inline void emit(Handle code, - RelocInfo::Mode rmode, - TypeFeedbackId id = TypeFeedbackId::None()); - inline void emit(const Immediate& x); - inline void emit_b(Immediate x); - inline void emit_w(const Immediate& x); - inline void emit_q(uint64_t x); - - // Emit the code-object-relative offset of the label's position - inline void emit_code_relative_offset(Label* label); - - // instruction generation - void emit_arith_b(int op1, int op2, Register dst, int imm8); - - // 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); - - void emit_operand(Register reg, const Operand& adr); - - void emit_label(Label* label); - - void emit_farith(int b1, int b2, int i); - - // labels - void print(Label* L); - void bind_to(Label* L, int pos); - - // 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); - inline void emit_near_disp(Label* L); - - // record reloc info for current pc_ - void RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data = 0); - - friend class CodePatcher; - friend class EnsureSpace; - - // Internal reference positions, required for (potential) patching in - // GrowBuffer(); contains only those internal references whose labels - // are already bound. - std::deque internal_reference_positions_; - - // code generation - RelocInfoWriter reloc_info_writer; -}; - - -// Helper class that ensures that there is enough space for generating -// instructions and relocation information. The constructor makes -// sure that there is enough space and (in debug mode) the destructor -// checks that we did not generate too much. -class EnsureSpace BASE_EMBEDDED { - public: - explicit EnsureSpace(Assembler* assembler) : assembler_(assembler) { - if (assembler_->buffer_overflow()) assembler_->GrowBuffer(); -#ifdef DEBUG - space_before_ = assembler_->available_space(); -#endif - } - -#ifdef DEBUG - ~EnsureSpace() { - int bytes_generated = space_before_ - assembler_->available_space(); - DCHECK(bytes_generated < assembler_->kGap); - } -#endif - - private: - Assembler* assembler_; -#ifdef DEBUG - int space_before_; -#endif -}; - -} // namespace internal -} // namespace v8 - -#endif // V8_X87_ASSEMBLER_X87_H_ diff --git a/src/x87/code-stubs-x87.cc b/src/x87/code-stubs-x87.cc deleted file mode 100644 index 5acc215c08..0000000000 --- a/src/x87/code-stubs-x87.cc +++ /dev/null @@ -1,3428 +0,0 @@ -// 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_X87 - -#include "src/code-stubs.h" -#include "src/api-arguments.h" -#include "src/base/bits.h" -#include "src/bootstrapper.h" -#include "src/codegen.h" -#include "src/ic/handler-compiler.h" -#include "src/ic/ic.h" -#include "src/ic/stub-cache.h" -#include "src/isolate.h" -#include "src/regexp/jsregexp.h" -#include "src/regexp/regexp-macro-assembler.h" -#include "src/runtime/runtime.h" -#include "src/x87/code-stubs-x87.h" -#include "src/x87/frames-x87.h" - -namespace v8 { -namespace internal { - -#define __ ACCESS_MASM(masm) - -void ArrayNArgumentsConstructorStub::Generate(MacroAssembler* masm) { - __ pop(ecx); - __ mov(MemOperand(esp, eax, times_4, 0), edi); - __ push(edi); - __ push(ebx); - __ push(ecx); - __ add(eax, Immediate(3)); - __ TailCallRuntime(Runtime::kNewArray); -} - - -void StoreBufferOverflowStub::Generate(MacroAssembler* masm) { - // We don't allow a GC during a store buffer overflow so there is no need to - // store the registers in any particular way, but we do have to store and - // restore them. - __ pushad(); - if (save_doubles()) { - // Save FPU stat in m108byte. - __ sub(esp, Immediate(108)); - __ fnsave(Operand(esp, 0)); - } - const int argument_count = 1; - - AllowExternalCallThatCantCauseGC scope(masm); - __ PrepareCallCFunction(argument_count, ecx); - __ mov(Operand(esp, 0 * kPointerSize), - Immediate(ExternalReference::isolate_address(isolate()))); - __ CallCFunction( - ExternalReference::store_buffer_overflow_function(isolate()), - argument_count); - if (save_doubles()) { - // Restore FPU stat in m108byte. - __ frstor(Operand(esp, 0)); - __ add(esp, Immediate(108)); - } - __ popad(); - __ ret(0); -} - - -class FloatingPointHelper : public AllStatic { - public: - enum ArgLocation { - ARGS_ON_STACK, - ARGS_IN_REGISTERS - }; - - // Code pattern for loading a floating point value. Input value must - // be either a smi or a heap number object (fp value). Requirements: - // operand in register number. Returns operand as floating point number - // on FPU stack. - static void LoadFloatOperand(MacroAssembler* masm, Register number); - - // Test if operands are smi or number objects (fp). Requirements: - // operand_1 in eax, operand_2 in edx; falls through on float - // operands, jumps to the non_float label otherwise. - static void CheckFloatOperands(MacroAssembler* masm, - Label* non_float, - Register scratch); -}; - - -void DoubleToIStub::Generate(MacroAssembler* masm) { - Register input_reg = this->source(); - Register final_result_reg = this->destination(); - DCHECK(is_truncating()); - - Label check_negative, process_64_bits, done, done_no_stash; - - int double_offset = offset(); - - // Account for return address and saved regs if input is esp. - if (input_reg.is(esp)) double_offset += 3 * kPointerSize; - - MemOperand mantissa_operand(MemOperand(input_reg, double_offset)); - MemOperand exponent_operand(MemOperand(input_reg, - double_offset + kDoubleSize / 2)); - - Register scratch1; - { - Register scratch_candidates[3] = { ebx, edx, edi }; - for (int i = 0; i < 3; i++) { - scratch1 = scratch_candidates[i]; - if (!final_result_reg.is(scratch1) && !input_reg.is(scratch1)) break; - } - } - // Since we must use ecx for shifts below, use some other register (eax) - // to calculate the result if ecx is the requested return register. - Register result_reg = final_result_reg.is(ecx) ? eax : final_result_reg; - // Save ecx if it isn't the return register and therefore volatile, or if it - // is the return register, then save the temp register we use in its stead for - // the result. - Register save_reg = final_result_reg.is(ecx) ? eax : ecx; - __ push(scratch1); - __ push(save_reg); - - bool stash_exponent_copy = !input_reg.is(esp); - __ mov(scratch1, mantissa_operand); - __ mov(ecx, exponent_operand); - if (stash_exponent_copy) __ push(ecx); - - __ and_(ecx, HeapNumber::kExponentMask); - __ shr(ecx, HeapNumber::kExponentShift); - __ lea(result_reg, MemOperand(ecx, -HeapNumber::kExponentBias)); - __ cmp(result_reg, Immediate(HeapNumber::kMantissaBits)); - __ j(below, &process_64_bits); - - // Result is entirely in lower 32-bits of mantissa - int delta = HeapNumber::kExponentBias + Double::kPhysicalSignificandSize; - __ sub(ecx, Immediate(delta)); - __ xor_(result_reg, result_reg); - __ cmp(ecx, Immediate(31)); - __ j(above, &done); - __ shl_cl(scratch1); - __ jmp(&check_negative); - - __ bind(&process_64_bits); - // Result must be extracted from shifted 32-bit mantissa - __ sub(ecx, Immediate(delta)); - __ neg(ecx); - if (stash_exponent_copy) { - __ mov(result_reg, MemOperand(esp, 0)); - } else { - __ mov(result_reg, exponent_operand); - } - __ and_(result_reg, - Immediate(static_cast(Double::kSignificandMask >> 32))); - __ add(result_reg, - Immediate(static_cast(Double::kHiddenBit >> 32))); - __ shrd_cl(scratch1, result_reg); - __ shr_cl(result_reg); - __ test(ecx, Immediate(32)); - { - Label skip_mov; - __ j(equal, &skip_mov, Label::kNear); - __ mov(scratch1, result_reg); - __ bind(&skip_mov); - } - - // If the double was negative, negate the integer result. - __ bind(&check_negative); - __ mov(result_reg, scratch1); - __ neg(result_reg); - if (stash_exponent_copy) { - __ cmp(MemOperand(esp, 0), Immediate(0)); - } else { - __ cmp(exponent_operand, Immediate(0)); - } - { - Label skip_mov; - __ j(less_equal, &skip_mov, Label::kNear); - __ mov(result_reg, scratch1); - __ bind(&skip_mov); - } - - // Restore registers - __ bind(&done); - if (stash_exponent_copy) { - __ add(esp, Immediate(kDoubleSize / 2)); - } - __ bind(&done_no_stash); - if (!final_result_reg.is(result_reg)) { - DCHECK(final_result_reg.is(ecx)); - __ mov(final_result_reg, result_reg); - } - __ pop(save_reg); - __ pop(scratch1); - __ ret(0); -} - - -void FloatingPointHelper::LoadFloatOperand(MacroAssembler* masm, - Register number) { - Label load_smi, done; - - __ JumpIfSmi(number, &load_smi, Label::kNear); - __ fld_d(FieldOperand(number, HeapNumber::kValueOffset)); - __ jmp(&done, Label::kNear); - - __ bind(&load_smi); - __ SmiUntag(number); - __ push(number); - __ fild_s(Operand(esp, 0)); - __ pop(number); - - __ bind(&done); -} - - -void FloatingPointHelper::CheckFloatOperands(MacroAssembler* masm, - Label* non_float, - Register scratch) { - Label test_other, done; - // Test if both operands are floats or smi -> scratch=k_is_float; - // Otherwise scratch = k_not_float. - __ JumpIfSmi(edx, &test_other, Label::kNear); - __ mov(scratch, FieldOperand(edx, HeapObject::kMapOffset)); - Factory* factory = masm->isolate()->factory(); - __ cmp(scratch, factory->heap_number_map()); - __ j(not_equal, non_float); // argument in edx is not a number -> NaN - - __ bind(&test_other); - __ JumpIfSmi(eax, &done, Label::kNear); - __ mov(scratch, FieldOperand(eax, HeapObject::kMapOffset)); - __ cmp(scratch, factory->heap_number_map()); - __ j(not_equal, non_float); // argument in eax is not a number -> NaN - - // Fall-through: Both operands are numbers. - __ bind(&done); -} - - -void MathPowStub::Generate(MacroAssembler* masm) { - const Register scratch = ecx; - - // Load the double_exponent into x87 FPU - __ fld_d(Operand(esp, 0 * kDoubleSize + 4)); - // Load the double_base into x87 FPU - __ fld_d(Operand(esp, 1 * kDoubleSize + 4)); - - // Call ieee754 runtime directly. - { - AllowExternalCallThatCantCauseGC scope(masm); - __ PrepareCallCFunction(4, scratch); - // Put the double_base parameter in call stack - __ fstp_d(Operand(esp, 0 * kDoubleSize)); - // Put the double_exponent parameter in call stack - __ fstp_d(Operand(esp, 1 * kDoubleSize)); - __ CallCFunction(ExternalReference::power_double_double_function(isolate()), - 4); - } - // Return value is in st(0) on ia32. - __ ret(0); -} - - -static int NegativeComparisonResult(Condition cc) { - DCHECK(cc != equal); - DCHECK((cc == less) || (cc == less_equal) - || (cc == greater) || (cc == greater_equal)); - return (cc == greater || cc == greater_equal) ? LESS : GREATER; -} - - -static void CheckInputType(MacroAssembler* masm, Register input, - CompareICState::State expected, Label* fail) { - Label ok; - if (expected == CompareICState::SMI) { - __ JumpIfNotSmi(input, fail); - } else if (expected == CompareICState::NUMBER) { - __ JumpIfSmi(input, &ok); - __ cmp(FieldOperand(input, HeapObject::kMapOffset), - Immediate(masm->isolate()->factory()->heap_number_map())); - __ j(not_equal, fail); - } - // We could be strict about internalized/non-internalized here, but as long as - // hydrogen doesn't care, the stub doesn't have to care either. - __ bind(&ok); -} - - -static void BranchIfNotInternalizedString(MacroAssembler* masm, - Label* label, - Register object, - Register scratch) { - __ JumpIfSmi(object, label); - __ mov(scratch, FieldOperand(object, HeapObject::kMapOffset)); - __ movzx_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset)); - STATIC_ASSERT(kInternalizedTag == 0 && kStringTag == 0); - __ test(scratch, Immediate(kIsNotStringMask | kIsNotInternalizedMask)); - __ j(not_zero, label); -} - - -void CompareICStub::GenerateGeneric(MacroAssembler* masm) { - Label runtime_call, check_unequal_objects; - Condition cc = GetCondition(); - - Label miss; - CheckInputType(masm, edx, left(), &miss); - CheckInputType(masm, eax, right(), &miss); - - // Compare two smis. - Label non_smi, smi_done; - __ mov(ecx, edx); - __ or_(ecx, eax); - __ JumpIfNotSmi(ecx, &non_smi, Label::kNear); - __ sub(edx, eax); // Return on the result of the subtraction. - __ j(no_overflow, &smi_done, Label::kNear); - __ not_(edx); // Correct sign in case of overflow. edx is never 0 here. - __ bind(&smi_done); - __ mov(eax, edx); - __ ret(0); - __ bind(&non_smi); - - // NOTICE! This code is only reached after a smi-fast-case check, so - // it is certain that at least one operand isn't a smi. - - // Identical objects can be compared fast, but there are some tricky cases - // for NaN and undefined. - Label generic_heap_number_comparison; - { - Label not_identical; - __ cmp(eax, edx); - __ j(not_equal, ¬_identical); - - if (cc != equal) { - // Check for undefined. undefined OP undefined is false even though - // undefined == undefined. - __ cmp(edx, isolate()->factory()->undefined_value()); - Label check_for_nan; - __ j(not_equal, &check_for_nan, Label::kNear); - __ Move(eax, Immediate(Smi::FromInt(NegativeComparisonResult(cc)))); - __ ret(0); - __ bind(&check_for_nan); - } - - // Test for NaN. Compare heap numbers in a general way, - // to handle NaNs correctly. - __ cmp(FieldOperand(edx, HeapObject::kMapOffset), - Immediate(isolate()->factory()->heap_number_map())); - __ j(equal, &generic_heap_number_comparison, Label::kNear); - if (cc != equal) { - __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset)); - __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset)); - // Call runtime on identical JSObjects. Otherwise return equal. - __ cmpb(ecx, Immediate(FIRST_JS_RECEIVER_TYPE)); - __ j(above_equal, &runtime_call, Label::kFar); - // Call runtime on identical symbols since we need to throw a TypeError. - __ cmpb(ecx, Immediate(SYMBOL_TYPE)); - __ j(equal, &runtime_call, Label::kFar); - } - __ Move(eax, Immediate(Smi::FromInt(EQUAL))); - __ ret(0); - - - __ bind(¬_identical); - } - - // Strict equality can quickly decide whether objects are equal. - // Non-strict object equality is slower, so it is handled later in the stub. - if (cc == equal && strict()) { - Label slow; // Fallthrough label. - Label not_smis; - // If we're doing a strict equality comparison, we don't have to do - // type conversion, so we generate code to do fast comparison for objects - // and oddballs. Non-smi numbers and strings still go through the usual - // slow-case code. - // If either is a Smi (we know that not both are), then they can only - // be equal if the other is a HeapNumber. If so, use the slow case. - STATIC_ASSERT(kSmiTag == 0); - DCHECK_EQ(static_cast(0), Smi::kZero); - __ mov(ecx, Immediate(kSmiTagMask)); - __ and_(ecx, eax); - __ test(ecx, edx); - __ j(not_zero, ¬_smis, Label::kNear); - // One operand is a smi. - - // Check whether the non-smi is a heap number. - STATIC_ASSERT(kSmiTagMask == 1); - // ecx still holds eax & kSmiTag, which is either zero or one. - __ sub(ecx, Immediate(0x01)); - __ mov(ebx, edx); - __ xor_(ebx, eax); - __ and_(ebx, ecx); // ebx holds either 0 or eax ^ edx. - __ xor_(ebx, eax); - // if eax was smi, ebx is now edx, else eax. - - // Check if the non-smi operand is a heap number. - __ cmp(FieldOperand(ebx, HeapObject::kMapOffset), - Immediate(isolate()->factory()->heap_number_map())); - // If heap number, handle it in the slow case. - __ j(equal, &slow, Label::kNear); - // Return non-equal (ebx is not zero) - __ mov(eax, ebx); - __ ret(0); - - __ bind(¬_smis); - // If either operand is a JSObject or an oddball value, then they are not - // equal since their pointers are different - // There is no test for undetectability in strict equality. - - // Get the type of the first operand. - // If the first object is a JS object, we have done pointer comparison. - Label first_non_object; - STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE); - __ CmpObjectType(eax, FIRST_JS_RECEIVER_TYPE, ecx); - __ j(below, &first_non_object, Label::kNear); - - // Return non-zero (eax is not zero) - Label return_not_equal; - STATIC_ASSERT(kHeapObjectTag != 0); - __ bind(&return_not_equal); - __ ret(0); - - __ bind(&first_non_object); - // Check for oddballs: true, false, null, undefined. - __ CmpInstanceType(ecx, ODDBALL_TYPE); - __ j(equal, &return_not_equal); - - __ CmpObjectType(edx, FIRST_JS_RECEIVER_TYPE, ecx); - __ j(above_equal, &return_not_equal); - - // Check for oddballs: true, false, null, undefined. - __ CmpInstanceType(ecx, ODDBALL_TYPE); - __ j(equal, &return_not_equal); - - // Fall through to the general case. - __ bind(&slow); - } - - // Generate the number comparison code. - Label non_number_comparison; - Label unordered; - __ bind(&generic_heap_number_comparison); - FloatingPointHelper::CheckFloatOperands( - masm, &non_number_comparison, ebx); - FloatingPointHelper::LoadFloatOperand(masm, eax); - FloatingPointHelper::LoadFloatOperand(masm, edx); - __ FCmp(); - - // Don't base result on EFLAGS when a NaN is involved. - __ j(parity_even, &unordered, Label::kNear); - - Label below_label, above_label; - // Return a result of -1, 0, or 1, based on EFLAGS. - __ j(below, &below_label, Label::kNear); - __ j(above, &above_label, Label::kNear); - - __ Move(eax, Immediate(0)); - __ ret(0); - - __ bind(&below_label); - __ mov(eax, Immediate(Smi::FromInt(-1))); - __ ret(0); - - __ bind(&above_label); - __ mov(eax, Immediate(Smi::FromInt(1))); - __ ret(0); - - // If one of the numbers was NaN, then the result is always false. - // The cc is never not-equal. - __ bind(&unordered); - DCHECK(cc != not_equal); - if (cc == less || cc == less_equal) { - __ mov(eax, Immediate(Smi::FromInt(1))); - } else { - __ mov(eax, Immediate(Smi::FromInt(-1))); - } - __ ret(0); - - // The number comparison code did not provide a valid result. - __ bind(&non_number_comparison); - - // Fast negative check for internalized-to-internalized equality. - Label check_for_strings; - if (cc == equal) { - BranchIfNotInternalizedString(masm, &check_for_strings, eax, ecx); - BranchIfNotInternalizedString(masm, &check_for_strings, edx, ecx); - - // We've already checked for object identity, so if both operands - // are internalized they aren't equal. Register eax already holds a - // non-zero value, which indicates not equal, so just return. - __ ret(0); - } - - __ bind(&check_for_strings); - - __ JumpIfNotBothSequentialOneByteStrings(edx, eax, ecx, ebx, - &check_unequal_objects); - - // Inline comparison of one-byte strings. - if (cc == equal) { - StringHelper::GenerateFlatOneByteStringEquals(masm, edx, eax, ecx, ebx); - } else { - StringHelper::GenerateCompareFlatOneByteStrings(masm, edx, eax, ecx, ebx, - edi); - } -#ifdef DEBUG - __ Abort(kUnexpectedFallThroughFromStringComparison); -#endif - - __ bind(&check_unequal_objects); - if (cc == equal && !strict()) { - // Non-strict equality. Objects are unequal if - // they are both JSObjects and not undetectable, - // and their pointers are different. - Label return_equal, return_unequal, undetectable; - // At most one is a smi, so we can test for smi by adding the two. - // A smi plus a heap object has the low bit set, a heap object plus - // a heap object has the low bit clear. - STATIC_ASSERT(kSmiTag == 0); - STATIC_ASSERT(kSmiTagMask == 1); - __ lea(ecx, Operand(eax, edx, times_1, 0)); - __ test(ecx, Immediate(kSmiTagMask)); - __ j(not_zero, &runtime_call); - - __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset)); - __ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset)); - - __ test_b(FieldOperand(ebx, Map::kBitFieldOffset), - Immediate(1 << Map::kIsUndetectable)); - __ j(not_zero, &undetectable, Label::kNear); - __ test_b(FieldOperand(ecx, Map::kBitFieldOffset), - Immediate(1 << Map::kIsUndetectable)); - __ j(not_zero, &return_unequal, Label::kNear); - - __ CmpInstanceType(ebx, FIRST_JS_RECEIVER_TYPE); - __ j(below, &runtime_call, Label::kNear); - __ CmpInstanceType(ecx, FIRST_JS_RECEIVER_TYPE); - __ j(below, &runtime_call, Label::kNear); - - __ bind(&return_unequal); - // Return non-equal by returning the non-zero object pointer in eax. - __ ret(0); // eax, edx were pushed - - __ bind(&undetectable); - __ test_b(FieldOperand(ecx, Map::kBitFieldOffset), - Immediate(1 << Map::kIsUndetectable)); - __ j(zero, &return_unequal, Label::kNear); - - // If both sides are JSReceivers, then the result is false according to - // the HTML specification, which says that only comparisons with null or - // undefined are affected by special casing for document.all. - __ CmpInstanceType(ebx, ODDBALL_TYPE); - __ j(zero, &return_equal, Label::kNear); - __ CmpInstanceType(ecx, ODDBALL_TYPE); - __ j(not_zero, &return_unequal, Label::kNear); - - __ bind(&return_equal); - __ Move(eax, Immediate(EQUAL)); - __ ret(0); // eax, edx were pushed - } - __ bind(&runtime_call); - - if (cc == equal) { - { - FrameScope scope(masm, StackFrame::INTERNAL); - __ Push(esi); - __ Call(strict() ? isolate()->builtins()->StrictEqual() - : isolate()->builtins()->Equal(), - RelocInfo::CODE_TARGET); - __ Pop(esi); - } - // Turn true into 0 and false into some non-zero value. - STATIC_ASSERT(EQUAL == 0); - __ sub(eax, Immediate(isolate()->factory()->true_value())); - __ Ret(); - } else { - // Push arguments below the return address. - __ pop(ecx); - __ push(edx); - __ push(eax); - __ push(Immediate(Smi::FromInt(NegativeComparisonResult(cc)))); - - // Restore return address on the stack. - __ push(ecx); - // Call the native; it returns -1 (less), 0 (equal), or 1 (greater) - // tagged as a small integer. - __ TailCallRuntime(Runtime::kCompare); - } - - __ bind(&miss); - GenerateMiss(masm); -} - - -static void CallStubInRecordCallTarget(MacroAssembler* masm, CodeStub* stub) { - // eax : number of arguments to the construct function - // ebx : feedback vector - // edx : slot in feedback vector (Smi) - // edi : the function to call - - { - FrameScope scope(masm, StackFrame::INTERNAL); - - // Number-of-arguments register must be smi-tagged to call out. - __ SmiTag(eax); - __ push(eax); - __ push(edi); - __ push(edx); - __ push(ebx); - __ push(esi); - - __ CallStub(stub); - - __ pop(esi); - __ pop(ebx); - __ pop(edx); - __ pop(edi); - __ pop(eax); - __ SmiUntag(eax); - } -} - - -static void GenerateRecordCallTarget(MacroAssembler* masm) { - // Cache the called function in a feedback vector slot. Cache states - // are uninitialized, monomorphic (indicated by a JSFunction), and - // megamorphic. - // eax : number of arguments to the construct function - // ebx : feedback vector - // edx : slot in feedback vector (Smi) - // edi : the function to call - Isolate* isolate = masm->isolate(); - Label initialize, done, miss, megamorphic, not_array_function; - - // Load the cache state into ecx. - __ mov(ecx, FieldOperand(ebx, edx, times_half_pointer_size, - FixedArray::kHeaderSize)); - - // A monomorphic cache hit or an already megamorphic state: invoke the - // function without changing the state. - // We don't know if ecx is a WeakCell or a Symbol, but it's harmless to read - // at this position in a symbol (see static asserts in feedback-vector.h). - Label check_allocation_site; - __ cmp(edi, FieldOperand(ecx, WeakCell::kValueOffset)); - __ j(equal, &done, Label::kFar); - __ CompareRoot(ecx, Heap::kmegamorphic_symbolRootIndex); - __ j(equal, &done, Label::kFar); - __ CompareRoot(FieldOperand(ecx, HeapObject::kMapOffset), - Heap::kWeakCellMapRootIndex); - __ j(not_equal, &check_allocation_site); - - // If the weak cell is cleared, we have a new chance to become monomorphic. - __ JumpIfSmi(FieldOperand(ecx, WeakCell::kValueOffset), &initialize); - __ jmp(&megamorphic); - - __ bind(&check_allocation_site); - // If we came here, we need to see if we are the array function. - // If we didn't have a matching function, and we didn't find the megamorph - // sentinel, then we have in the slot either some other function or an - // AllocationSite. - __ CompareRoot(FieldOperand(ecx, 0), Heap::kAllocationSiteMapRootIndex); - __ j(not_equal, &miss); - - // Make sure the function is the Array() function - __ LoadGlobalFunction(Context::ARRAY_FUNCTION_INDEX, ecx); - __ cmp(edi, ecx); - __ j(not_equal, &megamorphic); - __ jmp(&done, Label::kFar); - - __ bind(&miss); - - // A monomorphic miss (i.e, here the cache is not uninitialized) goes - // megamorphic. - __ CompareRoot(ecx, Heap::kuninitialized_symbolRootIndex); - __ j(equal, &initialize); - // MegamorphicSentinel is an immortal immovable object (undefined) so no - // write-barrier is needed. - __ bind(&megamorphic); - __ mov( - FieldOperand(ebx, edx, times_half_pointer_size, FixedArray::kHeaderSize), - Immediate(FeedbackVector::MegamorphicSentinel(isolate))); - __ jmp(&done, Label::kFar); - - // An uninitialized cache is patched with the function or sentinel to - // indicate the ElementsKind if function is the Array constructor. - __ bind(&initialize); - // Make sure the function is the Array() function - __ LoadGlobalFunction(Context::ARRAY_FUNCTION_INDEX, ecx); - __ cmp(edi, ecx); - __ j(not_equal, ¬_array_function); - - // The target function is the Array constructor, - // Create an AllocationSite if we don't already have it, store it in the - // slot. - CreateAllocationSiteStub create_stub(isolate); - CallStubInRecordCallTarget(masm, &create_stub); - __ jmp(&done); - - __ bind(¬_array_function); - CreateWeakCellStub weak_cell_stub(isolate); - CallStubInRecordCallTarget(masm, &weak_cell_stub); - - __ bind(&done); - // Increment the call count for all function calls. - __ add(FieldOperand(ebx, edx, times_half_pointer_size, - FixedArray::kHeaderSize + kPointerSize), - Immediate(Smi::FromInt(1))); -} - - -void CallConstructStub::Generate(MacroAssembler* masm) { - // eax : number of arguments - // ebx : feedback vector - // edx : slot in feedback vector (Smi, for RecordCallTarget) - // edi : constructor function - - Label non_function; - // Check that function is not a smi. - __ JumpIfSmi(edi, &non_function); - // Check that function is a JSFunction. - __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx); - __ j(not_equal, &non_function); - - GenerateRecordCallTarget(masm); - - Label feedback_register_initialized; - // Put the AllocationSite from the feedback vector into ebx, or undefined. - __ mov(ebx, FieldOperand(ebx, edx, times_half_pointer_size, - FixedArray::kHeaderSize)); - Handle allocation_site_map = isolate()->factory()->allocation_site_map(); - __ cmp(FieldOperand(ebx, 0), Immediate(allocation_site_map)); - __ j(equal, &feedback_register_initialized); - __ mov(ebx, isolate()->factory()->undefined_value()); - __ bind(&feedback_register_initialized); - - __ AssertUndefinedOrAllocationSite(ebx); - - // Pass new target to construct stub. - __ mov(edx, edi); - - // Tail call to the function-specific construct stub (still in the caller - // context at this point). - __ mov(ecx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); - __ mov(ecx, FieldOperand(ecx, SharedFunctionInfo::kConstructStubOffset)); - __ lea(ecx, FieldOperand(ecx, Code::kHeaderSize)); - __ jmp(ecx); - - __ bind(&non_function); - __ mov(edx, edi); - __ Jump(isolate()->builtins()->Construct(), RelocInfo::CODE_TARGET); -} - -static void IncrementCallCount(MacroAssembler* masm, Register feedback_vector, - Register slot) { - __ add(FieldOperand(feedback_vector, slot, times_half_pointer_size, - FixedArray::kHeaderSize + kPointerSize), - Immediate(Smi::FromInt(1))); -} - -void CallICStub::HandleArrayCase(MacroAssembler* masm, Label* miss) { - // eax - number of arguments - // edi - function - // edx - slot id - // ebx - vector - __ LoadGlobalFunction(Context::ARRAY_FUNCTION_INDEX, ecx); - __ cmp(edi, ecx); - __ j(not_equal, miss); - - // Reload ecx. - __ mov(ecx, FieldOperand(ebx, edx, times_half_pointer_size, - FixedArray::kHeaderSize)); - - // Increment the call count for monomorphic function calls. - IncrementCallCount(masm, ebx, edx); - - __ mov(ebx, ecx); - __ mov(edx, edi); - ArrayConstructorStub stub(masm->isolate()); - __ TailCallStub(&stub); - - // Unreachable. -} - - -void CallICStub::Generate(MacroAssembler* masm) { - // edi - number of arguments - // edi - function - // edx - slot id - // ebx - vector - Isolate* isolate = masm->isolate(); - Label extra_checks_or_miss, call, call_function, call_count_incremented; - - // The checks. First, does edi match the recorded monomorphic target? - __ mov(ecx, FieldOperand(ebx, edx, times_half_pointer_size, - FixedArray::kHeaderSize)); - - // We don't know that we have a weak cell. We might have a private symbol - // or an AllocationSite, but the memory is safe to examine. - // AllocationSite::kTransitionInfoOrBoilerplateOffset - contains a Smi or - // pointer to FixedArray. WeakCell::kValueOffset - contains a JSFunction or - // Smi(0) Symbol::kHashFieldSlot - if the low bit is 1, then the hash is not - // computed, meaning that it can't appear to be a pointer. If the low bit is - // 0, then hash is computed, but the 0 bit prevents the field from appearing - // to be a pointer. - STATIC_ASSERT(WeakCell::kSize >= kPointerSize); - STATIC_ASSERT(AllocationSite::kTransitionInfoOrBoilerplateOffset == - WeakCell::kValueOffset && - WeakCell::kValueOffset == Symbol::kHashFieldSlot); - - __ cmp(edi, FieldOperand(ecx, WeakCell::kValueOffset)); - __ j(not_equal, &extra_checks_or_miss); - - // The compare above could have been a SMI/SMI comparison. Guard against this - // convincing us that we have a monomorphic JSFunction. - __ JumpIfSmi(edi, &extra_checks_or_miss); - - __ bind(&call_function); - - // Increment the call count for monomorphic function calls. - IncrementCallCount(masm, ebx, edx); - - __ Jump(masm->isolate()->builtins()->CallFunction(convert_mode(), - tail_call_mode()), - RelocInfo::CODE_TARGET); - - __ bind(&extra_checks_or_miss); - Label uninitialized, miss, not_allocation_site; - - __ cmp(ecx, Immediate(FeedbackVector::MegamorphicSentinel(isolate))); - __ j(equal, &call); - - // Check if we have an allocation site. - __ CompareRoot(FieldOperand(ecx, HeapObject::kMapOffset), - Heap::kAllocationSiteMapRootIndex); - __ j(not_equal, ¬_allocation_site); - - // We have an allocation site. - HandleArrayCase(masm, &miss); - - __ bind(¬_allocation_site); - - // The following cases attempt to handle MISS cases without going to the - // runtime. - if (FLAG_trace_ic) { - __ jmp(&miss); - } - - __ cmp(ecx, Immediate(FeedbackVector::UninitializedSentinel(isolate))); - __ j(equal, &uninitialized); - - // We are going megamorphic. If the feedback is a JSFunction, it is fine - // to handle it here. More complex cases are dealt with in the runtime. - __ AssertNotSmi(ecx); - __ CmpObjectType(ecx, JS_FUNCTION_TYPE, ecx); - __ j(not_equal, &miss); - __ mov( - FieldOperand(ebx, edx, times_half_pointer_size, FixedArray::kHeaderSize), - Immediate(FeedbackVector::MegamorphicSentinel(isolate))); - - __ bind(&call); - - // Increment the call count for megamorphic function calls. - IncrementCallCount(masm, ebx, edx); - - __ bind(&call_count_incremented); - - __ Jump(masm->isolate()->builtins()->Call(convert_mode(), tail_call_mode()), - RelocInfo::CODE_TARGET); - - __ bind(&uninitialized); - - // We are going monomorphic, provided we actually have a JSFunction. - __ JumpIfSmi(edi, &miss); - - // Goto miss case if we do not have a function. - __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx); - __ j(not_equal, &miss); - - // Make sure the function is not the Array() function, which requires special - // behavior on MISS. - __ LoadGlobalFunction(Context::ARRAY_FUNCTION_INDEX, ecx); - __ cmp(edi, ecx); - __ j(equal, &miss); - - // Make sure the function belongs to the same native context. - __ mov(ecx, FieldOperand(edi, JSFunction::kContextOffset)); - __ mov(ecx, ContextOperand(ecx, Context::NATIVE_CONTEXT_INDEX)); - __ cmp(ecx, NativeContextOperand()); - __ j(not_equal, &miss); - - // Store the function. Use a stub since we need a frame for allocation. - // eax - number of arguments - // ebx - vector - // edx - slot - // edi - function - { - FrameScope scope(masm, StackFrame::INTERNAL); - CreateWeakCellStub create_stub(isolate); - __ SmiTag(eax); - __ push(eax); - __ push(ebx); - __ push(edx); - __ push(edi); - __ push(esi); - __ CallStub(&create_stub); - __ pop(esi); - __ pop(edi); - __ pop(edx); - __ pop(ebx); - __ pop(eax); - __ SmiUntag(eax); - } - - __ jmp(&call_function); - - // We are here because tracing is on or we encountered a MISS case we can't - // handle here. - __ bind(&miss); - GenerateMiss(masm); - - __ jmp(&call_count_incremented); - - // Unreachable - __ int3(); -} - - -void CallICStub::GenerateMiss(MacroAssembler* masm) { - FrameScope scope(masm, StackFrame::INTERNAL); - - // Preserve the number of arguments. - __ SmiTag(eax); - __ push(eax); - - // Push the function and feedback info. - __ push(edi); - __ push(ebx); - __ push(edx); - - // Call the entry. - __ CallRuntime(Runtime::kCallIC_Miss); - - // Move result to edi and exit the internal frame. - __ mov(edi, eax); - - // Restore number of arguments. - __ pop(eax); - __ SmiUntag(eax); -} - - -bool CEntryStub::NeedsImmovableCode() { - return false; -} - - -void CodeStub::GenerateStubsAheadOfTime(Isolate* isolate) { - CEntryStub::GenerateAheadOfTime(isolate); - StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime(isolate); - // It is important that the store buffer overflow stubs are generated first. - CommonArrayConstructorStub::GenerateStubsAheadOfTime(isolate); - CreateAllocationSiteStub::GenerateAheadOfTime(isolate); - CreateWeakCellStub::GenerateAheadOfTime(isolate); - StoreFastElementStub::GenerateAheadOfTime(isolate); -} - - -void CodeStub::GenerateFPStubs(Isolate* isolate) { - CEntryStub save_doubles(isolate, 1, kSaveFPRegs); - // Stubs might already be in the snapshot, detect that and don't regenerate, - // which would lead to code stub initialization state being messed up. - Code* save_doubles_code; - if (!save_doubles.FindCodeInCache(&save_doubles_code)) { - save_doubles_code = *(save_doubles.GetCode()); - } -} - - -void CEntryStub::GenerateAheadOfTime(Isolate* isolate) { - CEntryStub stub(isolate, 1, kDontSaveFPRegs); - stub.GetCode(); -} - - -void CEntryStub::Generate(MacroAssembler* masm) { - // eax: number of arguments including receiver - // ebx: pointer to C function (C callee-saved) - // ebp: frame pointer (restored after C call) - // esp: stack pointer (restored after C call) - // esi: current context (C callee-saved) - // edi: JS function of the caller (C callee-saved) - // - // If argv_in_register(): - // ecx: pointer to the first argument - - ProfileEntryHookStub::MaybeCallEntryHook(masm); - - // Reserve space on the stack for the three arguments passed to the call. If - // result size is greater than can be returned in registers, also reserve - // space for the hidden argument for the result location, and space for the - // result itself. - int arg_stack_space = result_size() < 3 ? 3 : 4 + result_size(); - - // Enter the exit frame that transitions from JavaScript to C++. - if (argv_in_register()) { - DCHECK(!save_doubles()); - DCHECK(!is_builtin_exit()); - __ EnterApiExitFrame(arg_stack_space); - - // Move argc and argv into the correct registers. - __ mov(esi, ecx); - __ mov(edi, eax); - } else { - __ EnterExitFrame( - arg_stack_space, save_doubles(), - is_builtin_exit() ? StackFrame::BUILTIN_EXIT : StackFrame::EXIT); - } - - // ebx: pointer to C function (C callee-saved) - // ebp: frame pointer (restored after C call) - // esp: stack pointer (restored after C call) - // edi: number of arguments including receiver (C callee-saved) - // esi: pointer to the first argument (C callee-saved) - - // Result returned in eax, or eax+edx if result size is 2. - - // Check stack alignment. - if (FLAG_debug_code) { - __ CheckStackAlignment(); - } - // Call C function. - if (result_size() <= 2) { - __ mov(Operand(esp, 0 * kPointerSize), edi); // argc. - __ mov(Operand(esp, 1 * kPointerSize), esi); // argv. - __ mov(Operand(esp, 2 * kPointerSize), - Immediate(ExternalReference::isolate_address(isolate()))); - } else { - DCHECK_EQ(3, result_size()); - // Pass a pointer to the result location as the first argument. - __ lea(eax, Operand(esp, 4 * kPointerSize)); - __ mov(Operand(esp, 0 * kPointerSize), eax); - __ mov(Operand(esp, 1 * kPointerSize), edi); // argc. - __ mov(Operand(esp, 2 * kPointerSize), esi); // argv. - __ mov(Operand(esp, 3 * kPointerSize), - Immediate(ExternalReference::isolate_address(isolate()))); - } - __ call(ebx); - - if (result_size() > 2) { - DCHECK_EQ(3, result_size()); -#ifndef _WIN32 - // Restore the "hidden" argument on the stack which was popped by caller. - __ sub(esp, Immediate(kPointerSize)); -#endif - // Read result values stored on stack. Result is stored above the arguments. - __ mov(kReturnRegister0, Operand(esp, 4 * kPointerSize)); - __ mov(kReturnRegister1, Operand(esp, 5 * kPointerSize)); - __ mov(kReturnRegister2, Operand(esp, 6 * kPointerSize)); - } - // Result is in eax, edx:eax or edi:edx:eax - do not destroy these registers! - - // Check result for exception sentinel. - Label exception_returned; - __ cmp(eax, isolate()->factory()->exception()); - __ j(equal, &exception_returned); - - // Check that there is no pending exception, otherwise we - // should have returned the exception sentinel. - if (FLAG_debug_code) { - __ push(edx); - __ mov(edx, Immediate(isolate()->factory()->the_hole_value())); - Label okay; - ExternalReference pending_exception_address( - IsolateAddressId::kPendingExceptionAddress, isolate()); - __ cmp(edx, Operand::StaticVariable(pending_exception_address)); - // Cannot use check here as it attempts to generate call into runtime. - __ j(equal, &okay, Label::kNear); - __ int3(); - __ bind(&okay); - __ pop(edx); - } - - // Exit the JavaScript to C++ exit frame. - __ LeaveExitFrame(save_doubles(), !argv_in_register()); - __ ret(0); - - // Handling of exception. - __ bind(&exception_returned); - - ExternalReference pending_handler_context_address( - IsolateAddressId::kPendingHandlerContextAddress, isolate()); - ExternalReference pending_handler_code_address( - IsolateAddressId::kPendingHandlerCodeAddress, isolate()); - ExternalReference pending_handler_offset_address( - IsolateAddressId::kPendingHandlerOffsetAddress, isolate()); - ExternalReference pending_handler_fp_address( - IsolateAddressId::kPendingHandlerFPAddress, isolate()); - ExternalReference pending_handler_sp_address( - IsolateAddressId::kPendingHandlerSPAddress, isolate()); - - // Ask the runtime for help to determine the handler. This will set eax to - // contain the current pending exception, don't clobber it. - ExternalReference find_handler(Runtime::kUnwindAndFindExceptionHandler, - isolate()); - { - FrameScope scope(masm, StackFrame::MANUAL); - __ PrepareCallCFunction(3, eax); - __ mov(Operand(esp, 0 * kPointerSize), Immediate(0)); // argc. - __ mov(Operand(esp, 1 * kPointerSize), Immediate(0)); // argv. - __ mov(Operand(esp, 2 * kPointerSize), - Immediate(ExternalReference::isolate_address(isolate()))); - __ CallCFunction(find_handler, 3); - } - - // Retrieve the handler context, SP and FP. - __ mov(esi, Operand::StaticVariable(pending_handler_context_address)); - __ mov(esp, Operand::StaticVariable(pending_handler_sp_address)); - __ mov(ebp, Operand::StaticVariable(pending_handler_fp_address)); - - // If the handler is a JS frame, restore the context to the frame. Note that - // the context will be set to (esi == 0) for non-JS frames. - Label skip; - __ test(esi, esi); - __ j(zero, &skip, Label::kNear); - __ mov(Operand(ebp, StandardFrameConstants::kContextOffset), esi); - __ bind(&skip); - - // Compute the handler entry address and jump to it. - __ mov(edi, Operand::StaticVariable(pending_handler_code_address)); - __ mov(edx, Operand::StaticVariable(pending_handler_offset_address)); - // Check whether it's a turbofanned exception handler code before jump to it. - Label not_turbo; - __ push(eax); - __ mov(eax, Operand(edi, Code::kKindSpecificFlags1Offset - kHeapObjectTag)); - __ and_(eax, Immediate(1 << Code::kIsTurbofannedBit)); - __ j(zero, ¬_turbo); - __ fninit(); - __ fld1(); - __ bind(¬_turbo); - __ pop(eax); - __ lea(edi, FieldOperand(edi, edx, times_1, Code::kHeaderSize)); - __ jmp(edi); -} - - -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. - int marker = type(); - __ push(Immediate(Smi::FromInt(marker))); // marker - ExternalReference context_address(IsolateAddressId::kContextAddress, - isolate()); - __ push(Operand::StaticVariable(context_address)); // context - // Save callee-saved registers (C calling conventions). - __ push(edi); - __ push(esi); - __ push(ebx); - - // Save copies of the top frame descriptor on the stack. - ExternalReference c_entry_fp(IsolateAddressId::kCEntryFPAddress, isolate()); - __ push(Operand::StaticVariable(c_entry_fp)); - - // If this is the outermost JS call, set js_entry_sp value. - ExternalReference js_entry_sp(IsolateAddressId::kJSEntrySPAddress, isolate()); - __ cmp(Operand::StaticVariable(js_entry_sp), Immediate(0)); - __ j(not_equal, ¬_outermost_js, Label::kNear); - __ mov(Operand::StaticVariable(js_entry_sp), ebp); - __ push(Immediate(Smi::FromInt(StackFrame::OUTERMOST_JSENTRY_FRAME))); - __ jmp(&invoke, Label::kNear); - __ bind(¬_outermost_js); - __ push(Immediate(Smi::FromInt(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( - IsolateAddressId::kPendingExceptionAddress, isolate()); - __ mov(Operand::StaticVariable(pending_exception), eax); - __ mov(eax, Immediate(isolate()->factory()->exception())); - __ jmp(&exit); - - // Invoke: Link this frame into the handler chain. - __ bind(&invoke); - __ PushStackHandler(); - - // 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. 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. - if (type() == StackFrame::ENTRY_CONSTRUCT) { - ExternalReference construct_entry(Builtins::kJSConstructEntryTrampoline, - isolate()); - __ mov(edx, Immediate(construct_entry)); - } else { - ExternalReference entry(Builtins::kJSEntryTrampoline, isolate()); - __ mov(edx, Immediate(entry)); - } - __ mov(edx, Operand(edx, 0)); // deref address - __ lea(edx, FieldOperand(edx, Code::kHeaderSize)); - __ call(edx); - - // Unlink this frame from the handler chain. - __ PopStackHandler(); - - __ bind(&exit); - // Check if the current stack frame is marked as the outermost JS frame. - __ pop(ebx); - __ cmp(ebx, Immediate(Smi::FromInt(StackFrame::OUTERMOST_JSENTRY_FRAME))); - __ j(not_equal, ¬_outermost_js_2); - __ mov(Operand::StaticVariable(js_entry_sp), Immediate(0)); - __ bind(¬_outermost_js_2); - - // Restore the top frame descriptor from the stack. - __ pop(Operand::StaticVariable( - ExternalReference(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); -} - - -// ------------------------------------------------------------------------- -// StringCharCodeAtGenerator - -void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) { - // If the receiver is a smi trigger the non-string case. - if (check_mode_ == RECEIVER_IS_UNKNOWN) { - __ JumpIfSmi(object_, receiver_not_string_); - - // Fetch the instance type of the receiver into result register. - __ mov(result_, FieldOperand(object_, HeapObject::kMapOffset)); - __ movzx_b(result_, FieldOperand(result_, Map::kInstanceTypeOffset)); - // If the receiver is not a string trigger the non-string case. - __ test(result_, Immediate(kIsNotStringMask)); - __ j(not_zero, receiver_not_string_); - } - - // If the index is non-smi trigger the non-smi case. - __ JumpIfNotSmi(index_, &index_not_smi_); - __ bind(&got_smi_index_); - - // Check for index out of range. - __ cmp(index_, FieldOperand(object_, String::kLengthOffset)); - __ j(above_equal, index_out_of_range_); - - __ SmiUntag(index_); - - Factory* factory = masm->isolate()->factory(); - StringCharLoadGenerator::Generate( - masm, factory, object_, index_, result_, &call_runtime_); - - __ SmiTag(result_); - __ bind(&exit_); -} - - -void StringCharCodeAtGenerator::GenerateSlow( - MacroAssembler* masm, EmbedMode embed_mode, - const RuntimeCallHelper& call_helper) { - __ Abort(kUnexpectedFallthroughToCharCodeAtSlowCase); - - // Index is not a smi. - __ bind(&index_not_smi_); - // If index is a heap number, try converting it to an integer. - __ CheckMap(index_, - masm->isolate()->factory()->heap_number_map(), - index_not_number_, - DONT_DO_SMI_CHECK); - call_helper.BeforeCall(masm); - if (embed_mode == PART_OF_IC_HANDLER) { - __ push(LoadWithVectorDescriptor::VectorRegister()); - __ push(LoadDescriptor::SlotRegister()); - } - __ push(object_); - __ push(index_); // Consumed by runtime conversion function. - __ CallRuntime(Runtime::kNumberToSmi); - if (!index_.is(eax)) { - // Save the conversion result before the pop instructions below - // have a chance to overwrite it. - __ mov(index_, eax); - } - __ pop(object_); - if (embed_mode == PART_OF_IC_HANDLER) { - __ pop(LoadDescriptor::SlotRegister()); - __ pop(LoadWithVectorDescriptor::VectorRegister()); - } - // Reload the instance type. - __ mov(result_, FieldOperand(object_, HeapObject::kMapOffset)); - __ movzx_b(result_, FieldOperand(result_, Map::kInstanceTypeOffset)); - call_helper.AfterCall(masm); - // If index is still not a smi, it must be out of range. - STATIC_ASSERT(kSmiTag == 0); - __ JumpIfNotSmi(index_, index_out_of_range_); - // Otherwise, return to the fast path. - __ jmp(&got_smi_index_); - - // Call runtime. We get here when the receiver is a string and the - // index is a number, but the code of getting the actual character - // is too complex (e.g., when the string needs to be flattened). - __ bind(&call_runtime_); - call_helper.BeforeCall(masm); - __ push(object_); - __ SmiTag(index_); - __ push(index_); - __ CallRuntime(Runtime::kStringCharCodeAtRT); - if (!result_.is(eax)) { - __ mov(result_, eax); - } - call_helper.AfterCall(masm); - __ jmp(&exit_); - - __ Abort(kUnexpectedFallthroughFromCharCodeAtSlowCase); -} - -void StringHelper::GenerateFlatOneByteStringEquals(MacroAssembler* masm, - Register left, - Register right, - Register scratch1, - Register scratch2) { - Register length = scratch1; - - // Compare lengths. - Label strings_not_equal, check_zero_length; - __ mov(length, FieldOperand(left, String::kLengthOffset)); - __ cmp(length, FieldOperand(right, String::kLengthOffset)); - __ j(equal, &check_zero_length, Label::kNear); - __ bind(&strings_not_equal); - __ Move(eax, Immediate(Smi::FromInt(NOT_EQUAL))); - __ ret(0); - - // Check if the length is zero. - Label compare_chars; - __ bind(&check_zero_length); - STATIC_ASSERT(kSmiTag == 0); - __ test(length, length); - __ j(not_zero, &compare_chars, Label::kNear); - __ Move(eax, Immediate(Smi::FromInt(EQUAL))); - __ ret(0); - - // Compare characters. - __ bind(&compare_chars); - GenerateOneByteCharsCompareLoop(masm, left, right, length, scratch2, - &strings_not_equal, Label::kNear); - - // Characters are equal. - __ Move(eax, Immediate(Smi::FromInt(EQUAL))); - __ ret(0); -} - - -void StringHelper::GenerateCompareFlatOneByteStrings( - MacroAssembler* masm, Register left, Register right, Register scratch1, - Register scratch2, Register scratch3) { - Counters* counters = masm->isolate()->counters(); - __ IncrementCounter(counters->string_compare_native(), 1); - - // Find minimum length. - Label left_shorter; - __ mov(scratch1, FieldOperand(left, String::kLengthOffset)); - __ mov(scratch3, scratch1); - __ sub(scratch3, FieldOperand(right, String::kLengthOffset)); - - Register length_delta = scratch3; - - __ j(less_equal, &left_shorter, Label::kNear); - // Right string is shorter. Change scratch1 to be length of right string. - __ sub(scratch1, length_delta); - __ bind(&left_shorter); - - Register min_length = scratch1; - - // If either length is zero, just compare lengths. - Label compare_lengths; - __ test(min_length, min_length); - __ j(zero, &compare_lengths, Label::kNear); - - // Compare characters. - Label result_not_equal; - GenerateOneByteCharsCompareLoop(masm, left, right, min_length, scratch2, - &result_not_equal, Label::kNear); - - // Compare lengths - strings up to min-length are equal. - __ bind(&compare_lengths); - __ test(length_delta, length_delta); - Label length_not_equal; - __ j(not_zero, &length_not_equal, Label::kNear); - - // Result is EQUAL. - STATIC_ASSERT(EQUAL == 0); - STATIC_ASSERT(kSmiTag == 0); - __ Move(eax, Immediate(Smi::FromInt(EQUAL))); - __ ret(0); - - Label result_greater; - Label result_less; - __ bind(&length_not_equal); - __ j(greater, &result_greater, Label::kNear); - __ jmp(&result_less, Label::kNear); - __ bind(&result_not_equal); - __ j(above, &result_greater, Label::kNear); - __ bind(&result_less); - - // Result is LESS. - __ Move(eax, Immediate(Smi::FromInt(LESS))); - __ ret(0); - - // Result is GREATER. - __ bind(&result_greater); - __ Move(eax, Immediate(Smi::FromInt(GREATER))); - __ ret(0); -} - - -void StringHelper::GenerateOneByteCharsCompareLoop( - MacroAssembler* masm, Register left, Register right, Register length, - Register scratch, Label* chars_not_equal, - Label::Distance chars_not_equal_near) { - // Change index to run from -length to -1 by adding length to string - // start. This means that loop ends when index reaches zero, which - // doesn't need an additional compare. - __ SmiUntag(length); - __ lea(left, - FieldOperand(left, length, times_1, SeqOneByteString::kHeaderSize)); - __ lea(right, - FieldOperand(right, length, times_1, SeqOneByteString::kHeaderSize)); - __ neg(length); - Register index = length; // index = -length; - - // Compare loop. - Label loop; - __ bind(&loop); - __ mov_b(scratch, Operand(left, index, times_1, 0)); - __ cmpb(scratch, Operand(right, index, times_1, 0)); - __ j(not_equal, chars_not_equal, chars_not_equal_near); - __ inc(index); - __ j(not_zero, &loop); -} - - -void CompareICStub::GenerateBooleans(MacroAssembler* masm) { - DCHECK_EQ(CompareICState::BOOLEAN, state()); - Label miss; - Label::Distance const miss_distance = - masm->emit_debug_code() ? Label::kFar : Label::kNear; - - __ JumpIfSmi(edx, &miss, miss_distance); - __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset)); - __ JumpIfSmi(eax, &miss, miss_distance); - __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset)); - __ JumpIfNotRoot(ecx, Heap::kBooleanMapRootIndex, &miss, miss_distance); - __ JumpIfNotRoot(ebx, Heap::kBooleanMapRootIndex, &miss, miss_distance); - if (!Token::IsEqualityOp(op())) { - __ mov(eax, FieldOperand(eax, Oddball::kToNumberOffset)); - __ AssertSmi(eax); - __ mov(edx, FieldOperand(edx, Oddball::kToNumberOffset)); - __ AssertSmi(edx); - __ xchg(eax, edx); - } - __ sub(eax, edx); - __ Ret(); - - __ bind(&miss); - GenerateMiss(masm); -} - - -void CompareICStub::GenerateSmis(MacroAssembler* masm) { - DCHECK(state() == CompareICState::SMI); - Label miss; - __ mov(ecx, edx); - __ or_(ecx, eax); - __ JumpIfNotSmi(ecx, &miss, Label::kNear); - - if (GetCondition() == equal) { - // For equality we do not care about the sign of the result. - __ sub(eax, edx); - } else { - Label done; - __ sub(edx, eax); - __ j(no_overflow, &done, Label::kNear); - // Correct sign of result in case of overflow. - __ not_(edx); - __ bind(&done); - __ mov(eax, edx); - } - __ ret(0); - - __ bind(&miss); - GenerateMiss(masm); -} - - -void CompareICStub::GenerateNumbers(MacroAssembler* masm) { - DCHECK(state() == CompareICState::NUMBER); - - Label generic_stub, check_left; - Label unordered, maybe_undefined1, maybe_undefined2; - Label miss; - - if (left() == CompareICState::SMI) { - __ JumpIfNotSmi(edx, &miss); - } - if (right() == CompareICState::SMI) { - __ JumpIfNotSmi(eax, &miss); - } - - // Inlining the double comparison and falling back to the general compare - // stub if NaN is involved or SSE2 or CMOV is unsupported. - __ JumpIfSmi(eax, &check_left, Label::kNear); - __ cmp(FieldOperand(eax, HeapObject::kMapOffset), - isolate()->factory()->heap_number_map()); - __ j(not_equal, &maybe_undefined1, Label::kNear); - - __ bind(&check_left); - __ JumpIfSmi(edx, &generic_stub, Label::kNear); - __ cmp(FieldOperand(edx, HeapObject::kMapOffset), - isolate()->factory()->heap_number_map()); - __ j(not_equal, &maybe_undefined2, Label::kNear); - - __ bind(&unordered); - __ bind(&generic_stub); - CompareICStub stub(isolate(), op(), CompareICState::GENERIC, - CompareICState::GENERIC, CompareICState::GENERIC); - __ jmp(stub.GetCode(), RelocInfo::CODE_TARGET); - - __ bind(&maybe_undefined1); - if (Token::IsOrderedRelationalCompareOp(op())) { - __ cmp(eax, Immediate(isolate()->factory()->undefined_value())); - __ j(not_equal, &miss); - __ JumpIfSmi(edx, &unordered); - __ CmpObjectType(edx, HEAP_NUMBER_TYPE, ecx); - __ j(not_equal, &maybe_undefined2, Label::kNear); - __ jmp(&unordered); - } - - __ bind(&maybe_undefined2); - if (Token::IsOrderedRelationalCompareOp(op())) { - __ cmp(edx, Immediate(isolate()->factory()->undefined_value())); - __ j(equal, &unordered); - } - - __ bind(&miss); - GenerateMiss(masm); -} - - -void CompareICStub::GenerateInternalizedStrings(MacroAssembler* masm) { - DCHECK(state() == CompareICState::INTERNALIZED_STRING); - DCHECK(GetCondition() == equal); - - // Registers containing left and right operands respectively. - Register left = edx; - Register right = eax; - Register tmp1 = ecx; - Register tmp2 = ebx; - - // Check that both operands are heap objects. - Label miss; - __ mov(tmp1, left); - STATIC_ASSERT(kSmiTag == 0); - __ and_(tmp1, right); - __ JumpIfSmi(tmp1, &miss, Label::kNear); - - // Check that both operands are internalized strings. - __ mov(tmp1, FieldOperand(left, HeapObject::kMapOffset)); - __ mov(tmp2, FieldOperand(right, HeapObject::kMapOffset)); - __ movzx_b(tmp1, FieldOperand(tmp1, Map::kInstanceTypeOffset)); - __ movzx_b(tmp2, FieldOperand(tmp2, Map::kInstanceTypeOffset)); - STATIC_ASSERT(kInternalizedTag == 0 && kStringTag == 0); - __ or_(tmp1, tmp2); - __ test(tmp1, Immediate(kIsNotStringMask | kIsNotInternalizedMask)); - __ j(not_zero, &miss, Label::kNear); - - // Internalized strings are compared by identity. - Label done; - __ cmp(left, right); - // Make sure eax is non-zero. At this point input operands are - // guaranteed to be non-zero. - DCHECK(right.is(eax)); - __ j(not_equal, &done, Label::kNear); - STATIC_ASSERT(EQUAL == 0); - STATIC_ASSERT(kSmiTag == 0); - __ Move(eax, Immediate(Smi::FromInt(EQUAL))); - __ bind(&done); - __ ret(0); - - __ bind(&miss); - GenerateMiss(masm); -} - - -void CompareICStub::GenerateUniqueNames(MacroAssembler* masm) { - DCHECK(state() == CompareICState::UNIQUE_NAME); - DCHECK(GetCondition() == equal); - - // Registers containing left and right operands respectively. - Register left = edx; - Register right = eax; - Register tmp1 = ecx; - Register tmp2 = ebx; - - // Check that both operands are heap objects. - Label miss; - __ mov(tmp1, left); - STATIC_ASSERT(kSmiTag == 0); - __ and_(tmp1, right); - __ JumpIfSmi(tmp1, &miss, Label::kNear); - - // Check that both operands are unique names. This leaves the instance - // types loaded in tmp1 and tmp2. - __ mov(tmp1, FieldOperand(left, HeapObject::kMapOffset)); - __ mov(tmp2, FieldOperand(right, HeapObject::kMapOffset)); - __ movzx_b(tmp1, FieldOperand(tmp1, Map::kInstanceTypeOffset)); - __ movzx_b(tmp2, FieldOperand(tmp2, Map::kInstanceTypeOffset)); - - __ JumpIfNotUniqueNameInstanceType(tmp1, &miss, Label::kNear); - __ JumpIfNotUniqueNameInstanceType(tmp2, &miss, Label::kNear); - - // Unique names are compared by identity. - Label done; - __ cmp(left, right); - // Make sure eax is non-zero. At this point input operands are - // guaranteed to be non-zero. - DCHECK(right.is(eax)); - __ j(not_equal, &done, Label::kNear); - STATIC_ASSERT(EQUAL == 0); - STATIC_ASSERT(kSmiTag == 0); - __ Move(eax, Immediate(Smi::FromInt(EQUAL))); - __ bind(&done); - __ ret(0); - - __ bind(&miss); - GenerateMiss(masm); -} - - -void CompareICStub::GenerateStrings(MacroAssembler* masm) { - DCHECK(state() == CompareICState::STRING); - Label miss; - - bool equality = Token::IsEqualityOp(op()); - - // Registers containing left and right operands respectively. - Register left = edx; - Register right = eax; - Register tmp1 = ecx; - Register tmp2 = ebx; - Register tmp3 = edi; - - // Check that both operands are heap objects. - __ mov(tmp1, left); - STATIC_ASSERT(kSmiTag == 0); - __ and_(tmp1, right); - __ JumpIfSmi(tmp1, &miss); - - // Check that both operands are strings. This leaves the instance - // types loaded in tmp1 and tmp2. - __ mov(tmp1, FieldOperand(left, HeapObject::kMapOffset)); - __ mov(tmp2, FieldOperand(right, HeapObject::kMapOffset)); - __ movzx_b(tmp1, FieldOperand(tmp1, Map::kInstanceTypeOffset)); - __ movzx_b(tmp2, FieldOperand(tmp2, Map::kInstanceTypeOffset)); - __ mov(tmp3, tmp1); - STATIC_ASSERT(kNotStringTag != 0); - __ or_(tmp3, tmp2); - __ test(tmp3, Immediate(kIsNotStringMask)); - __ j(not_zero, &miss); - - // Fast check for identical strings. - Label not_same; - __ cmp(left, right); - __ j(not_equal, ¬_same, Label::kNear); - STATIC_ASSERT(EQUAL == 0); - STATIC_ASSERT(kSmiTag == 0); - __ Move(eax, Immediate(Smi::FromInt(EQUAL))); - __ ret(0); - - // Handle not identical strings. - __ bind(¬_same); - - // Check that both strings are internalized. If they are, we're done - // because we already know they are not identical. But in the case of - // non-equality compare, we still need to determine the order. We - // also know they are both strings. - if (equality) { - Label do_compare; - STATIC_ASSERT(kInternalizedTag == 0); - __ or_(tmp1, tmp2); - __ test(tmp1, Immediate(kIsNotInternalizedMask)); - __ j(not_zero, &do_compare, Label::kNear); - // Make sure eax is non-zero. At this point input operands are - // guaranteed to be non-zero. - DCHECK(right.is(eax)); - __ ret(0); - __ bind(&do_compare); - } - - // Check that both strings are sequential one-byte. - Label runtime; - __ JumpIfNotBothSequentialOneByteStrings(left, right, tmp1, tmp2, &runtime); - - // Compare flat one byte strings. Returns when done. - if (equality) { - StringHelper::GenerateFlatOneByteStringEquals(masm, left, right, tmp1, - tmp2); - } else { - StringHelper::GenerateCompareFlatOneByteStrings(masm, left, right, tmp1, - tmp2, tmp3); - } - - // Handle more complex cases in runtime. - __ bind(&runtime); - if (equality) { - { - FrameScope scope(masm, StackFrame::INTERNAL); - __ Push(left); - __ Push(right); - __ CallRuntime(Runtime::kStringEqual); - } - __ sub(eax, Immediate(masm->isolate()->factory()->true_value())); - __ Ret(); - } else { - __ pop(tmp1); // Return address. - __ push(left); - __ push(right); - __ push(tmp1); - __ TailCallRuntime(Runtime::kStringCompare); - } - - __ bind(&miss); - GenerateMiss(masm); -} - - -void CompareICStub::GenerateReceivers(MacroAssembler* masm) { - DCHECK_EQ(CompareICState::RECEIVER, state()); - Label miss; - __ mov(ecx, edx); - __ and_(ecx, eax); - __ JumpIfSmi(ecx, &miss, Label::kNear); - - STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE); - __ CmpObjectType(eax, FIRST_JS_RECEIVER_TYPE, ecx); - __ j(below, &miss, Label::kNear); - __ CmpObjectType(edx, FIRST_JS_RECEIVER_TYPE, ecx); - __ j(below, &miss, Label::kNear); - - DCHECK_EQ(equal, GetCondition()); - __ sub(eax, edx); - __ ret(0); - - __ bind(&miss); - GenerateMiss(masm); -} - - -void CompareICStub::GenerateKnownReceivers(MacroAssembler* masm) { - Label miss; - Handle cell = Map::WeakCellForMap(known_map_); - __ mov(ecx, edx); - __ and_(ecx, eax); - __ JumpIfSmi(ecx, &miss, Label::kNear); - - __ GetWeakValue(edi, cell); - __ cmp(edi, FieldOperand(eax, HeapObject::kMapOffset)); - __ j(not_equal, &miss, Label::kNear); - __ cmp(edi, FieldOperand(edx, HeapObject::kMapOffset)); - __ j(not_equal, &miss, Label::kNear); - - if (Token::IsEqualityOp(op())) { - __ sub(eax, edx); - __ ret(0); - } else { - __ PopReturnAddressTo(ecx); - __ Push(edx); - __ Push(eax); - __ Push(Immediate(Smi::FromInt(NegativeComparisonResult(GetCondition())))); - __ PushReturnAddressFrom(ecx); - __ TailCallRuntime(Runtime::kCompare); - } - - __ bind(&miss); - GenerateMiss(masm); -} - - -void CompareICStub::GenerateMiss(MacroAssembler* masm) { - { - // Call the runtime system in a fresh internal frame. - FrameScope scope(masm, StackFrame::INTERNAL); - __ push(edx); // Preserve edx and eax. - __ push(eax); - __ push(edx); // And also use them as the arguments. - __ push(eax); - __ push(Immediate(Smi::FromInt(op()))); - __ CallRuntime(Runtime::kCompareIC_Miss); - // Compute the entry point of the rewritten stub. - __ lea(edi, FieldOperand(eax, Code::kHeaderSize)); - __ pop(eax); - __ pop(edx); - } - - // Do a tail call to the rewritten stub. - __ jmp(edi); -} - - -// Helper function used to check that the dictionary doesn't contain -// the property. This function may return false negatives, so miss_label -// must always call a backup property check that is complete. -// This function is safe to call if the receiver has fast properties. -// Name must be a unique name and receiver must be a heap object. -void NameDictionaryLookupStub::GenerateNegativeLookup(MacroAssembler* masm, - Label* miss, - Label* done, - Register properties, - Handle name, - Register r0) { - DCHECK(name->IsUniqueName()); - - // If names of slots in range from 1 to kProbes - 1 for the hash value are - // not equal to the name and kProbes-th slot is not used (its name is the - // undefined value), it guarantees the hash table doesn't contain the - // property. It's true even if some slots represent deleted properties - // (their names are the hole value). - for (int i = 0; i < kInlinedProbes; i++) { - // Compute the masked index: (hash + i + i * i) & mask. - Register index = r0; - // Capacity is smi 2^n. - __ mov(index, FieldOperand(properties, kCapacityOffset)); - __ dec(index); - __ and_(index, - Immediate(Smi::FromInt(name->Hash() + - NameDictionary::GetProbeOffset(i)))); - - // Scale the index by multiplying by the entry size. - STATIC_ASSERT(NameDictionary::kEntrySize == 3); - __ lea(index, Operand(index, index, times_2, 0)); // index *= 3. - Register entity_name = r0; - // Having undefined at this place means the name is not contained. - STATIC_ASSERT(kSmiTagSize == 1); - __ mov(entity_name, Operand(properties, index, times_half_pointer_size, - kElementsStartOffset - kHeapObjectTag)); - __ cmp(entity_name, masm->isolate()->factory()->undefined_value()); - __ j(equal, done); - - // Stop if found the property. - __ cmp(entity_name, Handle(name)); - __ j(equal, miss); - - Label good; - // Check for the hole and skip. - __ cmp(entity_name, masm->isolate()->factory()->the_hole_value()); - __ j(equal, &good, Label::kNear); - - // Check if the entry name is not a unique name. - __ mov(entity_name, FieldOperand(entity_name, HeapObject::kMapOffset)); - __ JumpIfNotUniqueNameInstanceType( - FieldOperand(entity_name, Map::kInstanceTypeOffset), miss); - __ bind(&good); - } - - NameDictionaryLookupStub stub(masm->isolate(), properties, r0, r0, - NEGATIVE_LOOKUP); - __ push(Immediate(Handle(name))); - __ push(Immediate(name->Hash())); - __ CallStub(&stub); - __ test(r0, r0); - __ j(not_zero, miss); - __ jmp(done); -} - -void NameDictionaryLookupStub::Generate(MacroAssembler* masm) { - // This stub overrides SometimesSetsUpAFrame() to return false. That means - // we cannot call anything that could cause a GC from this stub. - // Stack frame on entry: - // esp[0 * kPointerSize]: return address. - // esp[1 * kPointerSize]: key's hash. - // esp[2 * kPointerSize]: key. - // Registers: - // dictionary_: NameDictionary to probe. - // result_: used as scratch. - // index_: will hold an index of entry if lookup is successful. - // might alias with result_. - // Returns: - // result_ is zero if lookup failed, non zero otherwise. - - Label in_dictionary, maybe_in_dictionary, not_in_dictionary; - - Register scratch = result(); - - __ mov(scratch, FieldOperand(dictionary(), kCapacityOffset)); - __ dec(scratch); - __ SmiUntag(scratch); - __ push(scratch); - - // If names of slots in range from 1 to kProbes - 1 for the hash value are - // not equal to the name and kProbes-th slot is not used (its name is the - // undefined value), it guarantees the hash table doesn't contain the - // property. It's true even if some slots represent deleted properties - // (their names are the null value). - for (int i = kInlinedProbes; i < kTotalProbes; i++) { - // Compute the masked index: (hash + i + i * i) & mask. - __ mov(scratch, Operand(esp, 2 * kPointerSize)); - if (i > 0) { - __ add(scratch, Immediate(NameDictionary::GetProbeOffset(i))); - } - __ and_(scratch, Operand(esp, 0)); - - // Scale the index by multiplying by the entry size. - STATIC_ASSERT(NameDictionary::kEntrySize == 3); - __ lea(index(), Operand(scratch, scratch, times_2, 0)); // index *= 3. - - // Having undefined at this place means the name is not contained. - STATIC_ASSERT(kSmiTagSize == 1); - __ mov(scratch, Operand(dictionary(), index(), times_pointer_size, - kElementsStartOffset - kHeapObjectTag)); - __ cmp(scratch, isolate()->factory()->undefined_value()); - __ j(equal, ¬_in_dictionary); - - // Stop if found the property. - __ cmp(scratch, Operand(esp, 3 * kPointerSize)); - __ j(equal, &in_dictionary); - - if (i != kTotalProbes - 1 && mode() == NEGATIVE_LOOKUP) { - // If we hit a key that is not a unique name during negative - // lookup we have to bailout as this key might be equal to the - // key we are looking for. - - // Check if the entry name is not a unique name. - __ mov(scratch, FieldOperand(scratch, HeapObject::kMapOffset)); - __ JumpIfNotUniqueNameInstanceType( - FieldOperand(scratch, Map::kInstanceTypeOffset), - &maybe_in_dictionary); - } - } - - __ bind(&maybe_in_dictionary); - // If we are doing negative lookup then probing failure should be - // treated as a lookup success. For positive lookup probing failure - // should be treated as lookup failure. - if (mode() == POSITIVE_LOOKUP) { - __ mov(result(), Immediate(0)); - __ Drop(1); - __ ret(2 * kPointerSize); - } - - __ bind(&in_dictionary); - __ mov(result(), Immediate(1)); - __ Drop(1); - __ ret(2 * kPointerSize); - - __ bind(¬_in_dictionary); - __ mov(result(), Immediate(0)); - __ Drop(1); - __ ret(2 * kPointerSize); -} - - -void StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime( - Isolate* isolate) { - StoreBufferOverflowStub stub(isolate, kDontSaveFPRegs); - stub.GetCode(); - StoreBufferOverflowStub stub2(isolate, kSaveFPRegs); - stub2.GetCode(); -} - - -// Takes the input in 3 registers: address_ value_ and object_. A pointer to -// the value has just been written into the object, now this stub makes sure -// we keep the GC informed. The word in the object where the value has been -// written is in the address register. -void RecordWriteStub::Generate(MacroAssembler* masm) { - Label skip_to_incremental_noncompacting; - Label skip_to_incremental_compacting; - - // The first two instructions are generated with labels so as to get the - // offset fixed up correctly by the bind(Label*) call. We patch it back and - // forth between a compare instructions (a nop in this position) and the - // real branch when we start and stop incremental heap marking. - __ jmp(&skip_to_incremental_noncompacting, Label::kNear); - __ jmp(&skip_to_incremental_compacting, Label::kFar); - - if (remembered_set_action() == EMIT_REMEMBERED_SET) { - __ RememberedSetHelper(object(), address(), value(), save_fp_regs_mode(), - MacroAssembler::kReturnAtEnd); - } else { - __ ret(0); - } - - __ bind(&skip_to_incremental_noncompacting); - GenerateIncremental(masm, INCREMENTAL); - - __ bind(&skip_to_incremental_compacting); - GenerateIncremental(masm, INCREMENTAL_COMPACTION); - - // Initial mode of the stub is expected to be STORE_BUFFER_ONLY. - // Will be checked in IncrementalMarking::ActivateGeneratedStub. - masm->set_byte_at(0, kTwoByteNopInstruction); - masm->set_byte_at(2, kFiveByteNopInstruction); -} - - -void RecordWriteStub::GenerateIncremental(MacroAssembler* masm, Mode mode) { - regs_.Save(masm); - - if (remembered_set_action() == EMIT_REMEMBERED_SET) { - Label dont_need_remembered_set; - - __ mov(regs_.scratch0(), Operand(regs_.address(), 0)); - __ JumpIfNotInNewSpace(regs_.scratch0(), // Value. - regs_.scratch0(), - &dont_need_remembered_set); - - __ JumpIfInNewSpace(regs_.object(), regs_.scratch0(), - &dont_need_remembered_set); - - // First notify the incremental marker if necessary, then update the - // remembered set. - CheckNeedsToInformIncrementalMarker( - masm, - kUpdateRememberedSetOnNoNeedToInformIncrementalMarker, - mode); - InformIncrementalMarker(masm); - regs_.Restore(masm); - __ RememberedSetHelper(object(), address(), value(), save_fp_regs_mode(), - MacroAssembler::kReturnAtEnd); - - __ bind(&dont_need_remembered_set); - } - - CheckNeedsToInformIncrementalMarker( - masm, - kReturnOnNoNeedToInformIncrementalMarker, - mode); - InformIncrementalMarker(masm); - regs_.Restore(masm); - __ ret(0); -} - - -void RecordWriteStub::InformIncrementalMarker(MacroAssembler* masm) { - regs_.SaveCallerSaveRegisters(masm, save_fp_regs_mode()); - int argument_count = 3; - __ PrepareCallCFunction(argument_count, regs_.scratch0()); - __ mov(Operand(esp, 0 * kPointerSize), regs_.object()); - __ mov(Operand(esp, 1 * kPointerSize), regs_.address()); // Slot. - __ mov(Operand(esp, 2 * kPointerSize), - Immediate(ExternalReference::isolate_address(isolate()))); - - AllowExternalCallThatCantCauseGC scope(masm); - __ CallCFunction( - ExternalReference::incremental_marking_record_write_function(isolate()), - argument_count); - - regs_.RestoreCallerSaveRegisters(masm, save_fp_regs_mode()); -} - - -void RecordWriteStub::CheckNeedsToInformIncrementalMarker( - MacroAssembler* masm, - OnNoNeedToInformIncrementalMarker on_no_need, - Mode mode) { - Label need_incremental, need_incremental_pop_object; - -#ifndef V8_CONCURRENT_MARKING - Label object_is_black; - // Let's look at the color of the object: If it is not black we don't have - // to inform the incremental marker. - __ JumpIfBlack(regs_.object(), - regs_.scratch0(), - regs_.scratch1(), - &object_is_black, - Label::kNear); - - regs_.Restore(masm); - if (on_no_need == kUpdateRememberedSetOnNoNeedToInformIncrementalMarker) { - __ RememberedSetHelper(object(), address(), value(), save_fp_regs_mode(), - MacroAssembler::kReturnAtEnd); - } else { - __ ret(0); - } - - __ bind(&object_is_black); -#endif - - // Get the value from the slot. - __ mov(regs_.scratch0(), Operand(regs_.address(), 0)); - - if (mode == INCREMENTAL_COMPACTION) { - Label ensure_not_white; - - __ CheckPageFlag(regs_.scratch0(), // Contains value. - regs_.scratch1(), // Scratch. - MemoryChunk::kEvacuationCandidateMask, - zero, - &ensure_not_white, - Label::kNear); - - __ CheckPageFlag(regs_.object(), - regs_.scratch1(), // Scratch. - MemoryChunk::kSkipEvacuationSlotsRecordingMask, - not_zero, - &ensure_not_white, - Label::kNear); - - __ jmp(&need_incremental); - - __ bind(&ensure_not_white); - } - - // We need an extra register for this, so we push the object register - // temporarily. - __ push(regs_.object()); - __ JumpIfWhite(regs_.scratch0(), // The value. - regs_.scratch1(), // Scratch. - regs_.object(), // Scratch. - &need_incremental_pop_object, Label::kNear); - __ pop(regs_.object()); - - regs_.Restore(masm); - if (on_no_need == kUpdateRememberedSetOnNoNeedToInformIncrementalMarker) { - __ RememberedSetHelper(object(), address(), value(), save_fp_regs_mode(), - MacroAssembler::kReturnAtEnd); - } else { - __ ret(0); - } - - __ bind(&need_incremental_pop_object); - __ pop(regs_.object()); - - __ bind(&need_incremental); - - // Fall through when we need to inform the incremental marker. -} - - -void ProfileEntryHookStub::MaybeCallEntryHook(MacroAssembler* masm) { - if (masm->isolate()->function_entry_hook() != NULL) { - 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(isolate()->function_entry_hook() != NULL); - __ 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); -} - -template -static void CreateArrayDispatch(MacroAssembler* masm, - AllocationSiteOverrideMode mode) { - if (mode == DISABLE_ALLOCATION_SITES) { - T stub(masm->isolate(), GetInitialFastElementsKind(), mode); - __ TailCallStub(&stub); - } else if (mode == DONT_OVERRIDE) { - int last_index = - GetSequenceIndexFromFastElementsKind(TERMINAL_FAST_ELEMENTS_KIND); - for (int i = 0; i <= last_index; ++i) { - Label next; - ElementsKind kind = GetFastElementsKindFromSequenceIndex(i); - __ cmp(edx, kind); - __ j(not_equal, &next); - T stub(masm->isolate(), kind); - __ TailCallStub(&stub); - __ bind(&next); - } - - // If we reached this point there is a problem. - __ Abort(kUnexpectedElementsKindInArrayConstructor); - } else { - UNREACHABLE(); - } -} - -static void CreateArrayDispatchOneArgument(MacroAssembler* masm, - AllocationSiteOverrideMode mode) { - // ebx - allocation site (if mode != DISABLE_ALLOCATION_SITES) - // edx - kind (if mode != DISABLE_ALLOCATION_SITES) - // eax - number of arguments - // edi - constructor? - // esp[0] - return address - // esp[4] - last argument - Label normal_sequence; - if (mode == DONT_OVERRIDE) { - STATIC_ASSERT(PACKED_SMI_ELEMENTS == 0); - STATIC_ASSERT(HOLEY_SMI_ELEMENTS == 1); - STATIC_ASSERT(PACKED_ELEMENTS == 2); - STATIC_ASSERT(HOLEY_ELEMENTS == 3); - STATIC_ASSERT(PACKED_DOUBLE_ELEMENTS == 4); - STATIC_ASSERT(HOLEY_DOUBLE_ELEMENTS == 5); - - // is the low bit set? If so, we are holey and that is good. - __ test_b(edx, Immediate(1)); - __ j(not_zero, &normal_sequence); - } - - // look at the first argument - __ mov(ecx, Operand(esp, kPointerSize)); - __ test(ecx, ecx); - __ j(zero, &normal_sequence); - - if (mode == DISABLE_ALLOCATION_SITES) { - ElementsKind initial = GetInitialFastElementsKind(); - ElementsKind holey_initial = GetHoleyElementsKind(initial); - - ArraySingleArgumentConstructorStub stub_holey( - masm->isolate(), holey_initial, DISABLE_ALLOCATION_SITES); - __ TailCallStub(&stub_holey); - - __ bind(&normal_sequence); - ArraySingleArgumentConstructorStub stub(masm->isolate(), initial, - DISABLE_ALLOCATION_SITES); - __ TailCallStub(&stub); - } else if (mode == DONT_OVERRIDE) { - // We are going to create a holey array, but our kind is non-holey. - // Fix kind and retry. - __ inc(edx); - - if (FLAG_debug_code) { - Handle allocation_site_map = - masm->isolate()->factory()->allocation_site_map(); - __ cmp(FieldOperand(ebx, 0), Immediate(allocation_site_map)); - __ Assert(equal, kExpectedAllocationSite); - } - - // Save the resulting elements kind in type info. We can't just store r3 - // in the AllocationSite::transition_info field because elements kind is - // restricted to a portion of the field...upper bits need to be left alone. - STATIC_ASSERT(AllocationSite::ElementsKindBits::kShift == 0); - __ add( - FieldOperand(ebx, AllocationSite::kTransitionInfoOrBoilerplateOffset), - Immediate(Smi::FromInt(kFastElementsKindPackedToHoley))); - - __ bind(&normal_sequence); - int last_index = - GetSequenceIndexFromFastElementsKind(TERMINAL_FAST_ELEMENTS_KIND); - for (int i = 0; i <= last_index; ++i) { - Label next; - ElementsKind kind = GetFastElementsKindFromSequenceIndex(i); - __ cmp(edx, kind); - __ j(not_equal, &next); - ArraySingleArgumentConstructorStub stub(masm->isolate(), kind); - __ TailCallStub(&stub); - __ bind(&next); - } - - // If we reached this point there is a problem. - __ Abort(kUnexpectedElementsKindInArrayConstructor); - } else { - UNREACHABLE(); - } -} - -template -static void ArrayConstructorStubAheadOfTimeHelper(Isolate* isolate) { - int to_index = - GetSequenceIndexFromFastElementsKind(TERMINAL_FAST_ELEMENTS_KIND); - for (int i = 0; i <= to_index; ++i) { - ElementsKind kind = GetFastElementsKindFromSequenceIndex(i); - T stub(isolate, kind); - stub.GetCode(); - if (AllocationSite::ShouldTrack(kind)) { - T stub1(isolate, kind, DISABLE_ALLOCATION_SITES); - stub1.GetCode(); - } - } -} - -void CommonArrayConstructorStub::GenerateStubsAheadOfTime(Isolate* isolate) { - ArrayConstructorStubAheadOfTimeHelper( - isolate); - ArrayConstructorStubAheadOfTimeHelper( - isolate); - ArrayNArgumentsConstructorStub stub(isolate); - stub.GetCode(); - - ElementsKind kinds[2] = {PACKED_ELEMENTS, HOLEY_ELEMENTS}; - for (int i = 0; i < 2; i++) { - // For internal arrays we only need a few things - InternalArrayNoArgumentConstructorStub stubh1(isolate, kinds[i]); - stubh1.GetCode(); - InternalArraySingleArgumentConstructorStub stubh2(isolate, kinds[i]); - stubh2.GetCode(); - } -} - -void ArrayConstructorStub::GenerateDispatchToArrayStub( - MacroAssembler* masm, AllocationSiteOverrideMode mode) { - Label not_zero_case, not_one_case; - __ test(eax, eax); - __ j(not_zero, ¬_zero_case); - CreateArrayDispatch(masm, mode); - - __ bind(¬_zero_case); - __ cmp(eax, 1); - __ j(greater, ¬_one_case); - CreateArrayDispatchOneArgument(masm, mode); - - __ bind(¬_one_case); - ArrayNArgumentsConstructorStub stub(masm->isolate()); - __ TailCallStub(&stub); -} - -void ArrayConstructorStub::Generate(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- eax : argc (only if argument_count() is ANY or MORE_THAN_ONE) - // -- ebx : AllocationSite or undefined - // -- edi : constructor - // -- edx : Original constructor - // -- esp[0] : return address - // -- esp[4] : last argument - // ----------------------------------- - if (FLAG_debug_code) { - // The array construct code is only set for the global and natives - // builtin Array functions which always have maps. - - // Initial map for the builtin Array function should be a map. - __ mov(ecx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset)); - // Will both indicate a NULL and a Smi. - __ test(ecx, Immediate(kSmiTagMask)); - __ Assert(not_zero, kUnexpectedInitialMapForArrayFunction); - __ CmpObjectType(ecx, MAP_TYPE, ecx); - __ Assert(equal, kUnexpectedInitialMapForArrayFunction); - - // We should either have undefined in ebx or a valid AllocationSite - __ AssertUndefinedOrAllocationSite(ebx); - } - - Label subclassing; - - // Enter the context of the Array function. - __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); - - __ cmp(edx, edi); - __ j(not_equal, &subclassing); - - Label no_info; - // If the feedback vector is the undefined value call an array constructor - // that doesn't use AllocationSites. - __ cmp(ebx, isolate()->factory()->undefined_value()); - __ j(equal, &no_info); - - // Only look at the lower 16 bits of the transition info. - __ mov(edx, - FieldOperand(ebx, AllocationSite::kTransitionInfoOrBoilerplateOffset)); - __ SmiUntag(edx); - STATIC_ASSERT(AllocationSite::ElementsKindBits::kShift == 0); - __ and_(edx, Immediate(AllocationSite::ElementsKindBits::kMask)); - GenerateDispatchToArrayStub(masm, DONT_OVERRIDE); - - __ bind(&no_info); - GenerateDispatchToArrayStub(masm, DISABLE_ALLOCATION_SITES); - - // Subclassing. - __ bind(&subclassing); - __ mov(Operand(esp, eax, times_pointer_size, kPointerSize), edi); - __ add(eax, Immediate(3)); - __ PopReturnAddressTo(ecx); - __ Push(edx); - __ Push(ebx); - __ PushReturnAddressFrom(ecx); - __ JumpToExternalReference(ExternalReference(Runtime::kNewArray, isolate())); -} - -void InternalArrayConstructorStub::GenerateCase(MacroAssembler* masm, - ElementsKind kind) { - Label not_zero_case, not_one_case; - Label normal_sequence; - - __ test(eax, eax); - __ j(not_zero, ¬_zero_case); - InternalArrayNoArgumentConstructorStub stub0(isolate(), kind); - __ TailCallStub(&stub0); - - __ bind(¬_zero_case); - __ cmp(eax, 1); - __ j(greater, ¬_one_case); - - if (IsFastPackedElementsKind(kind)) { - // We might need to create a holey array - // look at the first argument - __ mov(ecx, Operand(esp, kPointerSize)); - __ test(ecx, ecx); - __ j(zero, &normal_sequence); - - InternalArraySingleArgumentConstructorStub stub1_holey( - isolate(), GetHoleyElementsKind(kind)); - __ TailCallStub(&stub1_holey); - } - - __ bind(&normal_sequence); - InternalArraySingleArgumentConstructorStub stub1(isolate(), kind); - __ TailCallStub(&stub1); - - __ bind(¬_one_case); - ArrayNArgumentsConstructorStub stubN(isolate()); - __ TailCallStub(&stubN); -} - -void InternalArrayConstructorStub::Generate(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- eax : argc - // -- edi : constructor - // -- esp[0] : return address - // -- esp[4] : last argument - // ----------------------------------- - - if (FLAG_debug_code) { - // The array construct code is only set for the global and natives - // builtin Array functions which always have maps. - - // Initial map for the builtin Array function should be a map. - __ mov(ecx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset)); - // Will both indicate a NULL and a Smi. - __ test(ecx, Immediate(kSmiTagMask)); - __ Assert(not_zero, kUnexpectedInitialMapForArrayFunction); - __ CmpObjectType(ecx, MAP_TYPE, ecx); - __ Assert(equal, kUnexpectedInitialMapForArrayFunction); - } - - // Figure out the right elements kind - __ mov(ecx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset)); - - // Load the map's "bit field 2" into |result|. We only need the first byte, - // but the following masking takes care of that anyway. - __ mov(ecx, FieldOperand(ecx, Map::kBitField2Offset)); - // Retrieve elements_kind from bit field 2. - __ DecodeField(ecx); - - if (FLAG_debug_code) { - Label done; - __ cmp(ecx, Immediate(PACKED_ELEMENTS)); - __ j(equal, &done); - __ cmp(ecx, Immediate(HOLEY_ELEMENTS)); - __ Assert(equal, kInvalidElementsKindForInternalArrayOrInternalPackedArray); - __ bind(&done); - } - - Label fast_elements_case; - __ cmp(ecx, Immediate(PACKED_ELEMENTS)); - __ j(equal, &fast_elements_case); - GenerateCase(masm, HOLEY_ELEMENTS); - - __ bind(&fast_elements_case); - GenerateCase(masm, PACKED_ELEMENTS); -} - -void FastNewRestParameterStub::Generate(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- edi : function - // -- esi : context - // -- ebp : frame pointer - // -- esp[0] : return address - // ----------------------------------- - __ AssertFunction(edi); - - // Make edx point to the JavaScript frame. - __ mov(edx, ebp); - if (skip_stub_frame()) { - // For Ignition we need to skip the handler/stub frame to reach the - // JavaScript frame for the function. - __ mov(edx, Operand(edx, StandardFrameConstants::kCallerFPOffset)); - } - if (FLAG_debug_code) { - Label ok; - __ cmp(edi, Operand(edx, StandardFrameConstants::kFunctionOffset)); - __ j(equal, &ok); - __ Abort(kInvalidFrameForFastNewRestArgumentsStub); - __ bind(&ok); - } - - // Check if we have rest parameters (only possible if we have an - // arguments adaptor frame below the function frame). - Label no_rest_parameters; - __ mov(ebx, Operand(edx, StandardFrameConstants::kCallerFPOffset)); - __ cmp(Operand(ebx, CommonFrameConstants::kContextOrFrameTypeOffset), - Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); - __ j(not_equal, &no_rest_parameters, Label::kNear); - - // Check if the arguments adaptor frame contains more arguments than - // specified by the function's internal formal parameter count. - Label rest_parameters; - __ mov(ecx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); - __ mov(eax, Operand(ebx, ArgumentsAdaptorFrameConstants::kLengthOffset)); - __ sub(eax, - FieldOperand(ecx, SharedFunctionInfo::kFormalParameterCountOffset)); - __ j(greater, &rest_parameters); - - // Return an empty rest parameter array. - __ bind(&no_rest_parameters); - { - // ----------- S t a t e ------------- - // -- esi : context - // -- esp[0] : return address - // ----------------------------------- - - // Allocate an empty rest parameter array. - Label allocate, done_allocate; - __ Allocate(JSArray::kSize, eax, edx, ecx, &allocate, NO_ALLOCATION_FLAGS); - __ bind(&done_allocate); - - // Setup the rest parameter array in rax. - __ LoadGlobalFunction(Context::JS_ARRAY_PACKED_ELEMENTS_MAP_INDEX, ecx); - __ mov(FieldOperand(eax, JSArray::kMapOffset), ecx); - __ mov(ecx, isolate()->factory()->empty_fixed_array()); - __ mov(FieldOperand(eax, JSArray::kPropertiesOrHashOffset), ecx); - __ mov(FieldOperand(eax, JSArray::kElementsOffset), ecx); - __ mov(FieldOperand(eax, JSArray::kLengthOffset), Immediate(Smi::kZero)); - STATIC_ASSERT(JSArray::kSize == 4 * kPointerSize); - __ Ret(); - - // Fall back to %AllocateInNewSpace. - __ bind(&allocate); - { - FrameScope scope(masm, StackFrame::INTERNAL); - __ Push(Smi::FromInt(JSArray::kSize)); - __ CallRuntime(Runtime::kAllocateInNewSpace); - } - __ jmp(&done_allocate); - } - - __ bind(&rest_parameters); - { - // Compute the pointer to the first rest parameter (skippping the receiver). - __ lea(ebx, - Operand(ebx, eax, times_half_pointer_size, - StandardFrameConstants::kCallerSPOffset - 1 * kPointerSize)); - - // ----------- S t a t e ------------- - // -- esi : context - // -- eax : number of rest parameters (tagged) - // -- ebx : pointer to first rest parameters - // -- esp[0] : return address - // ----------------------------------- - - // Allocate space for the rest parameter array plus the backing store. - Label allocate, done_allocate; - __ lea(ecx, Operand(eax, times_half_pointer_size, - JSArray::kSize + FixedArray::kHeaderSize)); - __ Allocate(ecx, edx, edi, no_reg, &allocate, NO_ALLOCATION_FLAGS); - __ bind(&done_allocate); - - // Setup the elements array in edx. - __ mov(FieldOperand(edx, FixedArray::kMapOffset), - isolate()->factory()->fixed_array_map()); - __ mov(FieldOperand(edx, FixedArray::kLengthOffset), eax); - { - Label loop, done_loop; - __ Move(ecx, Smi::kZero); - __ bind(&loop); - __ cmp(ecx, eax); - __ j(equal, &done_loop, Label::kNear); - __ mov(edi, Operand(ebx, 0 * kPointerSize)); - __ mov(FieldOperand(edx, ecx, times_half_pointer_size, - FixedArray::kHeaderSize), - edi); - __ sub(ebx, Immediate(1 * kPointerSize)); - __ add(ecx, Immediate(Smi::FromInt(1))); - __ jmp(&loop); - __ bind(&done_loop); - } - - // Setup the rest parameter array in edi. - __ lea(edi, - Operand(edx, eax, times_half_pointer_size, FixedArray::kHeaderSize)); - __ LoadGlobalFunction(Context::JS_ARRAY_PACKED_ELEMENTS_MAP_INDEX, ecx); - __ mov(FieldOperand(edi, JSArray::kMapOffset), ecx); - __ mov(FieldOperand(edi, JSArray::kPropertiesOrHashOffset), - isolate()->factory()->empty_fixed_array()); - __ mov(FieldOperand(edi, JSArray::kElementsOffset), edx); - __ mov(FieldOperand(edi, JSArray::kLengthOffset), eax); - STATIC_ASSERT(JSArray::kSize == 4 * kPointerSize); - __ mov(eax, edi); - __ Ret(); - - // Fall back to %AllocateInNewSpace (if not too big). - Label too_big_for_new_space; - __ bind(&allocate); - __ cmp(ecx, Immediate(kMaxRegularHeapObjectSize)); - __ j(greater, &too_big_for_new_space); - { - FrameScope scope(masm, StackFrame::INTERNAL); - __ SmiTag(ecx); - __ Push(eax); - __ Push(ebx); - __ Push(ecx); - __ CallRuntime(Runtime::kAllocateInNewSpace); - __ mov(edx, eax); - __ Pop(ebx); - __ Pop(eax); - } - __ jmp(&done_allocate); - - // Fall back to %NewRestParameter. - __ bind(&too_big_for_new_space); - __ PopReturnAddressTo(ecx); - // We reload the function from the caller frame due to register pressure - // within this stub. This is the slow path, hence reloading is preferable. - if (skip_stub_frame()) { - // For Ignition we need to skip the handler/stub frame to reach the - // JavaScript frame for the function. - __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset)); - __ Push(Operand(edx, StandardFrameConstants::kFunctionOffset)); - } else { - __ Push(Operand(ebp, StandardFrameConstants::kFunctionOffset)); - } - __ PushReturnAddressFrom(ecx); - __ TailCallRuntime(Runtime::kNewRestParameter); - } -} - -void FastNewSloppyArgumentsStub::Generate(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- edi : function - // -- esi : context - // -- ebp : frame pointer - // -- esp[0] : return address - // ----------------------------------- - __ AssertFunction(edi); - - // Make ecx point to the JavaScript frame. - __ mov(ecx, ebp); - if (skip_stub_frame()) { - // For Ignition we need to skip the handler/stub frame to reach the - // JavaScript frame for the function. - __ mov(ecx, Operand(ecx, StandardFrameConstants::kCallerFPOffset)); - } - if (FLAG_debug_code) { - Label ok; - __ cmp(edi, Operand(ecx, StandardFrameConstants::kFunctionOffset)); - __ j(equal, &ok); - __ Abort(kInvalidFrameForFastNewSloppyArgumentsStub); - __ bind(&ok); - } - - // TODO(bmeurer): Cleanup to match the FastNewStrictArgumentsStub. - __ mov(ebx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); - __ mov(ebx, - FieldOperand(ebx, SharedFunctionInfo::kFormalParameterCountOffset)); - __ lea(edx, Operand(ecx, ebx, times_half_pointer_size, - StandardFrameConstants::kCallerSPOffset)); - - // ebx : number of parameters (tagged) - // edx : parameters pointer - // edi : function - // ecx : JavaScript frame pointer. - // esp[0] : return address - - // Check if the calling frame is an arguments adaptor frame. - Label adaptor_frame, try_allocate, runtime; - __ mov(eax, Operand(ecx, StandardFrameConstants::kCallerFPOffset)); - __ mov(eax, Operand(eax, CommonFrameConstants::kContextOrFrameTypeOffset)); - __ cmp(eax, Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); - __ j(equal, &adaptor_frame, Label::kNear); - - // No adaptor, parameter count = argument count. - __ mov(ecx, ebx); - __ push(ebx); - __ jmp(&try_allocate, Label::kNear); - - // We have an adaptor frame. Patch the parameters pointer. - __ bind(&adaptor_frame); - __ push(ebx); - __ mov(edx, Operand(ecx, StandardFrameConstants::kCallerFPOffset)); - __ mov(ecx, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset)); - __ lea(edx, - Operand(edx, ecx, times_2, StandardFrameConstants::kCallerSPOffset)); - - // ebx = parameter count (tagged) - // ecx = argument count (smi-tagged) - // Compute the mapped parameter count = min(ebx, ecx) in ebx. - __ cmp(ebx, ecx); - __ j(less_equal, &try_allocate, Label::kNear); - __ mov(ebx, ecx); - - // Save mapped parameter count and function. - __ bind(&try_allocate); - __ push(edi); - __ push(ebx); - - // Compute the sizes of backing store, parameter map, and arguments object. - // 1. Parameter map, has 2 extra words containing context and backing store. - const int kParameterMapHeaderSize = - FixedArray::kHeaderSize + 2 * kPointerSize; - Label no_parameter_map; - __ test(ebx, ebx); - __ j(zero, &no_parameter_map, Label::kNear); - __ lea(ebx, Operand(ebx, times_2, kParameterMapHeaderSize)); - __ bind(&no_parameter_map); - - // 2. Backing store. - __ lea(ebx, Operand(ebx, ecx, times_2, FixedArray::kHeaderSize)); - - // 3. Arguments object. - __ add(ebx, Immediate(JSSloppyArgumentsObject::kSize)); - - // Do the allocation of all three objects in one go. - __ Allocate(ebx, eax, edi, no_reg, &runtime, NO_ALLOCATION_FLAGS); - - // eax = address of new object(s) (tagged) - // ecx = argument count (smi-tagged) - // esp[0] = mapped parameter count (tagged) - // esp[4] = function - // esp[8] = parameter count (tagged) - // Get the arguments map from the current native context into edi. - Label has_mapped_parameters, instantiate; - __ mov(edi, NativeContextOperand()); - __ mov(ebx, Operand(esp, 0 * kPointerSize)); - __ test(ebx, ebx); - __ j(not_zero, &has_mapped_parameters, Label::kNear); - __ mov( - edi, - Operand(edi, Context::SlotOffset(Context::SLOPPY_ARGUMENTS_MAP_INDEX))); - __ jmp(&instantiate, Label::kNear); - - __ bind(&has_mapped_parameters); - __ mov(edi, Operand(edi, Context::SlotOffset( - Context::FAST_ALIASED_ARGUMENTS_MAP_INDEX))); - __ bind(&instantiate); - - // eax = address of new object (tagged) - // ebx = mapped parameter count (tagged) - // ecx = argument count (smi-tagged) - // edi = address of arguments map (tagged) - // esp[0] = mapped parameter count (tagged) - // esp[4] = function - // esp[8] = parameter count (tagged) - // Copy the JS object part. - __ mov(FieldOperand(eax, JSObject::kMapOffset), edi); - __ mov(FieldOperand(eax, JSObject::kPropertiesOrHashOffset), - masm->isolate()->factory()->empty_fixed_array()); - __ mov(FieldOperand(eax, JSObject::kElementsOffset), - masm->isolate()->factory()->empty_fixed_array()); - - // Set up the callee in-object property. - STATIC_ASSERT(JSSloppyArgumentsObject::kCalleeIndex == 1); - __ mov(edi, Operand(esp, 1 * kPointerSize)); - __ AssertNotSmi(edi); - __ mov(FieldOperand(eax, JSSloppyArgumentsObject::kCalleeOffset), edi); - - // Use the length (smi tagged) and set that as an in-object property too. - __ AssertSmi(ecx); - __ mov(FieldOperand(eax, JSSloppyArgumentsObject::kLengthOffset), ecx); - - // Set up the elements pointer in the allocated arguments object. - // If we allocated a parameter map, edi will point there, otherwise to the - // backing store. - __ lea(edi, Operand(eax, JSSloppyArgumentsObject::kSize)); - __ mov(FieldOperand(eax, JSObject::kElementsOffset), edi); - - // eax = address of new object (tagged) - // ebx = mapped parameter count (tagged) - // ecx = argument count (tagged) - // edx = address of receiver argument - // edi = address of parameter map or backing store (tagged) - // esp[0] = mapped parameter count (tagged) - // esp[4] = function - // esp[8] = parameter count (tagged) - // Free two registers. - __ push(edx); - __ push(eax); - - // Initialize parameter map. If there are no mapped arguments, we're done. - Label skip_parameter_map; - __ test(ebx, ebx); - __ j(zero, &skip_parameter_map); - - __ mov(FieldOperand(edi, FixedArray::kMapOffset), - Immediate(isolate()->factory()->sloppy_arguments_elements_map())); - __ lea(eax, Operand(ebx, reinterpret_cast(Smi::FromInt(2)))); - __ mov(FieldOperand(edi, FixedArray::kLengthOffset), eax); - __ mov(FieldOperand(edi, FixedArray::kHeaderSize + 0 * kPointerSize), esi); - __ lea(eax, Operand(edi, ebx, times_2, kParameterMapHeaderSize)); - __ mov(FieldOperand(edi, FixedArray::kHeaderSize + 1 * kPointerSize), eax); - - // Copy the parameter slots and the holes in the arguments. - // We need to fill in mapped_parameter_count slots. They index the context, - // where parameters are stored in reverse order, at - // MIN_CONTEXT_SLOTS .. MIN_CONTEXT_SLOTS+parameter_count-1 - // The mapped parameter thus need to get indices - // MIN_CONTEXT_SLOTS+parameter_count-1 .. - // MIN_CONTEXT_SLOTS+parameter_count-mapped_parameter_count - // We loop from right to left. - Label parameters_loop, parameters_test; - __ push(ecx); - __ mov(eax, Operand(esp, 3 * kPointerSize)); - __ mov(ebx, Immediate(Smi::FromInt(Context::MIN_CONTEXT_SLOTS))); - __ add(ebx, Operand(esp, 5 * kPointerSize)); - __ sub(ebx, eax); - __ mov(ecx, isolate()->factory()->the_hole_value()); - __ mov(edx, edi); - __ lea(edi, Operand(edi, eax, times_2, kParameterMapHeaderSize)); - // eax = loop variable (tagged) - // ebx = mapping index (tagged) - // ecx = the hole value - // edx = address of parameter map (tagged) - // edi = address of backing store (tagged) - // esp[0] = argument count (tagged) - // esp[4] = address of new object (tagged) - // esp[8] = address of receiver argument - // esp[12] = mapped parameter count (tagged) - // esp[16] = function - // esp[20] = parameter count (tagged) - __ jmp(¶meters_test, Label::kNear); - - __ bind(¶meters_loop); - __ sub(eax, Immediate(Smi::FromInt(1))); - __ mov(FieldOperand(edx, eax, times_2, kParameterMapHeaderSize), ebx); - __ mov(FieldOperand(edi, eax, times_2, FixedArray::kHeaderSize), ecx); - __ add(ebx, Immediate(Smi::FromInt(1))); - __ bind(¶meters_test); - __ test(eax, eax); - __ j(not_zero, ¶meters_loop, Label::kNear); - __ pop(ecx); - - __ bind(&skip_parameter_map); - - // ecx = argument count (tagged) - // edi = address of backing store (tagged) - // esp[0] = address of new object (tagged) - // esp[4] = address of receiver argument - // esp[8] = mapped parameter count (tagged) - // esp[12] = function - // esp[16] = parameter count (tagged) - // Copy arguments header and remaining slots (if there are any). - __ mov(FieldOperand(edi, FixedArray::kMapOffset), - Immediate(isolate()->factory()->fixed_array_map())); - __ mov(FieldOperand(edi, FixedArray::kLengthOffset), ecx); - - Label arguments_loop, arguments_test; - __ mov(ebx, Operand(esp, 2 * kPointerSize)); - __ mov(edx, Operand(esp, 1 * kPointerSize)); - __ sub(edx, ebx); // Is there a smarter way to do negative scaling? - __ sub(edx, ebx); - __ jmp(&arguments_test, Label::kNear); - - __ bind(&arguments_loop); - __ sub(edx, Immediate(kPointerSize)); - __ mov(eax, Operand(edx, 0)); - __ mov(FieldOperand(edi, ebx, times_2, FixedArray::kHeaderSize), eax); - __ add(ebx, Immediate(Smi::FromInt(1))); - - __ bind(&arguments_test); - __ cmp(ebx, ecx); - __ j(less, &arguments_loop, Label::kNear); - - // Restore. - __ pop(eax); // Address of arguments object. - __ Drop(4); - - // Return. - __ ret(0); - - // Do the runtime call to allocate the arguments object. - __ bind(&runtime); - __ pop(eax); // Remove saved mapped parameter count. - __ pop(edi); // Pop saved function. - __ pop(eax); // Remove saved parameter count. - __ pop(eax); // Pop return address. - __ push(edi); // Push function. - __ push(edx); // Push parameters pointer. - __ push(ecx); // Push parameter count. - __ push(eax); // Push return address. - __ TailCallRuntime(Runtime::kNewSloppyArguments); -} - -void FastNewStrictArgumentsStub::Generate(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- edi : function - // -- esi : context - // -- ebp : frame pointer - // -- esp[0] : return address - // ----------------------------------- - __ AssertFunction(edi); - - // Make edx point to the JavaScript frame. - __ mov(edx, ebp); - if (skip_stub_frame()) { - // For Ignition we need to skip the handler/stub frame to reach the - // JavaScript frame for the function. - __ mov(edx, Operand(edx, StandardFrameConstants::kCallerFPOffset)); - } - if (FLAG_debug_code) { - Label ok; - __ cmp(edi, Operand(edx, StandardFrameConstants::kFunctionOffset)); - __ j(equal, &ok); - __ Abort(kInvalidFrameForFastNewStrictArgumentsStub); - __ bind(&ok); - } - - // Check if we have an arguments adaptor frame below the function frame. - Label arguments_adaptor, arguments_done; - __ mov(ebx, Operand(edx, StandardFrameConstants::kCallerFPOffset)); - __ cmp(Operand(ebx, CommonFrameConstants::kContextOrFrameTypeOffset), - Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); - __ j(equal, &arguments_adaptor, Label::kNear); - { - __ mov(eax, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); - __ mov(eax, - FieldOperand(eax, SharedFunctionInfo::kFormalParameterCountOffset)); - __ lea(ebx, - Operand(edx, eax, times_half_pointer_size, - StandardFrameConstants::kCallerSPOffset - 1 * kPointerSize)); - } - __ jmp(&arguments_done, Label::kNear); - __ bind(&arguments_adaptor); - { - __ mov(eax, Operand(ebx, ArgumentsAdaptorFrameConstants::kLengthOffset)); - __ lea(ebx, - Operand(ebx, eax, times_half_pointer_size, - StandardFrameConstants::kCallerSPOffset - 1 * kPointerSize)); - } - __ bind(&arguments_done); - - // ----------- S t a t e ------------- - // -- eax : number of arguments (tagged) - // -- ebx : pointer to the first argument - // -- esi : context - // -- esp[0] : return address - // ----------------------------------- - - // Allocate space for the strict arguments object plus the backing store. - Label allocate, done_allocate; - __ lea(ecx, - Operand(eax, times_half_pointer_size, - JSStrictArgumentsObject::kSize + FixedArray::kHeaderSize)); - __ Allocate(ecx, edx, edi, no_reg, &allocate, NO_ALLOCATION_FLAGS); - __ bind(&done_allocate); - - // Setup the elements array in edx. - __ mov(FieldOperand(edx, FixedArray::kMapOffset), - isolate()->factory()->fixed_array_map()); - __ mov(FieldOperand(edx, FixedArray::kLengthOffset), eax); - { - Label loop, done_loop; - __ Move(ecx, Smi::kZero); - __ bind(&loop); - __ cmp(ecx, eax); - __ j(equal, &done_loop, Label::kNear); - __ mov(edi, Operand(ebx, 0 * kPointerSize)); - __ mov(FieldOperand(edx, ecx, times_half_pointer_size, - FixedArray::kHeaderSize), - edi); - __ sub(ebx, Immediate(1 * kPointerSize)); - __ add(ecx, Immediate(Smi::FromInt(1))); - __ jmp(&loop); - __ bind(&done_loop); - } - - // Setup the rest parameter array in edi. - __ lea(edi, - Operand(edx, eax, times_half_pointer_size, FixedArray::kHeaderSize)); - __ LoadGlobalFunction(Context::STRICT_ARGUMENTS_MAP_INDEX, ecx); - __ mov(FieldOperand(edi, JSStrictArgumentsObject::kMapOffset), ecx); - __ mov(FieldOperand(edi, JSStrictArgumentsObject::kPropertiesOrHashOffset), - isolate()->factory()->empty_fixed_array()); - __ mov(FieldOperand(edi, JSStrictArgumentsObject::kElementsOffset), edx); - __ mov(FieldOperand(edi, JSStrictArgumentsObject::kLengthOffset), eax); - STATIC_ASSERT(JSStrictArgumentsObject::kSize == 4 * kPointerSize); - __ mov(eax, edi); - __ Ret(); - - // Fall back to %AllocateInNewSpace (if not too big). - Label too_big_for_new_space; - __ bind(&allocate); - __ cmp(ecx, Immediate(kMaxRegularHeapObjectSize)); - __ j(greater, &too_big_for_new_space); - { - FrameScope scope(masm, StackFrame::INTERNAL); - __ SmiTag(ecx); - __ Push(eax); - __ Push(ebx); - __ Push(ecx); - __ CallRuntime(Runtime::kAllocateInNewSpace); - __ mov(edx, eax); - __ Pop(ebx); - __ Pop(eax); - } - __ jmp(&done_allocate); - - // Fall back to %NewStrictArguments. - __ bind(&too_big_for_new_space); - __ PopReturnAddressTo(ecx); - // We reload the function from the caller frame due to register pressure - // within this stub. This is the slow path, hence reloading is preferable. - if (skip_stub_frame()) { - // For Ignition we need to skip the handler/stub frame to reach the - // JavaScript frame for the function. - __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset)); - __ Push(Operand(edx, StandardFrameConstants::kFunctionOffset)); - } else { - __ Push(Operand(ebp, StandardFrameConstants::kFunctionOffset)); - } - __ PushReturnAddressFrom(ecx); - __ TailCallRuntime(Runtime::kNewStrictArguments); -} - -// 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, - Operand* context_restore_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.is(function_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)); - - 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(isolate), - 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(isolate), - 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(Operand::StaticVariable(next_address), ebx); - __ sub(Operand::StaticVariable(level_address), Immediate(1)); - __ Assert(above_equal, kInvalidHandleScopeLevel); - __ cmp(edi, Operand::StaticVariable(limit_address)); - __ j(not_equal, &delete_allocated_handles); - - // Leave the API exit frame. - __ bind(&leave_exit_frame); - bool restore_context = context_restore_operand != NULL; - if (restore_context) { - __ mov(esi, *context_restore_operand); - } - if (stack_space_operand != nullptr) { - __ mov(ebx, *stack_space_operand); - } - __ LeaveApiExitFrame(!restore_context); - - // Check if the function scheduled an exception. - ExternalReference scheduled_exception_address = - ExternalReference::scheduled_exception_address(isolate); - __ cmp(Operand::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(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(isolate); - __ bind(&delete_allocated_handles); - __ mov(Operand::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 ------------- - // -- edi : callee - // -- 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 callee = edi; - Register call_data = ebx; - Register holder = ecx; - Register api_function_address = edx; - Register context = esi; - Register return_address = eax; - - typedef FunctionCallbackArguments FCA; - - STATIC_ASSERT(FCA::kContextSaveIndex == 6); - STATIC_ASSERT(FCA::kCalleeIndex == 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); - STATIC_ASSERT(FCA::kNewTargetIndex == 7); - STATIC_ASSERT(FCA::kArgsLength == 8); - - __ pop(return_address); - - // new target - __ PushRoot(Heap::kUndefinedValueRootIndex); - - // context save. - __ push(context); - - // callee - __ push(callee); - - // call data - __ push(call_data); - - Register scratch = call_data; - if (!call_data_undefined()) { - // return value - __ push(Immediate(masm->isolate()->factory()->undefined_value())); - // return value default - __ push(Immediate(masm->isolate()->factory()->undefined_value())); - } else { - // return value - __ push(scratch); - // return value default - __ push(scratch); - } - // isolate - __ push(Immediate(reinterpret_cast(masm->isolate()))); - // holder - __ push(holder); - - __ mov(scratch, esp); - - // push return address - __ push(return_address); - - if (!is_lazy()) { - // load context from callee - __ mov(context, FieldOperand(callee, JSFunction::kContextOffset)); - } - - // 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(masm->isolate()); - - Operand context_restore_operand(ebp, - (2 + FCA::kContextSaveIndex) * kPointerSize); - // Stores return the first js argument - int return_value_offset = 0; - if (is_store()) { - return_value_offset = 2 + FCA::kArgsLength; - } else { - return_value_offset = 2 + FCA::kReturnValueOffset; - } - Operand return_value_operand(ebp, return_value_offset * kPointerSize); - int stack_space = 0; - Operand length_operand = ApiParameterOperand(4); - Operand* stack_space_operand = &length_operand; - stack_space = argc() + FCA::kArgsLength + 1; - stack_space_operand = nullptr; - CallApiFunctionAndReturn(masm, api_function_address, thunk_ref, - ApiParameterOperand(1), stack_space, - stack_space_operand, return_value_operand, - &context_restore_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(isolate()); - - __ 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, - NULL); -} - -#undef __ - -} // namespace internal -} // namespace v8 - -#endif // V8_TARGET_ARCH_X87 diff --git a/src/x87/code-stubs-x87.h b/src/x87/code-stubs-x87.h deleted file mode 100644 index 4b936a86ed..0000000000 --- a/src/x87/code-stubs-x87.h +++ /dev/null @@ -1,351 +0,0 @@ -// Copyright 2011 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. - -#ifndef V8_X87_CODE_STUBS_X87_H_ -#define V8_X87_CODE_STUBS_X87_H_ - -namespace v8 { -namespace internal { - - -void ArrayNativeCode(MacroAssembler* masm, - bool construct_call, - Label* call_generic_code); - - -class StringHelper : public AllStatic { - public: - // Compares two flat one byte strings and returns result in eax. - static void GenerateCompareFlatOneByteStrings(MacroAssembler* masm, - Register left, Register right, - Register scratch1, - Register scratch2, - Register scratch3); - - // Compares two flat one byte strings for equality and returns result in eax. - static void GenerateFlatOneByteStringEquals(MacroAssembler* masm, - Register left, Register right, - Register scratch1, - Register scratch2); - - private: - static void GenerateOneByteCharsCompareLoop( - MacroAssembler* masm, Register left, Register right, Register length, - Register scratch, Label* chars_not_equal, - Label::Distance chars_not_equal_near = Label::kFar); - - DISALLOW_IMPLICIT_CONSTRUCTORS(StringHelper); -}; - - -class NameDictionaryLookupStub: public PlatformCodeStub { - public: - enum LookupMode { POSITIVE_LOOKUP, NEGATIVE_LOOKUP }; - - NameDictionaryLookupStub(Isolate* isolate, Register dictionary, - Register result, Register index, LookupMode mode) - : PlatformCodeStub(isolate) { - minor_key_ = DictionaryBits::encode(dictionary.code()) | - ResultBits::encode(result.code()) | - IndexBits::encode(index.code()) | LookupModeBits::encode(mode); - } - - static void GenerateNegativeLookup(MacroAssembler* masm, - Label* miss, - Label* done, - Register properties, - Handle name, - Register r0); - - bool SometimesSetsUpAFrame() override { return false; } - - private: - static const int kInlinedProbes = 4; - static const int kTotalProbes = 20; - - static const int kCapacityOffset = - NameDictionary::kHeaderSize + - NameDictionary::kCapacityIndex * kPointerSize; - - static const int kElementsStartOffset = - NameDictionary::kHeaderSize + - NameDictionary::kElementsStartIndex * kPointerSize; - - Register dictionary() const { - return Register::from_code(DictionaryBits::decode(minor_key_)); - } - - Register result() const { - return Register::from_code(ResultBits::decode(minor_key_)); - } - - Register index() const { - return Register::from_code(IndexBits::decode(minor_key_)); - } - - LookupMode mode() const { return LookupModeBits::decode(minor_key_); } - - class DictionaryBits: public BitField {}; - class ResultBits: public BitField {}; - class IndexBits: public BitField {}; - class LookupModeBits: public BitField {}; - - DEFINE_NULL_CALL_INTERFACE_DESCRIPTOR(); - DEFINE_PLATFORM_CODE_STUB(NameDictionaryLookup, PlatformCodeStub); -}; - - -class RecordWriteStub: public PlatformCodeStub { - public: - RecordWriteStub(Isolate* isolate, Register object, Register value, - Register address, RememberedSetAction remembered_set_action, - SaveFPRegsMode fp_mode) - : PlatformCodeStub(isolate), - regs_(object, // An input reg. - address, // An input reg. - value) { // One scratch reg. - minor_key_ = ObjectBits::encode(object.code()) | - ValueBits::encode(value.code()) | - AddressBits::encode(address.code()) | - RememberedSetActionBits::encode(remembered_set_action) | - SaveFPRegsModeBits::encode(fp_mode); - } - - RecordWriteStub(uint32_t key, Isolate* isolate) - : PlatformCodeStub(key, isolate), regs_(object(), address(), value()) {} - - enum Mode { - STORE_BUFFER_ONLY, - INCREMENTAL, - INCREMENTAL_COMPACTION - }; - - bool SometimesSetsUpAFrame() override { return false; } - - static const byte kTwoByteNopInstruction = 0x3c; // Cmpb al, #imm8. - static const byte kTwoByteJumpInstruction = 0xeb; // Jmp #imm8. - - static const byte kFiveByteNopInstruction = 0x3d; // Cmpl eax, #imm32. - static const byte kFiveByteJumpInstruction = 0xe9; // Jmp #imm32. - - static Mode GetMode(Code* stub) { - byte first_instruction = stub->instruction_start()[0]; - byte second_instruction = stub->instruction_start()[2]; - - if (first_instruction == kTwoByteJumpInstruction) { - return INCREMENTAL; - } - - DCHECK(first_instruction == kTwoByteNopInstruction); - - if (second_instruction == kFiveByteJumpInstruction) { - return INCREMENTAL_COMPACTION; - } - - DCHECK(second_instruction == kFiveByteNopInstruction); - - return STORE_BUFFER_ONLY; - } - - static void Patch(Code* stub, Mode mode) { - switch (mode) { - case STORE_BUFFER_ONLY: - DCHECK(GetMode(stub) == INCREMENTAL || - GetMode(stub) == INCREMENTAL_COMPACTION); - stub->instruction_start()[0] = kTwoByteNopInstruction; - stub->instruction_start()[2] = kFiveByteNopInstruction; - break; - case INCREMENTAL: - DCHECK(GetMode(stub) == STORE_BUFFER_ONLY); - stub->instruction_start()[0] = kTwoByteJumpInstruction; - break; - case INCREMENTAL_COMPACTION: - DCHECK(GetMode(stub) == STORE_BUFFER_ONLY); - stub->instruction_start()[0] = kTwoByteNopInstruction; - stub->instruction_start()[2] = kFiveByteJumpInstruction; - break; - } - DCHECK(GetMode(stub) == mode); - Assembler::FlushICache(stub->GetIsolate(), stub->instruction_start(), 7); - } - - DEFINE_NULL_CALL_INTERFACE_DESCRIPTOR(); - - private: - // This is a helper class for freeing up 3 scratch registers, where the third - // is always ecx (needed for shift operations). The input is two registers - // that must be preserved and one scratch register provided by the caller. - class RegisterAllocation { - public: - RegisterAllocation(Register object, - Register address, - Register scratch0) - : object_orig_(object), - address_orig_(address), - scratch0_orig_(scratch0), - object_(object), - address_(address), - scratch0_(scratch0) { - DCHECK(!AreAliased(scratch0, object, address, no_reg)); - scratch1_ = GetRegThatIsNotEcxOr(object_, address_, scratch0_); - if (scratch0.is(ecx)) { - scratch0_ = GetRegThatIsNotEcxOr(object_, address_, scratch1_); - } - if (object.is(ecx)) { - object_ = GetRegThatIsNotEcxOr(address_, scratch0_, scratch1_); - } - if (address.is(ecx)) { - address_ = GetRegThatIsNotEcxOr(object_, scratch0_, scratch1_); - } - DCHECK(!AreAliased(scratch0_, object_, address_, ecx)); - } - - void Save(MacroAssembler* masm) { - DCHECK(!address_orig_.is(object_)); - DCHECK(object_.is(object_orig_) || address_.is(address_orig_)); - DCHECK(!AreAliased(object_, address_, scratch1_, scratch0_)); - DCHECK(!AreAliased(object_orig_, address_, scratch1_, scratch0_)); - DCHECK(!AreAliased(object_, address_orig_, scratch1_, scratch0_)); - // We don't have to save scratch0_orig_ because it was given to us as - // a scratch register. But if we had to switch to a different reg then - // we should save the new scratch0_. - if (!scratch0_.is(scratch0_orig_)) masm->push(scratch0_); - if (!ecx.is(scratch0_orig_) && - !ecx.is(object_orig_) && - !ecx.is(address_orig_)) { - masm->push(ecx); - } - masm->push(scratch1_); - if (!address_.is(address_orig_)) { - masm->push(address_); - masm->mov(address_, address_orig_); - } - if (!object_.is(object_orig_)) { - masm->push(object_); - masm->mov(object_, object_orig_); - } - } - - void Restore(MacroAssembler* masm) { - // These will have been preserved the entire time, so we just need to move - // them back. Only in one case is the orig_ reg different from the plain - // one, since only one of them can alias with ecx. - if (!object_.is(object_orig_)) { - masm->mov(object_orig_, object_); - masm->pop(object_); - } - if (!address_.is(address_orig_)) { - masm->mov(address_orig_, address_); - masm->pop(address_); - } - masm->pop(scratch1_); - if (!ecx.is(scratch0_orig_) && - !ecx.is(object_orig_) && - !ecx.is(address_orig_)) { - masm->pop(ecx); - } - if (!scratch0_.is(scratch0_orig_)) masm->pop(scratch0_); - } - - // If we have to call into C then we need to save and restore all caller- - // saved registers that were not already preserved. The caller saved - // registers are eax, ecx and edx. The three scratch registers (incl. ecx) - // will be restored by other means so we don't bother pushing them here. - void SaveCallerSaveRegisters(MacroAssembler* masm, SaveFPRegsMode mode) { - masm->PushCallerSaved(mode, ecx, scratch0_, scratch1_); - } - - inline void RestoreCallerSaveRegisters(MacroAssembler* masm, - SaveFPRegsMode mode) { - masm->PopCallerSaved(mode, ecx, scratch0_, scratch1_); - } - - inline Register object() { return object_; } - inline Register address() { return address_; } - inline Register scratch0() { return scratch0_; } - inline Register scratch1() { return scratch1_; } - - private: - Register object_orig_; - Register address_orig_; - Register scratch0_orig_; - Register object_; - Register address_; - Register scratch0_; - Register scratch1_; - // Third scratch register is always ecx. - - Register GetRegThatIsNotEcxOr(Register r1, - Register r2, - Register r3) { - for (int i = 0; i < Register::kNumRegisters; i++) { - if (RegisterConfiguration::Crankshaft()->IsAllocatableGeneralCode(i)) { - Register candidate = Register::from_code(i); - if (candidate.is(ecx)) continue; - if (candidate.is(r1)) continue; - if (candidate.is(r2)) continue; - if (candidate.is(r3)) continue; - return candidate; - } - } - UNREACHABLE(); - } - friend class RecordWriteStub; - }; - - enum OnNoNeedToInformIncrementalMarker { - kReturnOnNoNeedToInformIncrementalMarker, - kUpdateRememberedSetOnNoNeedToInformIncrementalMarker - }; - - inline Major MajorKey() const final { return RecordWrite; } - - void Generate(MacroAssembler* masm) override; - void GenerateIncremental(MacroAssembler* masm, Mode mode); - void CheckNeedsToInformIncrementalMarker( - MacroAssembler* masm, - OnNoNeedToInformIncrementalMarker on_no_need, - Mode mode); - void InformIncrementalMarker(MacroAssembler* masm); - - void Activate(Code* code) override { - code->GetHeap()->incremental_marking()->ActivateGeneratedStub(code); - } - - Register object() const { - return Register::from_code(ObjectBits::decode(minor_key_)); - } - - Register value() const { - return Register::from_code(ValueBits::decode(minor_key_)); - } - - Register address() const { - return Register::from_code(AddressBits::decode(minor_key_)); - } - - RememberedSetAction remembered_set_action() const { - return RememberedSetActionBits::decode(minor_key_); - } - - SaveFPRegsMode save_fp_regs_mode() const { - return SaveFPRegsModeBits::decode(minor_key_); - } - - class ObjectBits: public BitField {}; - class ValueBits: public BitField {}; - class AddressBits: public BitField {}; - class RememberedSetActionBits: public BitField {}; - class SaveFPRegsModeBits : public BitField {}; - - RegisterAllocation regs_; - - DISALLOW_COPY_AND_ASSIGN(RecordWriteStub); -}; - - -} // namespace internal -} // namespace v8 - -#endif // V8_X87_CODE_STUBS_X87_H_ diff --git a/src/x87/codegen-x87.cc b/src/x87/codegen-x87.cc deleted file mode 100644 index 1a827788ff..0000000000 --- a/src/x87/codegen-x87.cc +++ /dev/null @@ -1,381 +0,0 @@ -// 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. - -#include "src/x87/codegen-x87.h" - -#if V8_TARGET_ARCH_X87 - -#include "src/codegen.h" -#include "src/heap/heap.h" -#include "src/macro-assembler.h" - -namespace v8 { -namespace internal { - - -// ------------------------------------------------------------------------- -// Platform-specific RuntimeCallHelper functions. - -void StubRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const { - masm->EnterFrame(StackFrame::INTERNAL); - DCHECK(!masm->has_frame()); - masm->set_has_frame(true); -} - - -void StubRuntimeCallHelper::AfterCall(MacroAssembler* masm) const { - masm->LeaveFrame(StackFrame::INTERNAL); - DCHECK(masm->has_frame()); - masm->set_has_frame(false); -} - - -#define __ masm. - - -UnaryMathFunctionWithIsolate CreateSqrtFunction(Isolate* isolate) { - size_t actual_size; - // Allocate buffer in executable space. - byte* buffer = - static_cast(base::OS::Allocate(1 * KB, &actual_size, true)); - if (buffer == nullptr) return nullptr; - - MacroAssembler masm(isolate, buffer, static_cast(actual_size), - CodeObjectRequired::kNo); - // Load double input into registers. - __ fld_d(MemOperand(esp, 4)); - __ X87SetFPUCW(0x027F); - __ fsqrt(); - __ X87SetFPUCW(0x037F); - __ Ret(); - - CodeDesc desc; - masm.GetCode(&desc); - DCHECK(!RelocInfo::RequiresRelocation(isolate, desc)); - - Assembler::FlushICache(isolate, buffer, actual_size); - base::OS::ProtectCode(buffer, actual_size); - return FUNCTION_CAST(buffer); -} - - -// Helper functions for CreateMemMoveFunction. -#undef __ -#define __ ACCESS_MASM(masm) - -enum Direction { FORWARD, BACKWARD }; -enum Alignment { MOVE_ALIGNED, MOVE_UNALIGNED }; - - -void MemMoveEmitPopAndReturn(MacroAssembler* masm) { - __ pop(esi); - __ pop(edi); - __ ret(0); -} - - -#undef __ -#define __ masm. - - -class LabelConverter { - public: - explicit LabelConverter(byte* buffer) : buffer_(buffer) {} - int32_t address(Label* l) const { - return reinterpret_cast(buffer_) + l->pos(); - } - private: - byte* buffer_; -}; - - -MemMoveFunction CreateMemMoveFunction(Isolate* isolate) { - size_t actual_size; - // Allocate buffer in executable space. - byte* buffer = - static_cast(base::OS::Allocate(1 * KB, &actual_size, true)); - if (buffer == nullptr) return nullptr; - MacroAssembler masm(isolate, buffer, static_cast(actual_size), - CodeObjectRequired::kNo); - LabelConverter conv(buffer); - - // Generated code is put into a fixed, unmovable buffer, and not into - // the V8 heap. We can't, and don't, refer to any relocatable addresses - // (e.g. the JavaScript nan-object). - - // 32-bit C declaration function calls pass arguments on stack. - - // Stack layout: - // esp[12]: Third argument, size. - // esp[8]: Second argument, source pointer. - // esp[4]: First argument, destination pointer. - // esp[0]: return address - - const int kDestinationOffset = 1 * kPointerSize; - const int kSourceOffset = 2 * kPointerSize; - const int kSizeOffset = 3 * kPointerSize; - - int stack_offset = 0; // Update if we change the stack height. - - Label backward, backward_much_overlap; - Label forward_much_overlap, small_size, medium_size, pop_and_return; - __ push(edi); - __ push(esi); - stack_offset += 2 * kPointerSize; - Register dst = edi; - Register src = esi; - Register count = ecx; - __ mov(dst, Operand(esp, stack_offset + kDestinationOffset)); - __ mov(src, Operand(esp, stack_offset + kSourceOffset)); - __ mov(count, Operand(esp, stack_offset + kSizeOffset)); - - __ cmp(dst, src); - __ j(equal, &pop_and_return); - - // No SSE2. - Label forward; - __ cmp(count, 0); - __ j(equal, &pop_and_return); - __ cmp(dst, src); - __ j(above, &backward); - __ jmp(&forward); - { - // Simple forward copier. - Label forward_loop_1byte, forward_loop_4byte; - __ bind(&forward_loop_4byte); - __ mov(eax, Operand(src, 0)); - __ sub(count, Immediate(4)); - __ add(src, Immediate(4)); - __ mov(Operand(dst, 0), eax); - __ add(dst, Immediate(4)); - __ bind(&forward); // Entry point. - __ cmp(count, 3); - __ j(above, &forward_loop_4byte); - __ bind(&forward_loop_1byte); - __ cmp(count, 0); - __ j(below_equal, &pop_and_return); - __ mov_b(eax, Operand(src, 0)); - __ dec(count); - __ inc(src); - __ mov_b(Operand(dst, 0), eax); - __ inc(dst); - __ jmp(&forward_loop_1byte); - } - { - // Simple backward copier. - Label backward_loop_1byte, backward_loop_4byte, entry_shortcut; - __ bind(&backward); - __ add(src, count); - __ add(dst, count); - __ cmp(count, 3); - __ j(below_equal, &entry_shortcut); - - __ bind(&backward_loop_4byte); - __ sub(src, Immediate(4)); - __ sub(count, Immediate(4)); - __ mov(eax, Operand(src, 0)); - __ sub(dst, Immediate(4)); - __ mov(Operand(dst, 0), eax); - __ cmp(count, 3); - __ j(above, &backward_loop_4byte); - __ bind(&backward_loop_1byte); - __ cmp(count, 0); - __ j(below_equal, &pop_and_return); - __ bind(&entry_shortcut); - __ dec(src); - __ dec(count); - __ mov_b(eax, Operand(src, 0)); - __ dec(dst); - __ mov_b(Operand(dst, 0), eax); - __ jmp(&backward_loop_1byte); - } - - __ bind(&pop_and_return); - MemMoveEmitPopAndReturn(&masm); - - CodeDesc desc; - masm.GetCode(&desc); - DCHECK(!RelocInfo::RequiresRelocation(isolate, desc)); - Assembler::FlushICache(isolate, buffer, actual_size); - base::OS::ProtectCode(buffer, actual_size); - // TODO(jkummerow): It would be nice to register this code creation event - // with the PROFILE / GDBJIT system. - return FUNCTION_CAST(buffer); -} - - -#undef __ - -// ------------------------------------------------------------------------- -// Code generators - -#define __ ACCESS_MASM(masm) - -void StringCharLoadGenerator::Generate(MacroAssembler* masm, - Factory* factory, - Register string, - Register index, - Register result, - Label* call_runtime) { - Label indirect_string_loaded; - __ bind(&indirect_string_loaded); - - // Fetch the instance type of the receiver into result register. - __ mov(result, FieldOperand(string, HeapObject::kMapOffset)); - __ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset)); - - // We need special handling for indirect strings. - Label check_sequential; - __ test(result, Immediate(kIsIndirectStringMask)); - __ j(zero, &check_sequential, Label::kNear); - - // Dispatch on the indirect string shape: slice or cons. - Label cons_string, thin_string; - __ and_(result, Immediate(kStringRepresentationMask)); - __ cmp(result, Immediate(kConsStringTag)); - __ j(equal, &cons_string, Label::kNear); - __ cmp(result, Immediate(kThinStringTag)); - __ j(equal, &thin_string, Label::kNear); - - // Handle slices. - __ mov(result, FieldOperand(string, SlicedString::kOffsetOffset)); - __ SmiUntag(result); - __ add(index, result); - __ mov(string, FieldOperand(string, SlicedString::kParentOffset)); - __ jmp(&indirect_string_loaded); - - // Handle thin strings. - __ bind(&thin_string); - __ mov(string, FieldOperand(string, ThinString::kActualOffset)); - __ jmp(&indirect_string_loaded); - - // Handle cons strings. - // Check whether the right hand side is the empty string (i.e. if - // this is really a flat string in a cons string). If that is not - // the case we would rather go to the runtime system now to flatten - // the string. - __ bind(&cons_string); - __ cmp(FieldOperand(string, ConsString::kSecondOffset), - Immediate(factory->empty_string())); - __ j(not_equal, call_runtime); - __ mov(string, FieldOperand(string, ConsString::kFirstOffset)); - __ jmp(&indirect_string_loaded); - - // Distinguish sequential and external strings. Only these two string - // representations can reach here (slices and flat cons strings have been - // reduced to the underlying sequential or external string). - Label seq_string; - __ bind(&check_sequential); - STATIC_ASSERT(kSeqStringTag == 0); - __ test(result, Immediate(kStringRepresentationMask)); - __ j(zero, &seq_string, Label::kNear); - - // Handle external strings. - Label one_byte_external, done; - if (FLAG_debug_code) { - // Assert that we do not have a cons or slice (indirect strings) here. - // Sequential strings have already been ruled out. - __ test(result, Immediate(kIsIndirectStringMask)); - __ Assert(zero, kExternalStringExpectedButNotFound); - } - // Rule out short external strings. - STATIC_ASSERT(kShortExternalStringTag != 0); - __ test_b(result, Immediate(kShortExternalStringMask)); - __ j(not_zero, call_runtime); - // Check encoding. - STATIC_ASSERT(kTwoByteStringTag == 0); - __ test_b(result, Immediate(kStringEncodingMask)); - __ mov(result, FieldOperand(string, ExternalString::kResourceDataOffset)); - __ j(not_equal, &one_byte_external, Label::kNear); - // Two-byte string. - __ movzx_w(result, Operand(result, index, times_2, 0)); - __ jmp(&done, Label::kNear); - __ bind(&one_byte_external); - // One-byte string. - __ movzx_b(result, Operand(result, index, times_1, 0)); - __ jmp(&done, Label::kNear); - - // Dispatch on the encoding: one-byte or two-byte. - Label one_byte; - __ bind(&seq_string); - STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0); - STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); - __ test(result, Immediate(kStringEncodingMask)); - __ j(not_zero, &one_byte, Label::kNear); - - // Two-byte string. - // Load the two-byte character code into the result register. - __ movzx_w(result, FieldOperand(string, - index, - times_2, - SeqTwoByteString::kHeaderSize)); - __ jmp(&done, Label::kNear); - - // One-byte string. - // Load the byte into the result register. - __ bind(&one_byte); - __ movzx_b(result, FieldOperand(string, - index, - times_1, - SeqOneByteString::kHeaderSize)); - __ bind(&done); -} - - -#undef __ - - -CodeAgingHelper::CodeAgingHelper(Isolate* isolate) { - USE(isolate); - DCHECK(young_sequence_.length() == kNoCodeAgeSequenceLength); - CodePatcher patcher(isolate, young_sequence_.start(), - young_sequence_.length()); - patcher.masm()->push(ebp); - patcher.masm()->mov(ebp, esp); - patcher.masm()->push(esi); - patcher.masm()->push(edi); -} - - -#ifdef DEBUG -bool CodeAgingHelper::IsOld(byte* candidate) const { - return *candidate == kCallOpcode; -} -#endif - - -bool Code::IsYoungSequence(Isolate* isolate, byte* sequence) { - bool result = isolate->code_aging_helper()->IsYoung(sequence); - DCHECK(result || isolate->code_aging_helper()->IsOld(sequence)); - return result; -} - -Code::Age Code::GetCodeAge(Isolate* isolate, byte* sequence) { - if (IsYoungSequence(isolate, sequence)) return kNoAgeCodeAge; - - sequence++; // Skip the kCallOpcode byte - Address target_address = sequence + *reinterpret_cast(sequence) + - Assembler::kCallTargetAddressOffset; - Code* stub = GetCodeFromTargetAddress(target_address); - return GetAgeOfCodeAgeStub(stub); -} - -void Code::PatchPlatformCodeAge(Isolate* isolate, byte* sequence, - Code::Age age) { - uint32_t young_length = isolate->code_aging_helper()->young_sequence_length(); - if (age == kNoAgeCodeAge) { - isolate->code_aging_helper()->CopyYoungSequenceTo(sequence); - Assembler::FlushICache(isolate, sequence, young_length); - } else { - Code* stub = GetCodeAgeStub(isolate, age); - CodePatcher patcher(isolate, sequence, young_length); - patcher.masm()->call(stub->instruction_start(), RelocInfo::NONE32); - } -} - - -} // namespace internal -} // namespace v8 - -#endif // V8_TARGET_ARCH_X87 diff --git a/src/x87/codegen-x87.h b/src/x87/codegen-x87.h deleted file mode 100644 index f034a9c2fa..0000000000 --- a/src/x87/codegen-x87.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2011 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. - -#ifndef V8_X87_CODEGEN_X87_H_ -#define V8_X87_CODEGEN_X87_H_ - -#include "src/macro-assembler.h" - -namespace v8 { -namespace internal { - - -class StringCharLoadGenerator : public AllStatic { - public: - // Generates the code for handling different string types and loading the - // indexed character into |result|. We expect |index| as untagged input and - // |result| as untagged output. - static void Generate(MacroAssembler* masm, - Factory* factory, - Register string, - Register index, - Register result, - Label* call_runtime); - - private: - DISALLOW_COPY_AND_ASSIGN(StringCharLoadGenerator); -}; - -} // namespace internal -} // namespace v8 - -#endif // V8_X87_CODEGEN_X87_H_ diff --git a/src/x87/cpu-x87.cc b/src/x87/cpu-x87.cc deleted file mode 100644 index 22906b31be..0000000000 --- a/src/x87/cpu-x87.cc +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2011 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. - -// CPU specific code for ia32 independent of OS goes here. - -#ifdef __GNUC__ -#include "src/third_party/valgrind/valgrind.h" -#endif - -#if V8_TARGET_ARCH_X87 - -#include "src/assembler.h" -#include "src/macro-assembler.h" - -namespace v8 { -namespace internal { - -void CpuFeatures::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. - - // By default, valgrind only checks the stack for writes that might need to - // invalidate already cached translated code. This leads to random - // instability when code patches or moves are sometimes unnoticed. One - // solution is to run valgrind with --smc-check=all, but this comes at a big - // performance cost. We can notify valgrind to invalidate its cache. -#ifdef VALGRIND_DISCARD_TRANSLATIONS - unsigned res = VALGRIND_DISCARD_TRANSLATIONS(start, size); - USE(res); -#endif -} - -} // namespace internal -} // namespace v8 - -#endif // V8_TARGET_ARCH_X87 diff --git a/src/x87/deoptimizer-x87.cc b/src/x87/deoptimizer-x87.cc deleted file mode 100644 index a3154343a0..0000000000 --- a/src/x87/deoptimizer-x87.cc +++ /dev/null @@ -1,412 +0,0 @@ -// 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_X87 - -#include "src/codegen.h" -#include "src/deoptimizer.h" -#include "src/full-codegen/full-codegen.h" -#include "src/register-configuration.h" -#include "src/safepoint-table.h" -#include "src/x87/frames-x87.h" - -namespace v8 { -namespace internal { - -const int Deoptimizer::table_entry_size_ = 10; - - -int Deoptimizer::patch_size() { - return Assembler::kCallInstructionLength; -} - - -void Deoptimizer::EnsureRelocSpaceForLazyDeoptimization(Handle code) { - Isolate* isolate = code->GetIsolate(); - HandleScope scope(isolate); - - // Compute the size of relocation information needed for the code - // patching in Deoptimizer::PatchCodeForDeoptimization below. - int min_reloc_size = 0; - int prev_pc_offset = 0; - DeoptimizationInputData* deopt_data = - DeoptimizationInputData::cast(code->deoptimization_data()); - for (int i = 0; i < deopt_data->DeoptCount(); i++) { - int pc_offset = deopt_data->Pc(i)->value(); - if (pc_offset == -1) continue; - pc_offset = pc_offset + 1; // We will encode the pc offset after the call. - DCHECK_GE(pc_offset, prev_pc_offset); - int pc_delta = pc_offset - prev_pc_offset; - // We use RUNTIME_ENTRY reloc info which has a size of 2 bytes - // if encodable with small pc delta encoding and up to 6 bytes - // otherwise. - if (pc_delta <= RelocInfo::kMaxSmallPCDelta) { - min_reloc_size += 2; - } else { - min_reloc_size += 6; - } - prev_pc_offset = pc_offset; - } - - // If the relocation information is not big enough we create a new - // relocation info object that is padded with comments to make it - // big enough for lazy doptimization. - int reloc_length = code->relocation_info()->length(); - if (min_reloc_size > reloc_length) { - int comment_reloc_size = RelocInfo::kMinRelocCommentSize; - // Padding needed. - int min_padding = min_reloc_size - reloc_length; - // Number of comments needed to take up at least that much space. - int additional_comments = - (min_padding + comment_reloc_size - 1) / comment_reloc_size; - // Actual padding size. - int padding = additional_comments * comment_reloc_size; - // Allocate new relocation info and copy old relocation to the end - // of the new relocation info array because relocation info is - // written and read backwards. - Factory* factory = isolate->factory(); - Handle new_reloc = - factory->NewByteArray(reloc_length + padding, TENURED); - MemCopy(new_reloc->GetDataStartAddress() + padding, - code->relocation_info()->GetDataStartAddress(), reloc_length); - // Create a relocation writer to write the comments in the padding - // space. Use position 0 for everything to ensure short encoding. - RelocInfoWriter reloc_info_writer( - new_reloc->GetDataStartAddress() + padding, 0); - intptr_t comment_string - = reinterpret_cast(RelocInfo::kFillerCommentString); - RelocInfo rinfo(isolate, 0, RelocInfo::COMMENT, comment_string, NULL); - for (int i = 0; i < additional_comments; ++i) { -#ifdef DEBUG - byte* pos_before = reloc_info_writer.pos(); -#endif - reloc_info_writer.Write(&rinfo); - DCHECK(RelocInfo::kMinRelocCommentSize == - pos_before - reloc_info_writer.pos()); - } - // Replace relocation information on the code object. - code->set_relocation_info(*new_reloc); - } -} - - -void Deoptimizer::PatchCodeForDeoptimization(Isolate* isolate, Code* code) { - Address code_start_address = code->instruction_start(); - - // Fail hard and early if we enter this code object again. - byte* pointer = code->FindCodeAgeSequence(); - if (pointer != NULL) { - pointer += kNoCodeAgeSequenceLength; - } else { - pointer = code->instruction_start(); - } - CodePatcher patcher(isolate, pointer, 1); - patcher.masm()->int3(); - - DeoptimizationInputData* data = - DeoptimizationInputData::cast(code->deoptimization_data()); - int osr_offset = data->OsrPcOffset()->value(); - if (osr_offset > 0) { - CodePatcher osr_patcher(isolate, code_start_address + osr_offset, 1); - osr_patcher.masm()->int3(); - } - - // We will overwrite the code's relocation info in-place. Relocation info - // is written backward. The relocation info is the payload of a byte - // array. Later on we will slide this to the start of the byte array and - // create a filler object in the remaining space. - ByteArray* reloc_info = code->relocation_info(); - Address reloc_end_address = reloc_info->address() + reloc_info->Size(); - RelocInfoWriter reloc_info_writer(reloc_end_address, code_start_address); - - // Since the call is a relative encoding, write new - // reloc info. We do not need any of the existing reloc info because the - // existing code will not be used again (we zap it in debug builds). - // - // Emit call to lazy deoptimization at all lazy deopt points. - DeoptimizationInputData* deopt_data = - DeoptimizationInputData::cast(code->deoptimization_data()); -#ifdef DEBUG - Address prev_call_address = NULL; -#endif - // For each LLazyBailout instruction insert a call to the corresponding - // deoptimization entry. - for (int i = 0; i < deopt_data->DeoptCount(); i++) { - if (deopt_data->Pc(i)->value() == -1) continue; - // Patch lazy deoptimization entry. - Address call_address = code_start_address + deopt_data->Pc(i)->value(); - CodePatcher patcher(isolate, call_address, patch_size()); - Address deopt_entry = GetDeoptimizationEntry(isolate, i, LAZY); - patcher.masm()->call(deopt_entry, RelocInfo::NONE32); - // We use RUNTIME_ENTRY for deoptimization bailouts. - RelocInfo rinfo(isolate, call_address + 1, // 1 after the call opcode. - RelocInfo::RUNTIME_ENTRY, - reinterpret_cast(deopt_entry), NULL); - reloc_info_writer.Write(&rinfo); - DCHECK_GE(reloc_info_writer.pos(), - reloc_info->address() + ByteArray::kHeaderSize); - DCHECK(prev_call_address == NULL || - call_address >= prev_call_address + patch_size()); - DCHECK(call_address + patch_size() <= code->instruction_end()); -#ifdef DEBUG - prev_call_address = call_address; -#endif - } - - // Move the relocation info to the beginning of the byte array. - const int new_reloc_length = reloc_end_address - reloc_info_writer.pos(); - MemMove(code->relocation_start(), reloc_info_writer.pos(), new_reloc_length); - - // Right trim the relocation info to free up remaining space. - const int delta = reloc_info->length() - new_reloc_length; - if (delta > 0) { - isolate->heap()->RightTrimFixedArray(reloc_info, delta); - } -} - - -#define __ masm()-> - -void Deoptimizer::TableEntryGenerator::Generate() { - GeneratePrologue(); - - // Save all general purpose registers before messing with them. - const int kNumberOfRegisters = Register::kNumRegisters; - - const int kDoubleRegsSize = kDoubleSize * X87Register::kMaxNumRegisters; - - // Reserve space for x87 fp registers. - __ sub(esp, Immediate(kDoubleRegsSize)); - - __ pushad(); - - ExternalReference c_entry_fp_address(IsolateAddressId::kCEntryFPAddress, - isolate()); - __ mov(Operand::StaticVariable(c_entry_fp_address), ebp); - - // GP registers are safe to use now. - // Save used x87 fp registers in correct position of previous reserve space. - Label loop, done; - // Get the layout of x87 stack. - __ sub(esp, Immediate(kPointerSize)); - __ fistp_s(MemOperand(esp, 0)); - __ pop(eax); - // Preserve stack layout in edi - __ mov(edi, eax); - // Get the x87 stack depth, the first 3 bits. - __ mov(ecx, eax); - __ and_(ecx, 0x7); - __ j(zero, &done, Label::kNear); - - __ bind(&loop); - __ shr(eax, 0x3); - __ mov(ebx, eax); - __ and_(ebx, 0x7); // Extract the st_x index into ebx. - // Pop TOS to the correct position. The disp(0x20) is due to pushad. - // The st_i should be saved to (esp + ebx * kDoubleSize + 0x20). - __ fstp_d(Operand(esp, ebx, times_8, 0x20)); - __ dec(ecx); // Decrease stack depth. - __ j(not_zero, &loop, Label::kNear); - __ bind(&done); - - const int kSavedRegistersAreaSize = - kNumberOfRegisters * kPointerSize + kDoubleRegsSize; - - // Get the bailout id from the stack. - __ mov(ebx, Operand(esp, kSavedRegistersAreaSize)); - - // Get the address of the location in the code object - // and compute the fp-to-sp delta in register edx. - __ mov(ecx, Operand(esp, kSavedRegistersAreaSize + 1 * kPointerSize)); - __ lea(edx, Operand(esp, kSavedRegistersAreaSize + 2 * kPointerSize)); - - __ sub(edx, ebp); - __ neg(edx); - - __ push(edi); - // Allocate a new deoptimizer object. - __ PrepareCallCFunction(6, eax); - __ mov(eax, Immediate(0)); - Label context_check; - __ mov(edi, Operand(ebp, CommonFrameConstants::kContextOrFrameTypeOffset)); - __ JumpIfSmi(edi, &context_check); - __ mov(eax, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); - __ bind(&context_check); - __ mov(Operand(esp, 0 * kPointerSize), eax); // Function. - __ mov(Operand(esp, 1 * kPointerSize), Immediate(type())); // Bailout type. - __ mov(Operand(esp, 2 * kPointerSize), ebx); // Bailout id. - __ mov(Operand(esp, 3 * kPointerSize), ecx); // Code address or 0. - __ mov(Operand(esp, 4 * kPointerSize), edx); // Fp-to-sp delta. - __ mov(Operand(esp, 5 * kPointerSize), - Immediate(ExternalReference::isolate_address(isolate()))); - { - AllowExternalCallThatCantCauseGC scope(masm()); - __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate()), 6); - } - - __ pop(edi); - - // Preserve deoptimizer object in register eax and get the input - // frame descriptor pointer. - __ mov(ebx, Operand(eax, Deoptimizer::input_offset())); - - // Fill in the input registers. - for (int i = kNumberOfRegisters - 1; i >= 0; i--) { - int offset = (i * kPointerSize) + FrameDescription::registers_offset(); - __ pop(Operand(ebx, offset)); - } - - int double_regs_offset = FrameDescription::double_registers_offset(); - const RegisterConfiguration* config = RegisterConfiguration::Crankshaft(); - // Fill in the double input registers. - for (int i = 0; i < X87Register::kMaxNumAllocatableRegisters; ++i) { - int code = config->GetAllocatableDoubleCode(i); - int dst_offset = code * kDoubleSize + double_regs_offset; - int src_offset = code * kDoubleSize; - __ fld_d(Operand(esp, src_offset)); - __ fstp_d(Operand(ebx, dst_offset)); - } - - // Clear FPU all exceptions. - // TODO(ulan): Find out why the TOP register is not zero here in some cases, - // and check that the generated code never deoptimizes with unbalanced stack. - __ fnclex(); - - // Remove the bailout id, return address and the double registers. - __ add(esp, Immediate(kDoubleRegsSize + 2 * kPointerSize)); - - // Compute a pointer to the unwinding limit in register ecx; that is - // the first stack slot not part of the input frame. - __ mov(ecx, Operand(ebx, FrameDescription::frame_size_offset())); - __ add(ecx, esp); - - // Unwind the stack down to - but not including - the unwinding - // limit and copy the contents of the activation frame to the input - // frame description. - __ lea(edx, Operand(ebx, FrameDescription::frame_content_offset())); - Label pop_loop_header; - __ jmp(&pop_loop_header); - Label pop_loop; - __ bind(&pop_loop); - __ pop(Operand(edx, 0)); - __ add(edx, Immediate(sizeof(uint32_t))); - __ bind(&pop_loop_header); - __ cmp(ecx, esp); - __ j(not_equal, &pop_loop); - - // Compute the output frame in the deoptimizer. - __ push(edi); - __ push(eax); - __ PrepareCallCFunction(1, ebx); - __ mov(Operand(esp, 0 * kPointerSize), eax); - { - AllowExternalCallThatCantCauseGC scope(masm()); - __ CallCFunction( - ExternalReference::compute_output_frames_function(isolate()), 1); - } - __ pop(eax); - __ pop(edi); - __ mov(esp, Operand(eax, Deoptimizer::caller_frame_top_offset())); - - // Replace the current (input) frame with the output frames. - Label outer_push_loop, inner_push_loop, - outer_loop_header, inner_loop_header; - // Outer loop state: eax = current FrameDescription**, edx = one past the - // last FrameDescription**. - __ mov(edx, Operand(eax, Deoptimizer::output_count_offset())); - __ mov(eax, Operand(eax, Deoptimizer::output_offset())); - __ lea(edx, Operand(eax, edx, times_4, 0)); - __ jmp(&outer_loop_header); - __ bind(&outer_push_loop); - // Inner loop state: ebx = current FrameDescription*, ecx = loop index. - __ mov(ebx, Operand(eax, 0)); - __ mov(ecx, Operand(ebx, FrameDescription::frame_size_offset())); - __ jmp(&inner_loop_header); - __ bind(&inner_push_loop); - __ sub(ecx, Immediate(sizeof(uint32_t))); - __ push(Operand(ebx, ecx, times_1, FrameDescription::frame_content_offset())); - __ bind(&inner_loop_header); - __ test(ecx, ecx); - __ j(not_zero, &inner_push_loop); - __ add(eax, Immediate(kPointerSize)); - __ bind(&outer_loop_header); - __ cmp(eax, edx); - __ j(below, &outer_push_loop); - - - // In case of a failed STUB, we have to restore the x87 stack. - // x87 stack layout is in edi. - Label loop2, done2; - // Get the x87 stack depth, the first 3 bits. - __ mov(ecx, edi); - __ and_(ecx, 0x7); - __ j(zero, &done2, Label::kNear); - - __ lea(ecx, Operand(ecx, ecx, times_2, 0)); - __ bind(&loop2); - __ mov(eax, edi); - __ shr_cl(eax); - __ and_(eax, 0x7); - __ fld_d(Operand(ebx, eax, times_8, double_regs_offset)); - __ sub(ecx, Immediate(0x3)); - __ j(not_zero, &loop2, Label::kNear); - __ bind(&done2); - - // Push state, pc, and continuation from the last output frame. - __ push(Operand(ebx, FrameDescription::state_offset())); - __ push(Operand(ebx, FrameDescription::pc_offset())); - __ push(Operand(ebx, FrameDescription::continuation_offset())); - - - // Push the registers from the last output frame. - for (int i = 0; i < kNumberOfRegisters; i++) { - int offset = (i * kPointerSize) + FrameDescription::registers_offset(); - __ push(Operand(ebx, offset)); - } - - // Restore the registers from the stack. - __ popad(); - - // Return to the continuation point. - __ ret(0); -} - - -void Deoptimizer::TableEntryGenerator::GeneratePrologue() { - // Create a sequence of deoptimization entries. - Label done; - for (int i = 0; i < count(); i++) { - int start = masm()->pc_offset(); - USE(start); - __ push_imm32(i); - __ jmp(&done); - DCHECK(masm()->pc_offset() - start == table_entry_size_); - } - __ bind(&done); -} - - -void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) { - SetFrameSlot(offset, value); -} - - -void FrameDescription::SetCallerFp(unsigned offset, intptr_t value) { - SetFrameSlot(offset, value); -} - - -void FrameDescription::SetCallerConstantPool(unsigned offset, intptr_t value) { - // No embedded constant pool support. - UNREACHABLE(); -} - - -#undef __ - - -} // namespace internal -} // namespace v8 - -#endif // V8_TARGET_ARCH_X87 diff --git a/src/x87/disasm-x87.cc b/src/x87/disasm-x87.cc deleted file mode 100644 index f7a2e52cd0..0000000000 --- a/src/x87/disasm-x87.cc +++ /dev/null @@ -1,1874 +0,0 @@ -// Copyright 2011 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. - -#include -#include -#include - -#if V8_TARGET_ARCH_X87 - -#include "src/base/compiler-specific.h" -#include "src/disasm.h" - -namespace disasm { - -enum OperandOrder { - UNSET_OP_ORDER = 0, - REG_OPER_OP_ORDER, - OPER_REG_OP_ORDER -}; - - -//------------------------------------------------------------------ -// Tables -//------------------------------------------------------------------ -struct ByteMnemonic { - int b; // -1 terminates, otherwise must be in range (0..255) - const char* mnem; - OperandOrder op_order_; -}; - -static const ByteMnemonic two_operands_instr[] = { - {0x01, "add", OPER_REG_OP_ORDER}, {0x03, "add", REG_OPER_OP_ORDER}, - {0x09, "or", OPER_REG_OP_ORDER}, {0x0B, "or", REG_OPER_OP_ORDER}, - {0x13, "adc", REG_OPER_OP_ORDER}, {0x1B, "sbb", REG_OPER_OP_ORDER}, - {0x21, "and", OPER_REG_OP_ORDER}, {0x23, "and", REG_OPER_OP_ORDER}, - {0x29, "sub", OPER_REG_OP_ORDER}, {0x2A, "subb", REG_OPER_OP_ORDER}, - {0x2B, "sub", REG_OPER_OP_ORDER}, {0x31, "xor", OPER_REG_OP_ORDER}, - {0x33, "xor", REG_OPER_OP_ORDER}, {0x38, "cmpb", OPER_REG_OP_ORDER}, - {0x39, "cmp", OPER_REG_OP_ORDER}, {0x3A, "cmpb", REG_OPER_OP_ORDER}, - {0x3B, "cmp", REG_OPER_OP_ORDER}, {0x84, "test_b", REG_OPER_OP_ORDER}, - {0x85, "test", REG_OPER_OP_ORDER}, {0x86, "xchg_b", REG_OPER_OP_ORDER}, - {0x87, "xchg", REG_OPER_OP_ORDER}, {0x8A, "mov_b", REG_OPER_OP_ORDER}, - {0x8B, "mov", REG_OPER_OP_ORDER}, {0x8D, "lea", REG_OPER_OP_ORDER}, - {-1, "", UNSET_OP_ORDER}}; - -static const ByteMnemonic zero_operands_instr[] = { - {0xC3, "ret", UNSET_OP_ORDER}, - {0xC9, "leave", UNSET_OP_ORDER}, - {0x90, "nop", UNSET_OP_ORDER}, - {0xF4, "hlt", UNSET_OP_ORDER}, - {0xCC, "int3", UNSET_OP_ORDER}, - {0x60, "pushad", UNSET_OP_ORDER}, - {0x61, "popad", UNSET_OP_ORDER}, - {0x9C, "pushfd", UNSET_OP_ORDER}, - {0x9D, "popfd", UNSET_OP_ORDER}, - {0x9E, "sahf", UNSET_OP_ORDER}, - {0x99, "cdq", UNSET_OP_ORDER}, - {0x9B, "fwait", UNSET_OP_ORDER}, - {0xFC, "cld", UNSET_OP_ORDER}, - {0xAB, "stos", UNSET_OP_ORDER}, - {-1, "", UNSET_OP_ORDER} -}; - - -static const ByteMnemonic call_jump_instr[] = { - {0xE8, "call", UNSET_OP_ORDER}, - {0xE9, "jmp", UNSET_OP_ORDER}, - {-1, "", UNSET_OP_ORDER} -}; - - -static const ByteMnemonic short_immediate_instr[] = { - {0x05, "add", UNSET_OP_ORDER}, - {0x0D, "or", UNSET_OP_ORDER}, - {0x15, "adc", UNSET_OP_ORDER}, - {0x25, "and", UNSET_OP_ORDER}, - {0x2D, "sub", UNSET_OP_ORDER}, - {0x35, "xor", UNSET_OP_ORDER}, - {0x3D, "cmp", UNSET_OP_ORDER}, - {-1, "", UNSET_OP_ORDER} -}; - - -// Generally we don't want to generate these because they are subject to partial -// register stalls. They are included for completeness and because the cmp -// variant is used by the RecordWrite stub. Because it does not update the -// register it is not subject to partial register stalls. -static ByteMnemonic byte_immediate_instr[] = { - {0x0c, "or", UNSET_OP_ORDER}, - {0x24, "and", UNSET_OP_ORDER}, - {0x34, "xor", UNSET_OP_ORDER}, - {0x3c, "cmp", UNSET_OP_ORDER}, - {-1, "", UNSET_OP_ORDER} -}; - - -static const char* const jump_conditional_mnem[] = { - /*0*/ "jo", "jno", "jc", "jnc", - /*4*/ "jz", "jnz", "jna", "ja", - /*8*/ "js", "jns", "jpe", "jpo", - /*12*/ "jl", "jnl", "jng", "jg" -}; - - -static const char* const set_conditional_mnem[] = { - /*0*/ "seto", "setno", "setc", "setnc", - /*4*/ "setz", "setnz", "setna", "seta", - /*8*/ "sets", "setns", "setpe", "setpo", - /*12*/ "setl", "setnl", "setng", "setg" -}; - - -static const char* const conditional_move_mnem[] = { - /*0*/ "cmovo", "cmovno", "cmovc", "cmovnc", - /*4*/ "cmovz", "cmovnz", "cmovna", "cmova", - /*8*/ "cmovs", "cmovns", "cmovpe", "cmovpo", - /*12*/ "cmovl", "cmovnl", "cmovng", "cmovg" -}; - - -enum InstructionType { - NO_INSTR, - ZERO_OPERANDS_INSTR, - TWO_OPERANDS_INSTR, - JUMP_CONDITIONAL_SHORT_INSTR, - REGISTER_INSTR, - MOVE_REG_INSTR, - CALL_JUMP_INSTR, - SHORT_IMMEDIATE_INSTR, - BYTE_IMMEDIATE_INSTR -}; - - -struct InstructionDesc { - const char* mnem; - InstructionType type; - OperandOrder op_order_; -}; - - -class InstructionTable { - public: - InstructionTable(); - const InstructionDesc& Get(byte x) const { return instructions_[x]; } - static InstructionTable* get_instance() { - static InstructionTable table; - return &table; - } - - private: - InstructionDesc instructions_[256]; - void Clear(); - void Init(); - void CopyTable(const ByteMnemonic bm[], InstructionType type); - void SetTableRange(InstructionType type, - byte start, - byte end, - const char* mnem); - void AddJumpConditionalShort(); -}; - - -InstructionTable::InstructionTable() { - Clear(); - Init(); -} - - -void InstructionTable::Clear() { - for (int i = 0; i < 256; i++) { - instructions_[i].mnem = ""; - instructions_[i].type = NO_INSTR; - instructions_[i].op_order_ = UNSET_OP_ORDER; - } -} - - -void InstructionTable::Init() { - CopyTable(two_operands_instr, TWO_OPERANDS_INSTR); - CopyTable(zero_operands_instr, ZERO_OPERANDS_INSTR); - CopyTable(call_jump_instr, CALL_JUMP_INSTR); - CopyTable(short_immediate_instr, SHORT_IMMEDIATE_INSTR); - CopyTable(byte_immediate_instr, BYTE_IMMEDIATE_INSTR); - AddJumpConditionalShort(); - SetTableRange(REGISTER_INSTR, 0x40, 0x47, "inc"); - SetTableRange(REGISTER_INSTR, 0x48, 0x4F, "dec"); - SetTableRange(REGISTER_INSTR, 0x50, 0x57, "push"); - SetTableRange(REGISTER_INSTR, 0x58, 0x5F, "pop"); - SetTableRange(REGISTER_INSTR, 0x91, 0x97, "xchg eax,"); // 0x90 is nop. - SetTableRange(MOVE_REG_INSTR, 0xB8, 0xBF, "mov"); -} - - -void InstructionTable::CopyTable(const ByteMnemonic bm[], - InstructionType type) { - for (int i = 0; bm[i].b >= 0; i++) { - InstructionDesc* id = &instructions_[bm[i].b]; - id->mnem = bm[i].mnem; - id->op_order_ = bm[i].op_order_; - DCHECK_EQ(NO_INSTR, id->type); // Information not already entered. - id->type = type; - } -} - - -void InstructionTable::SetTableRange(InstructionType type, - byte start, - byte end, - const char* mnem) { - for (byte b = start; b <= end; b++) { - InstructionDesc* id = &instructions_[b]; - DCHECK_EQ(NO_INSTR, id->type); // Information not already entered. - id->mnem = mnem; - id->type = type; - } -} - - -void InstructionTable::AddJumpConditionalShort() { - for (byte b = 0x70; b <= 0x7F; b++) { - InstructionDesc* id = &instructions_[b]; - DCHECK_EQ(NO_INSTR, id->type); // Information not already entered. - id->mnem = jump_conditional_mnem[b & 0x0F]; - id->type = JUMP_CONDITIONAL_SHORT_INSTR; - } -} - - -// The X87 disassembler implementation. -class DisassemblerX87 { - public: - DisassemblerX87(const NameConverter& converter, - bool abort_on_unimplemented = true) - : converter_(converter), - instruction_table_(InstructionTable::get_instance()), - tmp_buffer_pos_(0), - abort_on_unimplemented_(abort_on_unimplemented) { - tmp_buffer_[0] = '\0'; - } - - virtual ~DisassemblerX87() {} - - // Writes one disassembled instruction into 'buffer' (0-terminated). - // Returns the length of the disassembled machine instruction in bytes. - int InstructionDecode(v8::internal::Vector buffer, byte* instruction); - - private: - const NameConverter& converter_; - InstructionTable* instruction_table_; - v8::internal::EmbeddedVector tmp_buffer_; - unsigned int tmp_buffer_pos_; - bool abort_on_unimplemented_; - - enum { - eax = 0, - ecx = 1, - edx = 2, - ebx = 3, - esp = 4, - ebp = 5, - esi = 6, - edi = 7 - }; - - - enum ShiftOpcodeExtension { - kROL = 0, - kROR = 1, - kRCL = 2, - kRCR = 3, - kSHL = 4, - KSHR = 5, - kSAR = 7 - }; - - - const char* NameOfCPURegister(int reg) const { - return converter_.NameOfCPURegister(reg); - } - - - const char* NameOfByteCPURegister(int reg) const { - return converter_.NameOfByteCPURegister(reg); - } - - - const char* NameOfXMMRegister(int reg) const { - return converter_.NameOfXMMRegister(reg); - } - - - const char* NameOfAddress(byte* addr) const { - return converter_.NameOfAddress(addr); - } - - - // Disassembler helper functions. - static void get_modrm(byte data, int* mod, int* regop, int* rm) { - *mod = (data >> 6) & 3; - *regop = (data & 0x38) >> 3; - *rm = data & 7; - } - - - static void get_sib(byte data, int* scale, int* index, int* base) { - *scale = (data >> 6) & 3; - *index = (data >> 3) & 7; - *base = data & 7; - } - - typedef const char* (DisassemblerX87::*RegisterNameMapping)(int reg) const; - - int PrintRightOperandHelper(byte* modrmp, RegisterNameMapping register_name); - int PrintRightOperand(byte* modrmp); - int PrintRightByteOperand(byte* modrmp); - int PrintRightXMMOperand(byte* modrmp); - int PrintOperands(const char* mnem, OperandOrder op_order, byte* data); - int PrintImmediateOp(byte* data); - int F7Instruction(byte* data); - int D1D3C1Instruction(byte* data); - int JumpShort(byte* data); - int JumpConditional(byte* data, const char* comment); - int JumpConditionalShort(byte* data, const char* comment); - int SetCC(byte* data); - int CMov(byte* data); - int FPUInstruction(byte* data); - int MemoryFPUInstruction(int escape_opcode, int regop, byte* modrm_start); - int RegisterFPUInstruction(int escape_opcode, byte modrm_byte); - PRINTF_FORMAT(2, 3) void AppendToBuffer(const char* format, ...); - - void UnimplementedInstruction() { - if (abort_on_unimplemented_) { - UNIMPLEMENTED(); - } else { - AppendToBuffer("'Unimplemented Instruction'"); - } - } -}; - - -void DisassemblerX87::AppendToBuffer(const char* format, ...) { - v8::internal::Vector buf = tmp_buffer_ + tmp_buffer_pos_; - va_list args; - va_start(args, format); - int result = v8::internal::VSNPrintF(buf, format, args); - va_end(args); - tmp_buffer_pos_ += result; -} - -int DisassemblerX87::PrintRightOperandHelper( - byte* modrmp, - RegisterNameMapping direct_register_name) { - int mod, regop, rm; - get_modrm(*modrmp, &mod, ®op, &rm); - RegisterNameMapping register_name = (mod == 3) ? direct_register_name : - &DisassemblerX87::NameOfCPURegister; - switch (mod) { - case 0: - if (rm == ebp) { - int32_t disp = *reinterpret_cast(modrmp+1); - AppendToBuffer("[0x%x]", disp); - return 5; - } else if (rm == esp) { - byte sib = *(modrmp + 1); - int scale, index, base; - get_sib(sib, &scale, &index, &base); - if (index == esp && base == esp && scale == 0 /*times_1*/) { - AppendToBuffer("[%s]", (this->*register_name)(rm)); - return 2; - } else if (base == ebp) { - int32_t disp = *reinterpret_cast(modrmp + 2); - AppendToBuffer("[%s*%d%s0x%x]", - (this->*register_name)(index), - 1 << scale, - disp < 0 ? "-" : "+", - disp < 0 ? -disp : disp); - return 6; - } else if (index != esp && base != ebp) { - // [base+index*scale] - AppendToBuffer("[%s+%s*%d]", - (this->*register_name)(base), - (this->*register_name)(index), - 1 << scale); - return 2; - } else { - UnimplementedInstruction(); - return 1; - } - } else { - AppendToBuffer("[%s]", (this->*register_name)(rm)); - return 1; - } - break; - case 1: // fall through - case 2: - if (rm == esp) { - byte sib = *(modrmp + 1); - int scale, index, base; - get_sib(sib, &scale, &index, &base); - int disp = mod == 2 ? *reinterpret_cast(modrmp + 2) - : *reinterpret_cast(modrmp + 2); - if (index == base && index == rm /*esp*/ && scale == 0 /*times_1*/) { - AppendToBuffer("[%s%s0x%x]", - (this->*register_name)(rm), - disp < 0 ? "-" : "+", - disp < 0 ? -disp : disp); - } else { - AppendToBuffer("[%s+%s*%d%s0x%x]", - (this->*register_name)(base), - (this->*register_name)(index), - 1 << scale, - disp < 0 ? "-" : "+", - disp < 0 ? -disp : disp); - } - return mod == 2 ? 6 : 3; - } else { - // No sib. - int disp = mod == 2 ? *reinterpret_cast(modrmp + 1) - : *reinterpret_cast(modrmp + 1); - AppendToBuffer("[%s%s0x%x]", - (this->*register_name)(rm), - disp < 0 ? "-" : "+", - disp < 0 ? -disp : disp); - return mod == 2 ? 5 : 2; - } - break; - case 3: - AppendToBuffer("%s", (this->*register_name)(rm)); - return 1; - default: - UnimplementedInstruction(); - return 1; - } - UNREACHABLE(); -} - - -int DisassemblerX87::PrintRightOperand(byte* modrmp) { - return PrintRightOperandHelper(modrmp, &DisassemblerX87::NameOfCPURegister); -} - - -int DisassemblerX87::PrintRightByteOperand(byte* modrmp) { - return PrintRightOperandHelper(modrmp, - &DisassemblerX87::NameOfByteCPURegister); -} - - -int DisassemblerX87::PrintRightXMMOperand(byte* modrmp) { - return PrintRightOperandHelper(modrmp, - &DisassemblerX87::NameOfXMMRegister); -} - - -// Returns number of bytes used including the current *data. -// Writes instruction's mnemonic, left and right operands to 'tmp_buffer_'. -int DisassemblerX87::PrintOperands(const char* mnem, - OperandOrder op_order, - byte* data) { - byte modrm = *data; - int mod, regop, rm; - get_modrm(modrm, &mod, ®op, &rm); - int advance = 0; - switch (op_order) { - case REG_OPER_OP_ORDER: { - AppendToBuffer("%s %s,", mnem, NameOfCPURegister(regop)); - advance = PrintRightOperand(data); - break; - } - case OPER_REG_OP_ORDER: { - AppendToBuffer("%s ", mnem); - advance = PrintRightOperand(data); - AppendToBuffer(",%s", NameOfCPURegister(regop)); - break; - } - default: - UNREACHABLE(); - break; - } - return advance; -} - - -// Returns number of bytes used by machine instruction, including *data byte. -// Writes immediate instructions to 'tmp_buffer_'. -int DisassemblerX87::PrintImmediateOp(byte* data) { - bool sign_extension_bit = (*data & 0x02) != 0; - byte modrm = *(data+1); - int mod, regop, rm; - get_modrm(modrm, &mod, ®op, &rm); - const char* mnem = "Imm???"; - switch (regop) { - case 0: mnem = "add"; break; - case 1: mnem = "or"; break; - case 2: mnem = "adc"; break; - case 4: mnem = "and"; break; - case 5: mnem = "sub"; break; - case 6: mnem = "xor"; break; - case 7: mnem = "cmp"; break; - default: UnimplementedInstruction(); - } - AppendToBuffer("%s ", mnem); - int count = PrintRightOperand(data+1); - if (sign_extension_bit) { - AppendToBuffer(",0x%x", *(data + 1 + count)); - return 1 + count + 1 /*int8*/; - } else { - AppendToBuffer(",0x%x", *reinterpret_cast(data + 1 + count)); - return 1 + count + 4 /*int32_t*/; - } -} - - -// Returns number of bytes used, including *data. -int DisassemblerX87::F7Instruction(byte* data) { - DCHECK_EQ(0xF7, *data); - byte modrm = *++data; - int mod, regop, rm; - get_modrm(modrm, &mod, ®op, &rm); - const char* mnem = NULL; - switch (regop) { - case 0: - mnem = "test"; - break; - case 2: - mnem = "not"; - break; - case 3: - mnem = "neg"; - break; - case 4: - mnem = "mul"; - break; - case 5: - mnem = "imul"; - break; - case 6: - mnem = "div"; - break; - case 7: - mnem = "idiv"; - break; - default: - UnimplementedInstruction(); - } - AppendToBuffer("%s ", mnem); - int count = PrintRightOperand(data); - if (regop == 0) { - AppendToBuffer(",0x%x", *reinterpret_cast(data + count)); - count += 4; - } - return 1 + count; -} - - -int DisassemblerX87::D1D3C1Instruction(byte* data) { - byte op = *data; - DCHECK(op == 0xD1 || op == 0xD3 || op == 0xC1); - byte modrm = *++data; - int mod, regop, rm; - get_modrm(modrm, &mod, ®op, &rm); - int imm8 = -1; - const char* mnem = NULL; - switch (regop) { - case kROL: - mnem = "rol"; - break; - case kROR: - mnem = "ror"; - break; - case kRCL: - mnem = "rcl"; - break; - case kRCR: - mnem = "rcr"; - break; - case kSHL: - mnem = "shl"; - break; - case KSHR: - mnem = "shr"; - break; - case kSAR: - mnem = "sar"; - break; - default: - UnimplementedInstruction(); - } - AppendToBuffer("%s ", mnem); - int count = PrintRightOperand(data); - if (op == 0xD1) { - imm8 = 1; - } else if (op == 0xC1) { - imm8 = *(data + 1); - count++; - } else if (op == 0xD3) { - // Shift/rotate by cl. - } - if (imm8 >= 0) { - AppendToBuffer(",%d", imm8); - } else { - AppendToBuffer(",cl"); - } - return 1 + count; -} - - -// Returns number of bytes used, including *data. -int DisassemblerX87::JumpShort(byte* data) { - DCHECK_EQ(0xEB, *data); - byte b = *(data+1); - byte* dest = data + static_cast(b) + 2; - AppendToBuffer("jmp %s", NameOfAddress(dest)); - return 2; -} - - -// Returns number of bytes used, including *data. -int DisassemblerX87::JumpConditional(byte* data, const char* comment) { - DCHECK_EQ(0x0F, *data); - byte cond = *(data+1) & 0x0F; - byte* dest = data + *reinterpret_cast(data+2) + 6; - const char* mnem = jump_conditional_mnem[cond]; - AppendToBuffer("%s %s", mnem, NameOfAddress(dest)); - if (comment != NULL) { - AppendToBuffer(", %s", comment); - } - return 6; // includes 0x0F -} - - -// Returns number of bytes used, including *data. -int DisassemblerX87::JumpConditionalShort(byte* data, const char* comment) { - byte cond = *data & 0x0F; - byte b = *(data+1); - byte* dest = data + static_cast(b) + 2; - const char* mnem = jump_conditional_mnem[cond]; - AppendToBuffer("%s %s", mnem, NameOfAddress(dest)); - if (comment != NULL) { - AppendToBuffer(", %s", comment); - } - return 2; -} - - -// Returns number of bytes used, including *data. -int DisassemblerX87::SetCC(byte* data) { - DCHECK_EQ(0x0F, *data); - byte cond = *(data+1) & 0x0F; - const char* mnem = set_conditional_mnem[cond]; - AppendToBuffer("%s ", mnem); - PrintRightByteOperand(data+2); - return 3; // Includes 0x0F. -} - - -// Returns number of bytes used, including *data. -int DisassemblerX87::CMov(byte* data) { - DCHECK_EQ(0x0F, *data); - byte cond = *(data + 1) & 0x0F; - const char* mnem = conditional_move_mnem[cond]; - int op_size = PrintOperands(mnem, REG_OPER_OP_ORDER, data + 2); - return 2 + op_size; // includes 0x0F -} - - -// Returns number of bytes used, including *data. -int DisassemblerX87::FPUInstruction(byte* data) { - byte escape_opcode = *data; - DCHECK_EQ(0xD8, escape_opcode & 0xF8); - byte modrm_byte = *(data+1); - - if (modrm_byte >= 0xC0) { - return RegisterFPUInstruction(escape_opcode, modrm_byte); - } else { - return MemoryFPUInstruction(escape_opcode, modrm_byte, data+1); - } -} - -int DisassemblerX87::MemoryFPUInstruction(int escape_opcode, - int modrm_byte, - byte* modrm_start) { - const char* mnem = "?"; - int regop = (modrm_byte >> 3) & 0x7; // reg/op field of modrm byte. - switch (escape_opcode) { - case 0xD9: switch (regop) { - case 0: mnem = "fld_s"; break; - case 2: mnem = "fst_s"; break; - case 3: mnem = "fstp_s"; break; - case 5: - mnem = "fldcw"; - break; - case 7: - mnem = "fnstcw"; - break; - default: UnimplementedInstruction(); - } - break; - - case 0xDB: switch (regop) { - case 0: mnem = "fild_s"; break; - case 1: mnem = "fisttp_s"; break; - case 2: mnem = "fist_s"; break; - case 3: mnem = "fistp_s"; break; - default: UnimplementedInstruction(); - } - break; - - case 0xDC: - switch (regop) { - case 0: - mnem = "fadd_d"; - break; - case 1: - mnem = "fmul_d"; - break; - case 4: - mnem = "fsub_d"; - break; - case 5: - mnem = "fsubr_d"; - break; - case 6: - mnem = "fdiv_d"; - break; - case 7: - mnem = "fdivr_d"; - break; - default: - UnimplementedInstruction(); - } - break; - - case 0xDD: switch (regop) { - case 0: mnem = "fld_d"; break; - case 1: mnem = "fisttp_d"; break; - case 2: mnem = "fst_d"; break; - case 3: mnem = "fstp_d"; break; - case 4: - mnem = "frstor"; - break; - case 6: - mnem = "fnsave"; - break; - default: UnimplementedInstruction(); - } - break; - - case 0xDF: switch (regop) { - case 5: mnem = "fild_d"; break; - case 7: mnem = "fistp_d"; break; - default: UnimplementedInstruction(); - } - break; - - default: UnimplementedInstruction(); - } - AppendToBuffer("%s ", mnem); - int count = PrintRightOperand(modrm_start); - return count + 1; -} - -int DisassemblerX87::RegisterFPUInstruction(int escape_opcode, - byte modrm_byte) { - bool has_register = false; // Is the FPU register encoded in modrm_byte? - const char* mnem = "?"; - - switch (escape_opcode) { - case 0xD8: - has_register = true; - switch (modrm_byte & 0xF8) { - case 0xC0: mnem = "fadd_i"; break; - case 0xE0: mnem = "fsub_i"; break; - case 0xC8: mnem = "fmul_i"; break; - case 0xF0: mnem = "fdiv_i"; break; - default: UnimplementedInstruction(); - } - break; - - case 0xD9: - switch (modrm_byte & 0xF8) { - case 0xC0: - mnem = "fld"; - has_register = true; - break; - case 0xC8: - mnem = "fxch"; - has_register = true; - break; - default: - switch (modrm_byte) { - case 0xE0: mnem = "fchs"; break; - case 0xE1: mnem = "fabs"; break; - case 0xE4: mnem = "ftst"; break; - case 0xE8: mnem = "fld1"; break; - case 0xEB: mnem = "fldpi"; break; - case 0xED: mnem = "fldln2"; break; - case 0xEE: mnem = "fldz"; break; - case 0xF0: mnem = "f2xm1"; break; - case 0xF1: mnem = "fyl2x"; break; - case 0xF4: mnem = "fxtract"; break; - case 0xF5: mnem = "fprem1"; break; - case 0xF7: mnem = "fincstp"; break; - case 0xF8: mnem = "fprem"; break; - case 0xFC: mnem = "frndint"; break; - case 0xFD: mnem = "fscale"; break; - case 0xFE: mnem = "fsin"; break; - case 0xFF: mnem = "fcos"; break; - default: UnimplementedInstruction(); - } - } - break; - - case 0xDA: - if (modrm_byte == 0xE9) { - mnem = "fucompp"; - } else { - UnimplementedInstruction(); - } - break; - - case 0xDB: - if ((modrm_byte & 0xF8) == 0xE8) { - mnem = "fucomi"; - has_register = true; - } else if (modrm_byte == 0xE2) { - mnem = "fclex"; - } else if (modrm_byte == 0xE3) { - mnem = "fninit"; - } else { - UnimplementedInstruction(); - } - break; - - case 0xDC: - has_register = true; - switch (modrm_byte & 0xF8) { - case 0xC0: mnem = "fadd"; break; - case 0xE8: mnem = "fsub"; break; - case 0xC8: mnem = "fmul"; break; - case 0xF8: mnem = "fdiv"; break; - default: UnimplementedInstruction(); - } - break; - - case 0xDD: - has_register = true; - switch (modrm_byte & 0xF8) { - case 0xC0: mnem = "ffree"; break; - case 0xD0: mnem = "fst"; break; - case 0xD8: mnem = "fstp"; break; - default: UnimplementedInstruction(); - } - break; - - case 0xDE: - if (modrm_byte == 0xD9) { - mnem = "fcompp"; - } else { - has_register = true; - switch (modrm_byte & 0xF8) { - case 0xC0: mnem = "faddp"; break; - case 0xE8: mnem = "fsubp"; break; - case 0xC8: mnem = "fmulp"; break; - case 0xF8: mnem = "fdivp"; break; - default: UnimplementedInstruction(); - } - } - break; - - case 0xDF: - if (modrm_byte == 0xE0) { - mnem = "fnstsw_ax"; - } else if ((modrm_byte & 0xF8) == 0xE8) { - mnem = "fucomip"; - has_register = true; - } - break; - - default: UnimplementedInstruction(); - } - - if (has_register) { - AppendToBuffer("%s st%d", mnem, modrm_byte & 0x7); - } else { - AppendToBuffer("%s", mnem); - } - return 2; -} - - -// Mnemonics for instructions 0xF0 byte. -// Returns NULL if the instruction is not handled here. -static const char* F0Mnem(byte f0byte) { - switch (f0byte) { - case 0x0B: - return "ud2"; - case 0x18: - return "prefetch"; - case 0xA2: - return "cpuid"; - case 0xBE: - return "movsx_b"; - case 0xBF: - return "movsx_w"; - case 0xB6: - return "movzx_b"; - case 0xB7: - return "movzx_w"; - case 0xAF: - return "imul"; - case 0xA4: - return "shld"; - case 0xA5: - return "shld"; - case 0xAD: - return "shrd"; - case 0xAC: - return "shrd"; // 3-operand version. - case 0xAB: - return "bts"; - case 0xB0: - return "cmpxchg_b"; - case 0xB1: - return "cmpxchg"; - case 0xBC: - return "bsf"; - case 0xBD: - return "bsr"; - default: return NULL; - } -} - - -// Disassembled instruction '*instr' and writes it into 'out_buffer'. -int DisassemblerX87::InstructionDecode(v8::internal::Vector out_buffer, - byte* instr) { - tmp_buffer_pos_ = 0; // starting to write as position 0 - byte* data = instr; - // Check for hints. - const char* branch_hint = NULL; - // We use these two prefixes only with branch prediction - if (*data == 0x3E /*ds*/) { - branch_hint = "predicted taken"; - data++; - } else if (*data == 0x2E /*cs*/) { - branch_hint = "predicted not taken"; - data++; - } else if (*data == 0xF0 /*lock*/) { - AppendToBuffer("lock "); - data++; - } - - bool processed = true; // Will be set to false if the current instruction - // is not in 'instructions' table. - const InstructionDesc& idesc = instruction_table_->Get(*data); - switch (idesc.type) { - case ZERO_OPERANDS_INSTR: - AppendToBuffer("%s", idesc.mnem); - data++; - break; - - case TWO_OPERANDS_INSTR: - data++; - data += PrintOperands(idesc.mnem, idesc.op_order_, data); - break; - - case JUMP_CONDITIONAL_SHORT_INSTR: - data += JumpConditionalShort(data, branch_hint); - break; - - case REGISTER_INSTR: - AppendToBuffer("%s %s", idesc.mnem, NameOfCPURegister(*data & 0x07)); - data++; - break; - - case MOVE_REG_INSTR: { - byte* addr = reinterpret_cast(*reinterpret_cast(data+1)); - AppendToBuffer("mov %s,%s", - NameOfCPURegister(*data & 0x07), - NameOfAddress(addr)); - data += 5; - break; - } - - case CALL_JUMP_INSTR: { - byte* addr = data + *reinterpret_cast(data+1) + 5; - AppendToBuffer("%s %s", idesc.mnem, NameOfAddress(addr)); - data += 5; - break; - } - - case SHORT_IMMEDIATE_INSTR: { - byte* addr = reinterpret_cast(*reinterpret_cast(data+1)); - AppendToBuffer("%s eax,%s", idesc.mnem, NameOfAddress(addr)); - data += 5; - break; - } - - case BYTE_IMMEDIATE_INSTR: { - AppendToBuffer("%s al,0x%x", idesc.mnem, data[1]); - data += 2; - break; - } - - case NO_INSTR: - processed = false; - break; - - default: - UNIMPLEMENTED(); // This type is not implemented. - } - //---------------------------- - if (!processed) { - switch (*data) { - case 0xC2: - AppendToBuffer("ret 0x%x", *reinterpret_cast(data+1)); - data += 3; - break; - - case 0x6B: { - data++; - data += PrintOperands("imul", REG_OPER_OP_ORDER, data); - AppendToBuffer(",%d", *data); - data++; - } break; - - case 0x69: { - data++; - data += PrintOperands("imul", REG_OPER_OP_ORDER, data); - AppendToBuffer(",%d", *reinterpret_cast(data)); - data += 4; - } - break; - - case 0xF6: - { data++; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - if (regop == eax) { - AppendToBuffer("test_b "); - data += PrintRightByteOperand(data); - int32_t imm = *data; - AppendToBuffer(",0x%x", imm); - data++; - } else { - UnimplementedInstruction(); - } - } - break; - - case 0x81: // fall through - case 0x83: // 0x81 with sign extension bit set - data += PrintImmediateOp(data); - break; - - case 0x0F: - { byte f0byte = data[1]; - const char* f0mnem = F0Mnem(f0byte); - if (f0byte == 0x18) { - data += 2; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - const char* suffix[] = {"nta", "1", "2", "3"}; - AppendToBuffer("%s%s ", f0mnem, suffix[regop & 0x03]); - data += PrintRightOperand(data); - } else if (f0byte == 0x1F && data[2] == 0) { - AppendToBuffer("nop"); // 3 byte nop. - data += 3; - } else if (f0byte == 0x1F && data[2] == 0x40 && data[3] == 0) { - AppendToBuffer("nop"); // 4 byte nop. - data += 4; - } else if (f0byte == 0x1F && data[2] == 0x44 && data[3] == 0 && - data[4] == 0) { - AppendToBuffer("nop"); // 5 byte nop. - data += 5; - } else if (f0byte == 0x1F && data[2] == 0x80 && data[3] == 0 && - data[4] == 0 && data[5] == 0 && data[6] == 0) { - AppendToBuffer("nop"); // 7 byte nop. - data += 7; - } else if (f0byte == 0x1F && data[2] == 0x84 && data[3] == 0 && - data[4] == 0 && data[5] == 0 && data[6] == 0 && - data[7] == 0) { - AppendToBuffer("nop"); // 8 byte nop. - data += 8; - } else if (f0byte == 0x0B || f0byte == 0xA2 || f0byte == 0x31) { - AppendToBuffer("%s", f0mnem); - data += 2; - } else if (f0byte == 0x28) { - data += 2; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - AppendToBuffer("movaps %s,%s", - NameOfXMMRegister(regop), - NameOfXMMRegister(rm)); - data++; - } else if (f0byte >= 0x53 && f0byte <= 0x5F) { - const char* const pseudo_op[] = { - "rcpps", - "andps", - "andnps", - "orps", - "xorps", - "addps", - "mulps", - "cvtps2pd", - "cvtdq2ps", - "subps", - "minps", - "divps", - "maxps", - }; - - data += 2; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - AppendToBuffer("%s %s,", - pseudo_op[f0byte - 0x53], - NameOfXMMRegister(regop)); - data += PrintRightXMMOperand(data); - } else if (f0byte == 0x50) { - data += 2; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - AppendToBuffer("movmskps %s,%s", - NameOfCPURegister(regop), - NameOfXMMRegister(rm)); - data++; - } else if (f0byte== 0xC6) { - // shufps xmm, xmm/m128, imm8 - data += 2; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - int8_t imm8 = static_cast(data[1]); - AppendToBuffer("shufps %s,%s,%d", - NameOfXMMRegister(rm), - NameOfXMMRegister(regop), - static_cast(imm8)); - data += 2; - } else if ((f0byte & 0xF0) == 0x80) { - data += JumpConditional(data, branch_hint); - } else if (f0byte == 0xBE || f0byte == 0xBF || f0byte == 0xB6 || - f0byte == 0xB7 || f0byte == 0xAF) { - data += 2; - data += PrintOperands(f0mnem, REG_OPER_OP_ORDER, data); - } else if ((f0byte & 0xF0) == 0x90) { - data += SetCC(data); - } else if ((f0byte & 0xF0) == 0x40) { - data += CMov(data); - } else if (f0byte == 0xA4 || f0byte == 0xAC) { - // shld, shrd - data += 2; - AppendToBuffer("%s ", f0mnem); - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - int8_t imm8 = static_cast(data[1]); - data += 2; - AppendToBuffer("%s,%s,%d", NameOfCPURegister(rm), - NameOfCPURegister(regop), static_cast(imm8)); - } else if (f0byte == 0xAB || f0byte == 0xA5 || f0byte == 0xAD) { - // shrd_cl, shld_cl, bts - data += 2; - AppendToBuffer("%s ", f0mnem); - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - data += PrintRightOperand(data); - if (f0byte == 0xAB) { - AppendToBuffer(",%s", NameOfCPURegister(regop)); - } else { - AppendToBuffer(",%s,cl", NameOfCPURegister(regop)); - } - } else if (f0byte == 0xB0) { - // cmpxchg_b - data += 2; - AppendToBuffer("%s ", f0mnem); - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - data += PrintRightOperand(data); - AppendToBuffer(",%s", NameOfByteCPURegister(regop)); - } else if (f0byte == 0xB1) { - // cmpxchg - data += 2; - data += PrintOperands(f0mnem, OPER_REG_OP_ORDER, data); - } else if (f0byte == 0xBC) { - data += 2; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - AppendToBuffer("%s %s,", f0mnem, NameOfCPURegister(regop)); - data += PrintRightOperand(data); - } else if (f0byte == 0xBD) { - data += 2; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - AppendToBuffer("%s %s,", f0mnem, NameOfCPURegister(regop)); - data += PrintRightOperand(data); - } else { - UnimplementedInstruction(); - } - } - break; - - case 0x8F: - { data++; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - if (regop == eax) { - AppendToBuffer("pop "); - data += PrintRightOperand(data); - } - } - break; - - case 0xFF: - { data++; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - const char* mnem = NULL; - switch (regop) { - case esi: mnem = "push"; break; - case eax: mnem = "inc"; break; - case ecx: mnem = "dec"; break; - case edx: mnem = "call"; break; - case esp: mnem = "jmp"; break; - default: mnem = "???"; - } - AppendToBuffer("%s ", mnem); - data += PrintRightOperand(data); - } - break; - - case 0xC7: // imm32, fall through - case 0xC6: // imm8 - { bool is_byte = *data == 0xC6; - data++; - if (is_byte) { - AppendToBuffer("%s ", "mov_b"); - data += PrintRightByteOperand(data); - int32_t imm = *data; - AppendToBuffer(",0x%x", imm); - data++; - } else { - AppendToBuffer("%s ", "mov"); - data += PrintRightOperand(data); - int32_t imm = *reinterpret_cast(data); - AppendToBuffer(",0x%x", imm); - data += 4; - } - } - break; - - case 0x80: - { data++; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - const char* mnem = NULL; - switch (regop) { - case 5: mnem = "subb"; break; - case 7: mnem = "cmpb"; break; - default: UnimplementedInstruction(); - } - AppendToBuffer("%s ", mnem); - data += PrintRightByteOperand(data); - int32_t imm = *data; - AppendToBuffer(",0x%x", imm); - data++; - } - break; - - case 0x88: // 8bit, fall through - case 0x89: // 32bit - { bool is_byte = *data == 0x88; - int mod, regop, rm; - data++; - get_modrm(*data, &mod, ®op, &rm); - if (is_byte) { - AppendToBuffer("%s ", "mov_b"); - data += PrintRightByteOperand(data); - AppendToBuffer(",%s", NameOfByteCPURegister(regop)); - } else { - AppendToBuffer("%s ", "mov"); - data += PrintRightOperand(data); - AppendToBuffer(",%s", NameOfCPURegister(regop)); - } - } - break; - - case 0x66: // prefix - while (*data == 0x66) data++; - if (*data == 0xf && data[1] == 0x1f) { - AppendToBuffer("nop"); // 0x66 prefix - } else if (*data == 0x39) { - data++; - data += PrintOperands("cmpw", OPER_REG_OP_ORDER, data); - } else if (*data == 0x3B) { - data++; - data += PrintOperands("cmpw", REG_OPER_OP_ORDER, data); - } else if (*data == 0x81) { - data++; - AppendToBuffer("cmpw "); - data += PrintRightOperand(data); - int imm = *reinterpret_cast(data); - AppendToBuffer(",0x%x", imm); - data += 2; - } else if (*data == 0x87) { - data++; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - AppendToBuffer("xchg_w %s,", NameOfCPURegister(regop)); - data += PrintRightOperand(data); - } else if (*data == 0x89) { - data++; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - AppendToBuffer("mov_w "); - data += PrintRightOperand(data); - AppendToBuffer(",%s", NameOfCPURegister(regop)); - } else if (*data == 0x8B) { - data++; - data += PrintOperands("mov_w", REG_OPER_OP_ORDER, data); - } else if (*data == 0x90) { - AppendToBuffer("nop"); // 0x66 prefix - } else if (*data == 0xC7) { - data++; - AppendToBuffer("%s ", "mov_w"); - data += PrintRightOperand(data); - int imm = *reinterpret_cast(data); - AppendToBuffer(",0x%x", imm); - data += 2; - } else if (*data == 0xF7) { - data++; - AppendToBuffer("%s ", "test_w"); - data += PrintRightOperand(data); - int imm = *reinterpret_cast(data); - AppendToBuffer(",0x%x", imm); - data += 2; - } else if (*data == 0x0F) { - data++; - if (*data == 0x38) { - data++; - if (*data == 0x17) { - data++; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - AppendToBuffer("ptest %s,%s", - NameOfXMMRegister(regop), - NameOfXMMRegister(rm)); - data++; - } else if (*data == 0x2A) { - // movntdqa - UnimplementedInstruction(); - } else { - UnimplementedInstruction(); - } - } else if (*data == 0x3A) { - data++; - if (*data == 0x0B) { - data++; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - int8_t imm8 = static_cast(data[1]); - AppendToBuffer("roundsd %s,%s,%d", - NameOfXMMRegister(regop), - NameOfXMMRegister(rm), - static_cast(imm8)); - data += 2; - } else if (*data == 0x16) { - data++; - int mod, regop, rm; - get_modrm(*data, &mod, &rm, ®op); - int8_t imm8 = static_cast(data[1]); - AppendToBuffer("pextrd %s,%s,%d", - NameOfCPURegister(regop), - NameOfXMMRegister(rm), - static_cast(imm8)); - data += 2; - } else if (*data == 0x17) { - data++; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - int8_t imm8 = static_cast(data[1]); - AppendToBuffer("extractps %s,%s,%d", - NameOfCPURegister(rm), - NameOfXMMRegister(regop), - static_cast(imm8)); - data += 2; - } else if (*data == 0x22) { - data++; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - int8_t imm8 = static_cast(data[1]); - AppendToBuffer("pinsrd %s,%s,%d", - NameOfXMMRegister(regop), - NameOfCPURegister(rm), - static_cast(imm8)); - data += 2; - } else { - UnimplementedInstruction(); - } - } else if (*data == 0x2E || *data == 0x2F) { - const char* mnem = (*data == 0x2E) ? "ucomisd" : "comisd"; - data++; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - if (mod == 0x3) { - AppendToBuffer("%s %s,%s", mnem, - NameOfXMMRegister(regop), - NameOfXMMRegister(rm)); - data++; - } else { - AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop)); - data += PrintRightOperand(data); - } - } else if (*data == 0x50) { - data++; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - AppendToBuffer("movmskpd %s,%s", - NameOfCPURegister(regop), - NameOfXMMRegister(rm)); - data++; - } else if (*data == 0x54) { - data++; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - AppendToBuffer("andpd %s,%s", - NameOfXMMRegister(regop), - NameOfXMMRegister(rm)); - data++; - } else if (*data == 0x56) { - data++; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - AppendToBuffer("orpd %s,%s", - NameOfXMMRegister(regop), - NameOfXMMRegister(rm)); - data++; - } else if (*data == 0x57) { - data++; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - AppendToBuffer("xorpd %s,%s", - NameOfXMMRegister(regop), - NameOfXMMRegister(rm)); - data++; - } else if (*data == 0x6E) { - data++; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - AppendToBuffer("movd %s,", NameOfXMMRegister(regop)); - data += PrintRightOperand(data); - } else if (*data == 0x6F) { - data++; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - AppendToBuffer("movdqa %s,", NameOfXMMRegister(regop)); - data += PrintRightXMMOperand(data); - } else if (*data == 0x70) { - data++; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - int8_t imm8 = static_cast(data[1]); - AppendToBuffer("pshufd %s,%s,%d", - NameOfXMMRegister(regop), - NameOfXMMRegister(rm), - static_cast(imm8)); - data += 2; - } else if (*data == 0x76) { - data++; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - AppendToBuffer("pcmpeqd %s,%s", - NameOfXMMRegister(regop), - NameOfXMMRegister(rm)); - data++; - } else if (*data == 0x90) { - data++; - AppendToBuffer("nop"); // 2 byte nop. - } else if (*data == 0xF3) { - data++; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - AppendToBuffer("psllq %s,%s", - NameOfXMMRegister(regop), - NameOfXMMRegister(rm)); - data++; - } else if (*data == 0x73) { - data++; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - int8_t imm8 = static_cast(data[1]); - DCHECK(regop == esi || regop == edx); - AppendToBuffer("%s %s,%d", - (regop == esi) ? "psllq" : "psrlq", - NameOfXMMRegister(rm), - static_cast(imm8)); - data += 2; - } else if (*data == 0xD3) { - data++; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - AppendToBuffer("psrlq %s,%s", - NameOfXMMRegister(regop), - NameOfXMMRegister(rm)); - data++; - } else if (*data == 0x7F) { - AppendToBuffer("movdqa "); - data++; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - data += PrintRightXMMOperand(data); - AppendToBuffer(",%s", NameOfXMMRegister(regop)); - } else if (*data == 0x7E) { - data++; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - AppendToBuffer("movd "); - data += PrintRightOperand(data); - AppendToBuffer(",%s", NameOfXMMRegister(regop)); - } else if (*data == 0xDB) { - data++; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - AppendToBuffer("pand %s,%s", - NameOfXMMRegister(regop), - NameOfXMMRegister(rm)); - data++; - } else if (*data == 0xE7) { - data++; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - if (mod == 3) { - // movntdq - UnimplementedInstruction(); - } else { - UnimplementedInstruction(); - } - } else if (*data == 0xEF) { - data++; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - AppendToBuffer("pxor %s,%s", - NameOfXMMRegister(regop), - NameOfXMMRegister(rm)); - data++; - } else if (*data == 0xEB) { - data++; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - AppendToBuffer("por %s,%s", - NameOfXMMRegister(regop), - NameOfXMMRegister(rm)); - data++; - } else if (*data == 0xB1) { - data++; - data += PrintOperands("cmpxchg_w", OPER_REG_OP_ORDER, data); - } else { - UnimplementedInstruction(); - } - } else { - UnimplementedInstruction(); - } - break; - - case 0xFE: - { data++; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - if (regop == ecx) { - AppendToBuffer("dec_b "); - data += PrintRightOperand(data); - } else { - UnimplementedInstruction(); - } - } - break; - - case 0x68: - AppendToBuffer("push 0x%x", *reinterpret_cast(data+1)); - data += 5; - break; - - case 0x6A: - AppendToBuffer("push 0x%x", *reinterpret_cast(data + 1)); - data += 2; - break; - - case 0xA8: - AppendToBuffer("test al,0x%x", *reinterpret_cast(data+1)); - data += 2; - break; - - case 0xA9: - AppendToBuffer("test eax,0x%x", *reinterpret_cast(data+1)); - data += 5; - break; - - case 0xD1: // fall through - case 0xD3: // fall through - case 0xC1: - data += D1D3C1Instruction(data); - break; - - case 0xD8: // fall through - case 0xD9: // fall through - case 0xDA: // fall through - case 0xDB: // fall through - case 0xDC: // fall through - case 0xDD: // fall through - case 0xDE: // fall through - case 0xDF: - data += FPUInstruction(data); - break; - - case 0xEB: - data += JumpShort(data); - break; - - case 0xF2: - if (*(data+1) == 0x0F) { - byte b2 = *(data+2); - if (b2 == 0x11) { - AppendToBuffer("movsd "); - data += 3; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - data += PrintRightXMMOperand(data); - AppendToBuffer(",%s", NameOfXMMRegister(regop)); - } else if (b2 == 0x10) { - data += 3; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - AppendToBuffer("movsd %s,", NameOfXMMRegister(regop)); - data += PrintRightXMMOperand(data); - } else if (b2 == 0x5A) { - data += 3; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - AppendToBuffer("cvtsd2ss %s,", NameOfXMMRegister(regop)); - data += PrintRightXMMOperand(data); - } else { - const char* mnem = "?"; - switch (b2) { - case 0x2A: mnem = "cvtsi2sd"; break; - case 0x2C: mnem = "cvttsd2si"; break; - case 0x2D: mnem = "cvtsd2si"; break; - case 0x51: mnem = "sqrtsd"; break; - case 0x58: mnem = "addsd"; break; - case 0x59: mnem = "mulsd"; break; - case 0x5C: mnem = "subsd"; break; - case 0x5E: mnem = "divsd"; break; - } - data += 3; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - if (b2 == 0x2A) { - AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop)); - data += PrintRightOperand(data); - } else if (b2 == 0x2C || b2 == 0x2D) { - AppendToBuffer("%s %s,", mnem, NameOfCPURegister(regop)); - data += PrintRightXMMOperand(data); - } else if (b2 == 0xC2) { - // Intel manual 2A, Table 3-18. - const char* const pseudo_op[] = { - "cmpeqsd", - "cmpltsd", - "cmplesd", - "cmpunordsd", - "cmpneqsd", - "cmpnltsd", - "cmpnlesd", - "cmpordsd" - }; - AppendToBuffer("%s %s,%s", - pseudo_op[data[1]], - NameOfXMMRegister(regop), - NameOfXMMRegister(rm)); - data += 2; - } else { - AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop)); - data += PrintRightXMMOperand(data); - } - } - } else { - UnimplementedInstruction(); - } - break; - - case 0xF3: - if (*(data+1) == 0x0F) { - byte b2 = *(data+2); - if (b2 == 0x11) { - AppendToBuffer("movss "); - data += 3; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - data += PrintRightXMMOperand(data); - AppendToBuffer(",%s", NameOfXMMRegister(regop)); - } else if (b2 == 0x10) { - data += 3; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - AppendToBuffer("movss %s,", NameOfXMMRegister(regop)); - data += PrintRightXMMOperand(data); - } else if (b2 == 0x2C) { - data += 3; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - AppendToBuffer("cvttss2si %s,", NameOfCPURegister(regop)); - data += PrintRightXMMOperand(data); - } else if (b2 == 0x5A) { - data += 3; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - AppendToBuffer("cvtss2sd %s,", NameOfXMMRegister(regop)); - data += PrintRightXMMOperand(data); - } else if (b2 == 0x6F) { - data += 3; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - AppendToBuffer("movdqu %s,", NameOfXMMRegister(regop)); - data += PrintRightXMMOperand(data); - } else if (b2 == 0x7F) { - AppendToBuffer("movdqu "); - data += 3; - int mod, regop, rm; - get_modrm(*data, &mod, ®op, &rm); - data += PrintRightXMMOperand(data); - AppendToBuffer(",%s", NameOfXMMRegister(regop)); - } else { - UnimplementedInstruction(); - } - } else if (*(data+1) == 0xA5) { - data += 2; - AppendToBuffer("rep_movs"); - } else if (*(data+1) == 0xAB) { - data += 2; - AppendToBuffer("rep_stos"); - } else { - UnimplementedInstruction(); - } - break; - - case 0xF7: - data += F7Instruction(data); - break; - - default: - UnimplementedInstruction(); - } - } - - if (tmp_buffer_pos_ < sizeof tmp_buffer_) { - tmp_buffer_[tmp_buffer_pos_] = '\0'; - } - - int instr_len = data - instr; - if (instr_len == 0) { - printf("%02x", *data); - } - DCHECK(instr_len > 0); // Ensure progress. - - int outp = 0; - // Instruction bytes. - for (byte* bp = instr; bp < data; bp++) { - outp += v8::internal::SNPrintF(out_buffer + outp, "%02x", *bp); - } - for (int i = 6 - instr_len; i >= 0; i--) { - outp += v8::internal::SNPrintF(out_buffer + outp, " "); - } - - outp += v8::internal::SNPrintF(out_buffer + outp, " %s", tmp_buffer_.start()); - return instr_len; -} // NOLINT (function is too long) - - -//------------------------------------------------------------------------------ - - -static const char* const cpu_regs[8] = { - "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi" -}; - - -static const char* const byte_cpu_regs[8] = { - "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh" -}; - - -static const char* const xmm_regs[8] = { - "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7" -}; - - -const char* NameConverter::NameOfAddress(byte* addr) const { - v8::internal::SNPrintF(tmp_buffer_, "%p", static_cast(addr)); - return tmp_buffer_.start(); -} - - -const char* NameConverter::NameOfConstant(byte* addr) const { - return NameOfAddress(addr); -} - - -const char* NameConverter::NameOfCPURegister(int reg) const { - if (0 <= reg && reg < 8) return cpu_regs[reg]; - return "noreg"; -} - - -const char* NameConverter::NameOfByteCPURegister(int reg) const { - if (0 <= reg && reg < 8) return byte_cpu_regs[reg]; - return "noreg"; -} - - -const char* NameConverter::NameOfXMMRegister(int reg) const { - if (0 <= reg && reg < 8) return xmm_regs[reg]; - return "noxmmreg"; -} - - -const char* NameConverter::NameInCode(byte* addr) const { - // X87 does not embed debug strings at the moment. - UNREACHABLE(); -} - - -//------------------------------------------------------------------------------ - -Disassembler::Disassembler(const NameConverter& converter) - : converter_(converter) {} - - -Disassembler::~Disassembler() {} - - -int Disassembler::InstructionDecode(v8::internal::Vector buffer, - byte* instruction) { - DisassemblerX87 d(converter_, false /*do not crash if unimplemented*/); - return d.InstructionDecode(buffer, instruction); -} - - -// The IA-32 assembler does not currently use constant pools. -int Disassembler::ConstantPoolSizeAt(byte* instruction) { return -1; } - - -/*static*/ void Disassembler::Disassemble(FILE* f, byte* begin, byte* end) { - NameConverter converter; - Disassembler d(converter); - for (byte* pc = begin; pc < end;) { - v8::internal::EmbeddedVector buffer; - buffer[0] = '\0'; - byte* prev_pc = pc; - pc += d.InstructionDecode(buffer, pc); - fprintf(f, "%p", static_cast(prev_pc)); - fprintf(f, " "); - - for (byte* bp = prev_pc; bp < pc; bp++) { - fprintf(f, "%02x", *bp); - } - for (int i = 6 - (pc - prev_pc); i >= 0; i--) { - fprintf(f, " "); - } - fprintf(f, " %s\n", buffer.start()); - } -} - - -} // namespace disasm - -#endif // V8_TARGET_ARCH_X87 diff --git a/src/x87/frames-x87.cc b/src/x87/frames-x87.cc deleted file mode 100644 index 6a62404f17..0000000000 --- a/src/x87/frames-x87.cc +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2006-2008 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_X87 - -#include "src/assembler.h" -#include "src/frames.h" -#include "src/x87/assembler-x87-inl.h" -#include "src/x87/assembler-x87.h" -#include "src/x87/frames-x87.h" - -namespace v8 { -namespace internal { - - -Register JavaScriptFrame::fp_register() { return ebp; } -Register JavaScriptFrame::context_register() { return esi; } -Register JavaScriptFrame::constant_pool_pointer_register() { - UNREACHABLE(); -} - - -} // namespace internal -} // namespace v8 - -#endif // V8_TARGET_ARCH_X87 diff --git a/src/x87/frames-x87.h b/src/x87/frames-x87.h deleted file mode 100644 index 1a378ed3ec..0000000000 --- a/src/x87/frames-x87.h +++ /dev/null @@ -1,78 +0,0 @@ -// 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. - -#ifndef V8_X87_FRAMES_X87_H_ -#define V8_X87_FRAMES_X87_H_ - -namespace v8 { -namespace internal { - - -// Register lists -// Note that the bit values must match those used in actual instruction encoding -const int kNumRegs = 8; - - -// Caller-saved registers -const RegList kJSCallerSaved = - 1 << 0 | // eax - 1 << 1 | // ecx - 1 << 2 | // edx - 1 << 3 | // ebx - used as a caller-saved register in JavaScript code - 1 << 7; // edi - callee function - -const int kNumJSCallerSaved = 5; - - -// Number of registers for which space is reserved in safepoints. -const int kNumSafepointRegisters = 8; - -// ---------------------------------------------------- - - -class EntryFrameConstants : public AllStatic { - public: - static const int kCallerFPOffset = -6 * kPointerSize; - - static const int kNewTargetArgOffset = +2 * kPointerSize; - static const int kFunctionArgOffset = +3 * kPointerSize; - static const int kReceiverArgOffset = +4 * kPointerSize; - static const int kArgcOffset = +5 * kPointerSize; - static const int kArgvOffset = +6 * kPointerSize; -}; - -class ExitFrameConstants : public TypedFrameConstants { - public: - static const int kSPOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(0); - static const int kCodeOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(1); - DEFINE_TYPED_FRAME_SIZES(2); - - static const int kCallerFPOffset = 0 * kPointerSize; - static const int kCallerPCOffset = +1 * kPointerSize; - - // FP-relative displacement of the caller's SP. It points just - // below the saved PC. - static const int kCallerSPDisplacement = +2 * kPointerSize; - - static const int kConstantPoolOffset = 0; // Not used -}; - - -class JavaScriptFrameConstants : public AllStatic { - public: - // FP-relative. - static const int kLocal0Offset = StandardFrameConstants::kExpressionsOffset; - static const int kLastParameterOffset = +2 * kPointerSize; - static const int kFunctionOffset = StandardFrameConstants::kFunctionOffset; - - // Caller SP-relative. - static const int kParam0Offset = -2 * kPointerSize; - static const int kReceiverOffset = -1 * kPointerSize; -}; - - -} // namespace internal -} // namespace v8 - -#endif // V8_X87_FRAMES_X87_H_ diff --git a/src/x87/interface-descriptors-x87.cc b/src/x87/interface-descriptors-x87.cc deleted file mode 100644 index b849eb9937..0000000000 --- a/src/x87/interface-descriptors-x87.cc +++ /dev/null @@ -1,386 +0,0 @@ -// 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_X87 - -#include "src/interface-descriptors.h" - -namespace v8 { -namespace internal { - -const Register CallInterfaceDescriptor::ContextRegister() { return esi; } - -void CallInterfaceDescriptor::DefaultInitializePlatformSpecific( - CallInterfaceDescriptorData* data, int register_parameter_count) { - const Register default_stub_registers[] = {eax, ebx, ecx, edx, edi}; - CHECK_LE(static_cast(register_parameter_count), - arraysize(default_stub_registers)); - data->InitializePlatformSpecific(register_parameter_count, - default_stub_registers); -} - -const Register FastNewFunctionContextDescriptor::FunctionRegister() { - return edi; -} -const Register FastNewFunctionContextDescriptor::SlotsRegister() { return eax; } - -const Register LoadDescriptor::ReceiverRegister() { return edx; } -const Register LoadDescriptor::NameRegister() { return ecx; } -const Register LoadDescriptor::SlotRegister() { return eax; } - -const Register LoadWithVectorDescriptor::VectorRegister() { return ebx; } - -const Register LoadICProtoArrayDescriptor::HandlerRegister() { return edi; } - -const Register StoreDescriptor::ReceiverRegister() { return edx; } -const Register StoreDescriptor::NameRegister() { return ecx; } -const Register StoreDescriptor::ValueRegister() { return eax; } -const Register StoreDescriptor::SlotRegister() { return edi; } - -const Register StoreWithVectorDescriptor::VectorRegister() { return ebx; } - -const Register StoreTransitionDescriptor::SlotRegister() { return no_reg; } -const Register StoreTransitionDescriptor::VectorRegister() { return ebx; } -const Register StoreTransitionDescriptor::MapRegister() { return edi; } - -const Register StringCompareDescriptor::LeftRegister() { return edx; } -const Register StringCompareDescriptor::RightRegister() { return eax; } - -const Register StringConcatDescriptor::ArgumentsCountRegister() { return eax; } - -const Register ApiGetterDescriptor::HolderRegister() { return ecx; } -const Register ApiGetterDescriptor::CallbackRegister() { return eax; } - -const Register MathPowTaggedDescriptor::exponent() { return eax; } - -const Register MathPowIntegerDescriptor::exponent() { - return MathPowTaggedDescriptor::exponent(); -} - - -const Register GrowArrayElementsDescriptor::ObjectRegister() { return eax; } -const Register GrowArrayElementsDescriptor::KeyRegister() { return ebx; } - - -void FastNewClosureDescriptor::InitializePlatformSpecific( - CallInterfaceDescriptorData* data) { - // SharedFunctionInfo, vector, slot index. - Register registers[] = {ebx, ecx, edx}; - data->InitializePlatformSpecific(arraysize(registers), registers, NULL); -} - -void FastNewRestParameterDescriptor::InitializePlatformSpecific( - CallInterfaceDescriptorData* data) { - Register registers[] = {edi}; - data->InitializePlatformSpecific(arraysize(registers), registers, NULL); -} - -void FastNewSloppyArgumentsDescriptor::InitializePlatformSpecific( - CallInterfaceDescriptorData* data) { - Register registers[] = {edi}; - data->InitializePlatformSpecific(arraysize(registers), registers, NULL); -} - -void FastNewStrictArgumentsDescriptor::InitializePlatformSpecific( - CallInterfaceDescriptorData* data) { - Register registers[] = {edi}; - data->InitializePlatformSpecific(arraysize(registers), registers, NULL); -} - - -// static -const Register TypeConversionDescriptor::ArgumentRegister() { return eax; } - -void TypeofDescriptor::InitializePlatformSpecific( - CallInterfaceDescriptorData* data) { - Register registers[] = {ebx}; - data->InitializePlatformSpecific(arraysize(registers), registers, NULL); -} - - -void FastCloneRegExpDescriptor::InitializePlatformSpecific( - CallInterfaceDescriptorData* data) { - Register registers[] = {edi, eax, ecx, edx}; - data->InitializePlatformSpecific(arraysize(registers), registers); -} - - -void FastCloneShallowArrayDescriptor::InitializePlatformSpecific( - CallInterfaceDescriptorData* data) { - Register registers[] = {eax, ebx, ecx}; - data->InitializePlatformSpecific(arraysize(registers), registers); -} - - -void FastCloneShallowObjectDescriptor::InitializePlatformSpecific( - CallInterfaceDescriptorData* data) { - Register registers[] = {eax, ebx, ecx, edx}; - data->InitializePlatformSpecific(arraysize(registers), registers, NULL); -} - - -void CreateAllocationSiteDescriptor::InitializePlatformSpecific( - CallInterfaceDescriptorData* data) { - Register registers[] = {ebx, edx}; - data->InitializePlatformSpecific(arraysize(registers), registers); -} - - -void CreateWeakCellDescriptor::InitializePlatformSpecific( - CallInterfaceDescriptorData* data) { - Register registers[] = {ebx, edx, edi}; - data->InitializePlatformSpecific(arraysize(registers), registers); -} - - -void CallFunctionDescriptor::InitializePlatformSpecific( - CallInterfaceDescriptorData* data) { - Register registers[] = {edi}; - data->InitializePlatformSpecific(arraysize(registers), registers, NULL); -} - -void CallICTrampolineDescriptor::InitializePlatformSpecific( - CallInterfaceDescriptorData* data) { - Register registers[] = {edi, eax, edx}; - data->InitializePlatformSpecific(arraysize(registers), registers); -} - -void CallICDescriptor::InitializePlatformSpecific( - CallInterfaceDescriptorData* data) { - Register registers[] = {edi, eax, edx, ebx}; - data->InitializePlatformSpecific(arraysize(registers), registers); -} - - -void CallConstructDescriptor::InitializePlatformSpecific( - CallInterfaceDescriptorData* data) { - // eax : number of arguments - // ebx : feedback vector - // ecx : new target (for IsSuperConstructorCall) - // edx : slot in feedback vector (Smi, for RecordCallTarget) - // edi : constructor function - // TODO(turbofan): So far we don't gather type feedback and hence skip the - // slot parameter, but ArrayConstructStub needs the vector to be undefined. - Register registers[] = {eax, edi, ecx, ebx}; - data->InitializePlatformSpecific(arraysize(registers), registers, NULL); -} - - -void CallTrampolineDescriptor::InitializePlatformSpecific( - CallInterfaceDescriptorData* data) { - // eax : number of arguments - // edi : the target to call - Register registers[] = {edi, eax}; - data->InitializePlatformSpecific(arraysize(registers), registers); -} - -void CallForwardVarargsDescriptor::InitializePlatformSpecific( - CallInterfaceDescriptorData* data) { - // ecx : start index (to support rest parameters) - // edi : the target to call - Register registers[] = {edi, ecx}; - data->InitializePlatformSpecific(arraysize(registers), registers); -} - -void ConstructStubDescriptor::InitializePlatformSpecific( - CallInterfaceDescriptorData* data) { - // eax : number of arguments - // edx : the new target - // edi : the target to call - // ebx : allocation site or undefined - Register registers[] = {edi, edx, eax, ebx}; - data->InitializePlatformSpecific(arraysize(registers), registers); -} - - -void ConstructTrampolineDescriptor::InitializePlatformSpecific( - CallInterfaceDescriptorData* data) { - // eax : number of arguments - // edx : the new target - // edi : the target to call - Register registers[] = {edi, edx, eax}; - data->InitializePlatformSpecific(arraysize(registers), registers); -} - - -void TransitionElementsKindDescriptor::InitializePlatformSpecific( - CallInterfaceDescriptorData* data) { - Register registers[] = {eax, ebx}; - data->InitializePlatformSpecific(arraysize(registers), registers, NULL); -} - - -void AllocateHeapNumberDescriptor::InitializePlatformSpecific( - CallInterfaceDescriptorData* data) { - // register state - data->InitializePlatformSpecific(0, nullptr, nullptr); -} - -void ArrayNoArgumentConstructorDescriptor::InitializePlatformSpecific( - CallInterfaceDescriptorData* data) { - // register state - // eax -- number of arguments - // edi -- function - // ebx -- allocation site with elements kind - Register registers[] = {edi, ebx, eax}; - data->InitializePlatformSpecific(arraysize(registers), registers, NULL); -} - -void ArraySingleArgumentConstructorDescriptor::InitializePlatformSpecific( - CallInterfaceDescriptorData* data) { - // register state - // eax -- number of arguments - // edi -- function - // ebx -- allocation site with elements kind - Register registers[] = {edi, ebx, eax}; - data->InitializePlatformSpecific(arraysize(registers), registers, NULL); -} - -void ArrayNArgumentsConstructorDescriptor::InitializePlatformSpecific( - CallInterfaceDescriptorData* data) { - // register state - // eax -- number of arguments - // edi -- function - // ebx -- allocation site with elements kind - Register registers[] = {edi, ebx, eax}; - data->InitializePlatformSpecific(arraysize(registers), registers, NULL); -} - -void VarArgFunctionDescriptor::InitializePlatformSpecific( - CallInterfaceDescriptorData* data) { - // stack param count needs (arg count) - Register registers[] = {eax}; - data->InitializePlatformSpecific(arraysize(registers), registers); -} - -void CompareDescriptor::InitializePlatformSpecific( - CallInterfaceDescriptorData* data) { - Register registers[] = {edx, eax}; - data->InitializePlatformSpecific(arraysize(registers), registers, NULL); -} - - -void BinaryOpDescriptor::InitializePlatformSpecific( - CallInterfaceDescriptorData* data) { - Register registers[] = {edx, eax}; - data->InitializePlatformSpecific(arraysize(registers), registers, NULL); -} - - -void BinaryOpWithAllocationSiteDescriptor::InitializePlatformSpecific( - CallInterfaceDescriptorData* data) { - Register registers[] = {ecx, edx, eax}; - data->InitializePlatformSpecific(arraysize(registers), registers, NULL); -} - -void BinaryOpWithVectorDescriptor::InitializePlatformSpecific( - CallInterfaceDescriptorData* data) { - // register state - // edx -- lhs - // eax -- rhs - // edi -- slot id - // ebx -- vector - Register registers[] = {edx, eax, edi, ebx}; - data->InitializePlatformSpecific(arraysize(registers), registers); -} - -void CountOpDescriptor::InitializePlatformSpecific( - CallInterfaceDescriptorData* data) { - Register registers[] = {eax}; - data->InitializePlatformSpecific(arraysize(registers), registers); -} - -void StringAddDescriptor::InitializePlatformSpecific( - CallInterfaceDescriptorData* data) { - Register registers[] = {edx, eax}; - data->InitializePlatformSpecific(arraysize(registers), registers, NULL); -} - -void ArgumentAdaptorDescriptor::InitializePlatformSpecific( - CallInterfaceDescriptorData* data) { - Register registers[] = { - edi, // JSFunction - edx, // the new target - eax, // actual number of arguments - ebx, // expected number of arguments - }; - data->InitializePlatformSpecific(arraysize(registers), registers); -} - -void ApiCallbackDescriptor::InitializePlatformSpecific( - CallInterfaceDescriptorData* data) { - Register registers[] = { - edi, // callee - ebx, // call_data - ecx, // holder - edx, // api_function_address - }; - data->InitializePlatformSpecific(arraysize(registers), registers); -} - -void InterpreterDispatchDescriptor::InitializePlatformSpecific( - CallInterfaceDescriptorData* data) { - Register registers[] = { - kInterpreterAccumulatorRegister, kInterpreterBytecodeOffsetRegister, - kInterpreterBytecodeArrayRegister, kInterpreterDispatchTableRegister}; - data->InitializePlatformSpecific(arraysize(registers), registers); -} - -void InterpreterPushArgsThenCallDescriptor::InitializePlatformSpecific( - CallInterfaceDescriptorData* data) { - Register registers[] = { - eax, // argument count (not including receiver) - ebx, // address of first argument - edi // the target callable to be call - }; - data->InitializePlatformSpecific(arraysize(registers), registers); -} - -void InterpreterPushArgsThenConstructDescriptor::InitializePlatformSpecific( - CallInterfaceDescriptorData* data) { - Register registers[] = { - eax, // argument count (not including receiver) - edx, // new target - edi, // constructor - ebx, // allocation site feedback - ecx, // address of first argument - }; - data->InitializePlatformSpecific(arraysize(registers), registers); -} - -void InterpreterPushArgsThenConstructArrayDescriptor:: - InitializePlatformSpecific(CallInterfaceDescriptorData* data) { - Register registers[] = { - eax, // argument count (not including receiver) - edx, // target to the call. It is checked to be Array function. - ebx, // allocation site feedback - ecx, // address of first argument - }; - data->InitializePlatformSpecific(arraysize(registers), registers); -} - -void InterpreterCEntryDescriptor::InitializePlatformSpecific( - CallInterfaceDescriptorData* data) { - Register registers[] = { - eax, // argument count (argc) - ecx, // address of first argument (argv) - ebx // the runtime function to call - }; - data->InitializePlatformSpecific(arraysize(registers), registers); -} - -void ResumeGeneratorDescriptor::InitializePlatformSpecific( - CallInterfaceDescriptorData* data) { - Register registers[] = { - eax, // the value to pass to the generator - ebx, // the JSGeneratorObject to resume - edx // the resume mode (tagged) - }; - data->InitializePlatformSpecific(arraysize(registers), registers); -} - -} // namespace internal -} // namespace v8 - -#endif // V8_TARGET_ARCH_X87 diff --git a/src/x87/macro-assembler-x87.cc b/src/x87/macro-assembler-x87.cc deleted file mode 100644 index 3b3ae05158..0000000000 --- a/src/x87/macro-assembler-x87.cc +++ /dev/null @@ -1,2546 +0,0 @@ -// 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_X87 - -#include "src/base/bits.h" -#include "src/base/division-by-constant.h" -#include "src/bootstrapper.h" -#include "src/codegen.h" -#include "src/debug/debug.h" -#include "src/runtime/runtime.h" -#include "src/x87/frames-x87.h" -#include "src/x87/macro-assembler-x87.h" - -namespace v8 { -namespace internal { - -// ------------------------------------------------------------------------- -// MacroAssembler implementation. - -MacroAssembler::MacroAssembler(Isolate* isolate, void* buffer, int size, - CodeObjectRequired create_code_object) - : Assembler(arg_isolate, buffer, size), - generating_stub_(false), - has_frame_(false), - isolate_(isolate) { - if (create_code_object == CodeObjectRequired::kYes) { - code_object_ = - Handle::New(isolate_->heap()->undefined_value(), isolate_); - } -} - - -void MacroAssembler::Load(Register dst, const Operand& src, Representation r) { - DCHECK(!r.IsDouble()); - if (r.IsInteger8()) { - movsx_b(dst, src); - } else if (r.IsUInteger8()) { - movzx_b(dst, src); - } else if (r.IsInteger16()) { - movsx_w(dst, src); - } else if (r.IsUInteger16()) { - movzx_w(dst, src); - } else { - mov(dst, src); - } -} - - -void MacroAssembler::Store(Register src, const Operand& dst, Representation r) { - DCHECK(!r.IsDouble()); - if (r.IsInteger8() || r.IsUInteger8()) { - mov_b(dst, src); - } else if (r.IsInteger16() || r.IsUInteger16()) { - mov_w(dst, src); - } else { - if (r.IsHeapObject()) { - AssertNotSmi(src); - } else if (r.IsSmi()) { - AssertSmi(src); - } - mov(dst, src); - } -} - - -void MacroAssembler::LoadRoot(Register destination, Heap::RootListIndex index) { - if (isolate()->heap()->RootCanBeTreatedAsConstant(index)) { - mov(destination, isolate()->heap()->root_handle(index)); - return; - } - ExternalReference roots_array_start = - ExternalReference::roots_array_start(isolate()); - mov(destination, Immediate(index)); - mov(destination, Operand::StaticArray(destination, - times_pointer_size, - roots_array_start)); -} - - -void MacroAssembler::StoreRoot(Register source, - Register scratch, - Heap::RootListIndex index) { - DCHECK(Heap::RootCanBeWrittenAfterInitialization(index)); - ExternalReference roots_array_start = - ExternalReference::roots_array_start(isolate()); - mov(scratch, Immediate(index)); - mov(Operand::StaticArray(scratch, times_pointer_size, roots_array_start), - source); -} - - -void MacroAssembler::CompareRoot(Register with, - Register scratch, - Heap::RootListIndex index) { - ExternalReference roots_array_start = - ExternalReference::roots_array_start(isolate()); - mov(scratch, Immediate(index)); - cmp(with, Operand::StaticArray(scratch, - times_pointer_size, - roots_array_start)); -} - - -void MacroAssembler::CompareRoot(Register with, Heap::RootListIndex index) { - DCHECK(isolate()->heap()->RootCanBeTreatedAsConstant(index)); - cmp(with, isolate()->heap()->root_handle(index)); -} - - -void MacroAssembler::CompareRoot(const Operand& with, - Heap::RootListIndex index) { - DCHECK(isolate()->heap()->RootCanBeTreatedAsConstant(index)); - cmp(with, isolate()->heap()->root_handle(index)); -} - - -void MacroAssembler::PushRoot(Heap::RootListIndex index) { - DCHECK(isolate()->heap()->RootCanBeTreatedAsConstant(index)); - Push(isolate()->heap()->root_handle(index)); -} - -#define REG(Name) \ - { Register::kCode_##Name } - -static const Register saved_regs[] = {REG(eax), REG(ecx), REG(edx)}; - -#undef REG - -static const int kNumberOfSavedRegs = sizeof(saved_regs) / sizeof(Register); - -void MacroAssembler::PushCallerSaved(SaveFPRegsMode fp_mode, - Register exclusion1, Register exclusion2, - Register exclusion3) { - // We don't allow a GC during a store buffer overflow so there is no need to - // store the registers in any particular way, but we do have to store and - // restore them. - for (int i = 0; i < kNumberOfSavedRegs; i++) { - Register reg = saved_regs[i]; - if (!reg.is(exclusion1) && !reg.is(exclusion2) && !reg.is(exclusion3)) { - push(reg); - } - } - if (fp_mode == kSaveFPRegs) { - // Save FPU state in m108byte. - sub(esp, Immediate(108)); - fnsave(Operand(esp, 0)); - } -} - -void MacroAssembler::PopCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1, - Register exclusion2, Register exclusion3) { - if (fp_mode == kSaveFPRegs) { - // Restore FPU state in m108byte. - frstor(Operand(esp, 0)); - add(esp, Immediate(108)); - } - - for (int i = kNumberOfSavedRegs - 1; i >= 0; i--) { - Register reg = saved_regs[i]; - if (!reg.is(exclusion1) && !reg.is(exclusion2) && !reg.is(exclusion3)) { - pop(reg); - } - } -} - -void MacroAssembler::InNewSpace(Register object, Register scratch, Condition cc, - Label* condition_met, - Label::Distance distance) { - CheckPageFlag(object, scratch, MemoryChunk::kIsInNewSpaceMask, cc, - condition_met, distance); -} - - -void MacroAssembler::RememberedSetHelper( - Register object, // Only used for debug checks. - Register addr, Register scratch, SaveFPRegsMode save_fp, - MacroAssembler::RememberedSetFinalAction and_then) { - Label done; - if (emit_debug_code()) { - Label ok; - JumpIfNotInNewSpace(object, scratch, &ok, Label::kNear); - int3(); - bind(&ok); - } - // Load store buffer top. - ExternalReference store_buffer = - ExternalReference::store_buffer_top(isolate()); - mov(scratch, Operand::StaticVariable(store_buffer)); - // Store pointer to buffer. - mov(Operand(scratch, 0), addr); - // Increment buffer top. - add(scratch, Immediate(kPointerSize)); - // Write back new top of buffer. - mov(Operand::StaticVariable(store_buffer), scratch); - // Call stub on end of buffer. - // Check for end of buffer. - test(scratch, Immediate(StoreBuffer::kStoreBufferMask)); - if (and_then == kReturnAtEnd) { - Label buffer_overflowed; - j(equal, &buffer_overflowed, Label::kNear); - ret(0); - bind(&buffer_overflowed); - } else { - DCHECK(and_then == kFallThroughAtEnd); - j(not_equal, &done, Label::kNear); - } - StoreBufferOverflowStub store_buffer_overflow(isolate(), save_fp); - CallStub(&store_buffer_overflow); - if (and_then == kReturnAtEnd) { - ret(0); - } else { - DCHECK(and_then == kFallThroughAtEnd); - bind(&done); - } -} - - -void MacroAssembler::ClampTOSToUint8(Register result_reg) { - Label done, conv_failure; - sub(esp, Immediate(kPointerSize)); - fnclex(); - fist_s(Operand(esp, 0)); - pop(result_reg); - X87CheckIA(); - j(equal, &conv_failure, Label::kNear); - test(result_reg, Immediate(0xFFFFFF00)); - j(zero, &done, Label::kNear); - setcc(sign, result_reg); - sub(result_reg, Immediate(1)); - and_(result_reg, Immediate(255)); - jmp(&done, Label::kNear); - bind(&conv_failure); - fnclex(); - fldz(); - fld(1); - FCmp(); - setcc(below, result_reg); // 1 if negative, 0 if positive. - dec_b(result_reg); // 0 if negative, 255 if positive. - bind(&done); -} - - -void MacroAssembler::ClampUint8(Register reg) { - Label done; - test(reg, Immediate(0xFFFFFF00)); - j(zero, &done, Label::kNear); - setcc(negative, reg); // 1 if negative, 0 if positive. - dec_b(reg); // 0 if negative, 255 if positive. - bind(&done); -} - - -void MacroAssembler::SlowTruncateToI(Register result_reg, - Register input_reg, - int offset) { - DoubleToIStub stub(isolate(), input_reg, result_reg, offset, true); - call(stub.GetCode(), RelocInfo::CODE_TARGET); -} - - -void MacroAssembler::TruncateX87TOSToI(Register result_reg) { - sub(esp, Immediate(kDoubleSize)); - fst_d(MemOperand(esp, 0)); - SlowTruncateToI(result_reg, esp, 0); - add(esp, Immediate(kDoubleSize)); -} - - -void MacroAssembler::X87TOSToI(Register result_reg, - MinusZeroMode minus_zero_mode, - Label* lost_precision, Label* is_nan, - Label* minus_zero, Label::Distance dst) { - Label done; - sub(esp, Immediate(kPointerSize)); - fld(0); - fist_s(MemOperand(esp, 0)); - fild_s(MemOperand(esp, 0)); - pop(result_reg); - FCmp(); - j(not_equal, lost_precision, dst); - j(parity_even, is_nan, dst); - if (minus_zero_mode == FAIL_ON_MINUS_ZERO) { - test(result_reg, Operand(result_reg)); - j(not_zero, &done, Label::kNear); - // To check for minus zero, we load the value again as float, and check - // if that is still 0. - sub(esp, Immediate(kPointerSize)); - fst_s(MemOperand(esp, 0)); - pop(result_reg); - test(result_reg, Operand(result_reg)); - j(not_zero, minus_zero, dst); - } - bind(&done); -} - - -void MacroAssembler::TruncateHeapNumberToI(Register result_reg, - Register input_reg) { - Label done, slow_case; - - SlowTruncateToI(result_reg, input_reg); - bind(&done); -} - - -void MacroAssembler::LoadUint32NoSSE2(const Operand& src) { - Label done; - push(src); - fild_s(Operand(esp, 0)); - cmp(src, Immediate(0)); - j(not_sign, &done, Label::kNear); - ExternalReference uint32_bias = - ExternalReference::address_of_uint32_bias(); - fld_d(Operand::StaticVariable(uint32_bias)); - faddp(1); - bind(&done); - add(esp, Immediate(kPointerSize)); -} - - -void MacroAssembler::RecordWriteField( - Register object, int offset, Register value, Register dst, - SaveFPRegsMode save_fp, RememberedSetAction remembered_set_action, - SmiCheck smi_check, PointersToHereCheck pointers_to_here_check_for_value) { - // First, check if a write barrier is even needed. The tests below - // catch stores of Smis. - Label done; - - // Skip barrier if writing a smi. - if (smi_check == INLINE_SMI_CHECK) { - JumpIfSmi(value, &done, Label::kNear); - } - - // Although the object register is tagged, the offset is relative to the start - // of the object, so so offset must be a multiple of kPointerSize. - DCHECK(IsAligned(offset, kPointerSize)); - - lea(dst, FieldOperand(object, offset)); - if (emit_debug_code()) { - Label ok; - test_b(dst, Immediate(kPointerSize - 1)); - j(zero, &ok, Label::kNear); - int3(); - bind(&ok); - } - - RecordWrite(object, dst, value, save_fp, remembered_set_action, - OMIT_SMI_CHECK, pointers_to_here_check_for_value); - - bind(&done); - - // Clobber clobbered input registers when running with the debug-code flag - // turned on to provoke errors. - if (emit_debug_code()) { - mov(value, Immediate(bit_cast(kZapValue))); - mov(dst, Immediate(bit_cast(kZapValue))); - } -} - - -void MacroAssembler::RecordWriteForMap(Register object, Handle map, - Register scratch1, Register scratch2, - SaveFPRegsMode save_fp) { - Label done; - - Register address = scratch1; - Register value = scratch2; - if (emit_debug_code()) { - Label ok; - lea(address, FieldOperand(object, HeapObject::kMapOffset)); - test_b(address, Immediate(kPointerSize - 1)); - j(zero, &ok, Label::kNear); - int3(); - bind(&ok); - } - - DCHECK(!object.is(value)); - DCHECK(!object.is(address)); - DCHECK(!value.is(address)); - AssertNotSmi(object); - - if (!FLAG_incremental_marking) { - return; - } - - // Compute the address. - lea(address, FieldOperand(object, HeapObject::kMapOffset)); - - // A single check of the map's pages interesting flag suffices, since it is - // only set during incremental collection, and then it's also guaranteed that - // the from object's page's interesting flag is also set. This optimization - // relies on the fact that maps can never be in new space. - DCHECK(!isolate()->heap()->InNewSpace(*map)); - CheckPageFlagForMap(map, - MemoryChunk::kPointersToHereAreInterestingMask, - zero, - &done, - Label::kNear); - - RecordWriteStub stub(isolate(), object, value, address, OMIT_REMEMBERED_SET, - save_fp); - CallStub(&stub); - - bind(&done); - - // Count number of write barriers in generated code. - isolate()->counters()->write_barriers_static()->Increment(); - IncrementCounter(isolate()->counters()->write_barriers_dynamic(), 1); - - // Clobber clobbered input registers when running with the debug-code flag - // turned on to provoke errors. - if (emit_debug_code()) { - mov(value, Immediate(bit_cast(kZapValue))); - mov(scratch1, Immediate(bit_cast(kZapValue))); - mov(scratch2, Immediate(bit_cast(kZapValue))); - } -} - - -void MacroAssembler::RecordWrite( - Register object, Register address, Register value, SaveFPRegsMode fp_mode, - RememberedSetAction remembered_set_action, SmiCheck smi_check, - PointersToHereCheck pointers_to_here_check_for_value) { - DCHECK(!object.is(value)); - DCHECK(!object.is(address)); - DCHECK(!value.is(address)); - AssertNotSmi(object); - - if (remembered_set_action == OMIT_REMEMBERED_SET && - !FLAG_incremental_marking) { - return; - } - - if (emit_debug_code()) { - Label ok; - cmp(value, Operand(address, 0)); - j(equal, &ok, Label::kNear); - int3(); - bind(&ok); - } - - // First, check if a write barrier is even needed. The tests below - // catch stores of Smis and stores into young gen. - Label done; - - if (smi_check == INLINE_SMI_CHECK) { - // Skip barrier if writing a smi. - JumpIfSmi(value, &done, Label::kNear); - } - - if (pointers_to_here_check_for_value != kPointersToHereAreAlwaysInteresting) { - CheckPageFlag(value, - value, // Used as scratch. - MemoryChunk::kPointersToHereAreInterestingMask, - zero, - &done, - Label::kNear); - } - CheckPageFlag(object, - value, // Used as scratch. - MemoryChunk::kPointersFromHereAreInterestingMask, - zero, - &done, - Label::kNear); - - RecordWriteStub stub(isolate(), object, value, address, remembered_set_action, - fp_mode); - CallStub(&stub); - - bind(&done); - - // Count number of write barriers in generated code. - isolate()->counters()->write_barriers_static()->Increment(); - IncrementCounter(isolate()->counters()->write_barriers_dynamic(), 1); - - // Clobber clobbered registers when running with the debug-code flag - // turned on to provoke errors. - if (emit_debug_code()) { - mov(address, Immediate(bit_cast(kZapValue))); - mov(value, Immediate(bit_cast(kZapValue))); - } -} - -void MacroAssembler::RecordWriteCodeEntryField(Register js_function, - Register code_entry, - Register scratch) { - const int offset = JSFunction::kCodeEntryOffset; - - // Since a code entry (value) is always in old space, we don't need to update - // remembered set. If incremental marking is off, there is nothing for us to - // do. - if (!FLAG_incremental_marking) return; - - DCHECK(!js_function.is(code_entry)); - DCHECK(!js_function.is(scratch)); - DCHECK(!code_entry.is(scratch)); - AssertNotSmi(js_function); - - if (emit_debug_code()) { - Label ok; - lea(scratch, FieldOperand(js_function, offset)); - cmp(code_entry, Operand(scratch, 0)); - j(equal, &ok, Label::kNear); - int3(); - bind(&ok); - } - - // First, check if a write barrier is even needed. The tests below - // catch stores of Smis and stores into young gen. - Label done; - - CheckPageFlag(code_entry, scratch, - MemoryChunk::kPointersToHereAreInterestingMask, zero, &done, - Label::kNear); - CheckPageFlag(js_function, scratch, - MemoryChunk::kPointersFromHereAreInterestingMask, zero, &done, - Label::kNear); - - // Save input registers. - push(js_function); - push(code_entry); - - const Register dst = scratch; - lea(dst, FieldOperand(js_function, offset)); - - // Save caller-saved registers. - PushCallerSaved(kDontSaveFPRegs, js_function, code_entry); - - int argument_count = 3; - PrepareCallCFunction(argument_count, code_entry); - mov(Operand(esp, 0 * kPointerSize), js_function); - mov(Operand(esp, 1 * kPointerSize), dst); // Slot. - mov(Operand(esp, 2 * kPointerSize), - Immediate(ExternalReference::isolate_address(isolate()))); - - { - AllowExternalCallThatCantCauseGC scope(this); - CallCFunction( - ExternalReference::incremental_marking_record_write_code_entry_function( - isolate()), - argument_count); - } - - // Restore caller-saved registers. - PopCallerSaved(kDontSaveFPRegs, js_function, code_entry); - - // Restore input registers. - pop(code_entry); - pop(js_function); - - bind(&done); -} - -void MacroAssembler::DebugBreak() { - Move(eax, Immediate(0)); - mov(ebx, Immediate(ExternalReference(Runtime::kHandleDebuggerStatement, - isolate()))); - CEntryStub ces(isolate(), 1); - call(ces.GetCode(), RelocInfo::DEBUGGER_STATEMENT); -} - -void MacroAssembler::ShlPair(Register high, Register low, uint8_t shift) { - if (shift >= 32) { - mov(high, low); - shl(high, shift - 32); - xor_(low, low); - } else { - shld(high, low, shift); - shl(low, shift); - } -} - -void MacroAssembler::ShlPair_cl(Register high, Register low) { - shld_cl(high, low); - shl_cl(low); - Label done; - test(ecx, Immediate(0x20)); - j(equal, &done, Label::kNear); - mov(high, low); - xor_(low, low); - bind(&done); -} - -void MacroAssembler::ShrPair(Register high, Register low, uint8_t shift) { - if (shift >= 32) { - mov(low, high); - shr(low, shift - 32); - xor_(high, high); - } else { - shrd(high, low, shift); - shr(high, shift); - } -} - -void MacroAssembler::ShrPair_cl(Register high, Register low) { - shrd_cl(low, high); - shr_cl(high); - Label done; - test(ecx, Immediate(0x20)); - j(equal, &done, Label::kNear); - mov(low, high); - xor_(high, high); - bind(&done); -} - -void MacroAssembler::SarPair(Register high, Register low, uint8_t shift) { - if (shift >= 32) { - mov(low, high); - sar(low, shift - 32); - sar(high, 31); - } else { - shrd(high, low, shift); - sar(high, shift); - } -} - -void MacroAssembler::SarPair_cl(Register high, Register low) { - shrd_cl(low, high); - sar_cl(high); - Label done; - test(ecx, Immediate(0x20)); - j(equal, &done, Label::kNear); - mov(low, high); - sar(high, 31); - bind(&done); -} - -bool MacroAssembler::IsUnsafeImmediate(const Immediate& x) { - static const int kMaxImmediateBits = 17; - if (!RelocInfo::IsNone(x.rmode_)) return false; - return !is_intn(x.x_, kMaxImmediateBits); -} - - -void MacroAssembler::SafeMove(Register dst, const Immediate& x) { - if (IsUnsafeImmediate(x) && jit_cookie() != 0) { - Move(dst, Immediate(x.x_ ^ jit_cookie())); - xor_(dst, jit_cookie()); - } else { - Move(dst, x); - } -} - - -void MacroAssembler::SafePush(const Immediate& x) { - if (IsUnsafeImmediate(x) && jit_cookie() != 0) { - push(Immediate(x.x_ ^ jit_cookie())); - xor_(Operand(esp, 0), Immediate(jit_cookie())); - } else { - push(x); - } -} - - -void MacroAssembler::CmpObjectType(Register heap_object, - InstanceType type, - Register map) { - mov(map, FieldOperand(heap_object, HeapObject::kMapOffset)); - CmpInstanceType(map, type); -} - - -void MacroAssembler::CmpInstanceType(Register map, InstanceType type) { - cmpb(FieldOperand(map, Map::kInstanceTypeOffset), Immediate(type)); -} - -void MacroAssembler::CompareMap(Register obj, Handle map) { - cmp(FieldOperand(obj, HeapObject::kMapOffset), map); -} - - -void MacroAssembler::CheckMap(Register obj, - Handle map, - Label* fail, - SmiCheckType smi_check_type) { - if (smi_check_type == DO_SMI_CHECK) { - JumpIfSmi(obj, fail); - } - - CompareMap(obj, map); - j(not_equal, fail); -} - - -Condition MacroAssembler::IsObjectStringType(Register heap_object, - Register map, - Register instance_type) { - mov(map, FieldOperand(heap_object, HeapObject::kMapOffset)); - movzx_b(instance_type, FieldOperand(map, Map::kInstanceTypeOffset)); - STATIC_ASSERT(kNotStringTag != 0); - test(instance_type, Immediate(kIsNotStringMask)); - return zero; -} - - -void MacroAssembler::FCmp() { - fucompp(); - push(eax); - fnstsw_ax(); - sahf(); - pop(eax); -} - - -void MacroAssembler::FXamMinusZero() { - fxam(); - push(eax); - fnstsw_ax(); - and_(eax, Immediate(0x4700)); - // For minus zero, C3 == 1 && C1 == 1. - cmp(eax, Immediate(0x4200)); - pop(eax); - fstp(0); -} - - -void MacroAssembler::FXamSign() { - fxam(); - push(eax); - fnstsw_ax(); - // For negative value (including -0.0), C1 == 1. - and_(eax, Immediate(0x0200)); - pop(eax); - fstp(0); -} - - -void MacroAssembler::X87CheckIA() { - push(eax); - fnstsw_ax(); - // For #IA, IE == 1 && SF == 0. - and_(eax, Immediate(0x0041)); - cmp(eax, Immediate(0x0001)); - pop(eax); -} - - -// rc=00B, round to nearest. -// rc=01B, round down. -// rc=10B, round up. -// rc=11B, round toward zero. -void MacroAssembler::X87SetRC(int rc) { - sub(esp, Immediate(kPointerSize)); - fnstcw(MemOperand(esp, 0)); - and_(MemOperand(esp, 0), Immediate(0xF3FF)); - or_(MemOperand(esp, 0), Immediate(rc)); - fldcw(MemOperand(esp, 0)); - add(esp, Immediate(kPointerSize)); -} - - -void MacroAssembler::X87SetFPUCW(int cw) { - RecordComment("-- X87SetFPUCW start --"); - push(Immediate(cw)); - fldcw(MemOperand(esp, 0)); - add(esp, Immediate(kPointerSize)); - RecordComment("-- X87SetFPUCW end--"); -} - - -void MacroAssembler::AssertSmi(Register object) { - if (emit_debug_code()) { - test(object, Immediate(kSmiTagMask)); - Check(equal, kOperandIsNotASmi); - } -} - - -void MacroAssembler::AssertFunction(Register object) { - if (emit_debug_code()) { - test(object, Immediate(kSmiTagMask)); - Check(not_equal, kOperandIsASmiAndNotAFunction); - Push(object); - CmpObjectType(object, JS_FUNCTION_TYPE, object); - Pop(object); - Check(equal, kOperandIsNotAFunction); - } -} - - -void MacroAssembler::AssertBoundFunction(Register object) { - if (emit_debug_code()) { - test(object, Immediate(kSmiTagMask)); - Check(not_equal, kOperandIsASmiAndNotABoundFunction); - Push(object); - CmpObjectType(object, JS_BOUND_FUNCTION_TYPE, object); - Pop(object); - Check(equal, kOperandIsNotABoundFunction); - } -} - -void MacroAssembler::AssertGeneratorObject(Register object) { - if (emit_debug_code()) { - test(object, Immediate(kSmiTagMask)); - Check(not_equal, kOperandIsASmiAndNotAGeneratorObject); - Push(object); - CmpObjectType(object, JS_GENERATOR_OBJECT_TYPE, object); - Pop(object); - Check(equal, kOperandIsNotAGeneratorObject); - } -} - -void MacroAssembler::AssertUndefinedOrAllocationSite(Register object) { - if (emit_debug_code()) { - Label done_checking; - AssertNotSmi(object); - cmp(object, isolate()->factory()->undefined_value()); - j(equal, &done_checking); - cmp(FieldOperand(object, 0), - Immediate(isolate()->factory()->allocation_site_map())); - Assert(equal, kExpectedUndefinedOrCell); - bind(&done_checking); - } -} - - -void MacroAssembler::AssertNotSmi(Register object) { - if (emit_debug_code()) { - test(object, Immediate(kSmiTagMask)); - Check(not_equal, kOperandIsASmi); - } -} - -void MacroAssembler::StubPrologue(StackFrame::Type type) { - push(ebp); // Caller's frame pointer. - mov(ebp, esp); - push(Immediate(Smi::FromInt(type))); -} - - -void MacroAssembler::Prologue(bool code_pre_aging) { - PredictableCodeSizeScope predictible_code_size_scope(this, - kNoCodeAgeSequenceLength); - if (code_pre_aging) { - // Pre-age the code. - call(isolate()->builtins()->MarkCodeAsExecutedOnce(), - RelocInfo::CODE_AGE_SEQUENCE); - Nop(kNoCodeAgeSequenceLength - Assembler::kCallInstructionLength); - } else { - push(ebp); // Caller's frame pointer. - mov(ebp, esp); - push(esi); // Callee's context. - push(edi); // Callee's JS function. - } -} - -void MacroAssembler::EmitLoadFeedbackVector(Register vector) { - mov(vector, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); - mov(vector, FieldOperand(vector, JSFunction::kFeedbackVectorOffset)); - mov(vector, FieldOperand(vector, Cell::kValueOffset)); -} - - -void MacroAssembler::EnterFrame(StackFrame::Type type, - bool load_constant_pool_pointer_reg) { - // Out-of-line constant pool not implemented on x87. - UNREACHABLE(); -} - - -void MacroAssembler::EnterFrame(StackFrame::Type type) { - push(ebp); - mov(ebp, esp); - push(Immediate(Smi::FromInt(type))); - if (type == StackFrame::INTERNAL) { - push(Immediate(CodeObject())); - } - if (emit_debug_code()) { - cmp(Operand(esp, 0), Immediate(isolate()->factory()->undefined_value())); - Check(not_equal, kCodeObjectNotProperlyPatched); - } -} - - -void MacroAssembler::LeaveFrame(StackFrame::Type type) { - if (emit_debug_code()) { - cmp(Operand(ebp, CommonFrameConstants::kContextOrFrameTypeOffset), - Immediate(Smi::FromInt(type))); - Check(equal, kStackFrameTypesMustMatch); - } - leave(); -} - -void MacroAssembler::EnterBuiltinFrame(Register context, Register target, - Register argc) { - Push(ebp); - Move(ebp, esp); - Push(context); - Push(target); - Push(argc); -} - -void MacroAssembler::LeaveBuiltinFrame(Register context, Register target, - Register argc) { - Pop(argc); - Pop(target); - Pop(context); - leave(); -} - -void MacroAssembler::EnterExitFramePrologue(StackFrame::Type frame_type) { - DCHECK(frame_type == StackFrame::EXIT || - frame_type == StackFrame::BUILTIN_EXIT); - - // Set up the frame structure on the stack. - DCHECK_EQ(+2 * kPointerSize, ExitFrameConstants::kCallerSPDisplacement); - DCHECK_EQ(+1 * kPointerSize, ExitFrameConstants::kCallerPCOffset); - DCHECK_EQ(0 * kPointerSize, ExitFrameConstants::kCallerFPOffset); - push(ebp); - mov(ebp, esp); - - // Reserve room for entry stack pointer and push the code object. - push(Immediate(Smi::FromInt(frame_type))); - DCHECK_EQ(-2 * kPointerSize, ExitFrameConstants::kSPOffset); - push(Immediate(0)); // Saved entry sp, patched before call. - DCHECK_EQ(-3 * kPointerSize, ExitFrameConstants::kCodeOffset); - push(Immediate(CodeObject())); // Accessed from ExitFrame::code_slot. - - // Save the frame pointer and the context in top. - ExternalReference c_entry_fp_address(IsolateAddressId::kCEntryFPAddress, - isolate()); - ExternalReference context_address(IsolateAddressId::kContextAddress, - isolate()); - ExternalReference c_function_address(IsolateAddressId::kCFunctionAddress, - isolate()); - mov(Operand::StaticVariable(c_entry_fp_address), ebp); - mov(Operand::StaticVariable(context_address), esi); - mov(Operand::StaticVariable(c_function_address), ebx); -} - - -void MacroAssembler::EnterExitFrameEpilogue(int argc, bool save_doubles) { - // Optionally save FPU state. - if (save_doubles) { - // Store FPU state to m108byte. - int space = 108 + argc * kPointerSize; - sub(esp, Immediate(space)); - const int offset = -ExitFrameConstants::kFixedFrameSizeFromFp; - fnsave(MemOperand(ebp, offset - 108)); - } else { - sub(esp, Immediate(argc * kPointerSize)); - } - - // Get the required frame alignment for the OS. - const int kFrameAlignment = base::OS::ActivationFrameAlignment(); - if (kFrameAlignment > 0) { - DCHECK(base::bits::IsPowerOfTwo(kFrameAlignment)); - and_(esp, -kFrameAlignment); - } - - // Patch the saved entry sp. - mov(Operand(ebp, ExitFrameConstants::kSPOffset), esp); -} - -void MacroAssembler::EnterExitFrame(int argc, bool save_doubles, - StackFrame::Type frame_type) { - EnterExitFramePrologue(frame_type); - - // Set up argc and argv in callee-saved registers. - int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize; - mov(edi, eax); - lea(esi, Operand(ebp, eax, times_4, offset)); - - // Reserve space for argc, argv and isolate. - EnterExitFrameEpilogue(argc, save_doubles); -} - - -void MacroAssembler::EnterApiExitFrame(int argc) { - EnterExitFramePrologue(StackFrame::EXIT); - EnterExitFrameEpilogue(argc, false); -} - - -void MacroAssembler::LeaveExitFrame(bool save_doubles, bool pop_arguments) { - // Optionally restore FPU state. - if (save_doubles) { - const int offset = -ExitFrameConstants::kFixedFrameSizeFromFp; - frstor(MemOperand(ebp, offset - 108)); - } - - if (pop_arguments) { - // Get the return address from the stack and restore the frame pointer. - mov(ecx, Operand(ebp, 1 * kPointerSize)); - mov(ebp, Operand(ebp, 0 * kPointerSize)); - - // Pop the arguments and the receiver from the caller stack. - lea(esp, Operand(esi, 1 * kPointerSize)); - - // Push the return address to get ready to return. - push(ecx); - } else { - // Otherwise just leave the exit frame. - leave(); - } - - LeaveExitFrameEpilogue(true); -} - - -void MacroAssembler::LeaveExitFrameEpilogue(bool restore_context) { - // Restore current context from top and clear it in debug mode. - ExternalReference context_address(IsolateAddressId::kContextAddress, - isolate()); - if (restore_context) { - mov(esi, Operand::StaticVariable(context_address)); - } -#ifdef DEBUG - mov(Operand::StaticVariable(context_address), Immediate(0)); -#endif - - // Clear the top frame. - ExternalReference c_entry_fp_address(IsolateAddressId::kCEntryFPAddress, - isolate()); - mov(Operand::StaticVariable(c_entry_fp_address), Immediate(0)); -} - - -void MacroAssembler::LeaveApiExitFrame(bool restore_context) { - mov(esp, ebp); - pop(ebp); - - LeaveExitFrameEpilogue(restore_context); -} - - -void MacroAssembler::PushStackHandler() { - // Adjust this code if not the case. - STATIC_ASSERT(StackHandlerConstants::kSize == 1 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); - - // Link the current handler as the next handler. - ExternalReference handler_address(IsolateAddressId::kHandlerAddress, - isolate()); - push(Operand::StaticVariable(handler_address)); - - // Set this new handler as the current one. - mov(Operand::StaticVariable(handler_address), esp); -} - - -void MacroAssembler::PopStackHandler() { - STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); - ExternalReference handler_address(IsolateAddressId::kHandlerAddress, - isolate()); - pop(Operand::StaticVariable(handler_address)); - add(esp, Immediate(StackHandlerConstants::kSize - kPointerSize)); -} - - -// Compute the hash code from the untagged key. This must be kept in sync with -// ComputeIntegerHash in utils.h and KeyedLoadGenericStub in -// code-stub-hydrogen.cc -// -// Note: r0 will contain hash code -void MacroAssembler::GetNumberHash(Register r0, Register scratch) { - // Xor original key with a seed. - if (serializer_enabled()) { - ExternalReference roots_array_start = - ExternalReference::roots_array_start(isolate()); - mov(scratch, Immediate(Heap::kHashSeedRootIndex)); - mov(scratch, - Operand::StaticArray(scratch, times_pointer_size, roots_array_start)); - SmiUntag(scratch); - xor_(r0, scratch); - } else { - int32_t seed = isolate()->heap()->HashSeed(); - xor_(r0, Immediate(seed)); - } - - // hash = ~hash + (hash << 15); - mov(scratch, r0); - not_(r0); - shl(scratch, 15); - add(r0, scratch); - // hash = hash ^ (hash >> 12); - mov(scratch, r0); - shr(scratch, 12); - xor_(r0, scratch); - // hash = hash + (hash << 2); - lea(r0, Operand(r0, r0, times_4, 0)); - // hash = hash ^ (hash >> 4); - mov(scratch, r0); - shr(scratch, 4); - xor_(r0, scratch); - // hash = hash * 2057; - imul(r0, r0, 2057); - // hash = hash ^ (hash >> 16); - mov(scratch, r0); - shr(scratch, 16); - xor_(r0, scratch); - and_(r0, 0x3fffffff); -} - -void MacroAssembler::LoadAllocationTopHelper(Register result, - Register scratch, - AllocationFlags flags) { - ExternalReference allocation_top = - AllocationUtils::GetAllocationTopReference(isolate(), flags); - - // Just return if allocation top is already known. - if ((flags & RESULT_CONTAINS_TOP) != 0) { - // No use of scratch if allocation top is provided. - DCHECK(scratch.is(no_reg)); -#ifdef DEBUG - // Assert that result actually contains top on entry. - cmp(result, Operand::StaticVariable(allocation_top)); - Check(equal, kUnexpectedAllocationTop); -#endif - return; - } - - // Move address of new object to result. Use scratch register if available. - if (scratch.is(no_reg)) { - mov(result, Operand::StaticVariable(allocation_top)); - } else { - mov(scratch, Immediate(allocation_top)); - mov(result, Operand(scratch, 0)); - } -} - - -void MacroAssembler::UpdateAllocationTopHelper(Register result_end, - Register scratch, - AllocationFlags flags) { - if (emit_debug_code()) { - test(result_end, Immediate(kObjectAlignmentMask)); - Check(zero, kUnalignedAllocationInNewSpace); - } - - ExternalReference allocation_top = - AllocationUtils::GetAllocationTopReference(isolate(), flags); - - // Update new top. Use scratch if available. - if (scratch.is(no_reg)) { - mov(Operand::StaticVariable(allocation_top), result_end); - } else { - mov(Operand(scratch, 0), result_end); - } -} - - -void MacroAssembler::Allocate(int object_size, - Register result, - Register result_end, - Register scratch, - Label* gc_required, - AllocationFlags flags) { - DCHECK((flags & (RESULT_CONTAINS_TOP | SIZE_IN_WORDS)) == 0); - DCHECK(object_size <= kMaxRegularHeapObjectSize); - if (!FLAG_inline_new) { - if (emit_debug_code()) { - // Trash the registers to simulate an allocation failure. - mov(result, Immediate(0x7091)); - if (result_end.is_valid()) { - mov(result_end, Immediate(0x7191)); - } - if (scratch.is_valid()) { - mov(scratch, Immediate(0x7291)); - } - } - jmp(gc_required); - return; - } - DCHECK(!result.is(result_end)); - - // Load address of new object into result. - LoadAllocationTopHelper(result, scratch, flags); - - ExternalReference allocation_limit = - AllocationUtils::GetAllocationLimitReference(isolate(), flags); - - // Align the next allocation. Storing the filler map without checking top is - // safe in new-space because the limit of the heap is aligned there. - if ((flags & DOUBLE_ALIGNMENT) != 0) { - DCHECK(kPointerAlignment * 2 == kDoubleAlignment); - Label aligned; - test(result, Immediate(kDoubleAlignmentMask)); - j(zero, &aligned, Label::kNear); - if ((flags & PRETENURE) != 0) { - cmp(result, Operand::StaticVariable(allocation_limit)); - j(above_equal, gc_required); - } - mov(Operand(result, 0), - Immediate(isolate()->factory()->one_pointer_filler_map())); - add(result, Immediate(kDoubleSize / 2)); - bind(&aligned); - } - - // Calculate new top and bail out if space is exhausted. - Register top_reg = result_end.is_valid() ? result_end : result; - - if (!top_reg.is(result)) { - mov(top_reg, result); - } - add(top_reg, Immediate(object_size)); - cmp(top_reg, Operand::StaticVariable(allocation_limit)); - j(above, gc_required); - - UpdateAllocationTopHelper(top_reg, scratch, flags); - - if (top_reg.is(result)) { - sub(result, Immediate(object_size - kHeapObjectTag)); - } else { - // Tag the result. - DCHECK(kHeapObjectTag == 1); - inc(result); - } -} - - -void MacroAssembler::Allocate(int header_size, - ScaleFactor element_size, - Register element_count, - RegisterValueType element_count_type, - Register result, - Register result_end, - Register scratch, - Label* gc_required, - AllocationFlags flags) { - DCHECK((flags & SIZE_IN_WORDS) == 0); - if (!FLAG_inline_new) { - if (emit_debug_code()) { - // Trash the registers to simulate an allocation failure. - mov(result, Immediate(0x7091)); - mov(result_end, Immediate(0x7191)); - if (scratch.is_valid()) { - mov(scratch, Immediate(0x7291)); - } - // Register element_count is not modified by the function. - } - jmp(gc_required); - return; - } - DCHECK(!result.is(result_end)); - - // Load address of new object into result. - LoadAllocationTopHelper(result, scratch, flags); - - ExternalReference allocation_limit = - AllocationUtils::GetAllocationLimitReference(isolate(), flags); - - // Align the next allocation. Storing the filler map without checking top is - // safe in new-space because the limit of the heap is aligned there. - if ((flags & DOUBLE_ALIGNMENT) != 0) { - DCHECK(kPointerAlignment * 2 == kDoubleAlignment); - Label aligned; - test(result, Immediate(kDoubleAlignmentMask)); - j(zero, &aligned, Label::kNear); - if ((flags & PRETENURE) != 0) { - cmp(result, Operand::StaticVariable(allocation_limit)); - j(above_equal, gc_required); - } - mov(Operand(result, 0), - Immediate(isolate()->factory()->one_pointer_filler_map())); - add(result, Immediate(kDoubleSize / 2)); - bind(&aligned); - } - - // Calculate new top and bail out if space is exhausted. - // We assume that element_count*element_size + header_size does not - // overflow. - if (element_count_type == REGISTER_VALUE_IS_SMI) { - STATIC_ASSERT(static_cast(times_2 - 1) == times_1); - STATIC_ASSERT(static_cast(times_4 - 1) == times_2); - STATIC_ASSERT(static_cast(times_8 - 1) == times_4); - DCHECK(element_size >= times_2); - DCHECK(kSmiTagSize == 1); - element_size = static_cast(element_size - 1); - } else { - DCHECK(element_count_type == REGISTER_VALUE_IS_INT32); - } - lea(result_end, Operand(element_count, element_size, header_size)); - add(result_end, result); - j(carry, gc_required); - cmp(result_end, Operand::StaticVariable(allocation_limit)); - j(above, gc_required); - - // Tag result. - DCHECK(kHeapObjectTag == 1); - inc(result); - - // Update allocation top. - UpdateAllocationTopHelper(result_end, scratch, flags); -} - -void MacroAssembler::Allocate(Register object_size, - Register result, - Register result_end, - Register scratch, - Label* gc_required, - AllocationFlags flags) { - DCHECK((flags & (RESULT_CONTAINS_TOP | SIZE_IN_WORDS)) == 0); - if (!FLAG_inline_new) { - if (emit_debug_code()) { - // Trash the registers to simulate an allocation failure. - mov(result, Immediate(0x7091)); - mov(result_end, Immediate(0x7191)); - if (scratch.is_valid()) { - mov(scratch, Immediate(0x7291)); - } - // object_size is left unchanged by this function. - } - jmp(gc_required); - return; - } - DCHECK(!result.is(result_end)); - - // Load address of new object into result. - LoadAllocationTopHelper(result, scratch, flags); - - ExternalReference allocation_limit = - AllocationUtils::GetAllocationLimitReference(isolate(), flags); - - // Align the next allocation. Storing the filler map without checking top is - // safe in new-space because the limit of the heap is aligned there. - if ((flags & DOUBLE_ALIGNMENT) != 0) { - DCHECK(kPointerAlignment * 2 == kDoubleAlignment); - Label aligned; - test(result, Immediate(kDoubleAlignmentMask)); - j(zero, &aligned, Label::kNear); - if ((flags & PRETENURE) != 0) { - cmp(result, Operand::StaticVariable(allocation_limit)); - j(above_equal, gc_required); - } - mov(Operand(result, 0), - Immediate(isolate()->factory()->one_pointer_filler_map())); - add(result, Immediate(kDoubleSize / 2)); - bind(&aligned); - } - - // Calculate new top and bail out if space is exhausted. - if (!object_size.is(result_end)) { - mov(result_end, object_size); - } - add(result_end, result); - cmp(result_end, Operand::StaticVariable(allocation_limit)); - j(above, gc_required); - - // Tag result. - DCHECK(kHeapObjectTag == 1); - inc(result); - - UpdateAllocationTopHelper(result_end, scratch, flags); -} - -void MacroAssembler::AllocateHeapNumber(Register result, - Register scratch1, - Register scratch2, - Label* gc_required, - MutableMode mode) { - // Allocate heap number in new space. - Allocate(HeapNumber::kSize, result, scratch1, scratch2, gc_required, - NO_ALLOCATION_FLAGS); - - Handle map = mode == MUTABLE - ? isolate()->factory()->mutable_heap_number_map() - : isolate()->factory()->heap_number_map(); - - // Set the map. - mov(FieldOperand(result, HeapObject::kMapOffset), Immediate(map)); -} - -void MacroAssembler::AllocateJSValue(Register result, Register constructor, - Register value, Register scratch, - Label* gc_required) { - DCHECK(!result.is(constructor)); - DCHECK(!result.is(scratch)); - DCHECK(!result.is(value)); - - // Allocate JSValue in new space. - Allocate(JSValue::kSize, result, scratch, no_reg, gc_required, - NO_ALLOCATION_FLAGS); - - // Initialize the JSValue. - LoadGlobalFunctionInitialMap(constructor, scratch); - mov(FieldOperand(result, HeapObject::kMapOffset), scratch); - LoadRoot(scratch, Heap::kEmptyFixedArrayRootIndex); - mov(FieldOperand(result, JSObject::kPropertiesOrHashOffset), scratch); - mov(FieldOperand(result, JSObject::kElementsOffset), scratch); - mov(FieldOperand(result, JSValue::kValueOffset), value); - STATIC_ASSERT(JSValue::kSize == 4 * kPointerSize); -} - -void MacroAssembler::InitializeFieldsWithFiller(Register current_address, - Register end_address, - Register filler) { - Label loop, entry; - jmp(&entry, Label::kNear); - bind(&loop); - mov(Operand(current_address, 0), filler); - add(current_address, Immediate(kPointerSize)); - bind(&entry); - cmp(current_address, end_address); - j(below, &loop, Label::kNear); -} - - -void MacroAssembler::BooleanBitTest(Register object, - int field_offset, - int bit_index) { - bit_index += kSmiTagSize + kSmiShiftSize; - DCHECK(base::bits::IsPowerOfTwo(kBitsPerByte)); - int byte_index = bit_index / kBitsPerByte; - int byte_bit_index = bit_index & (kBitsPerByte - 1); - test_b(FieldOperand(object, field_offset + byte_index), - Immediate(1 << byte_bit_index)); -} - -void MacroAssembler::GetMapConstructor(Register result, Register map, - Register temp) { - Label done, loop; - mov(result, FieldOperand(map, Map::kConstructorOrBackPointerOffset)); - bind(&loop); - JumpIfSmi(result, &done, Label::kNear); - CmpObjectType(result, MAP_TYPE, temp); - j(not_equal, &done, Label::kNear); - mov(result, FieldOperand(result, Map::kConstructorOrBackPointerOffset)); - jmp(&loop); - bind(&done); -} - -void MacroAssembler::CallStub(CodeStub* stub, TypeFeedbackId ast_id) { - DCHECK(AllowThisStubCall(stub)); // Calls are not allowed in some stubs. - call(stub->GetCode(), RelocInfo::CODE_TARGET, ast_id); -} - - -void MacroAssembler::TailCallStub(CodeStub* stub) { - jmp(stub->GetCode(), RelocInfo::CODE_TARGET); -} - - - -bool MacroAssembler::AllowThisStubCall(CodeStub* stub) { - return has_frame_ || !stub->SometimesSetsUpAFrame(); -} - -void MacroAssembler::CallRuntime(const Runtime::Function* f, int num_arguments, - SaveFPRegsMode save_doubles) { - // If the expected number of arguments of the runtime function is - // constant, we check that the actual number of arguments match the - // expectation. - CHECK(f->nargs < 0 || f->nargs == num_arguments); - - // TODO(1236192): Most runtime routines don't need the number of - // arguments passed in because it is constant. At some point we - // should remove this need and make the runtime routine entry code - // smarter. - Move(eax, Immediate(num_arguments)); - mov(ebx, Immediate(ExternalReference(f, isolate()))); - CEntryStub ces(isolate(), 1, save_doubles); - CallStub(&ces); -} - - -void MacroAssembler::CallExternalReference(ExternalReference ref, - int num_arguments) { - mov(eax, Immediate(num_arguments)); - mov(ebx, Immediate(ref)); - - CEntryStub stub(isolate(), 1); - CallStub(&stub); -} - - -void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid) { - // ----------- S t a t e ------------- - // -- esp[0] : return address - // -- esp[8] : argument num_arguments - 1 - // ... - // -- esp[8 * num_arguments] : argument 0 (receiver) - // - // For runtime functions with variable arguments: - // -- eax : number of arguments - // ----------------------------------- - - const Runtime::Function* function = Runtime::FunctionForId(fid); - DCHECK_EQ(1, function->result_size); - if (function->nargs >= 0) { - // TODO(1236192): Most runtime routines don't need the number of - // arguments passed in because it is constant. At some point we - // should remove this need and make the runtime routine entry code - // smarter. - mov(eax, Immediate(function->nargs)); - } - JumpToExternalReference(ExternalReference(fid, isolate())); -} - -void MacroAssembler::JumpToExternalReference(const ExternalReference& ext, - bool builtin_exit_frame) { - // Set the entry point and jump to the C entry runtime stub. - mov(ebx, Immediate(ext)); - CEntryStub ces(isolate(), 1, kDontSaveFPRegs, kArgvOnStack, - builtin_exit_frame); - jmp(ces.GetCode(), RelocInfo::CODE_TARGET); -} - -void MacroAssembler::PrepareForTailCall( - const ParameterCount& callee_args_count, Register caller_args_count_reg, - Register scratch0, Register scratch1, ReturnAddressState ra_state, - int number_of_temp_values_after_return_address) { -#if DEBUG - if (callee_args_count.is_reg()) { - DCHECK(!AreAliased(callee_args_count.reg(), caller_args_count_reg, scratch0, - scratch1)); - } else { - DCHECK(!AreAliased(caller_args_count_reg, scratch0, scratch1)); - } - DCHECK(ra_state != ReturnAddressState::kNotOnStack || - number_of_temp_values_after_return_address == 0); -#endif - - // Calculate the destination address where we will put the return address - // after we drop current frame. - Register new_sp_reg = scratch0; - if (callee_args_count.is_reg()) { - sub(caller_args_count_reg, callee_args_count.reg()); - lea(new_sp_reg, - Operand(ebp, caller_args_count_reg, times_pointer_size, - StandardFrameConstants::kCallerPCOffset - - number_of_temp_values_after_return_address * kPointerSize)); - } else { - lea(new_sp_reg, Operand(ebp, caller_args_count_reg, times_pointer_size, - StandardFrameConstants::kCallerPCOffset - - (callee_args_count.immediate() + - number_of_temp_values_after_return_address) * - kPointerSize)); - } - - if (FLAG_debug_code) { - cmp(esp, new_sp_reg); - Check(below, kStackAccessBelowStackPointer); - } - - // Copy return address from caller's frame to current frame's return address - // to avoid its trashing and let the following loop copy it to the right - // place. - Register tmp_reg = scratch1; - if (ra_state == ReturnAddressState::kOnStack) { - mov(tmp_reg, Operand(ebp, StandardFrameConstants::kCallerPCOffset)); - mov(Operand(esp, number_of_temp_values_after_return_address * kPointerSize), - tmp_reg); - } else { - DCHECK(ReturnAddressState::kNotOnStack == ra_state); - DCHECK_EQ(0, number_of_temp_values_after_return_address); - Push(Operand(ebp, StandardFrameConstants::kCallerPCOffset)); - } - - // Restore caller's frame pointer now as it could be overwritten by - // the copying loop. - mov(ebp, Operand(ebp, StandardFrameConstants::kCallerFPOffset)); - - // +2 here is to copy both receiver and return address. - Register count_reg = caller_args_count_reg; - if (callee_args_count.is_reg()) { - lea(count_reg, Operand(callee_args_count.reg(), - 2 + number_of_temp_values_after_return_address)); - } else { - mov(count_reg, Immediate(callee_args_count.immediate() + 2 + - number_of_temp_values_after_return_address)); - // TODO(ishell): Unroll copying loop for small immediate values. - } - - // Now copy callee arguments to the caller frame going backwards to avoid - // callee arguments corruption (source and destination areas could overlap). - Label loop, entry; - jmp(&entry, Label::kNear); - bind(&loop); - dec(count_reg); - mov(tmp_reg, Operand(esp, count_reg, times_pointer_size, 0)); - mov(Operand(new_sp_reg, count_reg, times_pointer_size, 0), tmp_reg); - bind(&entry); - cmp(count_reg, Immediate(0)); - j(not_equal, &loop, Label::kNear); - - // Leave current frame. - mov(esp, new_sp_reg); -} - -void MacroAssembler::InvokePrologue(const ParameterCount& expected, - const ParameterCount& actual, - Label* done, - bool* definitely_mismatches, - InvokeFlag flag, - Label::Distance done_near, - const CallWrapper& call_wrapper) { - bool definitely_matches = false; - *definitely_mismatches = false; - Label invoke; - if (expected.is_immediate()) { - DCHECK(actual.is_immediate()); - mov(eax, actual.immediate()); - if (expected.immediate() == actual.immediate()) { - definitely_matches = true; - } else { - const int sentinel = SharedFunctionInfo::kDontAdaptArgumentsSentinel; - if (expected.immediate() == sentinel) { - // Don't worry about adapting arguments for builtins that - // don't want that done. Skip adaption code by making it look - // like we have a match between expected and actual number of - // arguments. - definitely_matches = true; - } else { - *definitely_mismatches = true; - mov(ebx, expected.immediate()); - } - } - } else { - if (actual.is_immediate()) { - // Expected is in register, actual is immediate. This is the - // case when we invoke function values without going through the - // IC mechanism. - mov(eax, actual.immediate()); - cmp(expected.reg(), actual.immediate()); - j(equal, &invoke); - DCHECK(expected.reg().is(ebx)); - } else if (!expected.reg().is(actual.reg())) { - // Both expected and actual are in (different) registers. This - // is the case when we invoke functions using call and apply. - cmp(expected.reg(), actual.reg()); - j(equal, &invoke); - DCHECK(actual.reg().is(eax)); - DCHECK(expected.reg().is(ebx)); - } else { - Move(eax, actual.reg()); - } - } - - if (!definitely_matches) { - Handle adaptor = - isolate()->builtins()->ArgumentsAdaptorTrampoline(); - if (flag == CALL_FUNCTION) { - call_wrapper.BeforeCall(CallSize(adaptor, RelocInfo::CODE_TARGET)); - call(adaptor, RelocInfo::CODE_TARGET); - call_wrapper.AfterCall(); - if (!*definitely_mismatches) { - jmp(done, done_near); - } - } else { - jmp(adaptor, RelocInfo::CODE_TARGET); - } - bind(&invoke); - } -} - -void MacroAssembler::CheckDebugHook(Register fun, Register new_target, - const ParameterCount& expected, - const ParameterCount& actual) { - Label skip_hook; - ExternalReference debug_hook_active = - ExternalReference::debug_hook_on_function_call_address(isolate()); - cmpb(Operand::StaticVariable(debug_hook_active), Immediate(0)); - j(equal, &skip_hook); - { - FrameScope frame(this, - has_frame() ? StackFrame::NONE : StackFrame::INTERNAL); - if (expected.is_reg()) { - SmiTag(expected.reg()); - Push(expected.reg()); - } - if (actual.is_reg()) { - SmiTag(actual.reg()); - Push(actual.reg()); - } - if (new_target.is_valid()) { - Push(new_target); - } - Push(fun); - Push(fun); - CallRuntime(Runtime::kDebugOnFunctionCall); - Pop(fun); - if (new_target.is_valid()) { - Pop(new_target); - } - if (actual.is_reg()) { - Pop(actual.reg()); - SmiUntag(actual.reg()); - } - if (expected.is_reg()) { - Pop(expected.reg()); - SmiUntag(expected.reg()); - } - } - bind(&skip_hook); -} - - -void MacroAssembler::InvokeFunctionCode(Register function, Register new_target, - const ParameterCount& expected, - const ParameterCount& actual, - InvokeFlag flag, - const CallWrapper& call_wrapper) { - // You can't call a function without a valid frame. - DCHECK(flag == JUMP_FUNCTION || has_frame()); - DCHECK(function.is(edi)); - DCHECK_IMPLIES(new_target.is_valid(), new_target.is(edx)); - - if (call_wrapper.NeedsDebugHookCheck()) { - CheckDebugHook(function, new_target, expected, actual); - } - - // Clear the new.target register if not given. - if (!new_target.is_valid()) { - mov(edx, isolate()->factory()->undefined_value()); - } - - Label done; - bool definitely_mismatches = false; - InvokePrologue(expected, actual, &done, &definitely_mismatches, flag, - Label::kNear, call_wrapper); - if (!definitely_mismatches) { - // We call indirectly through the code field in the function to - // allow recompilation to take effect without changing any of the - // call sites. - Operand code = FieldOperand(function, JSFunction::kCodeEntryOffset); - if (flag == CALL_FUNCTION) { - call_wrapper.BeforeCall(CallSize(code)); - call(code); - call_wrapper.AfterCall(); - } else { - DCHECK(flag == JUMP_FUNCTION); - jmp(code); - } - bind(&done); - } -} - - -void MacroAssembler::InvokeFunction(Register fun, Register new_target, - const ParameterCount& actual, - InvokeFlag flag, - const CallWrapper& call_wrapper) { - // You can't call a function without a valid frame. - DCHECK(flag == JUMP_FUNCTION || has_frame()); - - DCHECK(fun.is(edi)); - mov(ebx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); - mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); - mov(ebx, FieldOperand(ebx, SharedFunctionInfo::kFormalParameterCountOffset)); - - ParameterCount expected(ebx); - InvokeFunctionCode(edi, new_target, expected, actual, flag, call_wrapper); -} - - -void MacroAssembler::InvokeFunction(Register fun, - const ParameterCount& expected, - const ParameterCount& actual, - InvokeFlag flag, - const CallWrapper& call_wrapper) { - // You can't call a function without a valid frame. - DCHECK(flag == JUMP_FUNCTION || has_frame()); - - DCHECK(fun.is(edi)); - mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); - - InvokeFunctionCode(edi, no_reg, expected, actual, flag, call_wrapper); -} - - -void MacroAssembler::InvokeFunction(Handle function, - const ParameterCount& expected, - const ParameterCount& actual, - InvokeFlag flag, - const CallWrapper& call_wrapper) { - LoadHeapObject(edi, function); - InvokeFunction(edi, expected, actual, flag, call_wrapper); -} - - -void MacroAssembler::LoadContext(Register dst, int context_chain_length) { - if (context_chain_length > 0) { - // Move up the chain of contexts to the context containing the slot. - mov(dst, Operand(esi, Context::SlotOffset(Context::PREVIOUS_INDEX))); - for (int i = 1; i < context_chain_length; i++) { - mov(dst, Operand(dst, Context::SlotOffset(Context::PREVIOUS_INDEX))); - } - } else { - // Slot is in the current function context. Move it into the - // destination register in case we store into it (the write barrier - // cannot be allowed to destroy the context in esi). - mov(dst, esi); - } - - // We should not have found a with context by walking the context chain - // (i.e., the static scope chain and runtime context chain do not agree). - // A variable occurring in such a scope should have slot type LOOKUP and - // not CONTEXT. - if (emit_debug_code()) { - cmp(FieldOperand(dst, HeapObject::kMapOffset), - isolate()->factory()->with_context_map()); - Check(not_equal, kVariableResolvedToWithContext); - } -} - - -void MacroAssembler::LoadGlobalProxy(Register dst) { - mov(dst, NativeContextOperand()); - mov(dst, ContextOperand(dst, Context::GLOBAL_PROXY_INDEX)); -} - -void MacroAssembler::LoadGlobalFunction(int index, Register function) { - // Load the native context from the current context. - mov(function, NativeContextOperand()); - // Load the function from the native context. - mov(function, ContextOperand(function, index)); -} - - -void MacroAssembler::LoadGlobalFunctionInitialMap(Register function, - Register map) { - // Load the initial map. The global functions all have initial maps. - mov(map, FieldOperand(function, JSFunction::kPrototypeOrInitialMapOffset)); - if (emit_debug_code()) { - Label ok, fail; - CheckMap(map, isolate()->factory()->meta_map(), &fail, DO_SMI_CHECK); - jmp(&ok); - bind(&fail); - Abort(kGlobalFunctionsMustHaveInitialMap); - bind(&ok); - } -} - - -// Store the value in register src in the safepoint register stack -// slot for register dst. -void MacroAssembler::StoreToSafepointRegisterSlot(Register dst, Register src) { - mov(SafepointRegisterSlot(dst), src); -} - - -void MacroAssembler::StoreToSafepointRegisterSlot(Register dst, Immediate src) { - mov(SafepointRegisterSlot(dst), src); -} - - -void MacroAssembler::LoadFromSafepointRegisterSlot(Register dst, Register src) { - mov(dst, SafepointRegisterSlot(src)); -} - - -Operand MacroAssembler::SafepointRegisterSlot(Register reg) { - return Operand(esp, SafepointRegisterStackIndex(reg.code()) * kPointerSize); -} - - -int MacroAssembler::SafepointRegisterStackIndex(int reg_code) { - // The registers are pushed starting with the lowest encoding, - // which means that lowest encodings are furthest away from - // the stack pointer. - DCHECK(reg_code >= 0 && reg_code < kNumSafepointRegisters); - return kNumSafepointRegisters - reg_code - 1; -} - - -void MacroAssembler::LoadHeapObject(Register result, - Handle object) { - mov(result, object); -} - - -void MacroAssembler::CmpHeapObject(Register reg, Handle object) { - cmp(reg, object); -} - -void MacroAssembler::PushHeapObject(Handle object) { Push(object); } - -void MacroAssembler::GetWeakValue(Register value, Handle cell) { - mov(value, cell); - mov(value, FieldOperand(value, WeakCell::kValueOffset)); -} - - -void MacroAssembler::LoadWeakValue(Register value, Handle cell, - Label* miss) { - GetWeakValue(value, cell); - JumpIfSmi(value, miss); -} - - -void MacroAssembler::Ret() { - ret(0); -} - - -void MacroAssembler::Ret(int bytes_dropped, Register scratch) { - if (is_uint16(bytes_dropped)) { - ret(bytes_dropped); - } else { - pop(scratch); - add(esp, Immediate(bytes_dropped)); - push(scratch); - ret(0); - } -} - - -void MacroAssembler::VerifyX87StackDepth(uint32_t depth) { - // Turn off the stack depth check when serializer is enabled to reduce the - // code size. - if (serializer_enabled()) return; - // Make sure the floating point stack is either empty or has depth items. - DCHECK(depth <= 7); - // This is very expensive. - DCHECK(FLAG_debug_code && FLAG_enable_slow_asserts); - - // The top-of-stack (tos) is 7 if there is one item pushed. - int tos = (8 - depth) % 8; - const int kTopMask = 0x3800; - push(eax); - fwait(); - fnstsw_ax(); - and_(eax, kTopMask); - shr(eax, 11); - cmp(eax, Immediate(tos)); - Check(equal, kUnexpectedFPUStackDepthAfterInstruction); - fnclex(); - pop(eax); -} - - -void MacroAssembler::Drop(int stack_elements) { - if (stack_elements > 0) { - add(esp, Immediate(stack_elements * kPointerSize)); - } -} - - -void MacroAssembler::Move(Register dst, Register src) { - if (!dst.is(src)) { - mov(dst, src); - } -} - - -void MacroAssembler::Move(Register dst, const Immediate& x) { - if (x.is_zero() && RelocInfo::IsNone(x.rmode_)) { - xor_(dst, dst); // Shorter than mov of 32-bit immediate 0. - } else { - mov(dst, x); - } -} - - -void MacroAssembler::Move(const Operand& dst, const Immediate& x) { - mov(dst, x); -} - - -void MacroAssembler::Lzcnt(Register dst, const Operand& src) { - // TODO(intel): Add support for LZCNT (with ABM/BMI1). - Label not_zero_src; - bsr(dst, src); - j(not_zero, ¬_zero_src, Label::kNear); - Move(dst, Immediate(63)); // 63^31 == 32 - bind(¬_zero_src); - xor_(dst, Immediate(31)); // for x in [0..31], 31^x == 31-x. -} - - -void MacroAssembler::Tzcnt(Register dst, const Operand& src) { - // TODO(intel): Add support for TZCNT (with ABM/BMI1). - Label not_zero_src; - bsf(dst, src); - j(not_zero, ¬_zero_src, Label::kNear); - Move(dst, Immediate(32)); // The result of tzcnt is 32 if src = 0. - bind(¬_zero_src); -} - - -void MacroAssembler::Popcnt(Register dst, const Operand& src) { - // TODO(intel): Add support for POPCNT (with POPCNT) - // if (CpuFeatures::IsSupported(POPCNT)) { - // CpuFeatureScope scope(this, POPCNT); - // popcnt(dst, src); - // return; - // } - UNREACHABLE(); -} - - -void MacroAssembler::SetCounter(StatsCounter* counter, int value) { - if (FLAG_native_code_counters && counter->Enabled()) { - mov(Operand::StaticVariable(ExternalReference(counter)), Immediate(value)); - } -} - - -void MacroAssembler::IncrementCounter(StatsCounter* counter, int value) { - DCHECK(value > 0); - if (FLAG_native_code_counters && counter->Enabled()) { - Operand operand = Operand::StaticVariable(ExternalReference(counter)); - if (value == 1) { - inc(operand); - } else { - add(operand, Immediate(value)); - } - } -} - - -void MacroAssembler::DecrementCounter(StatsCounter* counter, int value) { - DCHECK(value > 0); - if (FLAG_native_code_counters && counter->Enabled()) { - Operand operand = Operand::StaticVariable(ExternalReference(counter)); - if (value == 1) { - dec(operand); - } else { - sub(operand, Immediate(value)); - } - } -} - - -void MacroAssembler::IncrementCounter(Condition cc, - StatsCounter* counter, - int value) { - DCHECK(value > 0); - if (FLAG_native_code_counters && counter->Enabled()) { - Label skip; - j(NegateCondition(cc), &skip); - pushfd(); - IncrementCounter(counter, value); - popfd(); - bind(&skip); - } -} - - -void MacroAssembler::DecrementCounter(Condition cc, - StatsCounter* counter, - int value) { - DCHECK(value > 0); - if (FLAG_native_code_counters && counter->Enabled()) { - Label skip; - j(NegateCondition(cc), &skip); - pushfd(); - DecrementCounter(counter, value); - popfd(); - bind(&skip); - } -} - - -void MacroAssembler::Assert(Condition cc, BailoutReason reason) { - if (emit_debug_code()) Check(cc, reason); -} - - - -void MacroAssembler::Check(Condition cc, BailoutReason reason) { - Label L; - j(cc, &L); - Abort(reason); - // will not return here - bind(&L); -} - - -void MacroAssembler::CheckStackAlignment() { - int frame_alignment = base::OS::ActivationFrameAlignment(); - int frame_alignment_mask = frame_alignment - 1; - if (frame_alignment > kPointerSize) { - DCHECK(base::bits::IsPowerOfTwo(frame_alignment)); - Label alignment_as_expected; - test(esp, Immediate(frame_alignment_mask)); - j(zero, &alignment_as_expected); - // Abort if stack is not aligned. - int3(); - bind(&alignment_as_expected); - } -} - - -void MacroAssembler::Abort(BailoutReason reason) { -#ifdef DEBUG - const char* msg = GetBailoutReason(reason); - if (msg != NULL) { - RecordComment("Abort message: "); - RecordComment(msg); - } - - if (FLAG_trap_on_abort) { - int3(); - return; - } -#endif - - // Check if Abort() has already been initialized. - DCHECK(isolate()->builtins()->Abort()->IsHeapObject()); - - Move(edx, Smi::FromInt(static_cast(reason))); - - // Disable stub call restrictions to always allow calls to abort. - if (!has_frame_) { - // We don't actually want to generate a pile of code for this, so just - // claim there is a stack frame, without generating one. - FrameScope scope(this, StackFrame::NONE); - Call(isolate()->builtins()->Abort(), RelocInfo::CODE_TARGET); - } else { - Call(isolate()->builtins()->Abort(), RelocInfo::CODE_TARGET); - } - // will not return here - int3(); -} - - -void MacroAssembler::LoadInstanceDescriptors(Register map, - Register descriptors) { - mov(descriptors, FieldOperand(map, Map::kDescriptorsOffset)); -} - - -void MacroAssembler::NumberOfOwnDescriptors(Register dst, Register map) { - mov(dst, FieldOperand(map, Map::kBitField3Offset)); - DecodeField(dst); -} - - -void MacroAssembler::LoadAccessor(Register dst, Register holder, - int accessor_index, - AccessorComponent accessor) { - mov(dst, FieldOperand(holder, HeapObject::kMapOffset)); - LoadInstanceDescriptors(dst, dst); - mov(dst, FieldOperand(dst, DescriptorArray::GetValueOffset(accessor_index))); - int offset = accessor == ACCESSOR_GETTER ? AccessorPair::kGetterOffset - : AccessorPair::kSetterOffset; - mov(dst, FieldOperand(dst, offset)); -} - -void MacroAssembler::JumpIfNotBothSequentialOneByteStrings(Register object1, - Register object2, - Register scratch1, - Register scratch2, - Label* failure) { - // Check that both objects are not smis. - STATIC_ASSERT(kSmiTag == 0); - mov(scratch1, object1); - and_(scratch1, object2); - JumpIfSmi(scratch1, failure); - - // Load instance type for both strings. - mov(scratch1, FieldOperand(object1, HeapObject::kMapOffset)); - mov(scratch2, FieldOperand(object2, HeapObject::kMapOffset)); - movzx_b(scratch1, FieldOperand(scratch1, Map::kInstanceTypeOffset)); - movzx_b(scratch2, FieldOperand(scratch2, Map::kInstanceTypeOffset)); - - // Check that both are flat one-byte strings. - const int kFlatOneByteStringMask = - kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask; - const int kFlatOneByteStringTag = - kStringTag | kOneByteStringTag | kSeqStringTag; - // Interleave bits from both instance types and compare them in one check. - const int kShift = 8; - DCHECK_EQ(0, kFlatOneByteStringMask & (kFlatOneByteStringMask << kShift)); - and_(scratch1, kFlatOneByteStringMask); - and_(scratch2, kFlatOneByteStringMask); - shl(scratch2, kShift); - or_(scratch1, scratch2); - cmp(scratch1, kFlatOneByteStringTag | (kFlatOneByteStringTag << kShift)); - j(not_equal, failure); -} - - -void MacroAssembler::JumpIfNotUniqueNameInstanceType(Operand operand, - Label* not_unique_name, - Label::Distance distance) { - STATIC_ASSERT(kInternalizedTag == 0 && kStringTag == 0); - Label succeed; - test(operand, Immediate(kIsNotStringMask | kIsNotInternalizedMask)); - j(zero, &succeed); - cmpb(operand, Immediate(SYMBOL_TYPE)); - j(not_equal, not_unique_name, distance); - - bind(&succeed); -} - - -void MacroAssembler::EmitSeqStringSetCharCheck(Register string, - Register index, - Register value, - uint32_t encoding_mask) { - Label is_object; - JumpIfNotSmi(string, &is_object, Label::kNear); - Abort(kNonObject); - bind(&is_object); - - push(value); - mov(value, FieldOperand(string, HeapObject::kMapOffset)); - movzx_b(value, FieldOperand(value, Map::kInstanceTypeOffset)); - - and_(value, Immediate(kStringRepresentationMask | kStringEncodingMask)); - cmp(value, Immediate(encoding_mask)); - pop(value); - Check(equal, kUnexpectedStringType); - - // The index is assumed to be untagged coming in, tag it to compare with the - // string length without using a temp register, it is restored at the end of - // this function. - SmiTag(index); - Check(no_overflow, kIndexIsTooLarge); - - cmp(index, FieldOperand(string, String::kLengthOffset)); - Check(less, kIndexIsTooLarge); - - cmp(index, Immediate(Smi::kZero)); - Check(greater_equal, kIndexIsNegative); - - // Restore the index - SmiUntag(index); -} - - -void MacroAssembler::PrepareCallCFunction(int num_arguments, Register scratch) { - int frame_alignment = base::OS::ActivationFrameAlignment(); - if (frame_alignment != 0) { - // Make stack end at alignment and make room for num_arguments words - // and the original value of esp. - mov(scratch, esp); - sub(esp, Immediate((num_arguments + 1) * kPointerSize)); - DCHECK(base::bits::IsPowerOfTwo(frame_alignment)); - and_(esp, -frame_alignment); - mov(Operand(esp, num_arguments * kPointerSize), scratch); - } else { - sub(esp, Immediate(num_arguments * kPointerSize)); - } -} - - -void MacroAssembler::CallCFunction(ExternalReference function, - int num_arguments) { - // Trashing eax is ok as it will be the return value. - mov(eax, Immediate(function)); - CallCFunction(eax, num_arguments); -} - - -void MacroAssembler::CallCFunction(Register function, - int num_arguments) { - DCHECK(has_frame()); - // Check stack alignment. - if (emit_debug_code()) { - CheckStackAlignment(); - } - - call(function); - if (base::OS::ActivationFrameAlignment() != 0) { - mov(esp, Operand(esp, num_arguments * kPointerSize)); - } else { - add(esp, Immediate(num_arguments * kPointerSize)); - } -} - - -#ifdef DEBUG -bool AreAliased(Register reg1, - Register reg2, - Register reg3, - Register reg4, - Register reg5, - Register reg6, - Register reg7, - Register reg8) { - int n_of_valid_regs = reg1.is_valid() + reg2.is_valid() + - reg3.is_valid() + reg4.is_valid() + reg5.is_valid() + reg6.is_valid() + - reg7.is_valid() + reg8.is_valid(); - - RegList regs = 0; - if (reg1.is_valid()) regs |= reg1.bit(); - if (reg2.is_valid()) regs |= reg2.bit(); - if (reg3.is_valid()) regs |= reg3.bit(); - if (reg4.is_valid()) regs |= reg4.bit(); - if (reg5.is_valid()) regs |= reg5.bit(); - if (reg6.is_valid()) regs |= reg6.bit(); - if (reg7.is_valid()) regs |= reg7.bit(); - if (reg8.is_valid()) regs |= reg8.bit(); - int n_of_non_aliasing_regs = NumRegs(regs); - - return n_of_valid_regs != n_of_non_aliasing_regs; -} -#endif - - -CodePatcher::CodePatcher(Isolate* isolate, byte* address, int size) - : address_(address), - size_(size), - masm_(isolate, address, size + Assembler::kGap, CodeObjectRequired::kNo) { - // Create a new macro assembler pointing to the address of the code to patch. - // The size is adjusted with kGap on order for the assembler to generate size - // bytes of instructions without failing with buffer size constraints. - DCHECK(masm_.reloc_info_writer.pos() == address_ + size_ + Assembler::kGap); -} - - -CodePatcher::~CodePatcher() { - // Indicate that code has changed. - Assembler::FlushICache(masm_.isolate(), address_, size_); - - // Check that the code was patched as expected. - DCHECK(masm_.pc_ == address_ + size_); - DCHECK(masm_.reloc_info_writer.pos() == address_ + size_ + Assembler::kGap); -} - - -void MacroAssembler::CheckPageFlag( - Register object, - Register scratch, - int mask, - Condition cc, - Label* condition_met, - Label::Distance condition_met_distance) { - DCHECK(cc == zero || cc == not_zero); - if (scratch.is(object)) { - and_(scratch, Immediate(~Page::kPageAlignmentMask)); - } else { - mov(scratch, Immediate(~Page::kPageAlignmentMask)); - and_(scratch, object); - } - if (mask < (1 << kBitsPerByte)) { - test_b(Operand(scratch, MemoryChunk::kFlagsOffset), Immediate(mask)); - } else { - test(Operand(scratch, MemoryChunk::kFlagsOffset), Immediate(mask)); - } - j(cc, condition_met, condition_met_distance); -} - - -void MacroAssembler::CheckPageFlagForMap( - Handle map, - int mask, - Condition cc, - Label* condition_met, - Label::Distance condition_met_distance) { - DCHECK(cc == zero || cc == not_zero); - Page* page = Page::FromAddress(map->address()); - DCHECK(!serializer_enabled()); // Serializer cannot match page_flags. - ExternalReference reference(ExternalReference::page_flags(page)); - // The inlined static address check of the page's flags relies - // on maps never being compacted. - DCHECK(!isolate()->heap()->mark_compact_collector()-> - IsOnEvacuationCandidate(*map)); - if (mask < (1 << kBitsPerByte)) { - test_b(Operand::StaticVariable(reference), Immediate(mask)); - } else { - test(Operand::StaticVariable(reference), Immediate(mask)); - } - j(cc, condition_met, condition_met_distance); -} - - -void MacroAssembler::JumpIfBlack(Register object, - Register scratch0, - Register scratch1, - Label* on_black, - Label::Distance on_black_near) { - HasColor(object, scratch0, scratch1, on_black, on_black_near, 1, - 1); // kBlackBitPattern. - DCHECK(strcmp(Marking::kBlackBitPattern, "11") == 0); -} - - -void MacroAssembler::HasColor(Register object, - Register bitmap_scratch, - Register mask_scratch, - Label* has_color, - Label::Distance has_color_distance, - int first_bit, - int second_bit) { - DCHECK(!AreAliased(object, bitmap_scratch, mask_scratch, ecx)); - - GetMarkBits(object, bitmap_scratch, mask_scratch); - - Label other_color, word_boundary; - test(mask_scratch, Operand(bitmap_scratch, MemoryChunk::kHeaderSize)); - j(first_bit == 1 ? zero : not_zero, &other_color, Label::kNear); - add(mask_scratch, mask_scratch); // Shift left 1 by adding. - j(zero, &word_boundary, Label::kNear); - test(mask_scratch, Operand(bitmap_scratch, MemoryChunk::kHeaderSize)); - j(second_bit == 1 ? not_zero : zero, has_color, has_color_distance); - jmp(&other_color, Label::kNear); - - bind(&word_boundary); - test_b(Operand(bitmap_scratch, MemoryChunk::kHeaderSize + kPointerSize), - Immediate(1)); - - j(second_bit == 1 ? not_zero : zero, has_color, has_color_distance); - bind(&other_color); -} - - -void MacroAssembler::GetMarkBits(Register addr_reg, - Register bitmap_reg, - Register mask_reg) { - DCHECK(!AreAliased(addr_reg, mask_reg, bitmap_reg, ecx)); - mov(bitmap_reg, Immediate(~Page::kPageAlignmentMask)); - and_(bitmap_reg, addr_reg); - mov(ecx, addr_reg); - int shift = - Bitmap::kBitsPerCellLog2 + kPointerSizeLog2 - Bitmap::kBytesPerCellLog2; - shr(ecx, shift); - and_(ecx, - (Page::kPageAlignmentMask >> shift) & ~(Bitmap::kBytesPerCell - 1)); - - add(bitmap_reg, ecx); - mov(ecx, addr_reg); - shr(ecx, kPointerSizeLog2); - and_(ecx, (1 << Bitmap::kBitsPerCellLog2) - 1); - mov(mask_reg, Immediate(1)); - shl_cl(mask_reg); -} - - -void MacroAssembler::JumpIfWhite(Register value, Register bitmap_scratch, - Register mask_scratch, Label* value_is_white, - Label::Distance distance) { - DCHECK(!AreAliased(value, bitmap_scratch, mask_scratch, ecx)); - GetMarkBits(value, bitmap_scratch, mask_scratch); - - // If the value is black or grey we don't need to do anything. - DCHECK(strcmp(Marking::kWhiteBitPattern, "00") == 0); - DCHECK(strcmp(Marking::kBlackBitPattern, "11") == 0); - DCHECK(strcmp(Marking::kGreyBitPattern, "10") == 0); - DCHECK(strcmp(Marking::kImpossibleBitPattern, "01") == 0); - - // Since both black and grey have a 1 in the first position and white does - // not have a 1 there we only need to check one bit. - test(mask_scratch, Operand(bitmap_scratch, MemoryChunk::kHeaderSize)); - j(zero, value_is_white, Label::kNear); -} - - -void MacroAssembler::EnumLength(Register dst, Register map) { - STATIC_ASSERT(Map::EnumLengthBits::kShift == 0); - mov(dst, FieldOperand(map, Map::kBitField3Offset)); - and_(dst, Immediate(Map::EnumLengthBits::kMask)); - SmiTag(dst); -} - - -void MacroAssembler::CheckEnumCache(Label* call_runtime) { - Label next, start; - mov(ecx, eax); - - // Check if the enum length field is properly initialized, indicating that - // there is an enum cache. - mov(ebx, FieldOperand(ecx, HeapObject::kMapOffset)); - - EnumLength(edx, ebx); - cmp(edx, Immediate(Smi::FromInt(kInvalidEnumCacheSentinel))); - j(equal, call_runtime); - - jmp(&start); - - bind(&next); - mov(ebx, FieldOperand(ecx, HeapObject::kMapOffset)); - - // For all objects but the receiver, check that the cache is empty. - EnumLength(edx, ebx); - cmp(edx, Immediate(Smi::kZero)); - j(not_equal, call_runtime); - - bind(&start); - - // Check that there are no elements. Register rcx contains the current JS - // object we've reached through the prototype chain. - Label no_elements; - mov(ecx, FieldOperand(ecx, JSObject::kElementsOffset)); - cmp(ecx, isolate()->factory()->empty_fixed_array()); - j(equal, &no_elements); - - // Second chance, the object may be using the empty slow element dictionary. - cmp(ecx, isolate()->factory()->empty_slow_element_dictionary()); - j(not_equal, call_runtime); - - bind(&no_elements); - mov(ecx, FieldOperand(ebx, Map::kPrototypeOffset)); - cmp(ecx, isolate()->factory()->null_value()); - j(not_equal, &next); -} - - -void MacroAssembler::TestJSArrayForAllocationMemento( - Register receiver_reg, - Register scratch_reg, - Label* no_memento_found) { - Label map_check; - Label top_check; - ExternalReference new_space_allocation_top = - ExternalReference::new_space_allocation_top_address(isolate()); - const int kMementoMapOffset = JSArray::kSize - kHeapObjectTag; - const int kMementoLastWordOffset = - kMementoMapOffset + AllocationMemento::kSize - kPointerSize; - - // Bail out if the object is not in new space. - JumpIfNotInNewSpace(receiver_reg, scratch_reg, no_memento_found); - // If the object is in new space, we need to check whether it is on the same - // page as the current top. - lea(scratch_reg, Operand(receiver_reg, kMementoLastWordOffset)); - xor_(scratch_reg, Operand::StaticVariable(new_space_allocation_top)); - test(scratch_reg, Immediate(~Page::kPageAlignmentMask)); - j(zero, &top_check); - // The object is on a different page than allocation top. Bail out if the - // object sits on the page boundary as no memento can follow and we cannot - // touch the memory following it. - lea(scratch_reg, Operand(receiver_reg, kMementoLastWordOffset)); - xor_(scratch_reg, receiver_reg); - test(scratch_reg, Immediate(~Page::kPageAlignmentMask)); - j(not_zero, no_memento_found); - // Continue with the actual map check. - jmp(&map_check); - // If top is on the same page as the current object, we need to check whether - // we are below top. - bind(&top_check); - lea(scratch_reg, Operand(receiver_reg, kMementoLastWordOffset)); - cmp(scratch_reg, Operand::StaticVariable(new_space_allocation_top)); - j(greater_equal, no_memento_found); - // Memento map check. - bind(&map_check); - mov(scratch_reg, Operand(receiver_reg, kMementoMapOffset)); - cmp(scratch_reg, Immediate(isolate()->factory()->allocation_memento_map())); -} - -void MacroAssembler::TruncatingDiv(Register dividend, int32_t divisor) { - DCHECK(!dividend.is(eax)); - DCHECK(!dividend.is(edx)); - base::MagicNumbersForDivision mag = - base::SignedDivisionByConstant(static_cast(divisor)); - mov(eax, Immediate(mag.multiplier)); - imul(dividend); - bool neg = (mag.multiplier & (static_cast(1) << 31)) != 0; - if (divisor > 0 && neg) add(edx, dividend); - if (divisor < 0 && !neg && mag.multiplier > 0) sub(edx, dividend); - if (mag.shift > 0) sar(edx, mag.shift); - mov(eax, dividend); - shr(eax, 31); - add(edx, eax); -} - - -} // namespace internal -} // namespace v8 - -#endif // V8_TARGET_ARCH_X87 diff --git a/src/x87/macro-assembler-x87.h b/src/x87/macro-assembler-x87.h deleted file mode 100644 index 2ecd0d061e..0000000000 --- a/src/x87/macro-assembler-x87.h +++ /dev/null @@ -1,906 +0,0 @@ -// 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. - -#ifndef V8_X87_MACRO_ASSEMBLER_X87_H_ -#define V8_X87_MACRO_ASSEMBLER_X87_H_ - -#include "src/assembler.h" -#include "src/bailout-reason.h" -#include "src/frames.h" -#include "src/globals.h" - -namespace v8 { -namespace internal { - -// Give alias names to registers for calling conventions. -const Register kReturnRegister0 = {Register::kCode_eax}; -const Register kReturnRegister1 = {Register::kCode_edx}; -const Register kReturnRegister2 = {Register::kCode_edi}; -const Register kJSFunctionRegister = {Register::kCode_edi}; -const Register kContextRegister = {Register::kCode_esi}; -const Register kAllocateSizeRegister = {Register::kCode_edx}; -const Register kInterpreterAccumulatorRegister = {Register::kCode_eax}; -const Register kInterpreterBytecodeOffsetRegister = {Register::kCode_ecx}; -const Register kInterpreterBytecodeArrayRegister = {Register::kCode_edi}; -const Register kInterpreterDispatchTableRegister = {Register::kCode_esi}; -const Register kJavaScriptCallArgCountRegister = {Register::kCode_eax}; -const Register kJavaScriptCallNewTargetRegister = {Register::kCode_edx}; -const Register kRuntimeCallFunctionRegister = {Register::kCode_ebx}; -const Register kRuntimeCallArgCountRegister = {Register::kCode_eax}; - -// Spill slots used by interpreter dispatch calling convention. -const int kInterpreterDispatchTableSpillSlot = -1; - -// Convenience for platform-independent signatures. We do not normally -// distinguish memory operands from other operands on ia32. -typedef Operand MemOperand; - -enum RememberedSetAction { EMIT_REMEMBERED_SET, OMIT_REMEMBERED_SET }; -enum SmiCheck { INLINE_SMI_CHECK, OMIT_SMI_CHECK }; -enum PointersToHereCheck { - kPointersToHereMaybeInteresting, - kPointersToHereAreAlwaysInteresting -}; - -enum RegisterValueType { REGISTER_VALUE_IS_SMI, REGISTER_VALUE_IS_INT32 }; - -enum class ReturnAddressState { kOnStack, kNotOnStack }; - -#ifdef DEBUG -bool AreAliased(Register reg1, Register reg2, Register reg3 = no_reg, - Register reg4 = no_reg, Register reg5 = no_reg, - Register reg6 = no_reg, Register reg7 = no_reg, - Register reg8 = no_reg); -#endif - -// MacroAssembler implements a collection of frequently used macros. -class MacroAssembler: public Assembler { - public: - MacroAssembler(Isolate* isolate, void* buffer, int size, - CodeObjectRequired create_code_object); - - Isolate* isolate() const { return isolate_; } - - void Load(Register dst, const Operand& src, Representation r); - void Store(Register src, const Operand& dst, Representation r); - - // Load a register with a long value as efficiently as possible. - void Set(Register dst, int32_t x) { - if (x == 0) { - xor_(dst, dst); - } else { - mov(dst, Immediate(x)); - } - } - void Set(const Operand& dst, int32_t x) { mov(dst, Immediate(x)); } - - // Operations on roots in the root-array. - void LoadRoot(Register destination, Heap::RootListIndex index); - void StoreRoot(Register source, Register scratch, Heap::RootListIndex index); - void CompareRoot(Register with, Register scratch, Heap::RootListIndex index); - // These methods can only be used with constant roots (i.e. non-writable - // and not in new space). - void CompareRoot(Register with, Heap::RootListIndex index); - void CompareRoot(const Operand& with, Heap::RootListIndex index); - void PushRoot(Heap::RootListIndex index); - - // Compare the object in a register to a value and jump if they are equal. - void JumpIfRoot(Register with, Heap::RootListIndex index, Label* if_equal, - Label::Distance if_equal_distance = Label::kFar) { - CompareRoot(with, index); - j(equal, if_equal, if_equal_distance); - } - void JumpIfRoot(const Operand& with, Heap::RootListIndex index, - Label* if_equal, - Label::Distance if_equal_distance = Label::kFar) { - CompareRoot(with, index); - j(equal, if_equal, if_equal_distance); - } - - // Compare the object in a register to a value and jump if they are not equal. - void JumpIfNotRoot(Register with, Heap::RootListIndex index, - Label* if_not_equal, - Label::Distance if_not_equal_distance = Label::kFar) { - CompareRoot(with, index); - j(not_equal, if_not_equal, if_not_equal_distance); - } - void JumpIfNotRoot(const Operand& with, Heap::RootListIndex index, - Label* if_not_equal, - Label::Distance if_not_equal_distance = Label::kFar) { - CompareRoot(with, index); - j(not_equal, if_not_equal, if_not_equal_distance); - } - - // These functions do not arrange the registers in any particular order so - // they are not useful for calls that can cause a GC. The caller can - // exclude up to 3 registers that do not need to be saved and restored. - void PushCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1 = no_reg, - Register exclusion2 = no_reg, - Register exclusion3 = no_reg); - void PopCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1 = no_reg, - Register exclusion2 = no_reg, - Register exclusion3 = no_reg); - - // --------------------------------------------------------------------------- - // GC Support - enum RememberedSetFinalAction { kReturnAtEnd, kFallThroughAtEnd }; - - // Record in the remembered set the fact that we have a pointer to new space - // at the address pointed to by the addr register. Only works if addr is not - // in new space. - void RememberedSetHelper(Register object, // Used for debug code. - Register addr, Register scratch, - SaveFPRegsMode save_fp, - RememberedSetFinalAction and_then); - - void CheckPageFlag(Register object, Register scratch, int mask, Condition cc, - Label* condition_met, - Label::Distance condition_met_distance = Label::kFar); - - void CheckPageFlagForMap( - Handle map, int mask, Condition cc, Label* condition_met, - Label::Distance condition_met_distance = Label::kFar); - - // Check if object is in new space. Jumps if the object is not in new space. - // The register scratch can be object itself, but scratch will be clobbered. - void JumpIfNotInNewSpace(Register object, Register scratch, Label* branch, - Label::Distance distance = Label::kFar) { - InNewSpace(object, scratch, zero, branch, distance); - } - - // Check if object is in new space. Jumps if the object is in new space. - // The register scratch can be object itself, but it will be clobbered. - void JumpIfInNewSpace(Register object, Register scratch, Label* branch, - Label::Distance distance = Label::kFar) { - InNewSpace(object, scratch, not_zero, branch, distance); - } - - // Check if an object has a given incremental marking color. Also uses ecx! - void HasColor(Register object, Register scratch0, Register scratch1, - Label* has_color, Label::Distance has_color_distance, - int first_bit, int second_bit); - - void JumpIfBlack(Register object, Register scratch0, Register scratch1, - Label* on_black, - Label::Distance on_black_distance = Label::kFar); - - // Checks the color of an object. If the object is white we jump to the - // incremental marker. - void JumpIfWhite(Register value, Register scratch1, Register scratch2, - Label* value_is_white, Label::Distance distance); - - // Notify the garbage collector that we wrote a pointer into an object. - // |object| is the object being stored into, |value| is the object being - // stored. value and scratch registers are clobbered by the operation. - // The offset is the offset from the start of the object, not the offset from - // the tagged HeapObject pointer. For use with FieldOperand(reg, off). - void RecordWriteField( - Register object, int offset, Register value, Register scratch, - SaveFPRegsMode save_fp, - RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET, - SmiCheck smi_check = INLINE_SMI_CHECK, - PointersToHereCheck pointers_to_here_check_for_value = - kPointersToHereMaybeInteresting); - - // As above, but the offset has the tag presubtracted. For use with - // Operand(reg, off). - void RecordWriteContextSlot( - Register context, int offset, Register value, Register scratch, - SaveFPRegsMode save_fp, - RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET, - SmiCheck smi_check = INLINE_SMI_CHECK, - PointersToHereCheck pointers_to_here_check_for_value = - kPointersToHereMaybeInteresting) { - RecordWriteField(context, offset + kHeapObjectTag, value, scratch, save_fp, - remembered_set_action, smi_check, - pointers_to_here_check_for_value); - } - - // For page containing |object| mark region covering |address| - // dirty. |object| is the object being stored into, |value| is the - // object being stored. The address and value registers are clobbered by the - // operation. RecordWrite filters out smis so it does not update the - // write barrier if the value is a smi. - void RecordWrite( - Register object, Register address, Register value, SaveFPRegsMode save_fp, - RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET, - SmiCheck smi_check = INLINE_SMI_CHECK, - PointersToHereCheck pointers_to_here_check_for_value = - kPointersToHereMaybeInteresting); - - // Notify the garbage collector that we wrote a code entry into a - // JSFunction. Only scratch is clobbered by the operation. - void RecordWriteCodeEntryField(Register js_function, Register code_entry, - Register scratch); - - // For page containing |object| mark the region covering the object's map - // dirty. |object| is the object being stored into, |map| is the Map object - // that was stored. - void RecordWriteForMap(Register object, Handle map, Register scratch1, - Register scratch2, SaveFPRegsMode save_fp); - - // --------------------------------------------------------------------------- - // Debugger Support - - void DebugBreak(); - - // Generates function and stub prologue code. - void StubPrologue(StackFrame::Type type); - void Prologue(bool code_pre_aging); - - // Enter specific kind of exit frame. Expects the number of - // arguments in register eax and sets up the number of arguments in - // register edi and the pointer to the first argument in register - // esi. - void EnterExitFrame(int argc, bool save_doubles, StackFrame::Type frame_type); - - void EnterApiExitFrame(int argc); - - // Leave the current exit frame. Expects the return value in - // register eax:edx (untouched) and the pointer to the first - // argument in register esi (if pop_arguments == true). - void LeaveExitFrame(bool save_doubles, bool pop_arguments = true); - - // Leave the current exit frame. Expects the return value in - // register eax (untouched). - void LeaveApiExitFrame(bool restore_context); - - // Find the function context up the context chain. - void LoadContext(Register dst, int context_chain_length); - - // Load the global proxy from the current context. - void LoadGlobalProxy(Register dst); - - // Load the global function with the given index. - void LoadGlobalFunction(int index, Register function); - - // Load the initial map from the global function. The registers - // function and map can be the same. - void LoadGlobalFunctionInitialMap(Register function, Register map); - - // Push and pop the registers that can hold pointers. - void PushSafepointRegisters() { pushad(); } - void PopSafepointRegisters() { popad(); } - // Store the value in register/immediate src in the safepoint - // register stack slot for register dst. - void StoreToSafepointRegisterSlot(Register dst, Register src); - void StoreToSafepointRegisterSlot(Register dst, Immediate src); - void LoadFromSafepointRegisterSlot(Register dst, Register src); - - // Nop, because x87 does not have a root register. - void InitializeRootRegister() {} - - void LoadHeapObject(Register result, Handle object); - void CmpHeapObject(Register reg, Handle object); - void PushHeapObject(Handle object); - - void LoadObject(Register result, Handle object) { - AllowDeferredHandleDereference heap_object_check; - if (object->IsHeapObject()) { - LoadHeapObject(result, Handle::cast(object)); - } else { - Move(result, Immediate(object)); - } - } - - void CmpObject(Register reg, Handle object) { - AllowDeferredHandleDereference heap_object_check; - if (object->IsHeapObject()) { - CmpHeapObject(reg, Handle::cast(object)); - } else { - cmp(reg, Immediate(object)); - } - } - - void GetWeakValue(Register value, Handle cell); - void LoadWeakValue(Register value, Handle cell, Label* miss); - - // --------------------------------------------------------------------------- - // JavaScript invokes - - // Removes current frame and its arguments from the stack preserving - // the arguments and a return address pushed to the stack for the next call. - // |ra_state| defines whether return address is already pushed to stack or - // not. Both |callee_args_count| and |caller_args_count_reg| do not include - // receiver. |callee_args_count| is not modified, |caller_args_count_reg| - // is trashed. |number_of_temp_values_after_return_address| specifies - // the number of words pushed to the stack after the return address. This is - // to allow "allocation" of scratch registers that this function requires - // by saving their values on the stack. - void PrepareForTailCall(const ParameterCount& callee_args_count, - Register caller_args_count_reg, Register scratch0, - Register scratch1, ReturnAddressState ra_state, - int number_of_temp_values_after_return_address); - - // Invoke the JavaScript function code by either calling or jumping. - - void InvokeFunctionCode(Register function, Register new_target, - const ParameterCount& expected, - const ParameterCount& actual, InvokeFlag flag, - const CallWrapper& call_wrapper); - - // On function call, call into the debugger if necessary. - void CheckDebugHook(Register fun, Register new_target, - const ParameterCount& expected, - const ParameterCount& actual); - - // Invoke the JavaScript function in the given register. Changes the - // current context to the context in the function before invoking. - void InvokeFunction(Register function, Register new_target, - const ParameterCount& actual, InvokeFlag flag, - const CallWrapper& call_wrapper); - - void InvokeFunction(Register function, const ParameterCount& expected, - const ParameterCount& actual, InvokeFlag flag, - const CallWrapper& call_wrapper); - - void InvokeFunction(Handle function, - const ParameterCount& expected, - const ParameterCount& actual, InvokeFlag flag, - const CallWrapper& call_wrapper); - - void ShlPair(Register high, Register low, uint8_t imm8); - void ShlPair_cl(Register high, Register low); - void ShrPair(Register high, Register low, uint8_t imm8); - void ShrPair_cl(Register high, Register src); - void SarPair(Register high, Register low, uint8_t imm8); - void SarPair_cl(Register high, Register low); - - // Expression support - // Support for constant splitting. - bool IsUnsafeImmediate(const Immediate& x); - void SafeMove(Register dst, const Immediate& x); - void SafePush(const Immediate& x); - - // Compare object type for heap object. - // Incoming register is heap_object and outgoing register is map. - void CmpObjectType(Register heap_object, InstanceType type, Register map); - - // Compare instance type for map. - void CmpInstanceType(Register map, InstanceType type); - - // Compare an object's map with the specified map. - void CompareMap(Register obj, Handle map); - - // Check if the map of an object is equal to a specified map and branch to - // label if not. Skip the smi check if not required (object is known to be a - // heap object). If mode is ALLOW_ELEMENT_TRANSITION_MAPS, then also match - // against maps that are ElementsKind transition maps of the specified map. - void CheckMap(Register obj, Handle map, Label* fail, - SmiCheckType smi_check_type); - - // Check if the object in register heap_object is a string. Afterwards the - // register map contains the object map and the register instance_type - // contains the instance_type. The registers map and instance_type can be the - // same in which case it contains the instance type afterwards. Either of the - // registers map and instance_type can be the same as heap_object. - Condition IsObjectStringType(Register heap_object, Register map, - Register instance_type); - - // FCmp is similar to integer cmp, but requires unsigned - // jcc instructions (je, ja, jae, jb, jbe, je, and jz). - void FCmp(); - void FXamMinusZero(); - void FXamSign(); - void X87CheckIA(); - void X87SetRC(int rc); - void X87SetFPUCW(int cw); - - void ClampUint8(Register reg); - void ClampTOSToUint8(Register result_reg); - - void SlowTruncateToI(Register result_reg, Register input_reg, - int offset = HeapNumber::kValueOffset - kHeapObjectTag); - - void TruncateHeapNumberToI(Register result_reg, Register input_reg); - void TruncateX87TOSToI(Register result_reg); - - void X87TOSToI(Register result_reg, MinusZeroMode minus_zero_mode, - Label* lost_precision, Label* is_nan, Label* minus_zero, - Label::Distance dst = Label::kFar); - - // Smi tagging support. - void SmiTag(Register reg) { - STATIC_ASSERT(kSmiTag == 0); - STATIC_ASSERT(kSmiTagSize == 1); - add(reg, reg); - } - void SmiUntag(Register reg) { - sar(reg, kSmiTagSize); - } - - // Modifies the register even if it does not contain a Smi! - void SmiUntag(Register reg, Label* is_smi) { - STATIC_ASSERT(kSmiTagSize == 1); - sar(reg, kSmiTagSize); - STATIC_ASSERT(kSmiTag == 0); - j(not_carry, is_smi); - } - - void LoadUint32NoSSE2(Register src) { - LoadUint32NoSSE2(Operand(src)); - } - void LoadUint32NoSSE2(const Operand& src); - - // Jump the register contains a smi. - inline void JumpIfSmi(Register value, Label* smi_label, - Label::Distance distance = Label::kFar) { - test(value, Immediate(kSmiTagMask)); - j(zero, smi_label, distance); - } - // Jump if the operand is a smi. - inline void JumpIfSmi(Operand value, Label* smi_label, - Label::Distance distance = Label::kFar) { - test(value, Immediate(kSmiTagMask)); - j(zero, smi_label, distance); - } - // Jump if register contain a non-smi. - inline void JumpIfNotSmi(Register value, Label* not_smi_label, - Label::Distance distance = Label::kFar) { - test(value, Immediate(kSmiTagMask)); - j(not_zero, not_smi_label, distance); - } - // Jump if the operand is not a smi. - inline void JumpIfNotSmi(Operand value, Label* smi_label, - Label::Distance distance = Label::kFar) { - test(value, Immediate(kSmiTagMask)); - j(not_zero, smi_label, distance); - } - // Jump if the value cannot be represented by a smi. - inline void JumpIfNotValidSmiValue(Register value, Register scratch, - Label* on_invalid, - Label::Distance distance = Label::kFar) { - mov(scratch, value); - add(scratch, Immediate(0x40000000U)); - j(sign, on_invalid, distance); - } - - // Jump if the unsigned integer value cannot be represented by a smi. - inline void JumpIfUIntNotValidSmiValue( - Register value, Label* on_invalid, - Label::Distance distance = Label::kFar) { - cmp(value, Immediate(0x40000000U)); - j(above_equal, on_invalid, distance); - } - - void LoadInstanceDescriptors(Register map, Register descriptors); - void EnumLength(Register dst, Register map); - void NumberOfOwnDescriptors(Register dst, Register map); - void LoadAccessor(Register dst, Register holder, int accessor_index, - AccessorComponent accessor); - - template - void DecodeField(Register reg) { - static const int shift = Field::kShift; - static const int mask = Field::kMask >> Field::kShift; - if (shift != 0) { - sar(reg, shift); - } - and_(reg, Immediate(mask)); - } - - template - void DecodeFieldToSmi(Register reg) { - static const int shift = Field::kShift; - static const int mask = (Field::kMask >> Field::kShift) << kSmiTagSize; - STATIC_ASSERT((mask & (0x80000000u >> (kSmiTagSize - 1))) == 0); - STATIC_ASSERT(kSmiTag == 0); - if (shift < kSmiTagSize) { - shl(reg, kSmiTagSize - shift); - } else if (shift > kSmiTagSize) { - sar(reg, shift - kSmiTagSize); - } - and_(reg, Immediate(mask)); - } - - // Abort execution if argument is not a smi, enabled via --debug-code. - void AssertSmi(Register object); - - // Abort execution if argument is a smi, enabled via --debug-code. - void AssertNotSmi(Register object); - - // Abort execution if argument is not a JSFunction, enabled via --debug-code. - void AssertFunction(Register object); - - // Abort execution if argument is not a JSBoundFunction, - // enabled via --debug-code. - void AssertBoundFunction(Register object); - - // Abort execution if argument is not a JSGeneratorObject, - // enabled via --debug-code. - void AssertGeneratorObject(Register object); - - // Abort execution if argument is not undefined or an AllocationSite, enabled - // via --debug-code. - void AssertUndefinedOrAllocationSite(Register object); - - // --------------------------------------------------------------------------- - // Exception handling - - // Push a new stack handler and link it into stack handler chain. - void PushStackHandler(); - - // Unlink the stack handler on top of the stack from the stack handler chain. - void PopStackHandler(); - - // --------------------------------------------------------------------------- - // Inline caching support - - void GetNumberHash(Register r0, Register scratch); - - // --------------------------------------------------------------------------- - // Allocation support - - // Allocate an object in new space or old space. If the given space - // is exhausted control continues at the gc_required label. The allocated - // object is returned in result and end of the new object is returned in - // result_end. The register scratch can be passed as no_reg in which case - // an additional object reference will be added to the reloc info. The - // returned pointers in result and result_end have not yet been tagged as - // heap objects. If result_contains_top_on_entry is true the content of - // result is known to be the allocation top on entry (could be result_end - // from a previous call). If result_contains_top_on_entry is true scratch - // should be no_reg as it is never used. - void Allocate(int object_size, Register result, Register result_end, - Register scratch, Label* gc_required, AllocationFlags flags); - - void Allocate(int header_size, ScaleFactor element_size, - Register element_count, RegisterValueType element_count_type, - Register result, Register result_end, Register scratch, - Label* gc_required, AllocationFlags flags); - - void Allocate(Register object_size, Register result, Register result_end, - Register scratch, Label* gc_required, AllocationFlags flags); - - // Allocate a heap number in new space with undefined value. The - // register scratch2 can be passed as no_reg; the others must be - // valid registers. Returns tagged pointer in result register, or - // jumps to gc_required if new space is full. - void AllocateHeapNumber(Register result, Register scratch1, Register scratch2, - Label* gc_required, MutableMode mode = IMMUTABLE); - - // Allocate and initialize a JSValue wrapper with the specified {constructor} - // and {value}. - void AllocateJSValue(Register result, Register constructor, Register value, - Register scratch, Label* gc_required); - - // Initialize fields with filler values. Fields starting at |current_address| - // not including |end_address| are overwritten with the value in |filler|. At - // the end the loop, |current_address| takes the value of |end_address|. - void InitializeFieldsWithFiller(Register current_address, - Register end_address, Register filler); - - // --------------------------------------------------------------------------- - // Support functions. - - // Check a boolean-bit of a Smi field. - void BooleanBitTest(Register object, int field_offset, int bit_index); - - // Machine code version of Map::GetConstructor(). - // |temp| holds |result|'s map when done. - void GetMapConstructor(Register result, Register map, Register temp); - - // --------------------------------------------------------------------------- - // Runtime calls - - // Call a code stub. Generate the code if necessary. - void CallStub(CodeStub* stub, TypeFeedbackId ast_id = TypeFeedbackId::None()); - - // Tail call a code stub (jump). Generate the code if necessary. - void TailCallStub(CodeStub* stub); - - // Call a runtime routine. - void CallRuntime(const Runtime::Function* f, int num_arguments, - SaveFPRegsMode save_doubles = kDontSaveFPRegs); - void CallRuntimeSaveDoubles(Runtime::FunctionId fid) { - const Runtime::Function* function = Runtime::FunctionForId(fid); - CallRuntime(function, function->nargs, kSaveFPRegs); - } - - // Convenience function: Same as above, but takes the fid instead. - void CallRuntime(Runtime::FunctionId fid, - SaveFPRegsMode save_doubles = kDontSaveFPRegs) { - const Runtime::Function* function = Runtime::FunctionForId(fid); - CallRuntime(function, function->nargs, save_doubles); - } - - // Convenience function: Same as above, but takes the fid instead. - void CallRuntime(Runtime::FunctionId fid, int num_arguments, - SaveFPRegsMode save_doubles = kDontSaveFPRegs) { - CallRuntime(Runtime::FunctionForId(fid), num_arguments, save_doubles); - } - - // Convenience function: call an external reference. - void CallExternalReference(ExternalReference ref, int num_arguments); - - // Convenience function: tail call a runtime routine (jump). - void TailCallRuntime(Runtime::FunctionId fid); - - // 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. - // Some compilers/platforms require the stack to be aligned when calling - // C++ code. - // Needs a scratch register to do some arithmetic. This register will be - // trashed. - void PrepareCallCFunction(int num_arguments, Register scratch); - - // Calls a C function and cleans up the space for arguments allocated - // by PrepareCallCFunction. The called function is not allowed to trigger a - // garbage collection, since that might move the code and invalidate the - // return address (unless this is somehow accounted for by the called - // function). - void CallCFunction(ExternalReference function, int num_arguments); - void CallCFunction(Register function, int num_arguments); - - // Jump to a runtime routine. - void JumpToExternalReference(const ExternalReference& ext, - bool builtin_exit_frame = false); - - // --------------------------------------------------------------------------- - // Utilities - - void Ret(); - - // Return and drop arguments from stack, where the number of arguments - // may be bigger than 2^16 - 1. Requires a scratch register. - void Ret(int bytes_dropped, Register scratch); - - // Emit code that loads |parameter_index|'th parameter from the stack to - // the register according to the CallInterfaceDescriptor definition. - // |sp_to_caller_sp_offset_in_words| specifies the number of words pushed - // below the caller's sp (on x87 it's at least return address). - template - void LoadParameterFromStack( - Register reg, typename Descriptor::ParameterIndices parameter_index, - int sp_to_ra_offset_in_words = 1) { - DCHECK(Descriptor::kPassLastArgsOnStack); - DCHECK_LT(parameter_index, Descriptor::kParameterCount); - DCHECK_LE(Descriptor::kParameterCount - Descriptor::kStackArgumentsCount, - parameter_index); - int offset = (Descriptor::kParameterCount - parameter_index - 1 + - sp_to_ra_offset_in_words) * - kPointerSize; - mov(reg, Operand(esp, offset)); - } - - // Emit code to discard a non-negative number of pointer-sized elements - // from the stack, clobbering only the esp register. - void Drop(int element_count); - - void Call(Label* target) { call(target); } - void Call(Handle target, RelocInfo::Mode rmode, - TypeFeedbackId id = TypeFeedbackId::None()) { - call(target, rmode, id); - } - void Jump(Handle target, RelocInfo::Mode rmode) { jmp(target, rmode); } - void Push(Register src) { push(src); } - void Push(const Operand& src) { push(src); } - void Push(Immediate value) { push(value); } - void Pop(Register dst) { pop(dst); } - void Pop(const Operand& dst) { pop(dst); } - void PushReturnAddressFrom(Register src) { push(src); } - void PopReturnAddressTo(Register dst) { pop(dst); } - - void Lzcnt(Register dst, Register src) { Lzcnt(dst, Operand(src)); } - void Lzcnt(Register dst, const Operand& src); - - void Tzcnt(Register dst, Register src) { Tzcnt(dst, Operand(src)); } - void Tzcnt(Register dst, const Operand& src); - - void Popcnt(Register dst, Register src) { Popcnt(dst, Operand(src)); } - void Popcnt(Register dst, const Operand& src); - - // Move if the registers are not identical. - void Move(Register target, Register source); - - // Move a constant into a destination using the most efficient encoding. - void Move(Register dst, const Immediate& x); - void Move(const Operand& dst, const Immediate& x); - - void Move(Register dst, Handle handle) { LoadObject(dst, handle); } - void Move(Register dst, Smi* source) { Move(dst, Immediate(source)); } - - // Push a handle value. - void Push(Handle handle) { push(Immediate(handle)); } - void Push(Smi* smi) { Push(Immediate(smi)); } - - Handle CodeObject() { - DCHECK(!code_object_.is_null()); - return code_object_; - } - - // Insert code to verify that the x87 stack has the specified depth (0-7) - void VerifyX87StackDepth(uint32_t depth); - - // Emit code for a truncating division by a constant. The dividend register is - // unchanged, the result is in edx, and eax gets clobbered. - void TruncatingDiv(Register dividend, int32_t divisor); - - // --------------------------------------------------------------------------- - // StatsCounter support - - void SetCounter(StatsCounter* counter, int value); - void IncrementCounter(StatsCounter* counter, int value); - void DecrementCounter(StatsCounter* counter, int value); - void IncrementCounter(Condition cc, StatsCounter* counter, int value); - void DecrementCounter(Condition cc, StatsCounter* counter, int value); - - // --------------------------------------------------------------------------- - // Debugging - - // Calls Abort(msg) if the condition cc is not satisfied. - // Use --debug_code to enable. - void Assert(Condition cc, BailoutReason reason); - - // Like Assert(), but always enabled. - void Check(Condition cc, BailoutReason reason); - - // Print a message to stdout and abort execution. - void Abort(BailoutReason reason); - - // Check that the stack is aligned. - void CheckStackAlignment(); - - // Verify restrictions about code generated in stubs. - void set_generating_stub(bool value) { generating_stub_ = value; } - bool generating_stub() { return generating_stub_; } - void set_has_frame(bool value) { has_frame_ = value; } - bool has_frame() { return has_frame_; } - inline bool AllowThisStubCall(CodeStub* stub); - - // --------------------------------------------------------------------------- - // String utilities. - - // Checks if both objects are sequential one-byte strings, and jumps to label - // if either is not. - void JumpIfNotBothSequentialOneByteStrings( - Register object1, Register object2, Register scratch1, Register scratch2, - Label* on_not_flat_one_byte_strings); - - // Checks if the given register or operand is a unique name - void JumpIfNotUniqueNameInstanceType(Register reg, Label* not_unique_name, - Label::Distance distance = Label::kFar) { - JumpIfNotUniqueNameInstanceType(Operand(reg), not_unique_name, distance); - } - - void JumpIfNotUniqueNameInstanceType(Operand operand, Label* not_unique_name, - Label::Distance distance = Label::kFar); - - void EmitSeqStringSetCharCheck(Register string, Register index, - Register value, uint32_t encoding_mask); - - static int SafepointRegisterStackIndex(Register reg) { - return SafepointRegisterStackIndex(reg.code()); - } - - // Load the type feedback vector from a JavaScript frame. - void EmitLoadFeedbackVector(Register vector); - - // Activation support. - void EnterFrame(StackFrame::Type type); - void EnterFrame(StackFrame::Type type, bool load_constant_pool_pointer_reg); - void LeaveFrame(StackFrame::Type type); - - void EnterBuiltinFrame(Register context, Register target, Register argc); - void LeaveBuiltinFrame(Register context, Register target, Register argc); - - // Expects object in eax and returns map with validated enum cache - // in eax. Assumes that any other register can be used as a scratch. - void CheckEnumCache(Label* call_runtime); - - // AllocationMemento support. Arrays may have an associated - // AllocationMemento object that can be checked for in order to pretransition - // to another type. - // On entry, receiver_reg should point to the array object. - // scratch_reg gets clobbered. - // If allocation info is present, conditional code is set to equal. - void TestJSArrayForAllocationMemento(Register receiver_reg, - Register scratch_reg, - Label* no_memento_found); - - private: - bool generating_stub_; - bool has_frame_; - Isolate* isolate_; - // This handle will be patched with the code object on installation. - Handle code_object_; - - // Helper functions for generating invokes. - void InvokePrologue(const ParameterCount& expected, - const ParameterCount& actual, Label* done, - bool* definitely_mismatches, InvokeFlag flag, - Label::Distance done_distance, - const CallWrapper& call_wrapper); - - void EnterExitFramePrologue(StackFrame::Type frame_type); - void EnterExitFrameEpilogue(int argc, bool save_doubles); - - void LeaveExitFrameEpilogue(bool restore_context); - - // Allocation support helpers. - void LoadAllocationTopHelper(Register result, Register scratch, - AllocationFlags flags); - - void UpdateAllocationTopHelper(Register result_end, Register scratch, - AllocationFlags flags); - - // Helper for implementing JumpIfNotInNewSpace and JumpIfInNewSpace. - void InNewSpace(Register object, Register scratch, Condition cc, - Label* condition_met, - Label::Distance condition_met_distance = Label::kFar); - - // Helper for finding the mark bits for an address. Afterwards, the - // bitmap register points at the word with the mark bits and the mask - // the position of the first bit. Uses ecx as scratch and leaves addr_reg - // unchanged. - inline void GetMarkBits(Register addr_reg, Register bitmap_reg, - Register mask_reg); - - // Compute memory operands for safepoint stack slots. - Operand SafepointRegisterSlot(Register reg); - static int SafepointRegisterStackIndex(int reg_code); - - // Needs access to SafepointRegisterStackIndex for compiled frame - // traversal. - friend class StandardFrame; -}; - -// The code patcher is used to patch (typically) small parts of code e.g. for -// debugging and other types of instrumentation. When using the code patcher -// the exact number of bytes specified must be emitted. Is not legal to emit -// relocation information. If any of these constraints are violated it causes -// an assertion. -class CodePatcher { - public: - CodePatcher(Isolate* isolate, byte* address, int size); - ~CodePatcher(); - - // Macro assembler to emit code. - MacroAssembler* masm() { return &masm_; } - - private: - byte* address_; // The address of the code being patched. - int size_; // Number of bytes of the expected patch size. - MacroAssembler masm_; // Macro assembler used to generate the code. -}; - -// ----------------------------------------------------------------------------- -// Static helper functions. - -// Generate an Operand for loading a field from an object. -inline Operand FieldOperand(Register object, int offset) { - return Operand(object, offset - kHeapObjectTag); -} - -// Generate an Operand for loading an indexed field from an object. -inline Operand FieldOperand(Register object, Register index, ScaleFactor scale, - int offset) { - return Operand(object, index, scale, offset - kHeapObjectTag); -} - -inline Operand FixedArrayElementOperand(Register array, Register index_as_smi, - int additional_offset = 0) { - int offset = FixedArray::kHeaderSize + additional_offset * kPointerSize; - return FieldOperand(array, index_as_smi, times_half_pointer_size, offset); -} - -inline Operand ContextOperand(Register context, int index) { - return Operand(context, Context::SlotOffset(index)); -} - -inline Operand ContextOperand(Register context, Register index) { - return Operand(context, index, times_pointer_size, Context::SlotOffset(0)); -} - -inline Operand NativeContextOperand() { - return ContextOperand(esi, Context::NATIVE_CONTEXT_INDEX); -} - -#define ACCESS_MASM(masm) masm-> - -} // namespace internal -} // namespace v8 - -#endif // V8_X87_MACRO_ASSEMBLER_X87_H_ diff --git a/src/x87/simulator-x87.cc b/src/x87/simulator-x87.cc deleted file mode 100644 index cb5652b581..0000000000 --- a/src/x87/simulator-x87.cc +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2008 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. - -#include "src/x87/simulator-x87.h" - -// Since there is no simulator for the ia32 architecture this file is empty. diff --git a/src/x87/simulator-x87.h b/src/x87/simulator-x87.h deleted file mode 100644 index 667f0fd6d7..0000000000 --- a/src/x87/simulator-x87.h +++ /dev/null @@ -1,52 +0,0 @@ -// 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. - -#ifndef V8_X87_SIMULATOR_X87_H_ -#define V8_X87_SIMULATOR_X87_H_ - -#include "src/allocation.h" - -namespace v8 { -namespace internal { - -// 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(isolate, entry, p0, p1, p2, p3, p4) \ - (entry(p0, p1, p2, p3, p4)) - - -typedef int (*regexp_matcher)(String*, int, const byte*, - const byte*, int*, int, Address, int, Isolate*); - -// Call the generated regexp code directly. The code at the entry address should -// expect eight int/pointer sized arguments and return an int. -#define CALL_GENERATED_REGEXP_CODE(isolate, entry, p0, p1, p2, p3, p4, p5, p6, \ - p7, p8) \ - (FUNCTION_CAST(entry)(p0, p1, p2, p3, p4, p5, p6, p7, p8)) - - -// 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(Isolate* isolate, - uintptr_t c_limit) { - USE(isolate); - return c_limit; - } - - static inline uintptr_t RegisterCTryCatch(Isolate* isolate, - uintptr_t try_catch_address) { - USE(isolate); - return try_catch_address; - } - - static inline void UnregisterCTryCatch(Isolate* isolate) { USE(isolate); } -}; - -} // namespace internal -} // namespace v8 - -#endif // V8_X87_SIMULATOR_X87_H_ diff --git a/test/cctest/BUILD.gn b/test/cctest/BUILD.gn index 406180f441..24fd69f44e 100644 --- a/test/cctest/BUILD.gn +++ b/test/cctest/BUILD.gn @@ -287,17 +287,6 @@ v8_executable("cctest") { "test-macro-assembler-x64.cc", "test-run-wasm-relocation-x64.cc", ] - } else if (v8_current_cpu == "x87") { - sources += [ ### gcmole(arch:x87) ### - "test-assembler-x87.cc", - "test-code-stubs-x87.cc", - "test-code-stubs.cc", - "test-code-stubs.h", - "test-disasm-x87.cc", - "test-log-stack-tracer.cc", - "test-macro-assembler-x87.cc", - "test-run-wasm-relocation-x87.cc", - ] } else if (v8_current_cpu == "ppc" || v8_current_cpu == "ppc64") { sources += [ ### gcmole(arch:ppc) ### "test-assembler-ppc.cc", diff --git a/test/cctest/OWNERS b/test/cctest/OWNERS index 92ab93045f..f8bf2773cf 100644 --- a/test/cctest/OWNERS +++ b/test/cctest/OWNERS @@ -11,5 +11,3 @@ per-file *-s390*=joransiu@ca.ibm.com per-file *-s390*=jyan@ca.ibm.com per-file *-s390*=mbrandy@us.ibm.com per-file *-s390*=michael_dawson@ca.ibm.com -per-file *-x87*=chunyang.dai@intel.com -per-file *-x87*=weiliang.lin@intel.com diff --git a/test/cctest/cctest.gyp b/test/cctest/cctest.gyp index 350d885e9e..639c4a2c44 100644 --- a/test/cctest/cctest.gyp +++ b/test/cctest/cctest.gyp @@ -308,16 +308,6 @@ 'test-disasm-mips64.cc', 'test-macro-assembler-mips64.cc', ], - 'cctest_sources_x87': [ ### gcmole(arch:x87) ### - 'test-assembler-x87.cc', - 'test-code-stubs.cc', - 'test-code-stubs.h', - 'test-code-stubs-x87.cc', - 'test-disasm-x87.cc', - 'test-macro-assembler-x87.cc', - 'test-log-stack-tracer.cc', - 'test-run-wasm-relocation-x87.cc', - ], }, 'includes': ['../../gypfiles/toolchain.gypi', '../../gypfiles/features.gypi'], 'targets': [ @@ -402,11 +392,6 @@ '<@(cctest_sources_mips64el)', ], }], - ['v8_target_arch=="x87"', { - 'sources': [ - '<@(cctest_sources_x87)', - ], - }], [ 'OS=="linux" or OS=="qnx"', { 'sources': [ 'test-platform-linux.cc', diff --git a/test/cctest/cctest.status b/test/cctest/cctest.status index 0ffd53285c..f3f5e7bfcc 100644 --- a/test/cctest/cctest.status +++ b/test/cctest/cctest.status @@ -291,31 +291,6 @@ 'test-run-wasm-simd/*': [SKIP], }], # 'arch == mips or arch == mipsel or arch == mips64 or arch == mips64el' -############################################################################## -['arch == x87', { - 'test-run-machops/RunFloat64InsertLowWord32': [SKIP], - 'test-run-native-calls/MixedParams_0': [SKIP], - 'test-run-native-calls/MixedParams_1': [SKIP], - 'test-run-native-calls/MixedParams_2': [SKIP], - 'test-run-native-calls/MixedParams_3': [SKIP], - 'test-run-machops/RunFloat64MulAndFloat64Add1': [SKIP], - 'test-run-machops/RunFloat64MulAndFloat64Add2': [SKIP], - 'test-run-machops/RunFloat64MulAndFloat64Sub1': [SKIP], - 'test-run-machops/RunFloat64MulAndFloat64Sub2': [SKIP], - 'test-run-machops/RunFloat64Sin': [SKIP], - 'test-run-machops/RunFloat64Cos': [SKIP], - 'test-run-machops/RunFloat64Expm1': [SKIP], - 'test-run-machops/RunFloat64Tan': [SKIP], - 'test-cpu-profiler/Inlining': [SKIP], - 'test-gap-resolver/FuzzResolver': [SKIP], - 'test-run-wasm/RunWasmCompiled_MultiReturnSelect_f32': [SKIP], - 'test-run-wasm/RunWasmCompiled_MultiReturnSelect_f64': [SKIP], - 'test-run-wasm/RunWasmCompiled_SignallingNanSurvivesI32ReinterpretF32': [SKIP], - 'test-run-wasm-64/RunWasmCompiled_SignallingNanSurvivesI64ReinterpretF64': [SKIP], - 'test-run-wasm/RunWasmInterpreted_SignallingNanSurvivesI32ReinterpretF32': [SKIP], - 'test-run-wasm-64/RunWasmInterpreted_SignallingNanSurvivesI64ReinterpretF64': [SKIP], -}], # 'arch == x87' - ############################################################################## ['arch == android_arm or arch == android_ia32', { diff --git a/test/cctest/test-assembler-x87.cc b/test/cctest/test-assembler-x87.cc deleted file mode 100644 index d8285290fa..0000000000 --- a/test/cctest/test-assembler-x87.cc +++ /dev/null @@ -1,451 +0,0 @@ -// Copyright 2011 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 - -#include "src/v8.h" - -#include "src/base/platform/platform.h" -#include "src/base/utils/random-number-generator.h" -#include "src/disassembler.h" -#include "src/factory.h" -#include "src/macro-assembler.h" -#include "src/ostreams.h" -#include "test/cctest/cctest.h" - -using namespace v8::internal; - - -typedef int (*F0)(); -typedef int (*F1)(int x); -typedef int (*F2)(int x, int y); - - -#define __ assm. - -TEST(AssemblerIa320) { - CcTest::InitializeVM(); - Isolate* isolate = reinterpret_cast(CcTest::isolate()); - HandleScope scope(isolate); - - v8::internal::byte buffer[256]; - Assembler assm(isolate, buffer, sizeof buffer); - - __ mov(eax, Operand(esp, 4)); - __ add(eax, Operand(esp, 8)); - __ ret(0); - - CodeDesc desc; - assm.GetCode(&desc); - Handle code = isolate->factory()->NewCode( - desc, Code::ComputeFlags(Code::STUB), Handle()); -#ifdef OBJECT_PRINT - OFStream os(stdout); - code->Print(os); -#endif - F2 f = FUNCTION_CAST(code->entry()); - int res = f(3, 4); - ::printf("f() = %d\n", res); - CHECK_EQ(7, res); -} - - -TEST(AssemblerIa321) { - CcTest::InitializeVM(); - Isolate* isolate = reinterpret_cast(CcTest::isolate()); - HandleScope scope(isolate); - - v8::internal::byte buffer[256]; - Assembler assm(isolate, buffer, sizeof buffer); - Label L, C; - - __ mov(edx, Operand(esp, 4)); - __ xor_(eax, eax); // clear eax - __ jmp(&C); - - __ bind(&L); - __ add(eax, edx); - __ sub(edx, Immediate(1)); - - __ bind(&C); - __ test(edx, edx); - __ j(not_zero, &L); - __ ret(0); - - CodeDesc desc; - assm.GetCode(&desc); - Handle code = isolate->factory()->NewCode( - desc, Code::ComputeFlags(Code::STUB), Handle()); -#ifdef OBJECT_PRINT - OFStream os(stdout); - code->Print(os); -#endif - F1 f = FUNCTION_CAST(code->entry()); - int res = f(100); - ::printf("f() = %d\n", res); - CHECK_EQ(5050, res); -} - - -TEST(AssemblerIa322) { - CcTest::InitializeVM(); - Isolate* isolate = reinterpret_cast(CcTest::isolate()); - HandleScope scope(isolate); - - v8::internal::byte buffer[256]; - Assembler assm(isolate, buffer, sizeof buffer); - Label L, C; - - __ mov(edx, Operand(esp, 4)); - __ mov(eax, 1); - __ jmp(&C); - - __ bind(&L); - __ imul(eax, edx); - __ sub(edx, Immediate(1)); - - __ bind(&C); - __ test(edx, edx); - __ j(not_zero, &L); - __ ret(0); - - // some relocated stuff here, not executed - __ mov(eax, isolate->factory()->true_value()); - __ jmp(NULL, RelocInfo::RUNTIME_ENTRY); - - CodeDesc desc; - assm.GetCode(&desc); - Handle code = isolate->factory()->NewCode( - desc, Code::ComputeFlags(Code::STUB), Handle()); -#ifdef OBJECT_PRINT - OFStream os(stdout); - code->Print(os); -#endif - F1 f = FUNCTION_CAST(code->entry()); - int res = f(10); - ::printf("f() = %d\n", res); - CHECK_EQ(3628800, res); -} - - -typedef int (*F3)(float x); - -typedef int (*F4)(double x); - -static int baz = 42; -TEST(AssemblerIa325) { - CcTest::InitializeVM(); - Isolate* isolate = reinterpret_cast(CcTest::isolate()); - HandleScope scope(isolate); - - v8::internal::byte buffer[256]; - Assembler assm(isolate, buffer, sizeof buffer); - - __ mov(eax, Operand(reinterpret_cast(&baz), RelocInfo::NONE32)); - __ ret(0); - - CodeDesc desc; - assm.GetCode(&desc); - Handle code = isolate->factory()->NewCode( - desc, Code::ComputeFlags(Code::STUB), Handle()); - F0 f = FUNCTION_CAST(code->entry()); - int res = f(); - CHECK_EQ(42, res); -} - - -typedef int (*F7)(double x, double y); - -TEST(AssemblerIa329) { - CcTest::InitializeVM(); - Isolate* isolate = reinterpret_cast(CcTest::isolate()); - HandleScope scope(isolate); - v8::internal::byte buffer[256]; - MacroAssembler assm(isolate, buffer, sizeof(buffer), - v8::internal::CodeObjectRequired::kYes); - enum { kEqual = 0, kGreater = 1, kLess = 2, kNaN = 3, kUndefined = 4 }; - Label equal_l, less_l, greater_l, nan_l; - __ fld_d(Operand(esp, 3 * kPointerSize)); - __ fld_d(Operand(esp, 1 * kPointerSize)); - __ FCmp(); - __ j(parity_even, &nan_l); - __ j(equal, &equal_l); - __ j(below, &less_l); - __ j(above, &greater_l); - - __ mov(eax, kUndefined); - __ ret(0); - - __ bind(&equal_l); - __ mov(eax, kEqual); - __ ret(0); - - __ bind(&greater_l); - __ mov(eax, kGreater); - __ ret(0); - - __ bind(&less_l); - __ mov(eax, kLess); - __ ret(0); - - __ bind(&nan_l); - __ mov(eax, kNaN); - __ ret(0); - - - CodeDesc desc; - assm.GetCode(&desc); - Handle code = isolate->factory()->NewCode( - desc, Code::ComputeFlags(Code::STUB), Handle()); -#ifdef OBJECT_PRINT - OFStream os(stdout); - code->Print(os); -#endif - - F7 f = FUNCTION_CAST(code->entry()); - CHECK_EQ(kLess, f(1.1, 2.2)); - CHECK_EQ(kEqual, f(2.2, 2.2)); - CHECK_EQ(kGreater, f(3.3, 2.2)); - CHECK_EQ(kNaN, f(std::numeric_limits::quiet_NaN(), 1.1)); -} - - -TEST(AssemblerIa3210) { - // Test chaining of label usages within instructions (issue 1644). - CcTest::InitializeVM(); - Isolate* isolate = reinterpret_cast(CcTest::isolate()); - HandleScope scope(isolate); - Assembler assm(isolate, NULL, 0); - - Label target; - __ j(equal, &target); - __ j(not_equal, &target); - __ bind(&target); - __ nop(); -} - - -TEST(AssemblerMultiByteNop) { - CcTest::InitializeVM(); - Isolate* isolate = reinterpret_cast(CcTest::isolate()); - HandleScope scope(isolate); - v8::internal::byte buffer[1024]; - Assembler assm(isolate, buffer, sizeof(buffer)); - __ push(ebx); - __ push(ecx); - __ push(edx); - __ push(edi); - __ push(esi); - __ mov(eax, 1); - __ mov(ebx, 2); - __ mov(ecx, 3); - __ mov(edx, 4); - __ mov(edi, 5); - __ mov(esi, 6); - for (int i = 0; i < 16; i++) { - int before = assm.pc_offset(); - __ Nop(i); - CHECK_EQ(assm.pc_offset() - before, i); - } - - Label fail; - __ cmp(eax, 1); - __ j(not_equal, &fail); - __ cmp(ebx, 2); - __ j(not_equal, &fail); - __ cmp(ecx, 3); - __ j(not_equal, &fail); - __ cmp(edx, 4); - __ j(not_equal, &fail); - __ cmp(edi, 5); - __ j(not_equal, &fail); - __ cmp(esi, 6); - __ j(not_equal, &fail); - __ mov(eax, 42); - __ pop(esi); - __ pop(edi); - __ pop(edx); - __ pop(ecx); - __ pop(ebx); - __ ret(0); - __ bind(&fail); - __ mov(eax, 13); - __ pop(esi); - __ pop(edi); - __ pop(edx); - __ pop(ecx); - __ pop(ebx); - __ ret(0); - - CodeDesc desc; - assm.GetCode(&desc); - Handle code = isolate->factory()->NewCode( - desc, Code::ComputeFlags(Code::STUB), Handle()); - CHECK(code->IsCode()); - - F0 f = FUNCTION_CAST(code->entry()); - int res = f(); - CHECK_EQ(42, res); -} - - -TEST(AssemblerIa32JumpTables1) { - // Test jump tables with forward jumps. - CcTest::InitializeVM(); - Isolate* isolate = reinterpret_cast(CcTest::isolate()); - HandleScope scope(isolate); - Assembler assm(isolate, nullptr, 0); - - const int kNumCases = 512; - int values[kNumCases]; - isolate->random_number_generator()->NextBytes(values, sizeof(values)); - Label labels[kNumCases]; - - Label done, table; - __ mov(eax, Operand(esp, 4)); - __ jmp(Operand::JumpTable(eax, times_4, &table)); - __ ud2(); - __ bind(&table); - for (int i = 0; i < kNumCases; ++i) { - __ dd(&labels[i]); - } - - for (int i = 0; i < kNumCases; ++i) { - __ bind(&labels[i]); - __ mov(eax, Immediate(values[i])); - __ jmp(&done); - } - - __ bind(&done); - __ ret(0); - - CodeDesc desc; - assm.GetCode(&desc); - Handle code = isolate->factory()->NewCode( - desc, Code::ComputeFlags(Code::STUB), Handle()); -#ifdef OBJECT_PRINT - OFStream os(stdout); - code->Print(os); -#endif - F1 f = FUNCTION_CAST(code->entry()); - for (int i = 0; i < kNumCases; ++i) { - int res = f(i); - ::printf("f(%d) = %d\n", i, res); - CHECK_EQ(values[i], res); - } -} - - -TEST(AssemblerIa32JumpTables2) { - // Test jump tables with backward jumps. - CcTest::InitializeVM(); - Isolate* isolate = reinterpret_cast(CcTest::isolate()); - HandleScope scope(isolate); - Assembler assm(isolate, nullptr, 0); - - const int kNumCases = 512; - int values[kNumCases]; - isolate->random_number_generator()->NextBytes(values, sizeof(values)); - Label labels[kNumCases]; - - Label done, table; - __ mov(eax, Operand(esp, 4)); - __ jmp(Operand::JumpTable(eax, times_4, &table)); - __ ud2(); - - for (int i = 0; i < kNumCases; ++i) { - __ bind(&labels[i]); - __ mov(eax, Immediate(values[i])); - __ jmp(&done); - } - - __ bind(&table); - for (int i = 0; i < kNumCases; ++i) { - __ dd(&labels[i]); - } - - __ bind(&done); - __ ret(0); - - CodeDesc desc; - assm.GetCode(&desc); - Handle code = isolate->factory()->NewCode( - desc, Code::ComputeFlags(Code::STUB), Handle()); -#ifdef OBJECT_PRINT - OFStream os(stdout); - code->Print(os); -#endif - F1 f = FUNCTION_CAST(code->entry()); - for (int i = 0; i < kNumCases; ++i) { - int res = f(i); - ::printf("f(%d) = %d\n", i, res); - CHECK_EQ(values[i], res); - } -} - -TEST(Regress621926) { - // Bug description: - // The opcodes for cmpw r/m16, r16 and cmpw r16, r/m16 were swapped. - // This was causing non-commutative comparisons to produce the wrong result. - CcTest::InitializeVM(); - Isolate* isolate = reinterpret_cast(CcTest::isolate()); - HandleScope scope(isolate); - Assembler assm(isolate, nullptr, 0); - - uint16_t a = 42; - - Label fail; - __ push(ebx); - __ mov(ebx, Immediate(reinterpret_cast(&a))); - __ mov(eax, Immediate(41)); - __ cmpw(eax, Operand(ebx, 0)); - __ j(above_equal, &fail); - __ cmpw(Operand(ebx, 0), eax); - __ j(below_equal, &fail); - __ mov(eax, 1); - __ pop(ebx); - __ ret(0); - __ bind(&fail); - __ mov(eax, 0); - __ pop(ebx); - __ ret(0); - - CodeDesc desc; - assm.GetCode(&desc); - Handle code = isolate->factory()->NewCode( - desc, Code::ComputeFlags(Code::STUB), Handle()); - -#ifdef OBJECT_PRINT - OFStream os(stdout); - code->Print(os); -#endif - - F0 f = FUNCTION_CAST(code->entry()); - CHECK_EQ(1, f()); -} - -#undef __ diff --git a/test/cctest/test-code-stubs-x87.cc b/test/cctest/test-code-stubs-x87.cc deleted file mode 100644 index 5a420b1a80..0000000000 --- a/test/cctest/test-code-stubs-x87.cc +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright 2013 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 - -#include - -#include "src/v8.h" - -#include "src/base/platform/platform.h" -#include "src/code-stubs.h" -#include "src/factory.h" -#include "src/macro-assembler.h" -#include "test/cctest/cctest.h" -#include "test/cctest/test-code-stubs.h" - -using namespace v8::internal; - -#define __ assm. - -ConvertDToIFunc MakeConvertDToIFuncTrampoline(Isolate* isolate, - Register source_reg, - Register destination_reg) { - // Allocate an executable page of memory. - size_t actual_size; - byte* buffer = static_cast(v8::base::OS::Allocate( - Assembler::kMinimalBufferSize, &actual_size, true)); - CHECK(buffer); - HandleScope handles(isolate); - MacroAssembler assm(isolate, buffer, static_cast(actual_size), - v8::internal::CodeObjectRequired::kYes); - int offset = - source_reg.is(esp) ? 0 : (HeapNumber::kValueOffset - kSmiTagSize); - DoubleToIStub stub(isolate, source_reg, destination_reg, offset, true); - byte* start = stub.GetCode()->instruction_start(); - - __ push(ebx); - __ push(ecx); - __ push(edx); - __ push(esi); - __ push(edi); - - if (!source_reg.is(esp)) { - __ lea(source_reg, MemOperand(esp, 6 * kPointerSize - offset)); - } - - int param_offset = 7 * kPointerSize; - // Save registers make sure they don't get clobbered. - int reg_num = 0; - for (; reg_num < Register::kNumRegisters; ++reg_num) { - if (RegisterConfiguration::Crankshaft()->IsAllocatableGeneralCode( - reg_num)) { - Register reg = Register::from_code(reg_num); - if (!reg.is(esp) && !reg.is(ebp) && !reg.is(destination_reg)) { - __ push(reg); - param_offset += kPointerSize; - } - } - } - - // Re-push the double argument - __ push(MemOperand(esp, param_offset)); - __ push(MemOperand(esp, param_offset)); - - // Call through to the actual stub - __ call(start, RelocInfo::EXTERNAL_REFERENCE); - - __ add(esp, Immediate(kDoubleSize)); - - // Make sure no registers have been unexpectedly clobbered - for (--reg_num; reg_num >= 0; --reg_num) { - if (RegisterConfiguration::Crankshaft()->IsAllocatableGeneralCode( - reg_num)) { - Register reg = Register::from_code(reg_num); - if (!reg.is(esp) && !reg.is(ebp) && !reg.is(destination_reg)) { - __ cmp(reg, MemOperand(esp, 0)); - __ Assert(equal, kRegisterWasClobbered); - __ add(esp, Immediate(kPointerSize)); - } - } - } - - __ mov(eax, destination_reg); - - __ pop(edi); - __ pop(esi); - __ pop(edx); - __ pop(ecx); - __ pop(ebx); - - __ ret(kDoubleSize); - - CodeDesc desc; - assm.GetCode(&desc); - return reinterpret_cast( - reinterpret_cast(buffer)); -} - -#undef __ - - -static Isolate* GetIsolateFrom(LocalContext* context) { - return reinterpret_cast((*context)->GetIsolate()); -} - - -TEST(ConvertDToI) { - CcTest::InitializeVM(); - LocalContext context; - Isolate* isolate = GetIsolateFrom(&context); - HandleScope scope(isolate); - -#if DEBUG - // Verify that the tests actually work with the C version. In the release - // code, the compiler optimizes it away because it's all constant, but does it - // wrong, triggering an assert on gcc. - RunAllTruncationTests(&ConvertDToICVersion); -#endif - - Register source_registers[] = {esp, eax, ebx, ecx, edx, edi, esi}; - Register dest_registers[] = {eax, ebx, ecx, edx, edi, esi}; - - for (size_t s = 0; s < sizeof(source_registers) / sizeof(Register); s++) { - for (size_t d = 0; d < sizeof(dest_registers) / sizeof(Register); d++) { - RunAllTruncationTests( - MakeConvertDToIFuncTrampoline(isolate, - source_registers[s], - dest_registers[d])); - } - } -} diff --git a/test/cctest/test-disasm-x87.cc b/test/cctest/test-disasm-x87.cc deleted file mode 100644 index ace8db2b72..0000000000 --- a/test/cctest/test-disasm-x87.cc +++ /dev/null @@ -1,443 +0,0 @@ -// Copyright 2011 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 - -#include "src/v8.h" - -#include "src/code-factory.h" -#include "src/debug/debug.h" -#include "src/disasm.h" -#include "src/disassembler.h" -#include "src/macro-assembler.h" -#include "src/x87/frames-x87.h" -#include "test/cctest/cctest.h" - -using namespace v8::internal; - - -#define __ assm. - - -static void DummyStaticFunction(Object* result) { -} - - -TEST(DisasmIa320) { - CcTest::InitializeVM(); - Isolate* isolate = CcTest::i_isolate(); - HandleScope scope(isolate); - v8::internal::byte buffer[2048]; - Assembler assm(isolate, buffer, sizeof buffer); - DummyStaticFunction(NULL); // just bloody use it (DELETE; debugging) - - // Short immediate instructions - __ adc(eax, 12345678); - __ add(eax, Immediate(12345678)); - __ or_(eax, 12345678); - __ sub(eax, Immediate(12345678)); - __ xor_(eax, 12345678); - __ and_(eax, 12345678); - Handle foo = isolate->factory()->NewFixedArray(10, TENURED); - __ cmp(eax, foo); - - // ---- This one caused crash - __ mov(ebx, Operand(esp, ecx, times_2, 0)); // [esp+ecx*4] - - // ---- All instructions that I can think of - __ add(edx, ebx); - __ add(edx, Operand(12, RelocInfo::NONE32)); - __ add(edx, Operand(ebx, 0)); - __ add(edx, Operand(ebx, 16)); - __ add(edx, Operand(ebx, 1999)); - __ add(edx, Operand(ebx, -4)); - __ add(edx, Operand(ebx, -1999)); - __ add(edx, Operand(esp, 0)); - __ add(edx, Operand(esp, 16)); - __ add(edx, Operand(esp, 1999)); - __ add(edx, Operand(esp, -4)); - __ add(edx, Operand(esp, -1999)); - __ nop(); - __ add(esi, Operand(ecx, times_4, 0)); - __ add(esi, Operand(ecx, times_4, 24)); - __ add(esi, Operand(ecx, times_4, -4)); - __ add(esi, Operand(ecx, times_4, -1999)); - __ nop(); - __ add(edi, Operand(ebp, ecx, times_4, 0)); - __ add(edi, Operand(ebp, ecx, times_4, 12)); - __ add(edi, Operand(ebp, ecx, times_4, -8)); - __ add(edi, Operand(ebp, ecx, times_4, -3999)); - __ add(Operand(ebp, ecx, times_4, 12), Immediate(12)); - - __ nop(); - __ add(ebx, Immediate(12)); - __ nop(); - __ adc(edx, Operand(ebx)); - __ adc(ecx, 12); - __ adc(ecx, 1000); - __ nop(); - __ and_(edx, 3); - __ and_(edx, Operand(esp, 4)); - __ cmp(edx, 3); - __ cmp(edx, Operand(esp, 4)); - __ cmp(Operand(ebp, ecx, times_4, 0), Immediate(1000)); - Handle foo2 = isolate->factory()->NewFixedArray(10, TENURED); - __ cmp(ebx, foo2); - __ cmpb(ebx, Operand(ebp, ecx, times_2, 0)); - __ cmpb(Operand(ebp, ecx, times_2, 0), ebx); - __ or_(edx, 3); - __ xor_(edx, 3); - __ nop(); - __ cpuid(); - __ movsx_b(edx, ecx); - __ movsx_w(edx, ecx); - __ movzx_b(edx, ecx); - __ movzx_w(edx, ecx); - - __ nop(); - __ imul(edx, ecx); - __ shld(edx, ecx, 10); - __ shld_cl(edx, ecx); - __ shrd(edx, ecx, 10); - __ shrd_cl(edx, ecx); - __ bts(edx, ecx); - __ bts(Operand(ebx, ecx, times_4, 0), ecx); - __ nop(); - __ pushad(); - __ popad(); - __ pushfd(); - __ popfd(); - __ push(Immediate(12)); - __ push(Immediate(23456)); - __ push(ecx); - __ push(esi); - __ push(Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); - __ push(Operand(ebx, ecx, times_4, 0)); - __ push(Operand(ebx, ecx, times_4, 0)); - __ push(Operand(ebx, ecx, times_4, 10000)); - __ pop(edx); - __ pop(eax); - __ pop(Operand(ebx, ecx, times_4, 0)); - __ nop(); - - __ add(edx, Operand(esp, 16)); - __ add(edx, ecx); - __ mov_b(edx, ecx); - __ mov_b(ecx, 6); - __ mov_b(Operand(ebx, ecx, times_4, 10000), 6); - __ mov_b(Operand(esp, 16), edx); - __ mov_w(edx, Operand(esp, 16)); - __ mov_w(Operand(esp, 16), edx); - __ nop(); - __ movsx_w(edx, Operand(esp, 12)); - __ movsx_b(edx, Operand(esp, 12)); - __ movzx_w(edx, Operand(esp, 12)); - __ movzx_b(edx, Operand(esp, 12)); - __ nop(); - __ mov(edx, 1234567); - __ mov(edx, Operand(esp, 12)); - __ mov(Operand(ebx, ecx, times_4, 10000), Immediate(12345)); - __ mov(Operand(ebx, ecx, times_4, 10000), edx); - __ nop(); - __ dec_b(edx); - __ dec_b(Operand(eax, 10)); - __ dec_b(Operand(ebx, ecx, times_4, 10000)); - __ dec(edx); - __ cdq(); - - __ nop(); - __ idiv(edx); - __ idiv(Operand(edx, ecx, times_1, 1)); - __ idiv(Operand(esp, 12)); - __ div(edx); - __ div(Operand(edx, ecx, times_1, 1)); - __ div(Operand(esp, 12)); - __ mul(edx); - __ neg(edx); - __ not_(edx); - __ test(Operand(ebx, ecx, times_4, 10000), Immediate(123456)); - - __ imul(edx, Operand(ebx, ecx, times_4, 10000)); - __ imul(edx, ecx, 12); - __ imul(edx, Operand(edx, eax, times_2, 42), 8); - __ imul(edx, ecx, 1000); - __ imul(edx, Operand(ebx, ecx, times_4, 1), 9000); - - __ inc(edx); - __ inc(Operand(ebx, ecx, times_4, 10000)); - __ push(Operand(ebx, ecx, times_4, 10000)); - __ pop(Operand(ebx, ecx, times_4, 10000)); - __ call(Operand(ebx, ecx, times_4, 10000)); - __ jmp(Operand(ebx, ecx, times_4, 10000)); - - __ lea(edx, Operand(ebx, ecx, times_4, 10000)); - __ or_(edx, 12345); - __ or_(edx, Operand(ebx, ecx, times_4, 10000)); - - __ nop(); - - __ rcl(edx, 1); - __ rcl(edx, 7); - __ rcr(edx, 1); - __ rcr(edx, 7); - __ ror(edx, 1); - __ ror(edx, 6); - __ ror_cl(edx); - __ ror(Operand(ebx, ecx, times_4, 10000), 1); - __ ror(Operand(ebx, ecx, times_4, 10000), 6); - __ ror_cl(Operand(ebx, ecx, times_4, 10000)); - __ sar(edx, 1); - __ sar(edx, 6); - __ sar_cl(edx); - __ sar(Operand(ebx, ecx, times_4, 10000), 1); - __ sar(Operand(ebx, ecx, times_4, 10000), 6); - __ sar_cl(Operand(ebx, ecx, times_4, 10000)); - __ sbb(edx, Operand(ebx, ecx, times_4, 10000)); - __ shl(edx, 1); - __ shl(edx, 6); - __ shl_cl(edx); - __ shl(Operand(ebx, ecx, times_4, 10000), 1); - __ shl(Operand(ebx, ecx, times_4, 10000), 6); - __ shl_cl(Operand(ebx, ecx, times_4, 10000)); - __ shrd_cl(Operand(ebx, ecx, times_4, 10000), edx); - __ shr(edx, 1); - __ shr(edx, 7); - __ shr_cl(edx); - __ shr(Operand(ebx, ecx, times_4, 10000), 1); - __ shr(Operand(ebx, ecx, times_4, 10000), 6); - __ shr_cl(Operand(ebx, ecx, times_4, 10000)); - - - // Immediates - - __ adc(edx, 12345); - - __ add(ebx, Immediate(12)); - __ add(Operand(edx, ecx, times_4, 10000), Immediate(12)); - - __ and_(ebx, 12345); - - __ cmp(ebx, 12345); - __ cmp(ebx, Immediate(12)); - __ cmp(Operand(edx, ecx, times_4, 10000), Immediate(12)); - __ cmpb(eax, Immediate(100)); - - __ or_(ebx, 12345); - - __ sub(ebx, Immediate(12)); - __ sub(Operand(edx, ecx, times_4, 10000), Immediate(12)); - - __ xor_(ebx, 12345); - - __ imul(edx, ecx, 12); - __ imul(edx, ecx, 1000); - - __ cld(); - __ rep_movs(); - __ rep_stos(); - __ stos(); - - __ sub(edx, Operand(ebx, ecx, times_4, 10000)); - __ sub(edx, ebx); - - __ test(edx, Immediate(12345)); - __ test(edx, Operand(ebx, ecx, times_8, 10000)); - __ test(Operand(esi, edi, times_1, -20000000), Immediate(300000000)); - __ test_b(edx, Operand(ecx, ebx, times_2, 1000)); - __ test_b(Operand(eax, -20), Immediate(0x9A)); - __ nop(); - - __ xor_(edx, 12345); - __ xor_(edx, Operand(ebx, ecx, times_8, 10000)); - __ bts(Operand(ebx, ecx, times_8, 10000), edx); - __ hlt(); - __ int3(); - __ ret(0); - __ ret(8); - - // Calls - - Label L1, L2; - __ bind(&L1); - __ nop(); - __ call(&L1); - __ call(&L2); - __ nop(); - __ bind(&L2); - __ call(Operand(ebx, ecx, times_4, 10000)); - __ nop(); - Handle ic = isolate->builtins()->LoadIC(); - __ call(ic, RelocInfo::CODE_TARGET); - __ nop(); - __ call(FUNCTION_ADDR(DummyStaticFunction), RelocInfo::RUNTIME_ENTRY); - __ nop(); - - __ jmp(&L1); - __ jmp(Operand(ebx, ecx, times_4, 10000)); - ExternalReference after_break_target = - ExternalReference::debug_after_break_target_address(isolate); - __ jmp(Operand::StaticVariable(after_break_target)); - __ jmp(ic, RelocInfo::CODE_TARGET); - __ nop(); - - - Label Ljcc; - __ nop(); - // long jumps - __ j(overflow, &Ljcc); - __ j(no_overflow, &Ljcc); - __ j(below, &Ljcc); - __ j(above_equal, &Ljcc); - __ j(equal, &Ljcc); - __ j(not_equal, &Ljcc); - __ j(below_equal, &Ljcc); - __ j(above, &Ljcc); - __ j(sign, &Ljcc); - __ j(not_sign, &Ljcc); - __ j(parity_even, &Ljcc); - __ j(parity_odd, &Ljcc); - __ j(less, &Ljcc); - __ j(greater_equal, &Ljcc); - __ j(less_equal, &Ljcc); - __ j(greater, &Ljcc); - __ nop(); - __ bind(&Ljcc); - // short jumps - __ j(overflow, &Ljcc); - __ j(no_overflow, &Ljcc); - __ j(below, &Ljcc); - __ j(above_equal, &Ljcc); - __ j(equal, &Ljcc); - __ j(not_equal, &Ljcc); - __ j(below_equal, &Ljcc); - __ j(above, &Ljcc); - __ j(sign, &Ljcc); - __ j(not_sign, &Ljcc); - __ j(parity_even, &Ljcc); - __ j(parity_odd, &Ljcc); - __ j(less, &Ljcc); - __ j(greater_equal, &Ljcc); - __ j(less_equal, &Ljcc); - __ j(greater, &Ljcc); - - // 0xD9 instructions - __ nop(); - - __ fld(1); - __ fld1(); - __ fldz(); - __ fldpi(); - __ fabs(); - __ fchs(); - __ fprem(); - __ fprem1(); - __ fincstp(); - __ ftst(); - __ fxam(); - __ fxch(3); - __ fld_s(Operand(ebx, ecx, times_4, 10000)); - __ fstp_s(Operand(ebx, ecx, times_4, 10000)); - __ ffree(3); - __ fld_d(Operand(ebx, ecx, times_4, 10000)); - __ fstp_d(Operand(ebx, ecx, times_4, 10000)); - __ nop(); - - __ fild_s(Operand(ebx, ecx, times_4, 10000)); - __ fistp_s(Operand(ebx, ecx, times_4, 10000)); - __ fild_d(Operand(ebx, ecx, times_4, 10000)); - __ fistp_d(Operand(ebx, ecx, times_4, 10000)); - __ fnstsw_ax(); - __ nop(); - __ fadd(3); - __ fsub(3); - __ fmul(3); - __ fdiv(3); - - __ faddp(3); - __ fsubp(3); - __ fmulp(3); - __ fdivp(3); - __ fcompp(); - __ fwait(); - __ frndint(); - __ fninit(); - __ nop(); - - __ fldcw(Operand(ebx, ecx, times_4, 10000)); - __ fnstcw(Operand(ebx, ecx, times_4, 10000)); - __ fadd_d(Operand(ebx, ecx, times_4, 10000)); - __ fnsave(Operand(ebx, ecx, times_4, 10000)); - __ frstor(Operand(ebx, ecx, times_4, 10000)); - - // xchg. - { - __ xchg_b(eax, Operand(eax, 8)); - __ xchg_w(eax, Operand(ebx, 8)); - __ xchg(eax, eax); - __ xchg(eax, ebx); - __ xchg(ebx, ebx); - __ xchg(ebx, Operand(esp, 12)); - } - - // cmpxchg. - { - __ cmpxchg_b(Operand(esp, 12), eax); - __ cmpxchg_w(Operand(ebx, ecx, times_4, 10000), eax); - __ cmpxchg(Operand(ebx, ecx, times_4, 10000), eax); - } - - // lock prefix. - { - __ lock(); - __ cmpxchg(Operand(esp, 12), ebx); - - __ lock(); - __ xchg_w(eax, Operand(ecx, 8)); - } - - // Nop instructions - for (int i = 0; i < 16; i++) { - __ Nop(i); - } - - __ ret(0); - - CodeDesc desc; - assm.GetCode(&desc); - Handle code = isolate->factory()->NewCode( - desc, Code::ComputeFlags(Code::STUB), Handle()); - USE(code); -#ifdef OBJECT_PRINT - OFStream os(stdout); - code->Print(os); - byte* begin = code->instruction_start(); - byte* end = begin + code->instruction_size(); - disasm::Disassembler::Disassemble(stdout, begin, end); -#endif -} - -#undef __ diff --git a/test/cctest/test-macro-assembler-x87.cc b/test/cctest/test-macro-assembler-x87.cc deleted file mode 100644 index ac2a8e3917..0000000000 --- a/test/cctest/test-macro-assembler-x87.cc +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright 2013 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 - -#include "src/v8.h" -#include "test/cctest/cctest.h" - -#include "src/base/platform/platform.h" -#include "src/factory.h" -#include "src/macro-assembler.h" - -using namespace v8::internal; - -#if __GNUC__ -#define STDCALL __attribute__((stdcall)) -#else -#define STDCALL __stdcall -#endif - -typedef int STDCALL F0Type(); -typedef F0Type* F0; - -#define __ masm-> - - -TEST(LoadAndStoreWithRepresentation) { - // Allocate an executable page of memory. - size_t actual_size; - byte* buffer = static_cast(v8::base::OS::Allocate( - Assembler::kMinimalBufferSize, &actual_size, true)); - CHECK(buffer); - Isolate* isolate = CcTest::i_isolate(); - HandleScope handles(isolate); - MacroAssembler assembler(isolate, buffer, static_cast(actual_size), - v8::internal::CodeObjectRequired::kYes); - MacroAssembler* masm = &assembler; // Create a pointer for the __ macro. - __ push(ebx); - __ push(edx); - __ sub(esp, Immediate(1 * kPointerSize)); - Label exit; - - // Test 1. - __ mov(eax, Immediate(1)); // Test number. - __ mov(Operand(esp, 0 * kPointerSize), Immediate(0)); - __ mov(ebx, Immediate(-1)); - __ Store(ebx, Operand(esp, 0 * kPointerSize), Representation::UInteger8()); - __ mov(ebx, Operand(esp, 0 * kPointerSize)); - __ mov(edx, Immediate(255)); - __ cmp(ebx, edx); - __ j(not_equal, &exit); - __ Load(ebx, Operand(esp, 0 * kPointerSize), Representation::UInteger8()); - __ cmp(ebx, edx); - __ j(not_equal, &exit); - - - // Test 2. - __ mov(eax, Immediate(2)); // Test number. - __ mov(Operand(esp, 0 * kPointerSize), Immediate(0)); - __ mov(ebx, Immediate(-1)); - __ Store(ebx, Operand(esp, 0 * kPointerSize), Representation::Integer8()); - __ mov(ebx, Operand(esp, 0 * kPointerSize)); - __ mov(edx, Immediate(255)); - __ cmp(ebx, edx); - __ j(not_equal, &exit); - __ Load(ebx, Operand(esp, 0 * kPointerSize), Representation::Integer8()); - __ mov(edx, Immediate(-1)); - __ cmp(ebx, edx); - __ j(not_equal, &exit); - - // Test 3. - __ mov(eax, Immediate(3)); // Test number. - __ mov(Operand(esp, 0 * kPointerSize), Immediate(0)); - __ mov(ebx, Immediate(-1)); - __ Store(ebx, Operand(esp, 0 * kPointerSize), Representation::Integer16()); - __ mov(ebx, Operand(esp, 0 * kPointerSize)); - __ mov(edx, Immediate(65535)); - __ cmp(ebx, edx); - __ j(not_equal, &exit); - __ Load(edx, Operand(esp, 0 * kPointerSize), Representation::Integer16()); - __ mov(ebx, Immediate(-1)); - __ cmp(ebx, edx); - __ j(not_equal, &exit); - - // Test 4. - __ mov(eax, Immediate(4)); // Test number. - __ mov(Operand(esp, 0 * kPointerSize), Immediate(0)); - __ mov(ebx, Immediate(-1)); - __ Store(ebx, Operand(esp, 0 * kPointerSize), Representation::UInteger16()); - __ mov(ebx, Operand(esp, 0 * kPointerSize)); - __ mov(edx, Immediate(65535)); - __ cmp(ebx, edx); - __ j(not_equal, &exit); - __ Load(edx, Operand(esp, 0 * kPointerSize), Representation::UInteger16()); - __ cmp(ebx, edx); - __ j(not_equal, &exit); - - // Test 5. - __ mov(eax, Immediate(5)); - __ Move(edx, Immediate(0)); // Test Move() - __ cmp(edx, Immediate(0)); - __ j(not_equal, &exit); - __ Move(ecx, Immediate(-1)); - __ cmp(ecx, Immediate(-1)); - __ j(not_equal, &exit); - __ Move(ebx, Immediate(0x77)); - __ cmp(ebx, Immediate(0x77)); - __ j(not_equal, &exit); - - __ xor_(eax, eax); // Success. - __ bind(&exit); - __ add(esp, Immediate(1 * kPointerSize)); - __ pop(edx); - __ pop(ebx); - __ ret(0); - - CodeDesc desc; - masm->GetCode(&desc); - // Call the function from C++. - int result = FUNCTION_CAST(buffer)(); - CHECK_EQ(0, result); -} - -#undef __ diff --git a/test/cctest/test-regexp.cc b/test/cctest/test-regexp.cc index 3b318cefe4..7a7116e0d5 100644 --- a/test/cctest/test-regexp.cc +++ b/test/cctest/test-regexp.cc @@ -86,11 +86,6 @@ #include "src/ia32/macro-assembler-ia32.h" #include "src/regexp/ia32/regexp-macro-assembler-ia32.h" #endif -#if V8_TARGET_ARCH_X87 -#include "src/regexp/x87/regexp-macro-assembler-x87.h" -#include "src/x87/assembler-x87.h" -#include "src/x87/macro-assembler-x87.h" -#endif #endif // V8_INTERPRETED_REGEXP #include "test/cctest/cctest.h" diff --git a/test/cctest/test-run-wasm-relocation-x87.cc b/test/cctest/test-run-wasm-relocation-x87.cc deleted file mode 100644 index 77dc86e0fb..0000000000 --- a/test/cctest/test-run-wasm-relocation-x87.cc +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2015 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. - -#include - -#include "src/v8.h" - -#include "src/debug/debug.h" -#include "src/disasm.h" -#include "src/disassembler.h" -#include "src/ic/ic.h" -#include "src/macro-assembler.h" -#include "src/x87/frames-x87.h" -#include "test/cctest/cctest.h" -#include "test/cctest/compiler/c-signature.h" -#include "test/cctest/compiler/call-tester.h" - -using namespace v8::internal; -using namespace v8::internal::compiler; - -#define __ assm. - -static int32_t DummyStaticFunction(Object* result) { return 1; } - -TEST(WasmRelocationX87MemoryReference) { - Isolate* isolate = CcTest::i_isolate(); - Zone zone(isolate->allocator(), ZONE_NAME); - HandleScope scope(isolate); - v8::internal::byte buffer[4096]; - Assembler assm(isolate, buffer, sizeof buffer); - DummyStaticFunction(NULL); - int32_t imm = 1234567; - - __ mov(eax, Immediate(reinterpret_cast
(imm), - RelocInfo::WASM_MEMORY_REFERENCE)); - __ nop(); - __ ret(0); - - CSignature0 csig; - CodeDesc desc; - assm.GetCode(&desc); - Handle code = isolate->factory()->NewCode( - desc, Code::ComputeFlags(Code::STUB), Handle()); - USE(code); - - CodeRunner runnable(isolate, code, &csig); - int32_t ret_value = runnable.Call(); - CHECK_EQ(ret_value, imm); - -#ifdef OBJECT_PRINT - OFStream os(stdout); - code->Print(os); - byte* begin = code->instruction_start(); - byte* end = begin + code->instruction_size(); - disasm::Disassembler::Disassemble(stdout, begin, end); -#endif - - int offset = 1234; - - // Relocating references by offset - int mode_mask = (1 << RelocInfo::WASM_MEMORY_REFERENCE); - for (RelocIterator it(*code, mode_mask); !it.done(); it.next()) { - DCHECK(RelocInfo::IsWasmMemoryReference(it.rinfo()->rmode())); - it.rinfo()->update_wasm_memory_reference( - it.rinfo()->wasm_memory_reference(), - it.rinfo()->wasm_memory_reference() + offset, SKIP_ICACHE_FLUSH); - } - - // Check if immediate is updated correctly - ret_value = runnable.Call(); - CHECK_EQ(ret_value, imm + offset); - -#ifdef OBJECT_PRINT - code->Print(os); - begin = code->instruction_start(); - end = begin + code->instruction_size(); - disasm::Disassembler::Disassemble(stdout, begin, end); -#endif -} - -TEST(WasmRelocationX87MemorySizeReference) { - CcTest::InitializeVM(); - Isolate* isolate = CcTest::i_isolate(); - Zone zone(isolate->allocator(), ZONE_NAME); - HandleScope scope(isolate); - v8::internal::byte buffer[4096]; - Assembler assm(isolate, buffer, sizeof buffer); - DummyStaticFunction(NULL); - int32_t size = 80; - Label fail; - - __ mov(eax, Immediate(reinterpret_cast
(size), - RelocInfo::WASM_MEMORY_SIZE_REFERENCE)); - __ cmp(eax, Immediate(reinterpret_cast
(size), - RelocInfo::WASM_MEMORY_SIZE_REFERENCE)); - __ j(not_equal, &fail); - __ ret(0); - __ bind(&fail); - __ mov(eax, 0xdeadbeef); - __ ret(0); - - CSignature0 csig; - CodeDesc desc; - assm.GetCode(&desc); - Handle code = isolate->factory()->NewCode( - desc, Code::ComputeFlags(Code::STUB), Handle()); - USE(code); - - CodeRunner runnable(isolate, code, &csig); - int32_t ret_value = runnable.Call(); - CHECK_NE(ret_value, bit_cast(0xdeadbeef)); - -#ifdef OBJECT_PRINT - OFStream os(stdout); - code->Print(os); - byte* begin = code->instruction_start(); - byte* end = begin + code->instruction_size(); - disasm::Disassembler::Disassemble(stdout, begin, end); -#endif - - size_t offset = 10; - - int mode_mask = (1 << RelocInfo::WASM_MEMORY_SIZE_REFERENCE); - for (RelocIterator it(*code, mode_mask); !it.done(); it.next()) { - RelocInfo::Mode mode = it.rinfo()->rmode(); - DCHECK(RelocInfo::IsWasmMemorySizeReference(mode)); - it.rinfo()->update_wasm_memory_size( - it.rinfo()->wasm_memory_size_reference(), - it.rinfo()->wasm_memory_size_reference() + offset, SKIP_ICACHE_FLUSH); - } - - ret_value = runnable.Call(); - CHECK_NE(ret_value, bit_cast(0xdeadbeef)); - -#ifdef OBJECT_PRINT - code->Print(os); - begin = code->instruction_start(); - end = begin + code->instruction_size(); - disasm::Disassembler::Disassemble(stdout, begin, end); -#endif -} -#undef __ diff --git a/test/mjsunit/mjsunit.status b/test/mjsunit/mjsunit.status index 1b0b165166..90b56e33a9 100644 --- a/test/mjsunit/mjsunit.status +++ b/test/mjsunit/mjsunit.status @@ -124,9 +124,8 @@ 'compiler/alloc-number-debug': [PASS, ['mode == release', SKIP]], 'regress/regress-634-debug': [PASS, ['mode == release', SKIP]], - # BUG(v8:2989). PASS/FAIL on linux32 because crankshaft is turned off for - # nosse2. Also for arm novfp3. - 'regress/regress-2989': [FAIL, NO_VARIANTS, ['system == linux and arch == x87 or arch == arm and simulator == True', PASS]], + # BUG(v8:2989). + 'regress/regress-2989': [FAIL, NO_VARIANTS], # This test variant makes only sense on arm. 'math-floor-of-div-nosudiv': [PASS, SLOW, ['arch not in [arm, arm64, android_arm, android_arm64]', SKIP]], @@ -520,12 +519,6 @@ 'math-floor-of-div-minus-zero': [SKIP], }], # 'arch == mips64el or arch == mips64' -['arch == x87', { - # The result produced by Gcc on linux platform is extended 80-bit double - # precision and not the expected standard 64-bit double precision. - 'number-tostring-big-integer': [SKIP], -}], # 'arch == x87' - ############################################################################## ['system == windows', { # TODO(mstarzinger): Too slow with turbo fan. diff --git a/test/unittests/unittests.status b/test/unittests/unittests.status index 9e3bda697a..d8b7c1102d 100644 --- a/test/unittests/unittests.status +++ b/test/unittests/unittests.status @@ -7,15 +7,4 @@ # BUG(5677): Real timers are flaky 'RuntimeCallStatsTest.*': [SKIP], }], # ALWAYS - -['arch == x87', { - 'Ieee754.Expm1': [SKIP], - 'Ieee754.Cos': [SKIP], - 'Ieee754.Tan': [SKIP], - 'Ieee754.Acosh': [SKIP], - 'Ieee754.Asinh': [SKIP], - 'MoveOptimizerTest.RemovesRedundantExplicit': [SKIP], - 'RegisterAllocatorTest.CanAllocateFPRegisters': [SKIP], -}], # 'arch == x87' - ] diff --git a/test/webkit/webkit.status b/test/webkit/webkit.status index 60ddea7fd7..93e694ed4f 100644 --- a/test/webkit/webkit.status +++ b/test/webkit/webkit.status @@ -72,10 +72,6 @@ # Too slow. 'dfg-int-overflow-in-loop': [SKIP], }], # 'arch == s390 or arch == s390x' -['arch == x87', { - # Too slow. - 'dfg-negative-array-index': [SKIP], -}], # 'arch == x87' ############################################################################## ['asan == True', { diff --git a/tools/dev/gen-tags.py b/tools/dev/gen-tags.py index 4e0e98c8d4..256f65a401 100755 --- a/tools/dev/gen-tags.py +++ b/tools/dev/gen-tags.py @@ -20,7 +20,7 @@ import subprocess import sys # All arches that this script understands. -ARCHES = ["ia32", "x64", "arm", "arm64", "mips", "mips64", "ppc", "s390", "x87"] +ARCHES = ["ia32", "x64", "arm", "arm64", "mips", "mips64", "ppc", "s390"] def PrintHelpAndExit(): print(__doc__) diff --git a/tools/dev/gm.py b/tools/dev/gm.py index 8f467ab548..21af4ff31c 100755 --- a/tools/dev/gm.py +++ b/tools/dev/gm.py @@ -33,7 +33,7 @@ BUILD_TARGETS_ALL = ["all"] # All arches that this script understands. ARCHES = ["ia32", "x64", "arm", "arm64", "mipsel", "mips64el", "ppc", "ppc64", - "s390", "s390x", "x87"] + "s390", "s390x"] # Arches that get built/run when you don't specify any. DEFAULT_ARCHES = ["ia32", "x64", "arm", "arm64"] # Modes that this script understands. diff --git a/tools/run-tests.py b/tools/run-tests.py index 9855677665..8be8d42cad 100755 --- a/tools/run-tests.py +++ b/tools/run-tests.py @@ -187,7 +187,6 @@ SUPPORTED_ARCHS = ["android_arm", "android_x64", "arm", "ia32", - "x87", "mips", "mipsel", "mips64", @@ -211,7 +210,6 @@ SLOW_ARCHS = ["android_arm", "mips64el", "s390", "s390x", - "x87", "arm64"] diff --git a/tools/testrunner/local/statusfile.py b/tools/testrunner/local/statusfile.py index 1f99d2fdee..880837b8a7 100644 --- a/tools/testrunner/local/statusfile.py +++ b/tools/testrunner/local/statusfile.py @@ -59,10 +59,10 @@ DEFS = {FAIL_OK: [FAIL, OKAY], # Support arches, modes to be written as keywords instead of strings. VARIABLES = {ALWAYS: True} for var in ["debug", "release", "big", "little", - "android_arm", "android_arm64", "android_ia32", "android_x87", - "android_x64", "arm", "arm64", "ia32", "mips", "mipsel", "mips64", - "mips64el", "x64", "x87", "ppc", "ppc64", "s390", "s390x", "macos", - "windows", "linux", "aix"]: + "android_arm", "android_arm64", "android_ia32", "android_x64", + "arm", "arm64", "ia32", "mips", "mipsel", "mips64", "mips64el", + "x64", "ppc", "ppc64", "s390", "s390x", "macos", "windows", + "linux", "aix"]: VARIABLES[var] = var # Allow using variants as keywords. diff --git a/tools/verify_source_deps.py b/tools/verify_source_deps.py index e3a39c1d17..c49d51ab5d 100755 --- a/tools/verify_source_deps.py +++ b/tools/verify_source_deps.py @@ -82,7 +82,6 @@ GN_UNSUPPORTED_FEATURES = [ 'solaris', 'vtune', 'v8-version.h', - 'x87', ] ALL_GN_PREFIXES = [