diff --git a/deps/v8/AUTHORS b/deps/v8/AUTHORS index 4ef2bcca33..597b0ff0ae 100644 --- a/deps/v8/AUTHORS +++ b/deps/v8/AUTHORS @@ -17,10 +17,8 @@ Opera Software ASA Akinori MUSHA Alexander Botero-Lowry Alexander Karpinsky -Alexandre Rames Alexandre Vassalotti Andreas Anyuru -Baptiste Afsa Bert Belder Burcu Dogan Craig Schlenter @@ -33,7 +31,6 @@ Fedor Indutny Filipe David Manana Haitao Feng Ioseb Dzmanashvili -Jacob Bramley Jan de Mooij Jay Freeman James Pike @@ -62,7 +59,6 @@ Sandro Santilli Sanjoy Das Subrato K De Tobias Burnus -Vincent Belliard Vlad Burlik Xi Qian Yuqiang Xian diff --git a/deps/v8/ChangeLog b/deps/v8/ChangeLog index 443f8d5ba5..1d3f139e11 100644 --- a/deps/v8/ChangeLog +++ b/deps/v8/ChangeLog @@ -1,47 +1,3 @@ -2014-02-19: Version 3.24.40 - - A64: Let the MacroAssembler resolve branches to distant targets (issue - 3148). - - Fixed and improved code for integral division. Fixed and extended tests - (issue 3151). - - MIPS: Fix assignment of function name constant (issue 3138). - - Fix assignment of function name constant (issue 3138). - - Performance and stability improvements on all platforms. - - -2014-02-14: Version 3.24.39 - - Introduce --job-based-sweeping flag and use individual jobs for sweeping - if set (issue 3104). - - Performance and stability improvements on all platforms. - - -2014-02-13: Version 3.24.38 - - Merge experimental/a64 to bleeding_edge (issue 3113). - - Performance and stability improvements on all platforms. - - -2014-02-12: Version 3.24.37 - - Fix spec violations in JSON.stringify wrt replacer array (issue 3135). - - Performance and stability improvements on all platforms. - - -2014-02-11: Version 3.24.36 - - Fix inconsistencies wrt whitespaces (issue 3109). - - Performance and stability improvements on all platforms. - - 2014-02-10: Version 3.24.35 Fix inconsistencies wrt whitespaces (issue 3109). diff --git a/deps/v8/LICENSE b/deps/v8/LICENSE index 2f5bce8369..2e516bab62 100644 --- a/deps/v8/LICENSE +++ b/deps/v8/LICENSE @@ -26,7 +26,7 @@ are: These libraries have their own licenses; we recommend you read them, as their terms may differ from the terms below. -Copyright 2014, the V8 project authors. All rights reserved. +Copyright 2006-2012, 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: diff --git a/deps/v8/Makefile b/deps/v8/Makefile index 9491412155..2f47fa9a88 100644 --- a/deps/v8/Makefile +++ b/deps/v8/Makefile @@ -223,11 +223,11 @@ 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 a64 mipsel +ARCHES = ia32 x64 arm mipsel DEFAULT_ARCHES = ia32 x64 arm MODES = release debug optdebug DEFAULT_MODES = release debug -ANDROID_ARCHES = android_ia32 android_arm android_a64 android_mipsel +ANDROID_ARCHES = android_ia32 android_arm android_mipsel NACL_ARCHES = nacl_ia32 nacl_x64 # List of files that trigger Makefile regeneration: @@ -359,12 +359,11 @@ native.check: native --arch-and-mode=. $(TESTFLAGS) FASTTESTMODES = ia32.release,x64.release,ia32.optdebug,x64.optdebug,arm.optdebug -FASTCOMPILEMODES = $(FASTTESTMODES),a64.optdebug COMMA = , EMPTY = SPACE = $(EMPTY) $(EMPTY) -quickcheck: $(subst $(COMMA),$(SPACE),$(FASTCOMPILEMODES)) +quickcheck: $(subst $(COMMA),$(SPACE),$(FASTTESTMODES)) tools/run-tests.py $(TESTJOBS) --outdir=$(OUTDIR) \ --arch-and-mode=$(FASTTESTMODES) $(TESTFLAGS) --quickcheck qc: quickcheck diff --git a/deps/v8/Makefile.android b/deps/v8/Makefile.android index ea2c6e284b..fad5fe9943 100644 --- a/deps/v8/Makefile.android +++ b/deps/v8/Makefile.android @@ -26,7 +26,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # Those definitions should be consistent with the main Makefile -ANDROID_ARCHES = android_ia32 android_arm android_a64 android_mipsel +ANDROID_ARCHES = android_ia32 android_arm android_mipsel MODES = release debug # Generates all combinations of ANDROID ARCHES and MODES, @@ -49,40 +49,24 @@ endif ifeq ($(ARCH), android_arm) DEFINES = target_arch=arm v8_target_arch=arm android_target_arch=arm DEFINES += arm_neon=0 arm_version=7 - TOOLCHAIN_ARCH = arm-linux-androideabi - TOOLCHAIN_PREFIX = $(TOOLCHAIN_ARCH) - TOOLCHAIN_VER = 4.6 + TOOLCHAIN_ARCH = arm-linux-androideabi-4.6 else - ifeq ($(ARCH), android_a64) - DEFINES = target_arch=a64 v8_target_arch=a64 android_target_arch=arm64 - TOOLCHAIN_ARCH = aarch64-linux-android - TOOLCHAIN_PREFIX = $(TOOLCHAIN_ARCH) - TOOLCHAIN_VER = 4.8 + ifeq ($(ARCH), android_mipsel) + DEFINES = target_arch=mipsel v8_target_arch=mipsel android_target_arch=mips + DEFINES += mips_arch_variant=mips32r2 + TOOLCHAIN_ARCH = mipsel-linux-android-4.6 else - ifeq ($(ARCH), android_mipsel) - DEFINES = target_arch=mipsel v8_target_arch=mipsel - DEFINES += android_target_arch=mips mips_arch_variant=mips32r2 - TOOLCHAIN_ARCH = mipsel-linux-android - TOOLCHAIN_PREFIX = $(TOOLCHAIN_ARCH) - TOOLCHAIN_VER = 4.6 - + ifeq ($(ARCH), android_ia32) + DEFINES = target_arch=ia32 v8_target_arch=ia32 android_target_arch=x86 + TOOLCHAIN_ARCH = x86-4.6 else - ifeq ($(ARCH), android_ia32) - DEFINES = target_arch=ia32 v8_target_arch=ia32 android_target_arch=x86 - TOOLCHAIN_ARCH = x86 - TOOLCHAIN_PREFIX = i686-linux-android - TOOLCHAIN_VER = 4.6 - else - $(error Target architecture "${ARCH}" is not supported) - endif + $(error Target architecture "${ARCH}" is not supported) endif endif endif -TOOLCHAIN_PATH = \ - ${ANDROID_NDK_ROOT}/toolchains/${TOOLCHAIN_ARCH}-${TOOLCHAIN_VER}/prebuilt +TOOLCHAIN_PATH = ${ANDROID_NDK_ROOT}/toolchains/${TOOLCHAIN_ARCH}/prebuilt ANDROID_TOOLCHAIN ?= ${TOOLCHAIN_PATH}/${TOOLCHAIN_DIR} - ifeq ($(wildcard $(ANDROID_TOOLCHAIN)),) $(error Cannot find Android toolchain in "${ANDROID_TOOLCHAIN}". Please \ check that ANDROID_NDK_ROOT and ANDROID_NDK_HOST_ARCH are set \ @@ -95,23 +79,23 @@ DEFINES += host_os=${HOST_OS} .SECONDEXPANSION: $(ANDROID_BUILDS): $(OUTDIR)/Makefile.$$@ @$(MAKE) -C "$(OUTDIR)" -f Makefile.$@ \ - CXX="$(ANDROID_TOOLCHAIN)/bin/${TOOLCHAIN_PREFIX}-g++" \ - AR="$(ANDROID_TOOLCHAIN)/bin/${TOOLCHAIN_PREFIX}-ar" \ - RANLIB="$(ANDROID_TOOLCHAIN)/bin/${TOOLCHAIN_PREFIX}-ranlib" \ - CC="$(ANDROID_TOOLCHAIN)/bin/${TOOLCHAIN_PREFIX}-gcc" \ - LD="$(ANDROID_TOOLCHAIN)/bin/${TOOLCHAIN_PREFIX}-ld" \ - LINK="$(ANDROID_TOOLCHAIN)/bin/${TOOLCHAIN_PREFIX}-g++" \ - BUILDTYPE=$(shell echo $(subst .,,$(suffix $@)) | \ - python -c "print raw_input().capitalize()") \ - builddir="$(shell pwd)/$(OUTDIR)/$@" + CXX="$(ANDROID_TOOLCHAIN)/bin/*-g++" \ + AR="$(ANDROID_TOOLCHAIN)/bin/*-ar" \ + RANLIB="$(ANDROID_TOOLCHAIN)/bin/*-ranlib" \ + CC="$(ANDROID_TOOLCHAIN)/bin/*-gcc" \ + LD="$(ANDROID_TOOLCHAIN)/bin/*-ld" \ + LINK="$(ANDROID_TOOLCHAIN)/bin/*-g++" \ + BUILDTYPE=$(shell echo $(subst .,,$(suffix $@)) | \ + python -c "print raw_input().capitalize()") \ + builddir="$(shell pwd)/$(OUTDIR)/$@" # Android GYP file generation targets. ANDROID_MAKEFILES = $(addprefix $(OUTDIR)/Makefile.,$(ANDROID_BUILDS)) $(ANDROID_MAKEFILES): GYP_GENERATORS=make-android \ GYP_DEFINES="${DEFINES}" \ - CC="${ANDROID_TOOLCHAIN}/bin/${TOOLCHAIN_PREFIX}-gcc" \ - CXX="${ANDROID_TOOLCHAIN}/bin/${TOOLCHAIN_PREFIX}-g++" \ + CC="${ANDROID_TOOLCHAIN}/bin/*-gcc" \ + CXX="${ANDROID_TOOLCHAIN}/bin/*-g++" \ PYTHONPATH="$(shell pwd)/tools/generate_shim_headers:$(PYTHONPATH)" \ build/gyp/gyp --generator-output="${OUTDIR}" build/all.gyp \ -Ibuild/standalone.gypi --depth=. -Ibuild/android.gypi \ diff --git a/deps/v8/build/android.gypi b/deps/v8/build/android.gypi index 2f32be0e81..0ea899d6ed 100644 --- a/deps/v8/build/android.gypi +++ b/deps/v8/build/android.gypi @@ -184,11 +184,6 @@ '-L<(android_stlport_libs)/x86', ], }], - ['target_arch=="a64"', { - 'ldflags': [ - '-L<(android_stlport_libs)/arm64', - ], - }], ], }], ['target_arch=="ia32"', { @@ -213,19 +208,10 @@ ], 'target_conditions': [ ['_type=="executable"', { - 'conditions': [ - ['target_arch=="a64"', { - 'ldflags': [ - '-Wl,-dynamic-linker,/system/bin/linker64', - ], - }, { - 'ldflags': [ - '-Wl,-dynamic-linker,/system/bin/linker', - ], - }] - ], 'ldflags': [ '-Bdynamic', + '-Wl,-dynamic-linker,/system/bin/linker', + '-Wl,--gc-sections', '-Wl,-z,nocopyreloc', # crtbegin_dynamic.o should be the last item in ldflags. '<(android_lib)/crtbegin_dynamic.o', diff --git a/deps/v8/build/features.gypi b/deps/v8/build/features.gypi index 85b8a38465..f0e7212096 100644 --- a/deps/v8/build/features.gypi +++ b/deps/v8/build/features.gypi @@ -115,7 +115,7 @@ 'Release': { 'variables': { 'v8_enable_extra_checks%': 0, - 'v8_enable_handle_zapping%': 1, + 'v8_enable_handle_zapping%': 0, }, 'conditions': [ ['v8_enable_extra_checks==1', { diff --git a/deps/v8/build/standalone.gypi b/deps/v8/build/standalone.gypi index cae63fe7ac..bcfce39ada 100644 --- a/deps/v8/build/standalone.gypi +++ b/deps/v8/build/standalone.gypi @@ -52,11 +52,7 @@ # to gyp. 'host_arch%': ' microtask); - - /** - * Experimental: Controls whether the Microtask Work Queue is automatically - * run when the script call depth decrements to zero. - */ - static void SetAutorunMicrotasks(Isolate *source, bool autorun); - /** * Initializes from snapshot if possible. Otherwise, attempts to * initialize from scratch. This function is called implicitly if @@ -5412,7 +5398,7 @@ class Internals { static const int kNullValueRootIndex = 7; static const int kTrueValueRootIndex = 8; static const int kFalseValueRootIndex = 9; - static const int kEmptyStringRootIndex = 141; + static const int kEmptyStringRootIndex = 147; static const int kNodeClassIdOffset = 1 * kApiPointerSize; static const int kNodeFlagsOffset = 1 * kApiPointerSize + 3; diff --git a/deps/v8/src/a64/OWNERS b/deps/v8/src/a64/OWNERS deleted file mode 100644 index 906a5ce641..0000000000 --- a/deps/v8/src/a64/OWNERS +++ /dev/null @@ -1 +0,0 @@ -rmcilroy@chromium.org diff --git a/deps/v8/src/a64/assembler-a64-inl.h b/deps/v8/src/a64/assembler-a64-inl.h deleted file mode 100644 index e68dee0738..0000000000 --- a/deps/v8/src/a64/assembler-a64-inl.h +++ /dev/null @@ -1,1200 +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. - -#ifndef V8_A64_ASSEMBLER_A64_INL_H_ -#define V8_A64_ASSEMBLER_A64_INL_H_ - -#include "a64/assembler-a64.h" -#include "cpu.h" -#include "debug.h" - - -namespace v8 { -namespace internal { - - -void RelocInfo::apply(intptr_t delta) { - UNIMPLEMENTED(); -} - - -void RelocInfo::set_target_address(Address target, WriteBarrierMode mode) { - ASSERT(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)); - Assembler::set_target_address_at(pc_, target); - if (mode == UPDATE_WRITE_BARRIER && host() != NULL && IsCodeTarget(rmode_)) { - Object* target_code = Code::GetCodeFromTargetAddress(target); - host()->GetHeap()->incremental_marking()->RecordWriteIntoCode( - host(), this, HeapObject::cast(target_code)); - } -} - - -inline unsigned CPURegister::code() const { - ASSERT(IsValid()); - return reg_code; -} - - -inline CPURegister::RegisterType CPURegister::type() const { - ASSERT(IsValidOrNone()); - return reg_type; -} - - -inline RegList CPURegister::Bit() const { - ASSERT(reg_code < (sizeof(RegList) * kBitsPerByte)); - return IsValid() ? 1UL << reg_code : 0; -} - - -inline unsigned CPURegister::SizeInBits() const { - ASSERT(IsValid()); - return reg_size; -} - - -inline int CPURegister::SizeInBytes() const { - ASSERT(IsValid()); - ASSERT(SizeInBits() % 8 == 0); - return reg_size / 8; -} - - -inline bool CPURegister::Is32Bits() const { - ASSERT(IsValid()); - return reg_size == 32; -} - - -inline bool CPURegister::Is64Bits() const { - ASSERT(IsValid()); - return reg_size == 64; -} - - -inline bool CPURegister::IsValid() const { - if (IsValidRegister() || IsValidFPRegister()) { - ASSERT(!IsNone()); - return true; - } else { - ASSERT(IsNone()); - return false; - } -} - - -inline bool CPURegister::IsValidRegister() const { - return IsRegister() && - ((reg_size == kWRegSize) || (reg_size == kXRegSize)) && - ((reg_code < kNumberOfRegisters) || (reg_code == kSPRegInternalCode)); -} - - -inline bool CPURegister::IsValidFPRegister() const { - return IsFPRegister() && - ((reg_size == kSRegSize) || (reg_size == kDRegSize)) && - (reg_code < kNumberOfFPRegisters); -} - - -inline bool CPURegister::IsNone() const { - // kNoRegister types should always have size 0 and code 0. - ASSERT((reg_type != kNoRegister) || (reg_code == 0)); - ASSERT((reg_type != kNoRegister) || (reg_size == 0)); - - return reg_type == kNoRegister; -} - - -inline bool CPURegister::Is(const CPURegister& other) const { - ASSERT(IsValidOrNone() && other.IsValidOrNone()); - return (reg_code == other.reg_code) && (reg_size == other.reg_size) && - (reg_type == other.reg_type); -} - - -inline bool CPURegister::IsRegister() const { - return reg_type == kRegister; -} - - -inline bool CPURegister::IsFPRegister() const { - return reg_type == kFPRegister; -} - - -inline bool CPURegister::IsSameSizeAndType(const CPURegister& other) const { - return (reg_size == other.reg_size) && (reg_type == other.reg_type); -} - - -inline bool CPURegister::IsValidOrNone() const { - return IsValid() || IsNone(); -} - - -inline bool CPURegister::IsZero() const { - ASSERT(IsValid()); - return IsRegister() && (reg_code == kZeroRegCode); -} - - -inline bool CPURegister::IsSP() const { - ASSERT(IsValid()); - return IsRegister() && (reg_code == kSPRegInternalCode); -} - - -inline void CPURegList::Combine(const CPURegList& other) { - ASSERT(IsValid()); - ASSERT(other.type() == type_); - ASSERT(other.RegisterSizeInBits() == size_); - list_ |= other.list(); -} - - -inline void CPURegList::Remove(const CPURegList& other) { - ASSERT(IsValid()); - ASSERT(other.type() == type_); - ASSERT(other.RegisterSizeInBits() == size_); - list_ &= ~other.list(); -} - - -inline void CPURegList::Combine(const CPURegister& other) { - ASSERT(other.type() == type_); - ASSERT(other.SizeInBits() == size_); - Combine(other.code()); -} - - -inline void CPURegList::Remove(const CPURegister& other) { - ASSERT(other.type() == type_); - ASSERT(other.SizeInBits() == size_); - Remove(other.code()); -} - - -inline void CPURegList::Combine(int code) { - ASSERT(IsValid()); - ASSERT(CPURegister::Create(code, size_, type_).IsValid()); - list_ |= (1UL << code); -} - - -inline void CPURegList::Remove(int code) { - ASSERT(IsValid()); - ASSERT(CPURegister::Create(code, size_, type_).IsValid()); - list_ &= ~(1UL << code); -} - - -inline Register Register::XRegFromCode(unsigned code) { - // This function returns the zero register when code = 31. The stack pointer - // can not be returned. - ASSERT(code < kNumberOfRegisters); - return Register::Create(code, kXRegSize); -} - - -inline Register Register::WRegFromCode(unsigned code) { - ASSERT(code < kNumberOfRegisters); - return Register::Create(code, kWRegSize); -} - - -inline FPRegister FPRegister::SRegFromCode(unsigned code) { - ASSERT(code < kNumberOfFPRegisters); - return FPRegister::Create(code, kSRegSize); -} - - -inline FPRegister FPRegister::DRegFromCode(unsigned code) { - ASSERT(code < kNumberOfFPRegisters); - return FPRegister::Create(code, kDRegSize); -} - - -inline Register CPURegister::W() const { - ASSERT(IsValidRegister()); - return Register::WRegFromCode(reg_code); -} - - -inline Register CPURegister::X() const { - ASSERT(IsValidRegister()); - return Register::XRegFromCode(reg_code); -} - - -inline FPRegister CPURegister::S() const { - ASSERT(IsValidFPRegister()); - return FPRegister::SRegFromCode(reg_code); -} - - -inline FPRegister CPURegister::D() const { - ASSERT(IsValidFPRegister()); - return FPRegister::DRegFromCode(reg_code); -} - - -// Operand. -template -Operand::Operand(Handle value) : reg_(NoReg) { - initialize_handle(value); -} - - -// Default initializer is for int types -template -struct OperandInitializer { - static const bool kIsIntType = true; - static inline RelocInfo::Mode rmode_for(int_t) { - return sizeof(int_t) == 8 ? RelocInfo::NONE64 : RelocInfo::NONE32; - } - static inline int64_t immediate_for(int_t t) { - STATIC_ASSERT(sizeof(int_t) <= 8); - return t; - } -}; - - -template<> -struct OperandInitializer { - static const bool kIsIntType = false; - static inline RelocInfo::Mode rmode_for(Smi* t) { - return RelocInfo::NONE64; - } - static inline int64_t immediate_for(Smi* t) {; - return reinterpret_cast(t); - } -}; - - -template<> -struct OperandInitializer { - static const bool kIsIntType = false; - static inline RelocInfo::Mode rmode_for(ExternalReference t) { - return RelocInfo::EXTERNAL_REFERENCE; - } - static inline int64_t immediate_for(ExternalReference t) {; - return reinterpret_cast(t.address()); - } -}; - - -template -Operand::Operand(T t) - : immediate_(OperandInitializer::immediate_for(t)), - reg_(NoReg), - rmode_(OperandInitializer::rmode_for(t)) {} - - -template -Operand::Operand(T t, RelocInfo::Mode rmode) - : immediate_(OperandInitializer::immediate_for(t)), - reg_(NoReg), - rmode_(rmode) { - STATIC_ASSERT(OperandInitializer::kIsIntType); -} - - -Operand::Operand(Register reg, Shift shift, unsigned shift_amount) - : reg_(reg), - shift_(shift), - extend_(NO_EXTEND), - shift_amount_(shift_amount), - rmode_(reg.Is64Bits() ? RelocInfo::NONE64 : RelocInfo::NONE32) { - ASSERT(reg.Is64Bits() || (shift_amount < kWRegSize)); - ASSERT(reg.Is32Bits() || (shift_amount < kXRegSize)); - ASSERT(!reg.IsSP()); -} - - -Operand::Operand(Register reg, Extend extend, unsigned shift_amount) - : reg_(reg), - shift_(NO_SHIFT), - extend_(extend), - shift_amount_(shift_amount), - rmode_(reg.Is64Bits() ? RelocInfo::NONE64 : RelocInfo::NONE32) { - ASSERT(reg.IsValid()); - ASSERT(shift_amount <= 4); - ASSERT(!reg.IsSP()); - - // Extend modes SXTX and UXTX require a 64-bit register. - ASSERT(reg.Is64Bits() || ((extend != SXTX) && (extend != UXTX))); -} - - -bool Operand::IsImmediate() const { - return reg_.Is(NoReg); -} - - -bool Operand::IsShiftedRegister() const { - return reg_.IsValid() && (shift_ != NO_SHIFT); -} - - -bool Operand::IsExtendedRegister() const { - return reg_.IsValid() && (extend_ != NO_EXTEND); -} - - -bool Operand::IsZero() const { - if (IsImmediate()) { - return immediate() == 0; - } else { - return reg().IsZero(); - } -} - - -Operand Operand::ToExtendedRegister() const { - ASSERT(IsShiftedRegister()); - ASSERT((shift_ == LSL) && (shift_amount_ <= 4)); - return Operand(reg_, reg_.Is64Bits() ? UXTX : UXTW, shift_amount_); -} - - -int64_t Operand::immediate() const { - ASSERT(IsImmediate()); - return immediate_; -} - - -Register Operand::reg() const { - ASSERT(IsShiftedRegister() || IsExtendedRegister()); - return reg_; -} - - -Shift Operand::shift() const { - ASSERT(IsShiftedRegister()); - return shift_; -} - - -Extend Operand::extend() const { - ASSERT(IsExtendedRegister()); - return extend_; -} - - -unsigned Operand::shift_amount() const { - ASSERT(IsShiftedRegister() || IsExtendedRegister()); - return shift_amount_; -} - - -Operand Operand::UntagSmi(Register smi) { - ASSERT(smi.Is64Bits()); - return Operand(smi, ASR, kSmiShift); -} - - -Operand Operand::UntagSmiAndScale(Register smi, int scale) { - ASSERT(smi.Is64Bits()); - ASSERT((scale >= 0) && (scale <= (64 - kSmiValueSize))); - if (scale > kSmiShift) { - return Operand(smi, LSL, scale - kSmiShift); - } else if (scale < kSmiShift) { - return Operand(smi, ASR, kSmiShift - scale); - } - return Operand(smi); -} - - -MemOperand::MemOperand(Register base, ptrdiff_t offset, AddrMode addrmode) - : base_(base), regoffset_(NoReg), offset_(offset), addrmode_(addrmode), - shift_(NO_SHIFT), extend_(NO_EXTEND), shift_amount_(0) { - ASSERT(base.Is64Bits() && !base.IsZero()); -} - - -MemOperand::MemOperand(Register base, - Register regoffset, - Extend extend, - unsigned shift_amount) - : base_(base), regoffset_(regoffset), offset_(0), addrmode_(Offset), - shift_(NO_SHIFT), extend_(extend), shift_amount_(shift_amount) { - ASSERT(base.Is64Bits() && !base.IsZero()); - ASSERT(!regoffset.IsSP()); - ASSERT((extend == UXTW) || (extend == SXTW) || (extend == SXTX)); - - // SXTX extend mode requires a 64-bit offset register. - ASSERT(regoffset.Is64Bits() || (extend != SXTX)); -} - - -MemOperand::MemOperand(Register base, - Register regoffset, - Shift shift, - unsigned shift_amount) - : base_(base), regoffset_(regoffset), offset_(0), addrmode_(Offset), - shift_(shift), extend_(NO_EXTEND), shift_amount_(shift_amount) { - ASSERT(base.Is64Bits() && !base.IsZero()); - ASSERT(regoffset.Is64Bits() && !regoffset.IsSP()); - ASSERT(shift == LSL); -} - - -MemOperand::MemOperand(Register base, const Operand& offset, AddrMode addrmode) - : base_(base), addrmode_(addrmode) { - ASSERT(base.Is64Bits() && !base.IsZero()); - - if (offset.IsImmediate()) { - offset_ = offset.immediate(); - - regoffset_ = NoReg; - } else if (offset.IsShiftedRegister()) { - ASSERT(addrmode == Offset); - - regoffset_ = offset.reg(); - shift_= offset.shift(); - shift_amount_ = offset.shift_amount(); - - extend_ = NO_EXTEND; - offset_ = 0; - - // These assertions match those in the shifted-register constructor. - ASSERT(regoffset_.Is64Bits() && !regoffset_.IsSP()); - ASSERT(shift_ == LSL); - } else { - ASSERT(offset.IsExtendedRegister()); - ASSERT(addrmode == Offset); - - regoffset_ = offset.reg(); - extend_ = offset.extend(); - shift_amount_ = offset.shift_amount(); - - shift_= NO_SHIFT; - offset_ = 0; - - // These assertions match those in the extended-register constructor. - ASSERT(!regoffset_.IsSP()); - ASSERT((extend_ == UXTW) || (extend_ == SXTW) || (extend_ == SXTX)); - ASSERT((regoffset_.Is64Bits() || (extend_ != SXTX))); - } -} - -bool MemOperand::IsImmediateOffset() const { - return (addrmode_ == Offset) && regoffset_.Is(NoReg); -} - - -bool MemOperand::IsRegisterOffset() const { - return (addrmode_ == Offset) && !regoffset_.Is(NoReg); -} - - -bool MemOperand::IsPreIndex() const { - return addrmode_ == PreIndex; -} - - -bool MemOperand::IsPostIndex() const { - return addrmode_ == PostIndex; -} - -Operand MemOperand::OffsetAsOperand() const { - if (IsImmediateOffset()) { - return offset(); - } else { - ASSERT(IsRegisterOffset()); - if (extend() == NO_EXTEND) { - return Operand(regoffset(), shift(), shift_amount()); - } else { - return Operand(regoffset(), extend(), shift_amount()); - } - } -} - - -void Assembler::Unreachable() { -#ifdef USE_SIMULATOR - debug("UNREACHABLE", __LINE__, BREAK); -#else - // Crash by branching to 0. lr now points near the fault. - Emit(BLR | Rn(xzr)); -#endif -} - - -Address Assembler::target_pointer_address_at(Address pc) { - Instruction* instr = reinterpret_cast(pc); - ASSERT(instr->IsLdrLiteralX()); - return reinterpret_cast
(instr->ImmPCOffsetTarget()); -} - - -// Read/Modify the code target address in the branch/call instruction at pc. -Address Assembler::target_address_at(Address pc) { - return Memory::Address_at(target_pointer_address_at(pc)); -} - - -Address Assembler::target_address_from_return_address(Address pc) { - // Returns the address of the call target from the return address that will - // be returned to after a call. - // Call sequence on A64 is: - // ldr ip0, #... @ load from literal pool - // blr ip0 - Address candidate = pc - 2 * kInstructionSize; - Instruction* instr = reinterpret_cast(candidate); - USE(instr); - ASSERT(instr->IsLdrLiteralX()); - return candidate; -} - - -Address Assembler::return_address_from_call_start(Address pc) { - // The call, generated by MacroAssembler::Call, is one of two possible - // sequences: - // - // Without relocation: - // movz ip0, #(target & 0x000000000000ffff) - // movk ip0, #(target & 0x00000000ffff0000) - // movk ip0, #(target & 0x0000ffff00000000) - // movk ip0, #(target & 0xffff000000000000) - // blr ip0 - // - // With relocation: - // ldr ip0, =target - // blr ip0 - // - // The return address is immediately after the blr instruction in both cases, - // so it can be found by adding the call size to the address at the start of - // the call sequence. - STATIC_ASSERT(Assembler::kCallSizeWithoutRelocation == 5 * kInstructionSize); - STATIC_ASSERT(Assembler::kCallSizeWithRelocation == 2 * kInstructionSize); - - Instruction* instr = reinterpret_cast(pc); - if (instr->IsMovz()) { - // Verify the instruction sequence. - ASSERT(instr->following(1)->IsMovk()); - ASSERT(instr->following(2)->IsMovk()); - ASSERT(instr->following(3)->IsMovk()); - ASSERT(instr->following(4)->IsBranchAndLinkToRegister()); - return pc + Assembler::kCallSizeWithoutRelocation; - } else { - // Verify the instruction sequence. - ASSERT(instr->IsLdrLiteralX()); - ASSERT(instr->following(1)->IsBranchAndLinkToRegister()); - return pc + Assembler::kCallSizeWithRelocation; - } -} - - -void Assembler::deserialization_set_special_target_at( - Address constant_pool_entry, Address target) { - Memory::Address_at(constant_pool_entry) = target; -} - - -void Assembler::set_target_address_at(Address pc, Address target) { - Memory::Address_at(target_pointer_address_at(pc)) = target; - // Intuitively, we would think it is necessary to always flush the - // instruction cache after patching a target address in the code as follows: - // CPU::FlushICache(pc, sizeof(target)); - // However, on ARM, an instruction is actually patched in the case of - // embedded constants of the form: - // ldr ip, [pc, #...] - // since the instruction accessing this address in the constant pool remains - // unchanged, a flush is not required. -} - - -int RelocInfo::target_address_size() { - return kPointerSize; -} - - -Address RelocInfo::target_address() { - ASSERT(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)); - return Assembler::target_address_at(pc_); -} - - -Address RelocInfo::target_address_address() { - ASSERT(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_) - || rmode_ == EMBEDDED_OBJECT - || rmode_ == EXTERNAL_REFERENCE); - return Assembler::target_pointer_address_at(pc_); -} - - -Object* RelocInfo::target_object() { - ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); - return reinterpret_cast(Assembler::target_address_at(pc_)); -} - - -Handle RelocInfo::target_object_handle(Assembler* origin) { - ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); - return Handle(reinterpret_cast( - Assembler::target_address_at(pc_))); -} - - -void RelocInfo::set_target_object(Object* target, WriteBarrierMode mode) { - ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); - ASSERT(!target->IsConsString()); - Assembler::set_target_address_at(pc_, reinterpret_cast
(target)); - if (mode == UPDATE_WRITE_BARRIER && - host() != NULL && - target->IsHeapObject()) { - host()->GetHeap()->incremental_marking()->RecordWrite( - host(), &Memory::Object_at(pc_), HeapObject::cast(target)); - } -} - - -Address RelocInfo::target_reference() { - ASSERT(rmode_ == EXTERNAL_REFERENCE); - return Assembler::target_address_at(pc_); -} - - -Address RelocInfo::target_runtime_entry(Assembler* origin) { - ASSERT(IsRuntimeEntry(rmode_)); - return target_address(); -} - - -void RelocInfo::set_target_runtime_entry(Address target, - WriteBarrierMode mode) { - ASSERT(IsRuntimeEntry(rmode_)); - if (target_address() != target) set_target_address(target, mode); -} - - -Handle RelocInfo::target_cell_handle() { - UNIMPLEMENTED(); - Cell *null_cell = NULL; - return Handle(null_cell); -} - - -Cell* RelocInfo::target_cell() { - ASSERT(rmode_ == RelocInfo::CELL); - return Cell::FromValueAddress(Memory::Address_at(pc_)); -} - - -void RelocInfo::set_target_cell(Cell* cell, WriteBarrierMode mode) { - UNIMPLEMENTED(); -} - - -static const int kCodeAgeSequenceSize = 5 * kInstructionSize; -static const int kCodeAgeStubEntryOffset = 3 * kInstructionSize; - - -Handle RelocInfo::code_age_stub_handle(Assembler* origin) { - UNREACHABLE(); // This should never be reached on A64. - return Handle(); -} - - -Code* RelocInfo::code_age_stub() { - ASSERT(rmode_ == RelocInfo::CODE_AGE_SEQUENCE); - ASSERT(!Code::IsYoungSequence(pc_)); - // Read the stub entry point from the code age sequence. - Address stub_entry_address = pc_ + kCodeAgeStubEntryOffset; - return Code::GetCodeFromTargetAddress(Memory::Address_at(stub_entry_address)); -} - - -void RelocInfo::set_code_age_stub(Code* stub) { - ASSERT(rmode_ == RelocInfo::CODE_AGE_SEQUENCE); - ASSERT(!Code::IsYoungSequence(pc_)); - // Overwrite the stub entry point in the code age sequence. This is loaded as - // a literal so there is no need to call FlushICache here. - Address stub_entry_address = pc_ + kCodeAgeStubEntryOffset; - Memory::Address_at(stub_entry_address) = stub->instruction_start(); -} - - -Address RelocInfo::call_address() { - ASSERT((IsJSReturn(rmode()) && IsPatchedReturnSequence()) || - (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence())); - // For the above sequences the Relocinfo points to the load literal loading - // the call address. - return Assembler::target_address_at(pc_); -} - - -void RelocInfo::set_call_address(Address target) { - ASSERT((IsJSReturn(rmode()) && IsPatchedReturnSequence()) || - (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence())); - Assembler::set_target_address_at(pc_, target); - if (host() != NULL) { - Object* target_code = Code::GetCodeFromTargetAddress(target); - host()->GetHeap()->incremental_marking()->RecordWriteIntoCode( - host(), this, HeapObject::cast(target_code)); - } -} - - -void RelocInfo::WipeOut() { - ASSERT(IsEmbeddedObject(rmode_) || - IsCodeTarget(rmode_) || - IsRuntimeEntry(rmode_) || - IsExternalReference(rmode_)); - Assembler::set_target_address_at(pc_, NULL); -} - - -bool RelocInfo::IsPatchedReturnSequence() { - // The sequence must be: - // ldr ip0, [pc, #offset] - // blr ip0 - // See a64/debug-a64.cc BreakLocationIterator::SetDebugBreakAtReturn(). - Instruction* i1 = reinterpret_cast(pc_); - Instruction* i2 = i1->following(); - return i1->IsLdrLiteralX() && (i1->Rt() == ip0.code()) && - i2->IsBranchAndLinkToRegister() && (i2->Rn() == ip0.code()); -} - - -bool RelocInfo::IsPatchedDebugBreakSlotSequence() { - Instruction* current_instr = reinterpret_cast(pc_); - return !current_instr->IsNop(Assembler::DEBUG_BREAK_NOP); -} - - -void RelocInfo::Visit(Isolate* isolate, ObjectVisitor* visitor) { - RelocInfo::Mode mode = rmode(); - if (mode == RelocInfo::EMBEDDED_OBJECT) { - visitor->VisitEmbeddedPointer(this); - } else if (RelocInfo::IsCodeTarget(mode)) { - visitor->VisitCodeTarget(this); - } else if (mode == RelocInfo::CELL) { - visitor->VisitCell(this); - } else if (mode == RelocInfo::EXTERNAL_REFERENCE) { - visitor->VisitExternalReference(this); -#ifdef ENABLE_DEBUGGER_SUPPORT - } else if (((RelocInfo::IsJSReturn(mode) && - IsPatchedReturnSequence()) || - (RelocInfo::IsDebugBreakSlot(mode) && - IsPatchedDebugBreakSlotSequence())) && - isolate->debug()->has_break_points()) { - visitor->VisitDebugTarget(this); -#endif - } else if (RelocInfo::IsRuntimeEntry(mode)) { - visitor->VisitRuntimeEntry(this); - } -} - - -template -void RelocInfo::Visit(Heap* heap) { - RelocInfo::Mode mode = rmode(); - if (mode == RelocInfo::EMBEDDED_OBJECT) { - StaticVisitor::VisitEmbeddedPointer(heap, this); - } 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); -#ifdef ENABLE_DEBUGGER_SUPPORT - } else if (heap->isolate()->debug()->has_break_points() && - ((RelocInfo::IsJSReturn(mode) && - IsPatchedReturnSequence()) || - (RelocInfo::IsDebugBreakSlot(mode) && - IsPatchedDebugBreakSlotSequence()))) { - StaticVisitor::VisitDebugTarget(heap, this); -#endif - } else if (RelocInfo::IsRuntimeEntry(mode)) { - StaticVisitor::VisitRuntimeEntry(this); - } -} - - -LoadStoreOp Assembler::LoadOpFor(const CPURegister& rt) { - ASSERT(rt.IsValid()); - if (rt.IsRegister()) { - return rt.Is64Bits() ? LDR_x : LDR_w; - } else { - ASSERT(rt.IsFPRegister()); - return rt.Is64Bits() ? LDR_d : LDR_s; - } -} - - -LoadStorePairOp Assembler::LoadPairOpFor(const CPURegister& rt, - const CPURegister& rt2) { - ASSERT(AreSameSizeAndType(rt, rt2)); - USE(rt2); - if (rt.IsRegister()) { - return rt.Is64Bits() ? LDP_x : LDP_w; - } else { - ASSERT(rt.IsFPRegister()); - return rt.Is64Bits() ? LDP_d : LDP_s; - } -} - - -LoadStoreOp Assembler::StoreOpFor(const CPURegister& rt) { - ASSERT(rt.IsValid()); - if (rt.IsRegister()) { - return rt.Is64Bits() ? STR_x : STR_w; - } else { - ASSERT(rt.IsFPRegister()); - return rt.Is64Bits() ? STR_d : STR_s; - } -} - - -LoadStorePairOp Assembler::StorePairOpFor(const CPURegister& rt, - const CPURegister& rt2) { - ASSERT(AreSameSizeAndType(rt, rt2)); - USE(rt2); - if (rt.IsRegister()) { - return rt.Is64Bits() ? STP_x : STP_w; - } else { - ASSERT(rt.IsFPRegister()); - return rt.Is64Bits() ? STP_d : STP_s; - } -} - - -LoadStorePairNonTemporalOp Assembler::LoadPairNonTemporalOpFor( - const CPURegister& rt, const CPURegister& rt2) { - ASSERT(AreSameSizeAndType(rt, rt2)); - USE(rt2); - if (rt.IsRegister()) { - return rt.Is64Bits() ? LDNP_x : LDNP_w; - } else { - ASSERT(rt.IsFPRegister()); - return rt.Is64Bits() ? LDNP_d : LDNP_s; - } -} - - -LoadStorePairNonTemporalOp Assembler::StorePairNonTemporalOpFor( - const CPURegister& rt, const CPURegister& rt2) { - ASSERT(AreSameSizeAndType(rt, rt2)); - USE(rt2); - if (rt.IsRegister()) { - return rt.Is64Bits() ? STNP_x : STNP_w; - } else { - ASSERT(rt.IsFPRegister()); - return rt.Is64Bits() ? STNP_d : STNP_s; - } -} - - -int Assembler::LinkAndGetInstructionOffsetTo(Label* label) { - ASSERT(kStartOfLabelLinkChain == 0); - int offset = LinkAndGetByteOffsetTo(label); - ASSERT(IsAligned(offset, kInstructionSize)); - return offset >> kInstructionSizeLog2; -} - - -Instr Assembler::Flags(FlagsUpdate S) { - if (S == SetFlags) { - return 1 << FlagsUpdate_offset; - } else if (S == LeaveFlags) { - return 0 << FlagsUpdate_offset; - } - UNREACHABLE(); - return 0; -} - - -Instr Assembler::Cond(Condition cond) { - return cond << Condition_offset; -} - - -Instr Assembler::ImmPCRelAddress(int imm21) { - CHECK(is_int21(imm21)); - Instr imm = static_cast(truncate_to_int21(imm21)); - Instr immhi = (imm >> ImmPCRelLo_width) << ImmPCRelHi_offset; - Instr immlo = imm << ImmPCRelLo_offset; - return (immhi & ImmPCRelHi_mask) | (immlo & ImmPCRelLo_mask); -} - - -Instr Assembler::ImmUncondBranch(int imm26) { - CHECK(is_int26(imm26)); - return truncate_to_int26(imm26) << ImmUncondBranch_offset; -} - - -Instr Assembler::ImmCondBranch(int imm19) { - CHECK(is_int19(imm19)); - return truncate_to_int19(imm19) << ImmCondBranch_offset; -} - - -Instr Assembler::ImmCmpBranch(int imm19) { - CHECK(is_int19(imm19)); - return truncate_to_int19(imm19) << ImmCmpBranch_offset; -} - - -Instr Assembler::ImmTestBranch(int imm14) { - CHECK(is_int14(imm14)); - return truncate_to_int14(imm14) << ImmTestBranch_offset; -} - - -Instr Assembler::ImmTestBranchBit(unsigned bit_pos) { - ASSERT(is_uint6(bit_pos)); - // Subtract five from the shift offset, as we need bit 5 from bit_pos. - unsigned b5 = bit_pos << (ImmTestBranchBit5_offset - 5); - unsigned b40 = bit_pos << ImmTestBranchBit40_offset; - b5 &= ImmTestBranchBit5_mask; - b40 &= ImmTestBranchBit40_mask; - return b5 | b40; -} - - -Instr Assembler::SF(Register rd) { - return rd.Is64Bits() ? SixtyFourBits : ThirtyTwoBits; -} - - -Instr Assembler::ImmAddSub(int64_t imm) { - ASSERT(IsImmAddSub(imm)); - if (is_uint12(imm)) { // No shift required. - return imm << ImmAddSub_offset; - } else { - return ((imm >> 12) << ImmAddSub_offset) | (1 << ShiftAddSub_offset); - } -} - - -Instr Assembler::ImmS(unsigned imms, unsigned reg_size) { - ASSERT(((reg_size == kXRegSize) && is_uint6(imms)) || - ((reg_size == kWRegSize) && is_uint5(imms))); - USE(reg_size); - return imms << ImmS_offset; -} - - -Instr Assembler::ImmR(unsigned immr, unsigned reg_size) { - ASSERT(((reg_size == kXRegSize) && is_uint6(immr)) || - ((reg_size == kWRegSize) && is_uint5(immr))); - USE(reg_size); - ASSERT(is_uint6(immr)); - return immr << ImmR_offset; -} - - -Instr Assembler::ImmSetBits(unsigned imms, unsigned reg_size) { - ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); - ASSERT(is_uint6(imms)); - ASSERT((reg_size == kXRegSize) || is_uint6(imms + 3)); - USE(reg_size); - return imms << ImmSetBits_offset; -} - - -Instr Assembler::ImmRotate(unsigned immr, unsigned reg_size) { - ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); - ASSERT(((reg_size == kXRegSize) && is_uint6(immr)) || - ((reg_size == kWRegSize) && is_uint5(immr))); - USE(reg_size); - return immr << ImmRotate_offset; -} - - -Instr Assembler::ImmLLiteral(int imm19) { - CHECK(is_int19(imm19)); - return truncate_to_int19(imm19) << ImmLLiteral_offset; -} - - -Instr Assembler::BitN(unsigned bitn, unsigned reg_size) { - ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); - ASSERT((reg_size == kXRegSize) || (bitn == 0)); - USE(reg_size); - return bitn << BitN_offset; -} - - -Instr Assembler::ShiftDP(Shift shift) { - ASSERT(shift == LSL || shift == LSR || shift == ASR || shift == ROR); - return shift << ShiftDP_offset; -} - - -Instr Assembler::ImmDPShift(unsigned amount) { - ASSERT(is_uint6(amount)); - return amount << ImmDPShift_offset; -} - - -Instr Assembler::ExtendMode(Extend extend) { - return extend << ExtendMode_offset; -} - - -Instr Assembler::ImmExtendShift(unsigned left_shift) { - ASSERT(left_shift <= 4); - return left_shift << ImmExtendShift_offset; -} - - -Instr Assembler::ImmCondCmp(unsigned imm) { - ASSERT(is_uint5(imm)); - return imm << ImmCondCmp_offset; -} - - -Instr Assembler::Nzcv(StatusFlags nzcv) { - return ((nzcv >> Flags_offset) & 0xf) << Nzcv_offset; -} - - -Instr Assembler::ImmLSUnsigned(int imm12) { - ASSERT(is_uint12(imm12)); - return imm12 << ImmLSUnsigned_offset; -} - - -Instr Assembler::ImmLS(int imm9) { - ASSERT(is_int9(imm9)); - return truncate_to_int9(imm9) << ImmLS_offset; -} - - -Instr Assembler::ImmLSPair(int imm7, LSDataSize size) { - ASSERT(((imm7 >> size) << size) == imm7); - int scaled_imm7 = imm7 >> size; - ASSERT(is_int7(scaled_imm7)); - return truncate_to_int7(scaled_imm7) << ImmLSPair_offset; -} - - -Instr Assembler::ImmShiftLS(unsigned shift_amount) { - ASSERT(is_uint1(shift_amount)); - return shift_amount << ImmShiftLS_offset; -} - - -Instr Assembler::ImmException(int imm16) { - ASSERT(is_uint16(imm16)); - return imm16 << ImmException_offset; -} - - -Instr Assembler::ImmSystemRegister(int imm15) { - ASSERT(is_uint15(imm15)); - return imm15 << ImmSystemRegister_offset; -} - - -Instr Assembler::ImmHint(int imm7) { - ASSERT(is_uint7(imm7)); - return imm7 << ImmHint_offset; -} - - -Instr Assembler::ImmBarrierDomain(int imm2) { - ASSERT(is_uint2(imm2)); - return imm2 << ImmBarrierDomain_offset; -} - - -Instr Assembler::ImmBarrierType(int imm2) { - ASSERT(is_uint2(imm2)); - return imm2 << ImmBarrierType_offset; -} - - -LSDataSize Assembler::CalcLSDataSize(LoadStoreOp op) { - ASSERT((SizeLS_offset + SizeLS_width) == (kInstructionSize * 8)); - return static_cast(op >> SizeLS_offset); -} - - -Instr Assembler::ImmMoveWide(uint64_t imm) { - ASSERT(is_uint16(imm)); - return imm << ImmMoveWide_offset; -} - - -Instr Assembler::ShiftMoveWide(int64_t shift) { - ASSERT(is_uint2(shift)); - return shift << ShiftMoveWide_offset; -} - - -Instr Assembler::FPType(FPRegister fd) { - return fd.Is64Bits() ? FP64 : FP32; -} - - -Instr Assembler::FPScale(unsigned scale) { - ASSERT(is_uint6(scale)); - return scale << FPScale_offset; -} - - -const Register& Assembler::AppropriateZeroRegFor(const CPURegister& reg) const { - return reg.Is64Bits() ? xzr : wzr; -} - - -void Assembler::LoadRelocated(const CPURegister& rt, const Operand& operand) { - LoadRelocatedValue(rt, operand, LDR_x_lit); -} - - -inline void Assembler::CheckBuffer() { - ASSERT(pc_ < (buffer_ + buffer_size_)); - if (buffer_space() < kGap) { - GrowBuffer(); - } - if (pc_offset() >= next_buffer_check_) { - CheckConstPool(false, true); - } -} - - -TypeFeedbackId Assembler::RecordedAstId() { - ASSERT(!recorded_ast_id_.IsNone()); - return recorded_ast_id_; -} - - -void Assembler::ClearRecordedAstId() { - recorded_ast_id_ = TypeFeedbackId::None(); -} - - -} } // namespace v8::internal - -#endif // V8_A64_ASSEMBLER_A64_INL_H_ diff --git a/deps/v8/src/a64/assembler-a64.cc b/deps/v8/src/a64/assembler-a64.cc deleted file mode 100644 index 43b1391605..0000000000 --- a/deps/v8/src/a64/assembler-a64.cc +++ /dev/null @@ -1,2606 +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 "v8.h" - -#if V8_TARGET_ARCH_A64 - -#define A64_DEFINE_REG_STATICS - -#include "a64/assembler-a64-inl.h" - -namespace v8 { -namespace internal { - - -// ----------------------------------------------------------------------------- -// CpuFeatures utilities (for V8 compatibility). - -ExternalReference ExternalReference::cpu_features() { - return ExternalReference(&CpuFeatures::supported_); -} - - -// ----------------------------------------------------------------------------- -// CPURegList utilities. - -CPURegister CPURegList::PopLowestIndex() { - ASSERT(IsValid()); - if (IsEmpty()) { - return NoCPUReg; - } - int index = CountTrailingZeros(list_, kRegListSizeInBits); - ASSERT((1 << index) & list_); - Remove(index); - return CPURegister::Create(index, size_, type_); -} - - -CPURegister CPURegList::PopHighestIndex() { - ASSERT(IsValid()); - if (IsEmpty()) { - return NoCPUReg; - } - int index = CountLeadingZeros(list_, kRegListSizeInBits); - index = kRegListSizeInBits - 1 - index; - ASSERT((1 << index) & list_); - Remove(index); - return CPURegister::Create(index, size_, type_); -} - - -void CPURegList::RemoveCalleeSaved() { - if (type() == CPURegister::kRegister) { - Remove(GetCalleeSaved(RegisterSizeInBits())); - } else if (type() == CPURegister::kFPRegister) { - Remove(GetCalleeSavedFP(RegisterSizeInBits())); - } else { - ASSERT(type() == CPURegister::kNoRegister); - ASSERT(IsEmpty()); - // The list must already be empty, so do nothing. - } -} - - -CPURegList CPURegList::GetCalleeSaved(unsigned size) { - return CPURegList(CPURegister::kRegister, size, 19, 29); -} - - -CPURegList CPURegList::GetCalleeSavedFP(unsigned size) { - return CPURegList(CPURegister::kFPRegister, size, 8, 15); -} - - -CPURegList CPURegList::GetCallerSaved(unsigned size) { - // Registers x0-x18 and lr (x30) are caller-saved. - CPURegList list = CPURegList(CPURegister::kRegister, size, 0, 18); - list.Combine(lr); - return list; -} - - -CPURegList CPURegList::GetCallerSavedFP(unsigned size) { - // Registers d0-d7 and d16-d31 are caller-saved. - CPURegList list = CPURegList(CPURegister::kFPRegister, size, 0, 7); - list.Combine(CPURegList(CPURegister::kFPRegister, size, 16, 31)); - return list; -} - - -// This function defines the list of registers which are associated with a -// safepoint slot. Safepoint register slots are saved contiguously on the stack. -// MacroAssembler::SafepointRegisterStackIndex handles mapping from register -// code to index in the safepoint register slots. Any change here can affect -// this mapping. -CPURegList CPURegList::GetSafepointSavedRegisters() { - CPURegList list = CPURegList::GetCalleeSaved(); - list.Combine(CPURegList(CPURegister::kRegister, kXRegSize, kJSCallerSaved)); - - // Note that unfortunately we can't use symbolic names for registers and have - // to directly use register codes. This is because this function is used to - // initialize some static variables and we can't rely on register variables - // to be initialized due to static initialization order issues in C++. - - // Drop ip0 and ip1 (i.e. x16 and x17), as they should not be expected to be - // preserved outside of the macro assembler. - list.Remove(16); - list.Remove(17); - - // Add x18 to the safepoint list, as although it's not in kJSCallerSaved, it - // is a caller-saved register according to the procedure call standard. - list.Combine(18); - - // Drop jssp as the stack pointer doesn't need to be included. - list.Remove(28); - - // Add the link register (x30) to the safepoint list. - list.Combine(30); - - return list; -} - - -// ----------------------------------------------------------------------------- -// Implementation of RelocInfo - -const int RelocInfo::kApplyMask = 0; - - -bool RelocInfo::IsCodedSpecially() { - // The deserializer needs to know whether a pointer is specially coded. Being - // specially coded on A64 means that it is a movz/movk sequence. We don't - // generate those for relocatable pointers. - return false; -} - - -void RelocInfo::PatchCode(byte* instructions, int instruction_count) { - // Patch the code at the current address with the supplied instructions. - Instr* pc = reinterpret_cast(pc_); - Instr* instr = reinterpret_cast(instructions); - for (int i = 0; i < instruction_count; i++) { - *(pc + i) = *(instr + i); - } - - // Indicate that code has changed. - CPU::FlushICache(pc_, instruction_count * kInstructionSize); -} - - -// Patch the code at the current PC with a call to the target address. -// Additional guard instructions can be added if required. -void RelocInfo::PatchCodeWithCall(Address target, int guard_bytes) { - UNIMPLEMENTED(); -} - - -Register GetAllocatableRegisterThatIsNotOneOf(Register reg1, Register reg2, - Register reg3, Register reg4) { - CPURegList regs(reg1, reg2, reg3, reg4); - for (int i = 0; i < Register::NumAllocatableRegisters(); i++) { - Register candidate = Register::FromAllocationIndex(i); - if (regs.IncludesAliasOf(candidate)) continue; - return candidate; - } - UNREACHABLE(); - return NoReg; -} - - -bool AreAliased(const CPURegister& reg1, const CPURegister& reg2, - const CPURegister& reg3, const CPURegister& reg4, - const CPURegister& reg5, const CPURegister& reg6, - const CPURegister& reg7, const CPURegister& reg8) { - int number_of_valid_regs = 0; - int number_of_valid_fpregs = 0; - - RegList unique_regs = 0; - RegList unique_fpregs = 0; - - const CPURegister regs[] = {reg1, reg2, reg3, reg4, reg5, reg6, reg7, reg8}; - - for (unsigned i = 0; i < sizeof(regs) / sizeof(regs[0]); i++) { - if (regs[i].IsRegister()) { - number_of_valid_regs++; - unique_regs |= regs[i].Bit(); - } else if (regs[i].IsFPRegister()) { - number_of_valid_fpregs++; - unique_fpregs |= regs[i].Bit(); - } else { - ASSERT(!regs[i].IsValid()); - } - } - - int number_of_unique_regs = - CountSetBits(unique_regs, sizeof(unique_regs) * kBitsPerByte); - int number_of_unique_fpregs = - CountSetBits(unique_fpregs, sizeof(unique_fpregs) * kBitsPerByte); - - ASSERT(number_of_valid_regs >= number_of_unique_regs); - ASSERT(number_of_valid_fpregs >= number_of_unique_fpregs); - - return (number_of_valid_regs != number_of_unique_regs) || - (number_of_valid_fpregs != number_of_unique_fpregs); -} - - -bool AreSameSizeAndType(const CPURegister& reg1, const CPURegister& reg2, - const CPURegister& reg3, const CPURegister& reg4, - const CPURegister& reg5, const CPURegister& reg6, - const CPURegister& reg7, const CPURegister& reg8) { - ASSERT(reg1.IsValid()); - bool match = true; - match &= !reg2.IsValid() || reg2.IsSameSizeAndType(reg1); - match &= !reg3.IsValid() || reg3.IsSameSizeAndType(reg1); - match &= !reg4.IsValid() || reg4.IsSameSizeAndType(reg1); - match &= !reg5.IsValid() || reg5.IsSameSizeAndType(reg1); - match &= !reg6.IsValid() || reg6.IsSameSizeAndType(reg1); - match &= !reg7.IsValid() || reg7.IsSameSizeAndType(reg1); - match &= !reg8.IsValid() || reg8.IsSameSizeAndType(reg1); - return match; -} - - -void Operand::initialize_handle(Handle handle) { - AllowDeferredHandleDereference using_raw_address; - - // Verify all Objects referred by code are NOT in new space. - Object* obj = *handle; - if (obj->IsHeapObject()) { - ASSERT(!HeapObject::cast(obj)->GetHeap()->InNewSpace(obj)); - immediate_ = reinterpret_cast(handle.location()); - rmode_ = RelocInfo::EMBEDDED_OBJECT; - } else { - STATIC_ASSERT(sizeof(intptr_t) == sizeof(int64_t)); - immediate_ = reinterpret_cast(obj); - rmode_ = RelocInfo::NONE64; - } -} - - -bool Operand::NeedsRelocation() const { - if (rmode_ == RelocInfo::EXTERNAL_REFERENCE) { -#ifdef DEBUG - if (!Serializer::enabled()) { - Serializer::TooLateToEnableNow(); - } -#endif - return Serializer::enabled(); - } - - return !RelocInfo::IsNone(rmode_); -} - - -// Assembler - -Assembler::Assembler(Isolate* isolate, void* buffer, int buffer_size) - : AssemblerBase(isolate, buffer, buffer_size), - recorded_ast_id_(TypeFeedbackId::None()), - unresolved_branches_(), - positions_recorder_(this) { - const_pool_blocked_nesting_ = 0; - Reset(); -} - - -Assembler::~Assembler() { - ASSERT(num_pending_reloc_info_ == 0); - ASSERT(const_pool_blocked_nesting_ == 0); -} - - -void Assembler::Reset() { -#ifdef DEBUG - ASSERT((pc_ >= buffer_) && (pc_ < buffer_ + buffer_size_)); - ASSERT(const_pool_blocked_nesting_ == 0); - memset(buffer_, 0, pc_ - buffer_); -#endif - pc_ = buffer_; - reloc_info_writer.Reposition(reinterpret_cast(buffer_ + buffer_size_), - reinterpret_cast(pc_)); - num_pending_reloc_info_ = 0; - next_buffer_check_ = 0; - no_const_pool_before_ = 0; - first_const_pool_use_ = -1; - ClearRecordedAstId(); -} - - -void Assembler::GetCode(CodeDesc* desc) { - // Emit constant pool if necessary. - CheckConstPool(true, false); - ASSERT(num_pending_reloc_info_ == 0); - - // Set up code descriptor. - if (desc) { - desc->buffer = reinterpret_cast(buffer_); - desc->buffer_size = buffer_size_; - desc->instr_size = pc_offset(); - desc->reloc_size = (reinterpret_cast(buffer_) + buffer_size_) - - reloc_info_writer.pos(); - desc->origin = this; - } -} - - -void Assembler::Align(int m) { - ASSERT(m >= 4 && IsPowerOf2(m)); - while ((pc_offset() & (m - 1)) != 0) { - nop(); - } -} - - -void Assembler::CheckLabelLinkChain(Label const * label) { -#ifdef DEBUG - if (label->is_linked()) { - int linkoffset = label->pos(); - bool end_of_chain = false; - while (!end_of_chain) { - Instruction * link = InstructionAt(linkoffset); - int linkpcoffset = link->ImmPCOffset(); - int prevlinkoffset = linkoffset + linkpcoffset; - - end_of_chain = (linkoffset == prevlinkoffset); - linkoffset = linkoffset + linkpcoffset; - } - } -#endif -} - - -void Assembler::RemoveBranchFromLabelLinkChain(Instruction* branch, - Label* label, - Instruction* label_veneer) { - ASSERT(label->is_linked()); - - CheckLabelLinkChain(label); - - Instruction* link = InstructionAt(label->pos()); - Instruction* prev_link = link; - Instruction* next_link; - bool end_of_chain = false; - - while (link != branch && !end_of_chain) { - next_link = link->ImmPCOffsetTarget(); - end_of_chain = (link == next_link); - prev_link = link; - link = next_link; - } - - ASSERT(branch == link); - next_link = branch->ImmPCOffsetTarget(); - - if (branch == prev_link) { - // The branch is the first instruction in the chain. - if (branch == next_link) { - // It is also the last instruction in the chain, so it is the only branch - // currently referring to this label. - label->Unuse(); - } else { - label->link_to(reinterpret_cast(next_link) - buffer_); - } - - } else if (branch == next_link) { - // The branch is the last (but not also the first) instruction in the chain. - prev_link->SetImmPCOffsetTarget(prev_link); - - } else { - // The branch is in the middle of the chain. - if (prev_link->IsTargetInImmPCOffsetRange(next_link)) { - prev_link->SetImmPCOffsetTarget(next_link); - } else if (label_veneer != NULL) { - // Use the veneer for all previous links in the chain. - prev_link->SetImmPCOffsetTarget(prev_link); - - end_of_chain = false; - link = next_link; - while (!end_of_chain) { - next_link = link->ImmPCOffsetTarget(); - end_of_chain = (link == next_link); - link->SetImmPCOffsetTarget(label_veneer); - link = next_link; - } - } else { - // The assert below will fire. - // Some other work could be attempted to fix up the chain, but it would be - // rather complicated. If we crash here, we may want to consider using an - // other mechanism than a chain of branches. - // - // Note that this situation currently should not happen, as we always call - // this function with a veneer to the target label. - // However this could happen with a MacroAssembler in the following state: - // [previous code] - // B(label); - // [20KB code] - // Tbz(label); // First tbz. Pointing to unconditional branch. - // [20KB code] - // Tbz(label); // Second tbz. Pointing to the first tbz. - // [more code] - // and this function is called to remove the first tbz from the label link - // chain. Since tbz has a range of +-32KB, the second tbz cannot point to - // the unconditional branch. - CHECK(prev_link->IsTargetInImmPCOffsetRange(next_link)); - UNREACHABLE(); - } - } - - CheckLabelLinkChain(label); -} - - -void Assembler::bind(Label* label) { - // Bind label to the address at pc_. All instructions (most likely branches) - // that are linked to this label will be updated to point to the newly-bound - // label. - - ASSERT(!label->is_near_linked()); - ASSERT(!label->is_bound()); - - // If the label is linked, the link chain looks something like this: - // - // |--I----I-------I-------L - // |---------------------->| pc_offset - // |-------------->| linkoffset = label->pos() - // |<------| link->ImmPCOffset() - // |------>| prevlinkoffset = linkoffset + link->ImmPCOffset() - // - // On each iteration, the last link is updated and then removed from the - // chain until only one remains. At that point, the label is bound. - // - // If the label is not linked, no preparation is required before binding. - while (label->is_linked()) { - int linkoffset = label->pos(); - Instruction* link = InstructionAt(linkoffset); - int prevlinkoffset = linkoffset + link->ImmPCOffset(); - - CheckLabelLinkChain(label); - - ASSERT(linkoffset >= 0); - ASSERT(linkoffset < pc_offset()); - ASSERT((linkoffset > prevlinkoffset) || - (linkoffset - prevlinkoffset == kStartOfLabelLinkChain)); - ASSERT(prevlinkoffset >= 0); - - // Update the link to point to the label. - link->SetImmPCOffsetTarget(reinterpret_cast(pc_)); - - // Link the label to the previous link in the chain. - if (linkoffset - prevlinkoffset == kStartOfLabelLinkChain) { - // We hit kStartOfLabelLinkChain, so the chain is fully processed. - label->Unuse(); - } else { - // Update the label for the next iteration. - label->link_to(prevlinkoffset); - } - } - label->bind_to(pc_offset()); - - ASSERT(label->is_bound()); - ASSERT(!label->is_linked()); - - DeleteUnresolvedBranchInfoForLabel(label); -} - - -int Assembler::LinkAndGetByteOffsetTo(Label* label) { - ASSERT(sizeof(*pc_) == 1); - CheckLabelLinkChain(label); - - int offset; - if (label->is_bound()) { - // The label is bound, so it does not need to be updated. Referring - // instructions must link directly to the label as they will not be - // updated. - // - // In this case, label->pos() returns the offset of the label from the - // start of the buffer. - // - // Note that offset can be zero for self-referential instructions. (This - // could be useful for ADR, for example.) - offset = label->pos() - pc_offset(); - ASSERT(offset <= 0); - } else { - if (label->is_linked()) { - // The label is linked, so the referring instruction should be added onto - // the end of the label's link chain. - // - // In this case, label->pos() returns the offset of the last linked - // instruction from the start of the buffer. - offset = label->pos() - pc_offset(); - ASSERT(offset != kStartOfLabelLinkChain); - // Note that the offset here needs to be PC-relative only so that the - // first instruction in a buffer can link to an unbound label. Otherwise, - // the offset would be 0 for this case, and 0 is reserved for - // kStartOfLabelLinkChain. - } else { - // The label is unused, so it now becomes linked and the referring - // instruction is at the start of the new link chain. - offset = kStartOfLabelLinkChain; - } - // The instruction at pc is now the last link in the label's chain. - label->link_to(pc_offset()); - } - - return offset; -} - - -void Assembler::DeleteUnresolvedBranchInfoForLabel(Label* label) { - // Branches to this label will be resolved when the label is bound below. - std::multimap::iterator it_tmp, it; - it = unresolved_branches_.begin(); - while (it != unresolved_branches_.end()) { - it_tmp = it++; - if (it_tmp->second.label_ == label) { - CHECK(it_tmp->first >= pc_offset()); - unresolved_branches_.erase(it_tmp); - } - } -} - - -void Assembler::StartBlockConstPool() { - if (const_pool_blocked_nesting_++ == 0) { - // Prevent constant pool checks happening by setting the next check to - // the biggest possible offset. - next_buffer_check_ = kMaxInt; - } -} - - -void Assembler::EndBlockConstPool() { - if (--const_pool_blocked_nesting_ == 0) { - // Check the constant pool hasn't been blocked for too long. - ASSERT((num_pending_reloc_info_ == 0) || - (pc_offset() < (first_const_pool_use_ + kMaxDistToPool))); - // Two cases: - // * no_const_pool_before_ >= next_buffer_check_ and the emission is - // still blocked - // * no_const_pool_before_ < next_buffer_check_ and the next emit will - // trigger a check. - next_buffer_check_ = no_const_pool_before_; - } -} - - -bool Assembler::is_const_pool_blocked() const { - return (const_pool_blocked_nesting_ > 0) || - (pc_offset() < no_const_pool_before_); -} - - -bool Assembler::IsConstantPoolAt(Instruction* instr) { - // The constant pool marker is made of two instructions. These instructions - // will never be emitted by the JIT, so checking for the first one is enough: - // 0: ldr xzr, # - bool result = instr->IsLdrLiteralX() && (instr->Rt() == xzr.code()); - - // It is still worth asserting the marker is complete. - // 4: blr xzr - ASSERT(!result || (instr->following()->IsBranchAndLinkToRegister() && - instr->following()->Rn() == xzr.code())); - - return result; -} - - -int Assembler::ConstantPoolSizeAt(Instruction* instr) { - if (IsConstantPoolAt(instr)) { - return instr->ImmLLiteral(); - } else { - return -1; - } -} - - -void Assembler::ConstantPoolMarker(uint32_t size) { - ASSERT(is_const_pool_blocked()); - // + 1 is for the crash guard. - Emit(LDR_x_lit | ImmLLiteral(2 * size + 1) | Rt(xzr)); -} - - -void Assembler::ConstantPoolGuard() { -#ifdef DEBUG - // Currently this is only used after a constant pool marker. - ASSERT(is_const_pool_blocked()); - Instruction* instr = reinterpret_cast(pc_); - ASSERT(instr->preceding()->IsLdrLiteralX() && - instr->preceding()->Rt() == xzr.code()); -#endif - - // We must generate only one instruction. - Emit(BLR | Rn(xzr)); -} - - -void Assembler::br(const Register& xn) { - positions_recorder()->WriteRecordedPositions(); - ASSERT(xn.Is64Bits()); - Emit(BR | Rn(xn)); -} - - -void Assembler::blr(const Register& xn) { - positions_recorder()->WriteRecordedPositions(); - ASSERT(xn.Is64Bits()); - // The pattern 'blr xzr' is used as a guard to detect when execution falls - // through the constant pool. It should not be emitted. - ASSERT(!xn.Is(xzr)); - Emit(BLR | Rn(xn)); -} - - -void Assembler::ret(const Register& xn) { - positions_recorder()->WriteRecordedPositions(); - ASSERT(xn.Is64Bits()); - Emit(RET | Rn(xn)); -} - - -void Assembler::b(int imm26) { - Emit(B | ImmUncondBranch(imm26)); -} - - -void Assembler::b(Label* label) { - positions_recorder()->WriteRecordedPositions(); - b(LinkAndGetInstructionOffsetTo(label)); -} - - -void Assembler::b(int imm19, Condition cond) { - Emit(B_cond | ImmCondBranch(imm19) | cond); -} - - -void Assembler::b(Label* label, Condition cond) { - positions_recorder()->WriteRecordedPositions(); - b(LinkAndGetInstructionOffsetTo(label), cond); -} - - -void Assembler::bl(int imm26) { - positions_recorder()->WriteRecordedPositions(); - Emit(BL | ImmUncondBranch(imm26)); -} - - -void Assembler::bl(Label* label) { - positions_recorder()->WriteRecordedPositions(); - bl(LinkAndGetInstructionOffsetTo(label)); -} - - -void Assembler::cbz(const Register& rt, - int imm19) { - positions_recorder()->WriteRecordedPositions(); - Emit(SF(rt) | CBZ | ImmCmpBranch(imm19) | Rt(rt)); -} - - -void Assembler::cbz(const Register& rt, - Label* label) { - positions_recorder()->WriteRecordedPositions(); - cbz(rt, LinkAndGetInstructionOffsetTo(label)); -} - - -void Assembler::cbnz(const Register& rt, - int imm19) { - positions_recorder()->WriteRecordedPositions(); - Emit(SF(rt) | CBNZ | ImmCmpBranch(imm19) | Rt(rt)); -} - - -void Assembler::cbnz(const Register& rt, - Label* label) { - positions_recorder()->WriteRecordedPositions(); - cbnz(rt, LinkAndGetInstructionOffsetTo(label)); -} - - -void Assembler::tbz(const Register& rt, - unsigned bit_pos, - int imm14) { - positions_recorder()->WriteRecordedPositions(); - ASSERT(rt.Is64Bits() || (rt.Is32Bits() && (bit_pos < kWRegSize))); - Emit(TBZ | ImmTestBranchBit(bit_pos) | ImmTestBranch(imm14) | Rt(rt)); -} - - -void Assembler::tbz(const Register& rt, - unsigned bit_pos, - Label* label) { - positions_recorder()->WriteRecordedPositions(); - tbz(rt, bit_pos, LinkAndGetInstructionOffsetTo(label)); -} - - -void Assembler::tbnz(const Register& rt, - unsigned bit_pos, - int imm14) { - positions_recorder()->WriteRecordedPositions(); - ASSERT(rt.Is64Bits() || (rt.Is32Bits() && (bit_pos < kWRegSize))); - Emit(TBNZ | ImmTestBranchBit(bit_pos) | ImmTestBranch(imm14) | Rt(rt)); -} - - -void Assembler::tbnz(const Register& rt, - unsigned bit_pos, - Label* label) { - positions_recorder()->WriteRecordedPositions(); - tbnz(rt, bit_pos, LinkAndGetInstructionOffsetTo(label)); -} - - -void Assembler::adr(const Register& rd, int imm21) { - ASSERT(rd.Is64Bits()); - Emit(ADR | ImmPCRelAddress(imm21) | Rd(rd)); -} - - -void Assembler::adr(const Register& rd, Label* label) { - adr(rd, LinkAndGetByteOffsetTo(label)); -} - - -void Assembler::add(const Register& rd, - const Register& rn, - const Operand& operand) { - AddSub(rd, rn, operand, LeaveFlags, ADD); -} - - -void Assembler::adds(const Register& rd, - const Register& rn, - const Operand& operand) { - AddSub(rd, rn, operand, SetFlags, ADD); -} - - -void Assembler::cmn(const Register& rn, - const Operand& operand) { - Register zr = AppropriateZeroRegFor(rn); - adds(zr, rn, operand); -} - - -void Assembler::sub(const Register& rd, - const Register& rn, - const Operand& operand) { - AddSub(rd, rn, operand, LeaveFlags, SUB); -} - - -void Assembler::subs(const Register& rd, - const Register& rn, - const Operand& operand) { - AddSub(rd, rn, operand, SetFlags, SUB); -} - - -void Assembler::cmp(const Register& rn, const Operand& operand) { - Register zr = AppropriateZeroRegFor(rn); - subs(zr, rn, operand); -} - - -void Assembler::neg(const Register& rd, const Operand& operand) { - Register zr = AppropriateZeroRegFor(rd); - sub(rd, zr, operand); -} - - -void Assembler::negs(const Register& rd, const Operand& operand) { - Register zr = AppropriateZeroRegFor(rd); - subs(rd, zr, operand); -} - - -void Assembler::adc(const Register& rd, - const Register& rn, - const Operand& operand) { - AddSubWithCarry(rd, rn, operand, LeaveFlags, ADC); -} - - -void Assembler::adcs(const Register& rd, - const Register& rn, - const Operand& operand) { - AddSubWithCarry(rd, rn, operand, SetFlags, ADC); -} - - -void Assembler::sbc(const Register& rd, - const Register& rn, - const Operand& operand) { - AddSubWithCarry(rd, rn, operand, LeaveFlags, SBC); -} - - -void Assembler::sbcs(const Register& rd, - const Register& rn, - const Operand& operand) { - AddSubWithCarry(rd, rn, operand, SetFlags, SBC); -} - - -void Assembler::ngc(const Register& rd, const Operand& operand) { - Register zr = AppropriateZeroRegFor(rd); - sbc(rd, zr, operand); -} - - -void Assembler::ngcs(const Register& rd, const Operand& operand) { - Register zr = AppropriateZeroRegFor(rd); - sbcs(rd, zr, operand); -} - - -// Logical instructions. -void Assembler::and_(const Register& rd, - const Register& rn, - const Operand& operand) { - Logical(rd, rn, operand, AND); -} - - -void Assembler::ands(const Register& rd, - const Register& rn, - const Operand& operand) { - Logical(rd, rn, operand, ANDS); -} - - -void Assembler::tst(const Register& rn, - const Operand& operand) { - ands(AppropriateZeroRegFor(rn), rn, operand); -} - - -void Assembler::bic(const Register& rd, - const Register& rn, - const Operand& operand) { - Logical(rd, rn, operand, BIC); -} - - -void Assembler::bics(const Register& rd, - const Register& rn, - const Operand& operand) { - Logical(rd, rn, operand, BICS); -} - - -void Assembler::orr(const Register& rd, - const Register& rn, - const Operand& operand) { - Logical(rd, rn, operand, ORR); -} - - -void Assembler::orn(const Register& rd, - const Register& rn, - const Operand& operand) { - Logical(rd, rn, operand, ORN); -} - - -void Assembler::eor(const Register& rd, - const Register& rn, - const Operand& operand) { - Logical(rd, rn, operand, EOR); -} - - -void Assembler::eon(const Register& rd, - const Register& rn, - const Operand& operand) { - Logical(rd, rn, operand, EON); -} - - -void Assembler::lslv(const Register& rd, - const Register& rn, - const Register& rm) { - ASSERT(rd.SizeInBits() == rn.SizeInBits()); - ASSERT(rd.SizeInBits() == rm.SizeInBits()); - Emit(SF(rd) | LSLV | Rm(rm) | Rn(rn) | Rd(rd)); -} - - -void Assembler::lsrv(const Register& rd, - const Register& rn, - const Register& rm) { - ASSERT(rd.SizeInBits() == rn.SizeInBits()); - ASSERT(rd.SizeInBits() == rm.SizeInBits()); - Emit(SF(rd) | LSRV | Rm(rm) | Rn(rn) | Rd(rd)); -} - - -void Assembler::asrv(const Register& rd, - const Register& rn, - const Register& rm) { - ASSERT(rd.SizeInBits() == rn.SizeInBits()); - ASSERT(rd.SizeInBits() == rm.SizeInBits()); - Emit(SF(rd) | ASRV | Rm(rm) | Rn(rn) | Rd(rd)); -} - - -void Assembler::rorv(const Register& rd, - const Register& rn, - const Register& rm) { - ASSERT(rd.SizeInBits() == rn.SizeInBits()); - ASSERT(rd.SizeInBits() == rm.SizeInBits()); - Emit(SF(rd) | RORV | Rm(rm) | Rn(rn) | Rd(rd)); -} - - -// Bitfield operations. -void Assembler::bfm(const Register& rd, - const Register& rn, - unsigned immr, - unsigned imms) { - ASSERT(rd.SizeInBits() == rn.SizeInBits()); - Instr N = SF(rd) >> (kSFOffset - kBitfieldNOffset); - Emit(SF(rd) | BFM | N | - ImmR(immr, rd.SizeInBits()) | - ImmS(imms, rn.SizeInBits()) | - Rn(rn) | Rd(rd)); -} - - -void Assembler::sbfm(const Register& rd, - const Register& rn, - unsigned immr, - unsigned imms) { - ASSERT(rd.Is64Bits() || rn.Is32Bits()); - Instr N = SF(rd) >> (kSFOffset - kBitfieldNOffset); - Emit(SF(rd) | SBFM | N | - ImmR(immr, rd.SizeInBits()) | - ImmS(imms, rn.SizeInBits()) | - Rn(rn) | Rd(rd)); -} - - -void Assembler::ubfm(const Register& rd, - const Register& rn, - unsigned immr, - unsigned imms) { - ASSERT(rd.SizeInBits() == rn.SizeInBits()); - Instr N = SF(rd) >> (kSFOffset - kBitfieldNOffset); - Emit(SF(rd) | UBFM | N | - ImmR(immr, rd.SizeInBits()) | - ImmS(imms, rn.SizeInBits()) | - Rn(rn) | Rd(rd)); -} - - -void Assembler::extr(const Register& rd, - const Register& rn, - const Register& rm, - unsigned lsb) { - ASSERT(rd.SizeInBits() == rn.SizeInBits()); - ASSERT(rd.SizeInBits() == rm.SizeInBits()); - Instr N = SF(rd) >> (kSFOffset - kBitfieldNOffset); - Emit(SF(rd) | EXTR | N | Rm(rm) | - ImmS(lsb, rn.SizeInBits()) | Rn(rn) | Rd(rd)); -} - - -void Assembler::csel(const Register& rd, - const Register& rn, - const Register& rm, - Condition cond) { - ConditionalSelect(rd, rn, rm, cond, CSEL); -} - - -void Assembler::csinc(const Register& rd, - const Register& rn, - const Register& rm, - Condition cond) { - ConditionalSelect(rd, rn, rm, cond, CSINC); -} - - -void Assembler::csinv(const Register& rd, - const Register& rn, - const Register& rm, - Condition cond) { - ConditionalSelect(rd, rn, rm, cond, CSINV); -} - - -void Assembler::csneg(const Register& rd, - const Register& rn, - const Register& rm, - Condition cond) { - ConditionalSelect(rd, rn, rm, cond, CSNEG); -} - - -void Assembler::cset(const Register &rd, Condition cond) { - ASSERT((cond != al) && (cond != nv)); - Register zr = AppropriateZeroRegFor(rd); - csinc(rd, zr, zr, InvertCondition(cond)); -} - - -void Assembler::csetm(const Register &rd, Condition cond) { - ASSERT((cond != al) && (cond != nv)); - Register zr = AppropriateZeroRegFor(rd); - csinv(rd, zr, zr, InvertCondition(cond)); -} - - -void Assembler::cinc(const Register &rd, const Register &rn, Condition cond) { - ASSERT((cond != al) && (cond != nv)); - csinc(rd, rn, rn, InvertCondition(cond)); -} - - -void Assembler::cinv(const Register &rd, const Register &rn, Condition cond) { - ASSERT((cond != al) && (cond != nv)); - csinv(rd, rn, rn, InvertCondition(cond)); -} - - -void Assembler::cneg(const Register &rd, const Register &rn, Condition cond) { - ASSERT((cond != al) && (cond != nv)); - csneg(rd, rn, rn, InvertCondition(cond)); -} - - -void Assembler::ConditionalSelect(const Register& rd, - const Register& rn, - const Register& rm, - Condition cond, - ConditionalSelectOp op) { - ASSERT(rd.SizeInBits() == rn.SizeInBits()); - ASSERT(rd.SizeInBits() == rm.SizeInBits()); - Emit(SF(rd) | op | Rm(rm) | Cond(cond) | Rn(rn) | Rd(rd)); -} - - -void Assembler::ccmn(const Register& rn, - const Operand& operand, - StatusFlags nzcv, - Condition cond) { - ConditionalCompare(rn, operand, nzcv, cond, CCMN); -} - - -void Assembler::ccmp(const Register& rn, - const Operand& operand, - StatusFlags nzcv, - Condition cond) { - ConditionalCompare(rn, operand, nzcv, cond, CCMP); -} - - -void Assembler::DataProcessing3Source(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra, - DataProcessing3SourceOp op) { - Emit(SF(rd) | op | Rm(rm) | Ra(ra) | Rn(rn) | Rd(rd)); -} - - -void Assembler::mul(const Register& rd, - const Register& rn, - const Register& rm) { - ASSERT(AreSameSizeAndType(rd, rn, rm)); - Register zr = AppropriateZeroRegFor(rn); - DataProcessing3Source(rd, rn, rm, zr, MADD); -} - - -void Assembler::madd(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra) { - ASSERT(AreSameSizeAndType(rd, rn, rm, ra)); - DataProcessing3Source(rd, rn, rm, ra, MADD); -} - - -void Assembler::mneg(const Register& rd, - const Register& rn, - const Register& rm) { - ASSERT(AreSameSizeAndType(rd, rn, rm)); - Register zr = AppropriateZeroRegFor(rn); - DataProcessing3Source(rd, rn, rm, zr, MSUB); -} - - -void Assembler::msub(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra) { - ASSERT(AreSameSizeAndType(rd, rn, rm, ra)); - DataProcessing3Source(rd, rn, rm, ra, MSUB); -} - - -void Assembler::smaddl(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra) { - ASSERT(rd.Is64Bits() && ra.Is64Bits()); - ASSERT(rn.Is32Bits() && rm.Is32Bits()); - DataProcessing3Source(rd, rn, rm, ra, SMADDL_x); -} - - -void Assembler::smsubl(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra) { - ASSERT(rd.Is64Bits() && ra.Is64Bits()); - ASSERT(rn.Is32Bits() && rm.Is32Bits()); - DataProcessing3Source(rd, rn, rm, ra, SMSUBL_x); -} - - -void Assembler::umaddl(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra) { - ASSERT(rd.Is64Bits() && ra.Is64Bits()); - ASSERT(rn.Is32Bits() && rm.Is32Bits()); - DataProcessing3Source(rd, rn, rm, ra, UMADDL_x); -} - - -void Assembler::umsubl(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra) { - ASSERT(rd.Is64Bits() && ra.Is64Bits()); - ASSERT(rn.Is32Bits() && rm.Is32Bits()); - DataProcessing3Source(rd, rn, rm, ra, UMSUBL_x); -} - - -void Assembler::smull(const Register& rd, - const Register& rn, - const Register& rm) { - ASSERT(rd.Is64Bits()); - ASSERT(rn.Is32Bits() && rm.Is32Bits()); - DataProcessing3Source(rd, rn, rm, xzr, SMADDL_x); -} - - -void Assembler::smulh(const Register& rd, - const Register& rn, - const Register& rm) { - ASSERT(AreSameSizeAndType(rd, rn, rm)); - DataProcessing3Source(rd, rn, rm, xzr, SMULH_x); -} - - -void Assembler::sdiv(const Register& rd, - const Register& rn, - const Register& rm) { - ASSERT(rd.SizeInBits() == rn.SizeInBits()); - ASSERT(rd.SizeInBits() == rm.SizeInBits()); - Emit(SF(rd) | SDIV | Rm(rm) | Rn(rn) | Rd(rd)); -} - - -void Assembler::udiv(const Register& rd, - const Register& rn, - const Register& rm) { - ASSERT(rd.SizeInBits() == rn.SizeInBits()); - ASSERT(rd.SizeInBits() == rm.SizeInBits()); - Emit(SF(rd) | UDIV | Rm(rm) | Rn(rn) | Rd(rd)); -} - - -void Assembler::rbit(const Register& rd, - const Register& rn) { - DataProcessing1Source(rd, rn, RBIT); -} - - -void Assembler::rev16(const Register& rd, - const Register& rn) { - DataProcessing1Source(rd, rn, REV16); -} - - -void Assembler::rev32(const Register& rd, - const Register& rn) { - ASSERT(rd.Is64Bits()); - DataProcessing1Source(rd, rn, REV); -} - - -void Assembler::rev(const Register& rd, - const Register& rn) { - DataProcessing1Source(rd, rn, rd.Is64Bits() ? REV_x : REV_w); -} - - -void Assembler::clz(const Register& rd, - const Register& rn) { - DataProcessing1Source(rd, rn, CLZ); -} - - -void Assembler::cls(const Register& rd, - const Register& rn) { - DataProcessing1Source(rd, rn, CLS); -} - - -void Assembler::ldp(const CPURegister& rt, - const CPURegister& rt2, - const MemOperand& src) { - LoadStorePair(rt, rt2, src, LoadPairOpFor(rt, rt2)); -} - - -void Assembler::stp(const CPURegister& rt, - const CPURegister& rt2, - const MemOperand& dst) { - LoadStorePair(rt, rt2, dst, StorePairOpFor(rt, rt2)); -} - - -void Assembler::ldpsw(const Register& rt, - const Register& rt2, - const MemOperand& src) { - ASSERT(rt.Is64Bits()); - LoadStorePair(rt, rt2, src, LDPSW_x); -} - - -void Assembler::LoadStorePair(const CPURegister& rt, - const CPURegister& rt2, - const MemOperand& addr, - LoadStorePairOp op) { - // 'rt' and 'rt2' can only be aliased for stores. - ASSERT(((op & LoadStorePairLBit) == 0) || !rt.Is(rt2)); - ASSERT(AreSameSizeAndType(rt, rt2)); - - Instr memop = op | Rt(rt) | Rt2(rt2) | RnSP(addr.base()) | - ImmLSPair(addr.offset(), CalcLSPairDataSize(op)); - - Instr addrmodeop; - if (addr.IsImmediateOffset()) { - addrmodeop = LoadStorePairOffsetFixed; - } else { - // Pre-index and post-index modes. - ASSERT(!rt.Is(addr.base())); - ASSERT(!rt2.Is(addr.base())); - ASSERT(addr.offset() != 0); - if (addr.IsPreIndex()) { - addrmodeop = LoadStorePairPreIndexFixed; - } else { - ASSERT(addr.IsPostIndex()); - addrmodeop = LoadStorePairPostIndexFixed; - } - } - Emit(addrmodeop | memop); -} - - -void Assembler::ldnp(const CPURegister& rt, - const CPURegister& rt2, - const MemOperand& src) { - LoadStorePairNonTemporal(rt, rt2, src, - LoadPairNonTemporalOpFor(rt, rt2)); -} - - -void Assembler::stnp(const CPURegister& rt, - const CPURegister& rt2, - const MemOperand& dst) { - LoadStorePairNonTemporal(rt, rt2, dst, - StorePairNonTemporalOpFor(rt, rt2)); -} - - -void Assembler::LoadStorePairNonTemporal(const CPURegister& rt, - const CPURegister& rt2, - const MemOperand& addr, - LoadStorePairNonTemporalOp op) { - ASSERT(!rt.Is(rt2)); - ASSERT(AreSameSizeAndType(rt, rt2)); - ASSERT(addr.IsImmediateOffset()); - - LSDataSize size = CalcLSPairDataSize( - static_cast(op & LoadStorePairMask)); - Emit(op | Rt(rt) | Rt2(rt2) | RnSP(addr.base()) | - ImmLSPair(addr.offset(), size)); -} - - -// Memory instructions. -void Assembler::ldrb(const Register& rt, const MemOperand& src) { - LoadStore(rt, src, LDRB_w); -} - - -void Assembler::strb(const Register& rt, const MemOperand& dst) { - LoadStore(rt, dst, STRB_w); -} - - -void Assembler::ldrsb(const Register& rt, const MemOperand& src) { - LoadStore(rt, src, rt.Is64Bits() ? LDRSB_x : LDRSB_w); -} - - -void Assembler::ldrh(const Register& rt, const MemOperand& src) { - LoadStore(rt, src, LDRH_w); -} - - -void Assembler::strh(const Register& rt, const MemOperand& dst) { - LoadStore(rt, dst, STRH_w); -} - - -void Assembler::ldrsh(const Register& rt, const MemOperand& src) { - LoadStore(rt, src, rt.Is64Bits() ? LDRSH_x : LDRSH_w); -} - - -void Assembler::ldr(const CPURegister& rt, const MemOperand& src) { - LoadStore(rt, src, LoadOpFor(rt)); -} - - -void Assembler::str(const CPURegister& rt, const MemOperand& src) { - LoadStore(rt, src, StoreOpFor(rt)); -} - - -void Assembler::ldrsw(const Register& rt, const MemOperand& src) { - ASSERT(rt.Is64Bits()); - LoadStore(rt, src, LDRSW_x); -} - - -void Assembler::ldr(const Register& rt, uint64_t imm) { - // TODO(all): Constant pool may be garbage collected. Hence we cannot store - // TODO(all): arbitrary values in them. Manually move it for now. - // TODO(all): Fix MacroAssembler::Fmov when this is implemented. - UNIMPLEMENTED(); -} - - -void Assembler::ldr(const FPRegister& ft, double imm) { - // TODO(all): Constant pool may be garbage collected. Hence we cannot store - // TODO(all): arbitrary values in them. Manually move it for now. - // TODO(all): Fix MacroAssembler::Fmov when this is implemented. - UNIMPLEMENTED(); -} - - -void Assembler::mov(const Register& rd, const Register& rm) { - // Moves involving the stack pointer are encoded as add immediate with - // second operand of zero. Otherwise, orr with first operand zr is - // used. - if (rd.IsSP() || rm.IsSP()) { - add(rd, rm, 0); - } else { - orr(rd, AppropriateZeroRegFor(rd), rm); - } -} - - -void Assembler::mvn(const Register& rd, const Operand& operand) { - orn(rd, AppropriateZeroRegFor(rd), operand); -} - - -void Assembler::mrs(const Register& rt, SystemRegister sysreg) { - ASSERT(rt.Is64Bits()); - Emit(MRS | ImmSystemRegister(sysreg) | Rt(rt)); -} - - -void Assembler::msr(SystemRegister sysreg, const Register& rt) { - ASSERT(rt.Is64Bits()); - Emit(MSR | Rt(rt) | ImmSystemRegister(sysreg)); -} - - -void Assembler::hint(SystemHint code) { - Emit(HINT | ImmHint(code) | Rt(xzr)); -} - - -void Assembler::dmb(BarrierDomain domain, BarrierType type) { - Emit(DMB | ImmBarrierDomain(domain) | ImmBarrierType(type)); -} - - -void Assembler::dsb(BarrierDomain domain, BarrierType type) { - Emit(DSB | ImmBarrierDomain(domain) | ImmBarrierType(type)); -} - - -void Assembler::isb() { - Emit(ISB | ImmBarrierDomain(FullSystem) | ImmBarrierType(BarrierAll)); -} - - -void Assembler::fmov(FPRegister fd, double imm) { - if (fd.Is64Bits() && IsImmFP64(imm)) { - Emit(FMOV_d_imm | Rd(fd) | ImmFP64(imm)); - } else if (fd.Is32Bits() && IsImmFP32(imm)) { - Emit(FMOV_s_imm | Rd(fd) | ImmFP32(static_cast(imm))); - } else if ((imm == 0.0) && (copysign(1.0, imm) == 1.0)) { - Register zr = AppropriateZeroRegFor(fd); - fmov(fd, zr); - } else { - ldr(fd, imm); - } -} - - -void Assembler::fmov(Register rd, FPRegister fn) { - ASSERT(rd.SizeInBits() == fn.SizeInBits()); - FPIntegerConvertOp op = rd.Is32Bits() ? FMOV_ws : FMOV_xd; - Emit(op | Rd(rd) | Rn(fn)); -} - - -void Assembler::fmov(FPRegister fd, Register rn) { - ASSERT(fd.SizeInBits() == rn.SizeInBits()); - FPIntegerConvertOp op = fd.Is32Bits() ? FMOV_sw : FMOV_dx; - Emit(op | Rd(fd) | Rn(rn)); -} - - -void Assembler::fmov(FPRegister fd, FPRegister fn) { - ASSERT(fd.SizeInBits() == fn.SizeInBits()); - Emit(FPType(fd) | FMOV | Rd(fd) | Rn(fn)); -} - - -void Assembler::fadd(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm) { - FPDataProcessing2Source(fd, fn, fm, FADD); -} - - -void Assembler::fsub(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm) { - FPDataProcessing2Source(fd, fn, fm, FSUB); -} - - -void Assembler::fmul(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm) { - FPDataProcessing2Source(fd, fn, fm, FMUL); -} - - -void Assembler::fmadd(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm, - const FPRegister& fa) { - FPDataProcessing3Source(fd, fn, fm, fa, fd.Is32Bits() ? FMADD_s : FMADD_d); -} - - -void Assembler::fmsub(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm, - const FPRegister& fa) { - FPDataProcessing3Source(fd, fn, fm, fa, fd.Is32Bits() ? FMSUB_s : FMSUB_d); -} - - -void Assembler::fnmadd(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm, - const FPRegister& fa) { - FPDataProcessing3Source(fd, fn, fm, fa, fd.Is32Bits() ? FNMADD_s : FNMADD_d); -} - - -void Assembler::fnmsub(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm, - const FPRegister& fa) { - FPDataProcessing3Source(fd, fn, fm, fa, fd.Is32Bits() ? FNMSUB_s : FNMSUB_d); -} - - -void Assembler::fdiv(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm) { - FPDataProcessing2Source(fd, fn, fm, FDIV); -} - - -void Assembler::fmax(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm) { - FPDataProcessing2Source(fd, fn, fm, FMAX); -} - - -void Assembler::fmaxnm(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm) { - FPDataProcessing2Source(fd, fn, fm, FMAXNM); -} - - -void Assembler::fmin(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm) { - FPDataProcessing2Source(fd, fn, fm, FMIN); -} - - -void Assembler::fminnm(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm) { - FPDataProcessing2Source(fd, fn, fm, FMINNM); -} - - -void Assembler::fabs(const FPRegister& fd, - const FPRegister& fn) { - ASSERT(fd.SizeInBits() == fn.SizeInBits()); - FPDataProcessing1Source(fd, fn, FABS); -} - - -void Assembler::fneg(const FPRegister& fd, - const FPRegister& fn) { - ASSERT(fd.SizeInBits() == fn.SizeInBits()); - FPDataProcessing1Source(fd, fn, FNEG); -} - - -void Assembler::fsqrt(const FPRegister& fd, - const FPRegister& fn) { - ASSERT(fd.SizeInBits() == fn.SizeInBits()); - FPDataProcessing1Source(fd, fn, FSQRT); -} - - -void Assembler::frinta(const FPRegister& fd, - const FPRegister& fn) { - ASSERT(fd.SizeInBits() == fn.SizeInBits()); - FPDataProcessing1Source(fd, fn, FRINTA); -} - - -void Assembler::frintn(const FPRegister& fd, - const FPRegister& fn) { - ASSERT(fd.SizeInBits() == fn.SizeInBits()); - FPDataProcessing1Source(fd, fn, FRINTN); -} - - -void Assembler::frintz(const FPRegister& fd, - const FPRegister& fn) { - ASSERT(fd.SizeInBits() == fn.SizeInBits()); - FPDataProcessing1Source(fd, fn, FRINTZ); -} - - -void Assembler::fcmp(const FPRegister& fn, - const FPRegister& fm) { - ASSERT(fn.SizeInBits() == fm.SizeInBits()); - Emit(FPType(fn) | FCMP | Rm(fm) | Rn(fn)); -} - - -void Assembler::fcmp(const FPRegister& fn, - double value) { - USE(value); - // Although the fcmp instruction can strictly only take an immediate value of - // +0.0, we don't need to check for -0.0 because the sign of 0.0 doesn't - // affect the result of the comparison. - ASSERT(value == 0.0); - Emit(FPType(fn) | FCMP_zero | Rn(fn)); -} - - -void Assembler::fccmp(const FPRegister& fn, - const FPRegister& fm, - StatusFlags nzcv, - Condition cond) { - ASSERT(fn.SizeInBits() == fm.SizeInBits()); - Emit(FPType(fn) | FCCMP | Rm(fm) | Cond(cond) | Rn(fn) | Nzcv(nzcv)); -} - - -void Assembler::fcsel(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm, - Condition cond) { - ASSERT(fd.SizeInBits() == fn.SizeInBits()); - ASSERT(fd.SizeInBits() == fm.SizeInBits()); - Emit(FPType(fd) | FCSEL | Rm(fm) | Cond(cond) | Rn(fn) | Rd(fd)); -} - - -void Assembler::FPConvertToInt(const Register& rd, - const FPRegister& fn, - FPIntegerConvertOp op) { - Emit(SF(rd) | FPType(fn) | op | Rn(fn) | Rd(rd)); -} - - -void Assembler::fcvt(const FPRegister& fd, - const FPRegister& fn) { - if (fd.Is64Bits()) { - // Convert float to double. - ASSERT(fn.Is32Bits()); - FPDataProcessing1Source(fd, fn, FCVT_ds); - } else { - // Convert double to float. - ASSERT(fn.Is64Bits()); - FPDataProcessing1Source(fd, fn, FCVT_sd); - } -} - - -void Assembler::fcvtau(const Register& rd, const FPRegister& fn) { - FPConvertToInt(rd, fn, FCVTAU); -} - - -void Assembler::fcvtas(const Register& rd, const FPRegister& fn) { - FPConvertToInt(rd, fn, FCVTAS); -} - - -void Assembler::fcvtmu(const Register& rd, const FPRegister& fn) { - FPConvertToInt(rd, fn, FCVTMU); -} - - -void Assembler::fcvtms(const Register& rd, const FPRegister& fn) { - FPConvertToInt(rd, fn, FCVTMS); -} - - -void Assembler::fcvtnu(const Register& rd, const FPRegister& fn) { - FPConvertToInt(rd, fn, FCVTNU); -} - - -void Assembler::fcvtns(const Register& rd, const FPRegister& fn) { - FPConvertToInt(rd, fn, FCVTNS); -} - - -void Assembler::fcvtzu(const Register& rd, const FPRegister& fn) { - FPConvertToInt(rd, fn, FCVTZU); -} - - -void Assembler::fcvtzs(const Register& rd, const FPRegister& fn) { - FPConvertToInt(rd, fn, FCVTZS); -} - - -void Assembler::scvtf(const FPRegister& fd, - const Register& rn, - unsigned fbits) { - if (fbits == 0) { - Emit(SF(rn) | FPType(fd) | SCVTF | Rn(rn) | Rd(fd)); - } else { - Emit(SF(rn) | FPType(fd) | SCVTF_fixed | FPScale(64 - fbits) | Rn(rn) | - Rd(fd)); - } -} - - -void Assembler::ucvtf(const FPRegister& fd, - const Register& rn, - unsigned fbits) { - if (fbits == 0) { - Emit(SF(rn) | FPType(fd) | UCVTF | Rn(rn) | Rd(fd)); - } else { - Emit(SF(rn) | FPType(fd) | UCVTF_fixed | FPScale(64 - fbits) | Rn(rn) | - Rd(fd)); - } -} - - -// Note: -// Below, a difference in case for the same letter indicates a -// negated bit. -// If b is 1, then B is 0. -Instr Assembler::ImmFP32(float imm) { - ASSERT(IsImmFP32(imm)); - // bits: aBbb.bbbc.defg.h000.0000.0000.0000.0000 - uint32_t bits = float_to_rawbits(imm); - // bit7: a000.0000 - uint32_t bit7 = ((bits >> 31) & 0x1) << 7; - // bit6: 0b00.0000 - uint32_t bit6 = ((bits >> 29) & 0x1) << 6; - // bit5_to_0: 00cd.efgh - uint32_t bit5_to_0 = (bits >> 19) & 0x3f; - - return (bit7 | bit6 | bit5_to_0) << ImmFP_offset; -} - - -Instr Assembler::ImmFP64(double imm) { - ASSERT(IsImmFP64(imm)); - // bits: aBbb.bbbb.bbcd.efgh.0000.0000.0000.0000 - // 0000.0000.0000.0000.0000.0000.0000.0000 - uint64_t bits = double_to_rawbits(imm); - // bit7: a000.0000 - uint32_t bit7 = ((bits >> 63) & 0x1) << 7; - // bit6: 0b00.0000 - uint32_t bit6 = ((bits >> 61) & 0x1) << 6; - // bit5_to_0: 00cd.efgh - uint32_t bit5_to_0 = (bits >> 48) & 0x3f; - - return (bit7 | bit6 | bit5_to_0) << ImmFP_offset; -} - - -// Code generation helpers. -void Assembler::MoveWide(const Register& rd, - uint64_t imm, - int shift, - MoveWideImmediateOp mov_op) { - if (shift >= 0) { - // Explicit shift specified. - ASSERT((shift == 0) || (shift == 16) || (shift == 32) || (shift == 48)); - ASSERT(rd.Is64Bits() || (shift == 0) || (shift == 16)); - shift /= 16; - } else { - // Calculate a new immediate and shift combination to encode the immediate - // argument. - shift = 0; - if ((imm & ~0xffffUL) == 0) { - // Nothing to do. - } else if ((imm & ~(0xffffUL << 16)) == 0) { - imm >>= 16; - shift = 1; - } else if ((imm & ~(0xffffUL << 32)) == 0) { - ASSERT(rd.Is64Bits()); - imm >>= 32; - shift = 2; - } else if ((imm & ~(0xffffUL << 48)) == 0) { - ASSERT(rd.Is64Bits()); - imm >>= 48; - shift = 3; - } - } - - ASSERT(is_uint16(imm)); - - Emit(SF(rd) | MoveWideImmediateFixed | mov_op | - Rd(rd) | ImmMoveWide(imm) | ShiftMoveWide(shift)); -} - - -void Assembler::AddSub(const Register& rd, - const Register& rn, - const Operand& operand, - FlagsUpdate S, - AddSubOp op) { - ASSERT(rd.SizeInBits() == rn.SizeInBits()); - ASSERT(!operand.NeedsRelocation()); - if (operand.IsImmediate()) { - int64_t immediate = operand.immediate(); - ASSERT(IsImmAddSub(immediate)); - Instr dest_reg = (S == SetFlags) ? Rd(rd) : RdSP(rd); - Emit(SF(rd) | AddSubImmediateFixed | op | Flags(S) | - ImmAddSub(immediate) | dest_reg | RnSP(rn)); - } else if (operand.IsShiftedRegister()) { - ASSERT(operand.reg().SizeInBits() == rd.SizeInBits()); - ASSERT(operand.shift() != ROR); - - // For instructions of the form: - // add/sub wsp, , [, LSL #0-3 ] - // add/sub , wsp, [, LSL #0-3 ] - // add/sub wsp, wsp, [, LSL #0-3 ] - // adds/subs , wsp, [, LSL #0-3 ] - // or their 64-bit register equivalents, convert the operand from shifted to - // extended register mode, and emit an add/sub extended instruction. - if (rn.IsSP() || rd.IsSP()) { - ASSERT(!(rd.IsSP() && (S == SetFlags))); - DataProcExtendedRegister(rd, rn, operand.ToExtendedRegister(), S, - AddSubExtendedFixed | op); - } else { - DataProcShiftedRegister(rd, rn, operand, S, AddSubShiftedFixed | op); - } - } else { - ASSERT(operand.IsExtendedRegister()); - DataProcExtendedRegister(rd, rn, operand, S, AddSubExtendedFixed | op); - } -} - - -void Assembler::AddSubWithCarry(const Register& rd, - const Register& rn, - const Operand& operand, - FlagsUpdate S, - AddSubWithCarryOp op) { - ASSERT(rd.SizeInBits() == rn.SizeInBits()); - ASSERT(rd.SizeInBits() == operand.reg().SizeInBits()); - ASSERT(operand.IsShiftedRegister() && (operand.shift_amount() == 0)); - ASSERT(!operand.NeedsRelocation()); - Emit(SF(rd) | op | Flags(S) | Rm(operand.reg()) | Rn(rn) | Rd(rd)); -} - - -void Assembler::hlt(int code) { - ASSERT(is_uint16(code)); - Emit(HLT | ImmException(code)); -} - - -void Assembler::brk(int code) { - ASSERT(is_uint16(code)); - Emit(BRK | ImmException(code)); -} - - -void Assembler::debug(const char* message, uint32_t code, Instr params) { -#ifdef USE_SIMULATOR - // Don't generate simulator specific code if we are building a snapshot, which - // might be run on real hardware. - if (!Serializer::enabled()) { -#ifdef DEBUG - Serializer::TooLateToEnableNow(); -#endif - // The arguments to the debug marker need to be contiguous in memory, so - // make sure we don't try to emit a literal pool. - BlockConstPoolScope scope(this); - - Label start; - bind(&start); - - // Refer to instructions-a64.h for a description of the marker and its - // arguments. - hlt(kImmExceptionIsDebug); - ASSERT(SizeOfCodeGeneratedSince(&start) == kDebugCodeOffset); - dc32(code); - ASSERT(SizeOfCodeGeneratedSince(&start) == kDebugParamsOffset); - dc32(params); - ASSERT(SizeOfCodeGeneratedSince(&start) == kDebugMessageOffset); - EmitStringData(message); - hlt(kImmExceptionIsUnreachable); - - return; - } - // Fall through if Serializer is enabled. -#endif - - if (params & BREAK) { - hlt(kImmExceptionIsDebug); - } -} - - -void Assembler::Logical(const Register& rd, - const Register& rn, - const Operand& operand, - LogicalOp op) { - ASSERT(rd.SizeInBits() == rn.SizeInBits()); - ASSERT(!operand.NeedsRelocation()); - if (operand.IsImmediate()) { - int64_t immediate = operand.immediate(); - unsigned reg_size = rd.SizeInBits(); - - ASSERT(immediate != 0); - ASSERT(immediate != -1); - ASSERT(rd.Is64Bits() || is_uint32(immediate)); - - // If the operation is NOT, invert the operation and immediate. - if ((op & NOT) == NOT) { - op = static_cast(op & ~NOT); - immediate = rd.Is64Bits() ? ~immediate : (~immediate & kWRegMask); - } - - unsigned n, imm_s, imm_r; - if (IsImmLogical(immediate, reg_size, &n, &imm_s, &imm_r)) { - // Immediate can be encoded in the instruction. - LogicalImmediate(rd, rn, n, imm_s, imm_r, op); - } else { - // This case is handled in the macro assembler. - UNREACHABLE(); - } - } else { - ASSERT(operand.IsShiftedRegister()); - ASSERT(operand.reg().SizeInBits() == rd.SizeInBits()); - Instr dp_op = static_cast(op | LogicalShiftedFixed); - DataProcShiftedRegister(rd, rn, operand, LeaveFlags, dp_op); - } -} - - -void Assembler::LogicalImmediate(const Register& rd, - const Register& rn, - unsigned n, - unsigned imm_s, - unsigned imm_r, - LogicalOp op) { - unsigned reg_size = rd.SizeInBits(); - Instr dest_reg = (op == ANDS) ? Rd(rd) : RdSP(rd); - Emit(SF(rd) | LogicalImmediateFixed | op | BitN(n, reg_size) | - ImmSetBits(imm_s, reg_size) | ImmRotate(imm_r, reg_size) | dest_reg | - Rn(rn)); -} - - -void Assembler::ConditionalCompare(const Register& rn, - const Operand& operand, - StatusFlags nzcv, - Condition cond, - ConditionalCompareOp op) { - Instr ccmpop; - ASSERT(!operand.NeedsRelocation()); - if (operand.IsImmediate()) { - int64_t immediate = operand.immediate(); - ASSERT(IsImmConditionalCompare(immediate)); - ccmpop = ConditionalCompareImmediateFixed | op | ImmCondCmp(immediate); - } else { - ASSERT(operand.IsShiftedRegister() && (operand.shift_amount() == 0)); - ccmpop = ConditionalCompareRegisterFixed | op | Rm(operand.reg()); - } - Emit(SF(rn) | ccmpop | Cond(cond) | Rn(rn) | Nzcv(nzcv)); -} - - -void Assembler::DataProcessing1Source(const Register& rd, - const Register& rn, - DataProcessing1SourceOp op) { - ASSERT(rd.SizeInBits() == rn.SizeInBits()); - Emit(SF(rn) | op | Rn(rn) | Rd(rd)); -} - - -void Assembler::FPDataProcessing1Source(const FPRegister& fd, - const FPRegister& fn, - FPDataProcessing1SourceOp op) { - Emit(FPType(fn) | op | Rn(fn) | Rd(fd)); -} - - -void Assembler::FPDataProcessing2Source(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm, - FPDataProcessing2SourceOp op) { - ASSERT(fd.SizeInBits() == fn.SizeInBits()); - ASSERT(fd.SizeInBits() == fm.SizeInBits()); - Emit(FPType(fd) | op | Rm(fm) | Rn(fn) | Rd(fd)); -} - - -void Assembler::FPDataProcessing3Source(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm, - const FPRegister& fa, - FPDataProcessing3SourceOp op) { - ASSERT(AreSameSizeAndType(fd, fn, fm, fa)); - Emit(FPType(fd) | op | Rm(fm) | Rn(fn) | Rd(fd) | Ra(fa)); -} - - -void Assembler::EmitShift(const Register& rd, - const Register& rn, - Shift shift, - unsigned shift_amount) { - switch (shift) { - case LSL: - lsl(rd, rn, shift_amount); - break; - case LSR: - lsr(rd, rn, shift_amount); - break; - case ASR: - asr(rd, rn, shift_amount); - break; - case ROR: - ror(rd, rn, shift_amount); - break; - default: - UNREACHABLE(); - } -} - - -void Assembler::EmitExtendShift(const Register& rd, - const Register& rn, - Extend extend, - unsigned left_shift) { - ASSERT(rd.SizeInBits() >= rn.SizeInBits()); - unsigned reg_size = rd.SizeInBits(); - // Use the correct size of register. - Register rn_ = Register::Create(rn.code(), rd.SizeInBits()); - // Bits extracted are high_bit:0. - unsigned high_bit = (8 << (extend & 0x3)) - 1; - // Number of bits left in the result that are not introduced by the shift. - unsigned non_shift_bits = (reg_size - left_shift) & (reg_size - 1); - - if ((non_shift_bits > high_bit) || (non_shift_bits == 0)) { - switch (extend) { - case UXTB: - case UXTH: - case UXTW: ubfm(rd, rn_, non_shift_bits, high_bit); break; - case SXTB: - case SXTH: - case SXTW: sbfm(rd, rn_, non_shift_bits, high_bit); break; - case UXTX: - case SXTX: { - ASSERT(rn.SizeInBits() == kXRegSize); - // Nothing to extend. Just shift. - lsl(rd, rn_, left_shift); - break; - } - default: UNREACHABLE(); - } - } else { - // No need to extend as the extended bits would be shifted away. - lsl(rd, rn_, left_shift); - } -} - - -void Assembler::DataProcShiftedRegister(const Register& rd, - const Register& rn, - const Operand& operand, - FlagsUpdate S, - Instr op) { - ASSERT(operand.IsShiftedRegister()); - ASSERT(rn.Is64Bits() || (rn.Is32Bits() && is_uint5(operand.shift_amount()))); - ASSERT(!operand.NeedsRelocation()); - Emit(SF(rd) | op | Flags(S) | - ShiftDP(operand.shift()) | ImmDPShift(operand.shift_amount()) | - Rm(operand.reg()) | Rn(rn) | Rd(rd)); -} - - -void Assembler::DataProcExtendedRegister(const Register& rd, - const Register& rn, - const Operand& operand, - FlagsUpdate S, - Instr op) { - ASSERT(!operand.NeedsRelocation()); - Instr dest_reg = (S == SetFlags) ? Rd(rd) : RdSP(rd); - Emit(SF(rd) | op | Flags(S) | Rm(operand.reg()) | - ExtendMode(operand.extend()) | ImmExtendShift(operand.shift_amount()) | - dest_reg | RnSP(rn)); -} - - -bool Assembler::IsImmAddSub(int64_t immediate) { - return is_uint12(immediate) || - (is_uint12(immediate >> 12) && ((immediate & 0xfff) == 0)); -} - -void Assembler::LoadStore(const CPURegister& rt, - const MemOperand& addr, - LoadStoreOp op) { - Instr memop = op | Rt(rt) | RnSP(addr.base()); - ptrdiff_t offset = addr.offset(); - - if (addr.IsImmediateOffset()) { - LSDataSize size = CalcLSDataSize(op); - if (IsImmLSScaled(offset, size)) { - // Use the scaled addressing mode. - Emit(LoadStoreUnsignedOffsetFixed | memop | - ImmLSUnsigned(offset >> size)); - } else if (IsImmLSUnscaled(offset)) { - // Use the unscaled addressing mode. - Emit(LoadStoreUnscaledOffsetFixed | memop | ImmLS(offset)); - } else { - // This case is handled in the macro assembler. - UNREACHABLE(); - } - } else if (addr.IsRegisterOffset()) { - Extend ext = addr.extend(); - Shift shift = addr.shift(); - unsigned shift_amount = addr.shift_amount(); - - // LSL is encoded in the option field as UXTX. - if (shift == LSL) { - ext = UXTX; - } - - // Shifts are encoded in one bit, indicating a left shift by the memory - // access size. - ASSERT((shift_amount == 0) || - (shift_amount == static_cast(CalcLSDataSize(op)))); - Emit(LoadStoreRegisterOffsetFixed | memop | Rm(addr.regoffset()) | - ExtendMode(ext) | ImmShiftLS((shift_amount > 0) ? 1 : 0)); - } else { - // Pre-index and post-index modes. - ASSERT(!rt.Is(addr.base())); - if (IsImmLSUnscaled(offset)) { - if (addr.IsPreIndex()) { - Emit(LoadStorePreIndexFixed | memop | ImmLS(offset)); - } else { - ASSERT(addr.IsPostIndex()); - Emit(LoadStorePostIndexFixed | memop | ImmLS(offset)); - } - } else { - // This case is handled in the macro assembler. - UNREACHABLE(); - } - } -} - - -bool Assembler::IsImmLSUnscaled(ptrdiff_t offset) { - return is_int9(offset); -} - - -bool Assembler::IsImmLSScaled(ptrdiff_t offset, LSDataSize size) { - bool offset_is_size_multiple = (((offset >> size) << size) == offset); - return offset_is_size_multiple && is_uint12(offset >> size); -} - - -void Assembler::LoadLiteral(const CPURegister& rt, int offset_from_pc) { - ASSERT((offset_from_pc & ((1 << kLiteralEntrySizeLog2) - 1)) == 0); - // The pattern 'ldr xzr, #offset' is used to indicate the beginning of a - // constant pool. It should not be emitted. - ASSERT(!rt.Is(xzr)); - Emit(LDR_x_lit | - ImmLLiteral(offset_from_pc >> kLiteralEntrySizeLog2) | - Rt(rt)); -} - - -void Assembler::LoadRelocatedValue(const CPURegister& rt, - const Operand& operand, - LoadLiteralOp op) { - int64_t imm = operand.immediate(); - ASSERT(is_int32(imm) || is_uint32(imm) || (rt.Is64Bits())); - RecordRelocInfo(operand.rmode(), imm); - BlockConstPoolFor(1); - Emit(op | ImmLLiteral(0) | Rt(rt)); -} - - -// Test if a given value can be encoded in the immediate field of a logical -// instruction. -// If it can be encoded, the function returns true, and values pointed to by n, -// imm_s and imm_r are updated with immediates encoded in the format required -// by the corresponding fields in the logical instruction. -// If it can not be encoded, the function returns false, and the values pointed -// to by n, imm_s and imm_r are undefined. -bool Assembler::IsImmLogical(uint64_t value, - unsigned width, - unsigned* n, - unsigned* imm_s, - unsigned* imm_r) { - ASSERT((n != NULL) && (imm_s != NULL) && (imm_r != NULL)); - ASSERT((width == kWRegSize) || (width == kXRegSize)); - - // Logical immediates are encoded using parameters n, imm_s and imm_r using - // the following table: - // - // N imms immr size S R - // 1 ssssss rrrrrr 64 UInt(ssssss) UInt(rrrrrr) - // 0 0sssss xrrrrr 32 UInt(sssss) UInt(rrrrr) - // 0 10ssss xxrrrr 16 UInt(ssss) UInt(rrrr) - // 0 110sss xxxrrr 8 UInt(sss) UInt(rrr) - // 0 1110ss xxxxrr 4 UInt(ss) UInt(rr) - // 0 11110s xxxxxr 2 UInt(s) UInt(r) - // (s bits must not be all set) - // - // A pattern is constructed of size bits, where the least significant S+1 - // bits are set. The pattern is rotated right by R, and repeated across a - // 32 or 64-bit value, depending on destination register width. - // - // To test if an arbitary immediate can be encoded using this scheme, an - // iterative algorithm is used. - // - // TODO(mcapewel) This code does not consider using X/W register overlap to - // support 64-bit immediates where the top 32-bits are zero, and the bottom - // 32-bits are an encodable logical immediate. - - // 1. If the value has all set or all clear bits, it can't be encoded. - if ((value == 0) || (value == 0xffffffffffffffffUL) || - ((width == kWRegSize) && (value == 0xffffffff))) { - return false; - } - - unsigned lead_zero = CountLeadingZeros(value, width); - unsigned lead_one = CountLeadingZeros(~value, width); - unsigned trail_zero = CountTrailingZeros(value, width); - unsigned trail_one = CountTrailingZeros(~value, width); - unsigned set_bits = CountSetBits(value, width); - - // The fixed bits in the immediate s field. - // If width == 64 (X reg), start at 0xFFFFFF80. - // If width == 32 (W reg), start at 0xFFFFFFC0, as the iteration for 64-bit - // widths won't be executed. - int imm_s_fixed = (width == kXRegSize) ? -128 : -64; - int imm_s_mask = 0x3F; - - for (;;) { - // 2. If the value is two bits wide, it can be encoded. - if (width == 2) { - *n = 0; - *imm_s = 0x3C; - *imm_r = (value & 3) - 1; - return true; - } - - *n = (width == 64) ? 1 : 0; - *imm_s = ((imm_s_fixed | (set_bits - 1)) & imm_s_mask); - if ((lead_zero + set_bits) == width) { - *imm_r = 0; - } else { - *imm_r = (lead_zero > 0) ? (width - trail_zero) : lead_one; - } - - // 3. If the sum of leading zeros, trailing zeros and set bits is equal to - // the bit width of the value, it can be encoded. - if (lead_zero + trail_zero + set_bits == width) { - return true; - } - - // 4. If the sum of leading ones, trailing ones and unset bits in the - // value is equal to the bit width of the value, it can be encoded. - if (lead_one + trail_one + (width - set_bits) == width) { - return true; - } - - // 5. If the most-significant half of the bitwise value is equal to the - // least-significant half, return to step 2 using the least-significant - // half of the value. - uint64_t mask = (1UL << (width >> 1)) - 1; - if ((value & mask) == ((value >> (width >> 1)) & mask)) { - width >>= 1; - set_bits >>= 1; - imm_s_fixed >>= 1; - continue; - } - - // 6. Otherwise, the value can't be encoded. - return false; - } -} - - -bool Assembler::IsImmConditionalCompare(int64_t immediate) { - return is_uint5(immediate); -} - - -bool Assembler::IsImmFP32(float imm) { - // Valid values will have the form: - // aBbb.bbbc.defg.h000.0000.0000.0000.0000 - uint32_t bits = float_to_rawbits(imm); - // bits[19..0] are cleared. - if ((bits & 0x7ffff) != 0) { - return false; - } - - // bits[29..25] are all set or all cleared. - uint32_t b_pattern = (bits >> 16) & 0x3e00; - if (b_pattern != 0 && b_pattern != 0x3e00) { - return false; - } - - // bit[30] and bit[29] are opposite. - if (((bits ^ (bits << 1)) & 0x40000000) == 0) { - return false; - } - - return true; -} - - -bool Assembler::IsImmFP64(double imm) { - // Valid values will have the form: - // aBbb.bbbb.bbcd.efgh.0000.0000.0000.0000 - // 0000.0000.0000.0000.0000.0000.0000.0000 - uint64_t bits = double_to_rawbits(imm); - // bits[47..0] are cleared. - if ((bits & 0xffffffffffffL) != 0) { - return false; - } - - // bits[61..54] are all set or all cleared. - uint32_t b_pattern = (bits >> 48) & 0x3fc0; - if (b_pattern != 0 && b_pattern != 0x3fc0) { - return false; - } - - // bit[62] and bit[61] are opposite. - if (((bits ^ (bits << 1)) & 0x4000000000000000L) == 0) { - return false; - } - - return true; -} - - -void Assembler::GrowBuffer() { - if (!own_buffer_) FATAL("external code buffer is too small"); - - // Compute new buffer size. - CodeDesc desc; // the new buffer - if (buffer_size_ < 4 * KB) { - desc.buffer_size = 4 * KB; - } else if (buffer_size_ < 1 * MB) { - desc.buffer_size = 2 * buffer_size_; - } else { - desc.buffer_size = buffer_size_ + 1 * MB; - } - CHECK_GT(desc.buffer_size, 0); // No overflow. - - byte* buffer = reinterpret_cast(buffer_); - - // Set up new buffer. - desc.buffer = NewArray(desc.buffer_size); - - desc.instr_size = pc_offset(); - desc.reloc_size = (buffer + buffer_size_) - reloc_info_writer.pos(); - - // Copy the data. - intptr_t pc_delta = desc.buffer - buffer; - intptr_t rc_delta = (desc.buffer + desc.buffer_size) - - (buffer + buffer_size_); - memmove(desc.buffer, buffer, desc.instr_size); - memmove(reloc_info_writer.pos() + rc_delta, - reloc_info_writer.pos(), desc.reloc_size); - - // Switch buffers. - DeleteArray(buffer_); - buffer_ = desc.buffer; - buffer_size_ = desc.buffer_size; - pc_ = reinterpret_cast(pc_) + pc_delta; - reloc_info_writer.Reposition(reloc_info_writer.pos() + rc_delta, - reloc_info_writer.last_pc() + pc_delta); - - // None of our relocation types are pc relative pointing outside the code - // buffer nor pc absolute pointing inside the code buffer, so there is no need - // to relocate any emitted relocation entries. - - // Relocate pending relocation entries. - for (int i = 0; i < num_pending_reloc_info_; i++) { - RelocInfo& rinfo = pending_reloc_info_[i]; - ASSERT(rinfo.rmode() != RelocInfo::COMMENT && - rinfo.rmode() != RelocInfo::POSITION); - if (rinfo.rmode() != RelocInfo::JS_RETURN) { - rinfo.set_pc(rinfo.pc() + pc_delta); - } - } -} - - -void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) { - // We do not try to reuse pool constants. - RelocInfo rinfo(reinterpret_cast(pc_), rmode, data, NULL); - if (((rmode >= RelocInfo::JS_RETURN) && - (rmode <= RelocInfo::DEBUG_BREAK_SLOT)) || - (rmode == RelocInfo::CONST_POOL)) { - // Adjust code for new modes. - ASSERT(RelocInfo::IsDebugBreakSlot(rmode) - || RelocInfo::IsJSReturn(rmode) - || RelocInfo::IsComment(rmode) - || RelocInfo::IsPosition(rmode) - || RelocInfo::IsConstPool(rmode)); - // These modes do not need an entry in the constant pool. - } else { - ASSERT(num_pending_reloc_info_ < kMaxNumPendingRelocInfo); - if (num_pending_reloc_info_ == 0) { - first_const_pool_use_ = pc_offset(); - } - pending_reloc_info_[num_pending_reloc_info_++] = rinfo; - // Make sure the constant pool is not emitted in place of the next - // instruction for which we just recorded relocation info. - BlockConstPoolFor(1); - } - - if (!RelocInfo::IsNone(rmode)) { - // Don't record external references unless the heap will be serialized. - if (rmode == RelocInfo::EXTERNAL_REFERENCE) { -#ifdef DEBUG - if (!Serializer::enabled()) { - Serializer::TooLateToEnableNow(); - } -#endif - if (!Serializer::enabled() && !emit_debug_code()) { - return; - } - } - ASSERT(buffer_space() >= kMaxRelocSize); // too late to grow buffer here - if (rmode == RelocInfo::CODE_TARGET_WITH_ID) { - RelocInfo reloc_info_with_ast_id( - reinterpret_cast(pc_), rmode, RecordedAstId().ToInt(), NULL); - ClearRecordedAstId(); - reloc_info_writer.Write(&reloc_info_with_ast_id); - } else { - reloc_info_writer.Write(&rinfo); - } - } -} - - -void Assembler::BlockConstPoolFor(int instructions) { - int pc_limit = pc_offset() + instructions * kInstructionSize; - if (no_const_pool_before_ < pc_limit) { - // If there are some pending entries, the constant pool cannot be blocked - // further than first_const_pool_use_ + kMaxDistToPool - ASSERT((num_pending_reloc_info_ == 0) || - (pc_limit < (first_const_pool_use_ + kMaxDistToPool))); - no_const_pool_before_ = pc_limit; - } - - if (next_buffer_check_ < no_const_pool_before_) { - next_buffer_check_ = no_const_pool_before_; - } -} - - -void Assembler::CheckConstPool(bool force_emit, bool require_jump) { - // Some short sequence of instruction mustn't be broken up by constant pool - // emission, such sequences are protected by calls to BlockConstPoolFor and - // BlockConstPoolScope. - if (is_const_pool_blocked()) { - // Something is wrong if emission is forced and blocked at the same time. - ASSERT(!force_emit); - return; - } - - // There is nothing to do if there are no pending constant pool entries. - if (num_pending_reloc_info_ == 0) { - // Calculate the offset of the next check. - next_buffer_check_ = pc_offset() + kCheckPoolInterval; - return; - } - - // We emit a constant pool when: - // * requested to do so by parameter force_emit (e.g. after each function). - // * the distance to the first instruction accessing the constant pool is - // kAvgDistToPool or more. - // * no jump is required and the distance to the first instruction accessing - // the constant pool is at least kMaxDistToPool / 2. - ASSERT(first_const_pool_use_ >= 0); - int dist = pc_offset() - first_const_pool_use_; - if (!force_emit && dist < kAvgDistToPool && - (require_jump || (dist < (kMaxDistToPool / 2)))) { - return; - } - - Label size_check; - bind(&size_check); - - // Check that the code buffer is large enough before emitting the constant - // pool (include the jump over the pool, the constant pool marker, the - // constant pool guard, and the gap to the relocation information). - int jump_instr = require_jump ? kInstructionSize : 0; - int size_pool_marker = kInstructionSize; - int size_pool_guard = kInstructionSize; - int pool_size = jump_instr + size_pool_marker + size_pool_guard + - num_pending_reloc_info_ * kPointerSize; - int needed_space = pool_size + kGap; - while (buffer_space() <= needed_space) { - GrowBuffer(); - } - - { - // Block recursive calls to CheckConstPool. - BlockConstPoolScope block_const_pool(this); - RecordComment("[ Constant Pool"); - RecordConstPool(pool_size); - - // Emit jump over constant pool if necessary. - Label after_pool; - if (require_jump) { - b(&after_pool); - } - - // Emit a constant pool header. The header has two goals: - // 1) Encode the size of the constant pool, for use by the disassembler. - // 2) Terminate the program, to try to prevent execution from accidentally - // flowing into the constant pool. - // The header is therefore made of two a64 instructions: - // ldr xzr, # - // blr xzr - // If executed the code will likely segfault and lr will point to the - // beginning of the constant pool. - // TODO(all): currently each relocated constant is 64 bits, consider adding - // support for 32-bit entries. - ConstantPoolMarker(2 * num_pending_reloc_info_); - ConstantPoolGuard(); - - // Emit constant pool entries. - for (int i = 0; i < num_pending_reloc_info_; i++) { - RelocInfo& rinfo = pending_reloc_info_[i]; - ASSERT(rinfo.rmode() != RelocInfo::COMMENT && - rinfo.rmode() != RelocInfo::POSITION && - rinfo.rmode() != RelocInfo::STATEMENT_POSITION && - rinfo.rmode() != RelocInfo::CONST_POOL); - - Instruction* instr = reinterpret_cast(rinfo.pc()); - // Instruction to patch must be 'ldr rd, [pc, #offset]' with offset == 0. - ASSERT(instr->IsLdrLiteral() && - instr->ImmLLiteral() == 0); - - instr->SetImmPCOffsetTarget(reinterpret_cast(pc_)); - dc64(rinfo.data()); - } - - num_pending_reloc_info_ = 0; - first_const_pool_use_ = -1; - - RecordComment("]"); - - if (after_pool.is_linked()) { - bind(&after_pool); - } - } - - // Since a constant pool was just emitted, move the check offset forward by - // the standard interval. - next_buffer_check_ = pc_offset() + kCheckPoolInterval; - - ASSERT(SizeOfCodeGeneratedSince(&size_check) == - static_cast(pool_size)); -} - - -void Assembler::RecordComment(const char* msg) { - if (FLAG_code_comments) { - CheckBuffer(); - RecordRelocInfo(RelocInfo::COMMENT, reinterpret_cast(msg)); - } -} - - -int Assembler::buffer_space() const { - return reloc_info_writer.pos() - reinterpret_cast(pc_); -} - - -void Assembler::RecordJSReturn() { - positions_recorder()->WriteRecordedPositions(); - CheckBuffer(); - RecordRelocInfo(RelocInfo::JS_RETURN); -} - - -void Assembler::RecordDebugBreakSlot() { - positions_recorder()->WriteRecordedPositions(); - CheckBuffer(); - RecordRelocInfo(RelocInfo::DEBUG_BREAK_SLOT); -} - - -void Assembler::RecordConstPool(int size) { - // We only need this for debugger support, to correctly compute offsets in the - // code. -#ifdef ENABLE_DEBUGGER_SUPPORT - RecordRelocInfo(RelocInfo::CONST_POOL, static_cast(size)); -#endif -} - - -} } // namespace v8::internal - -#endif // V8_TARGET_ARCH_A64 diff --git a/deps/v8/src/a64/assembler-a64.h b/deps/v8/src/a64/assembler-a64.h deleted file mode 100644 index a2c93df2ae..0000000000 --- a/deps/v8/src/a64/assembler-a64.h +++ /dev/null @@ -1,2085 +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. - -#ifndef V8_A64_ASSEMBLER_A64_H_ -#define V8_A64_ASSEMBLER_A64_H_ - -#include -#include - -#include "globals.h" -#include "utils.h" -#include "assembler.h" -#include "serialize.h" -#include "a64/instructions-a64.h" -#include "a64/cpu-a64.h" - - -namespace v8 { -namespace internal { - - -// ----------------------------------------------------------------------------- -// Registers. -#define REGISTER_CODE_LIST(R) \ -R(0) R(1) R(2) R(3) R(4) R(5) R(6) R(7) \ -R(8) R(9) R(10) R(11) R(12) R(13) R(14) R(15) \ -R(16) R(17) R(18) R(19) R(20) R(21) R(22) R(23) \ -R(24) R(25) R(26) R(27) R(28) R(29) R(30) R(31) - - -static const int kRegListSizeInBits = sizeof(RegList) * kBitsPerByte; - - -// Some CPURegister methods can return Register and FPRegister types, so we -// need to declare them in advance. -struct Register; -struct FPRegister; - - -struct CPURegister { - enum RegisterType { - // The kInvalid value is used to detect uninitialized static instances, - // which are always zero-initialized before any constructors are called. - kInvalid = 0, - kRegister, - kFPRegister, - kNoRegister - }; - - static CPURegister Create(unsigned code, unsigned size, RegisterType type) { - CPURegister r = {code, size, type}; - return r; - } - - unsigned code() const; - RegisterType type() const; - RegList Bit() const; - unsigned SizeInBits() const; - int SizeInBytes() const; - bool Is32Bits() const; - bool Is64Bits() const; - bool IsValid() const; - bool IsValidOrNone() const; - bool IsValidRegister() const; - bool IsValidFPRegister() const; - bool IsNone() const; - bool Is(const CPURegister& other) const; - - bool IsZero() const; - bool IsSP() const; - - bool IsRegister() const; - bool IsFPRegister() const; - - Register X() const; - Register W() const; - FPRegister D() const; - FPRegister S() const; - - bool IsSameSizeAndType(const CPURegister& other) const; - - // V8 compatibility. - bool is(const CPURegister& other) const { return Is(other); } - bool is_valid() const { return IsValid(); } - - unsigned reg_code; - unsigned reg_size; - RegisterType reg_type; -}; - - -struct Register : public CPURegister { - static Register Create(unsigned code, unsigned size) { - return CPURegister::Create(code, size, CPURegister::kRegister); - } - - Register() { - reg_code = 0; - reg_size = 0; - reg_type = CPURegister::kNoRegister; - } - - Register(const CPURegister& r) { // NOLINT(runtime/explicit) - reg_code = r.reg_code; - reg_size = r.reg_size; - reg_type = r.reg_type; - ASSERT(IsValidOrNone()); - } - - bool IsValid() const { - ASSERT(IsRegister() || IsNone()); - return IsValidRegister(); - } - - static Register XRegFromCode(unsigned code); - static Register WRegFromCode(unsigned code); - - // Start of V8 compatibility section --------------------- - // These memebers are necessary for compilation. - // A few of them may be unused for now. - - static const int kNumRegisters = kNumberOfRegisters; - static int NumRegisters() { return kNumRegisters; } - - // We allow crankshaft to use the following registers: - // - x0 to x15 - // - x18 to x24 - // - x27 (also context) - // - // TODO(all): Register x25 is currently free and could be available for - // crankshaft, but we don't use it as we might use it as a per function - // literal pool pointer in the future. - // - // TODO(all): Consider storing cp in x25 to have only two ranges. - // We split allocatable registers in three ranges called - // - "low range" - // - "high range" - // - "context" - static const unsigned kAllocatableLowRangeBegin = 0; - static const unsigned kAllocatableLowRangeEnd = 15; - static const unsigned kAllocatableHighRangeBegin = 18; - static const unsigned kAllocatableHighRangeEnd = 24; - static const unsigned kAllocatableContext = 27; - - // Gap between low and high ranges. - static const int kAllocatableRangeGapSize = - (kAllocatableHighRangeBegin - kAllocatableLowRangeEnd) - 1; - - static const int kMaxNumAllocatableRegisters = - (kAllocatableLowRangeEnd - kAllocatableLowRangeBegin + 1) + - (kAllocatableHighRangeEnd - kAllocatableHighRangeBegin + 1) + 1; // cp - static int NumAllocatableRegisters() { return kMaxNumAllocatableRegisters; } - - // Return true if the register is one that crankshaft can allocate. - bool IsAllocatable() const { - return ((reg_code == kAllocatableContext) || - (reg_code <= kAllocatableLowRangeEnd) || - ((reg_code >= kAllocatableHighRangeBegin) && - (reg_code <= kAllocatableHighRangeEnd))); - } - - static Register FromAllocationIndex(unsigned index) { - ASSERT(index < static_cast(NumAllocatableRegisters())); - // cp is the last allocatable register. - if (index == (static_cast(NumAllocatableRegisters() - 1))) { - return from_code(kAllocatableContext); - } - - // Handle low and high ranges. - return (index <= kAllocatableLowRangeEnd) - ? from_code(index) - : from_code(index + kAllocatableRangeGapSize); - } - - static const char* AllocationIndexToString(int index) { - ASSERT((index >= 0) && (index < NumAllocatableRegisters())); - ASSERT((kAllocatableLowRangeBegin == 0) && - (kAllocatableLowRangeEnd == 15) && - (kAllocatableHighRangeBegin == 18) && - (kAllocatableHighRangeEnd == 24) && - (kAllocatableContext == 27)); - const char* const names[] = { - "x0", "x1", "x2", "x3", "x4", - "x5", "x6", "x7", "x8", "x9", - "x10", "x11", "x12", "x13", "x14", - "x15", "x18", "x19", "x20", "x21", - "x22", "x23", "x24", "x27", - }; - return names[index]; - } - - static int ToAllocationIndex(Register reg) { - ASSERT(reg.IsAllocatable()); - unsigned code = reg.code(); - if (code == kAllocatableContext) { - return NumAllocatableRegisters() - 1; - } - - return (code <= kAllocatableLowRangeEnd) - ? code - : code - kAllocatableRangeGapSize; - } - - static Register from_code(int code) { - // Always return an X register. - return Register::Create(code, kXRegSize); - } - - // End of V8 compatibility section ----------------------- -}; - - -struct FPRegister : public CPURegister { - static FPRegister Create(unsigned code, unsigned size) { - return CPURegister::Create(code, size, CPURegister::kFPRegister); - } - - FPRegister() { - reg_code = 0; - reg_size = 0; - reg_type = CPURegister::kNoRegister; - } - - FPRegister(const CPURegister& r) { // NOLINT(runtime/explicit) - reg_code = r.reg_code; - reg_size = r.reg_size; - reg_type = r.reg_type; - ASSERT(IsValidOrNone()); - } - - bool IsValid() const { - ASSERT(IsFPRegister() || IsNone()); - return IsValidFPRegister(); - } - - static FPRegister SRegFromCode(unsigned code); - static FPRegister DRegFromCode(unsigned code); - - // Start of V8 compatibility section --------------------- - static const int kMaxNumRegisters = kNumberOfFPRegisters; - - // Crankshaft can use all the FP registers except: - // - d29 which is used in crankshaft as a double scratch register - // - d30 which is used to keep the 0 double value - // - d31 which is used in the MacroAssembler as a double scratch register - static const int kNumReservedRegisters = 3; - static const int kMaxNumAllocatableRegisters = - kNumberOfFPRegisters - kNumReservedRegisters; - static int NumAllocatableRegisters() { return kMaxNumAllocatableRegisters; } - static const RegList kAllocatableFPRegisters = - (1 << kMaxNumAllocatableRegisters) - 1; - - static FPRegister FromAllocationIndex(int index) { - ASSERT((index >= 0) && (index < NumAllocatableRegisters())); - return from_code(index); - } - - static const char* AllocationIndexToString(int index) { - ASSERT((index >= 0) && (index < NumAllocatableRegisters())); - const char* const names[] = { - "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", - "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15", - "d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23", - "d24", "d25", "d26", "d27", "d28", - }; - return names[index]; - } - - static int ToAllocationIndex(FPRegister reg) { - int code = reg.code(); - ASSERT(code < NumAllocatableRegisters()); - return code; - } - - static FPRegister from_code(int code) { - // Always return a D register. - return FPRegister::Create(code, kDRegSize); - } - // End of V8 compatibility section ----------------------- -}; - - -STATIC_ASSERT(sizeof(CPURegister) == sizeof(Register)); -STATIC_ASSERT(sizeof(CPURegister) == sizeof(FPRegister)); - - -#if defined(A64_DEFINE_REG_STATICS) -#define INITIALIZE_REGISTER(register_class, name, code, size, type) \ - const CPURegister init_##register_class##_##name = {code, size, type}; \ - const register_class& name = *reinterpret_cast( \ - &init_##register_class##_##name) -#define ALIAS_REGISTER(register_class, alias, name) \ - const register_class& alias = *reinterpret_cast( \ - &init_##register_class##_##name) -#else -#define INITIALIZE_REGISTER(register_class, name, code, size, type) \ - extern const register_class& name -#define ALIAS_REGISTER(register_class, alias, name) \ - extern const register_class& alias -#endif // defined(A64_DEFINE_REG_STATICS) - -// No*Reg is used to indicate an unused argument, or an error case. Note that -// these all compare equal (using the Is() method). The Register and FPRegister -// variants are provided for convenience. -INITIALIZE_REGISTER(Register, NoReg, 0, 0, CPURegister::kNoRegister); -INITIALIZE_REGISTER(FPRegister, NoFPReg, 0, 0, CPURegister::kNoRegister); -INITIALIZE_REGISTER(CPURegister, NoCPUReg, 0, 0, CPURegister::kNoRegister); - -// v8 compatibility. -INITIALIZE_REGISTER(Register, no_reg, 0, 0, CPURegister::kNoRegister); - -#define DEFINE_REGISTERS(N) \ - INITIALIZE_REGISTER(Register, w##N, N, kWRegSize, CPURegister::kRegister); \ - INITIALIZE_REGISTER(Register, x##N, N, kXRegSize, CPURegister::kRegister); -REGISTER_CODE_LIST(DEFINE_REGISTERS) -#undef DEFINE_REGISTERS - -INITIALIZE_REGISTER(Register, wcsp, kSPRegInternalCode, kWRegSize, - CPURegister::kRegister); -INITIALIZE_REGISTER(Register, csp, kSPRegInternalCode, kXRegSize, - CPURegister::kRegister); - -#define DEFINE_FPREGISTERS(N) \ - INITIALIZE_REGISTER(FPRegister, s##N, N, kSRegSize, \ - CPURegister::kFPRegister); \ - INITIALIZE_REGISTER(FPRegister, d##N, N, kDRegSize, CPURegister::kFPRegister); -REGISTER_CODE_LIST(DEFINE_FPREGISTERS) -#undef DEFINE_FPREGISTERS - -#undef INITIALIZE_REGISTER - -// Registers aliases. -ALIAS_REGISTER(Register, ip0, x16); -ALIAS_REGISTER(Register, ip1, x17); -ALIAS_REGISTER(Register, wip0, w16); -ALIAS_REGISTER(Register, wip1, w17); -// Root register. -ALIAS_REGISTER(Register, root, x26); -ALIAS_REGISTER(Register, rr, x26); -// Context pointer register. -ALIAS_REGISTER(Register, cp, x27); -// We use a register as a JS stack pointer to overcome the restriction on the -// architectural SP alignment. -// We chose x28 because it is contiguous with the other specific purpose -// registers. -STATIC_ASSERT(kJSSPCode == 28); -ALIAS_REGISTER(Register, jssp, x28); -ALIAS_REGISTER(Register, wjssp, w28); -ALIAS_REGISTER(Register, fp, x29); -ALIAS_REGISTER(Register, lr, x30); -ALIAS_REGISTER(Register, xzr, x31); -ALIAS_REGISTER(Register, wzr, w31); - -// Crankshaft double scratch register. -ALIAS_REGISTER(FPRegister, crankshaft_fp_scratch, d29); -// Keeps the 0 double value. -ALIAS_REGISTER(FPRegister, fp_zero, d30); -// MacroAssembler double scratch register. -ALIAS_REGISTER(FPRegister, fp_scratch, d31); - -#undef ALIAS_REGISTER - - -Register GetAllocatableRegisterThatIsNotOneOf(Register reg1, - Register reg2 = NoReg, - Register reg3 = NoReg, - Register reg4 = NoReg); - - -// AreAliased returns true if any of the named registers overlap. Arguments set -// to NoReg are ignored. The system stack pointer may be specified. -bool AreAliased(const CPURegister& reg1, - const CPURegister& reg2, - const CPURegister& reg3 = NoReg, - const CPURegister& reg4 = NoReg, - const CPURegister& reg5 = NoReg, - const CPURegister& reg6 = NoReg, - const CPURegister& reg7 = NoReg, - const CPURegister& reg8 = NoReg); - -// AreSameSizeAndType returns true if all of the specified registers have the -// same size, and are of the same type. The system stack pointer may be -// specified. Arguments set to NoReg are ignored, as are any subsequent -// arguments. At least one argument (reg1) must be valid (not NoCPUReg). -bool AreSameSizeAndType(const CPURegister& reg1, - const CPURegister& reg2, - const CPURegister& reg3 = NoCPUReg, - const CPURegister& reg4 = NoCPUReg, - const CPURegister& reg5 = NoCPUReg, - const CPURegister& reg6 = NoCPUReg, - const CPURegister& reg7 = NoCPUReg, - const CPURegister& reg8 = NoCPUReg); - - -typedef FPRegister DoubleRegister; - - -// ----------------------------------------------------------------------------- -// Lists of registers. -class CPURegList { - public: - explicit CPURegList(CPURegister reg1, - CPURegister reg2 = NoCPUReg, - CPURegister reg3 = NoCPUReg, - CPURegister reg4 = NoCPUReg) - : list_(reg1.Bit() | reg2.Bit() | reg3.Bit() | reg4.Bit()), - size_(reg1.SizeInBits()), type_(reg1.type()) { - ASSERT(AreSameSizeAndType(reg1, reg2, reg3, reg4)); - ASSERT(IsValid()); - } - - CPURegList(CPURegister::RegisterType type, unsigned size, RegList list) - : list_(list), size_(size), type_(type) { - ASSERT(IsValid()); - } - - CPURegList(CPURegister::RegisterType type, unsigned size, - unsigned first_reg, unsigned last_reg) - : size_(size), type_(type) { - ASSERT(((type == CPURegister::kRegister) && - (last_reg < kNumberOfRegisters)) || - ((type == CPURegister::kFPRegister) && - (last_reg < kNumberOfFPRegisters))); - ASSERT(last_reg >= first_reg); - list_ = (1UL << (last_reg + 1)) - 1; - list_ &= ~((1UL << first_reg) - 1); - ASSERT(IsValid()); - } - - CPURegister::RegisterType type() const { - ASSERT(IsValid()); - return type_; - } - - RegList list() const { - ASSERT(IsValid()); - return list_; - } - - // Combine another CPURegList into this one. Registers that already exist in - // this list are left unchanged. The type and size of the registers in the - // 'other' list must match those in this list. - void Combine(const CPURegList& other); - - // Remove every register in the other CPURegList from this one. Registers that - // do not exist in this list are ignored. The type and size of the registers - // in the 'other' list must match those in this list. - void Remove(const CPURegList& other); - - // Variants of Combine and Remove which take a single register. - void Combine(const CPURegister& other); - void Remove(const CPURegister& other); - - // Variants of Combine and Remove which take a single register by its code; - // the type and size of the register is inferred from this list. - void Combine(int code); - void Remove(int code); - - // Remove all callee-saved registers from the list. This can be useful when - // preparing registers for an AAPCS64 function call, for example. - void RemoveCalleeSaved(); - - CPURegister PopLowestIndex(); - CPURegister PopHighestIndex(); - - // AAPCS64 callee-saved registers. - static CPURegList GetCalleeSaved(unsigned size = kXRegSize); - static CPURegList GetCalleeSavedFP(unsigned size = kDRegSize); - - // AAPCS64 caller-saved registers. Note that this includes lr. - static CPURegList GetCallerSaved(unsigned size = kXRegSize); - static CPURegList GetCallerSavedFP(unsigned size = kDRegSize); - - // Registers saved as safepoints. - static CPURegList GetSafepointSavedRegisters(); - - bool IsEmpty() const { - ASSERT(IsValid()); - return list_ == 0; - } - - bool IncludesAliasOf(const CPURegister& other) const { - ASSERT(IsValid()); - return (type_ == other.type()) && (other.Bit() & list_); - } - - int Count() const { - ASSERT(IsValid()); - return CountSetBits(list_, kRegListSizeInBits); - } - - unsigned RegisterSizeInBits() const { - ASSERT(IsValid()); - return size_; - } - - unsigned RegisterSizeInBytes() const { - int size_in_bits = RegisterSizeInBits(); - ASSERT((size_in_bits % kBitsPerByte) == 0); - return size_in_bits / kBitsPerByte; - } - - private: - RegList list_; - unsigned size_; - CPURegister::RegisterType type_; - - bool IsValid() const { - if ((type_ == CPURegister::kRegister) || - (type_ == CPURegister::kFPRegister)) { - bool is_valid = true; - // Try to create a CPURegister for each element in the list. - for (int i = 0; i < kRegListSizeInBits; i++) { - if (((list_ >> i) & 1) != 0) { - is_valid &= CPURegister::Create(i, size_, type_).IsValid(); - } - } - return is_valid; - } else if (type_ == CPURegister::kNoRegister) { - // The kNoRegister type is valid only for empty lists. - // We can't use IsEmpty here because that asserts IsValid(). - return list_ == 0; - } else { - return false; - } - } -}; - - -// AAPCS64 callee-saved registers. -#define kCalleeSaved CPURegList::GetCalleeSaved() -#define kCalleeSavedFP CPURegList::GetCalleeSavedFP() - - -// AAPCS64 caller-saved registers. Note that this includes lr. -#define kCallerSaved CPURegList::GetCallerSaved() -#define kCallerSavedFP CPURegList::GetCallerSavedFP() - - -// ----------------------------------------------------------------------------- -// Operands. -const int kSmiShift = kSmiTagSize + kSmiShiftSize; -const uint64_t kSmiShiftMask = (1UL << kSmiShift) - 1; - -// Represents an operand in a machine instruction. -class Operand { - // TODO(all): If necessary, study more in details which methods - // TODO(all): should be inlined or not. - public: - // rm, { {#}} - // where is one of {LSL, LSR, ASR, ROR}. - // is uint6_t. - // This is allowed to be an implicit constructor because Operand is - // a wrapper class that doesn't normally perform any type conversion. - inline Operand(Register reg, - Shift shift = LSL, - unsigned shift_amount = 0); // NOLINT(runtime/explicit) - - // rm, {#} - // where is one of {UXTB, UXTH, UXTW, UXTX, SXTB, SXTH, SXTW, SXTX}. - // is uint2_t. - inline Operand(Register reg, - Extend extend, - unsigned shift_amount = 0); - - template - inline explicit Operand(Handle handle); - - // Implicit constructor for all int types, ExternalReference, and Smi. - template - inline Operand(T t); // NOLINT(runtime/explicit) - - // Implicit constructor for int types. - template - inline Operand(int_t t, RelocInfo::Mode rmode); - - inline bool IsImmediate() const; - inline bool IsShiftedRegister() const; - inline bool IsExtendedRegister() const; - inline bool IsZero() const; - - // This returns an LSL shift (<= 4) operand as an equivalent extend operand, - // which helps in the encoding of instructions that use the stack pointer. - inline Operand ToExtendedRegister() const; - - inline int64_t immediate() const; - inline Register reg() const; - inline Shift shift() const; - inline Extend extend() const; - inline unsigned shift_amount() const; - - // Relocation information. - RelocInfo::Mode rmode() const { return rmode_; } - void set_rmode(RelocInfo::Mode rmode) { rmode_ = rmode; } - bool NeedsRelocation() const; - - // Helpers - inline static Operand UntagSmi(Register smi); - inline static Operand UntagSmiAndScale(Register smi, int scale); - - private: - void initialize_handle(Handle value); - int64_t immediate_; - Register reg_; - Shift shift_; - Extend extend_; - unsigned shift_amount_; - RelocInfo::Mode rmode_; -}; - - -// MemOperand represents a memory operand in a load or store instruction. -class MemOperand { - public: - inline explicit MemOperand(Register base, - ptrdiff_t offset = 0, - AddrMode addrmode = Offset); - inline explicit MemOperand(Register base, - Register regoffset, - Shift shift = LSL, - unsigned shift_amount = 0); - inline explicit MemOperand(Register base, - Register regoffset, - Extend extend, - unsigned shift_amount = 0); - inline explicit MemOperand(Register base, - const Operand& offset, - AddrMode addrmode = Offset); - - const Register& base() const { return base_; } - const Register& regoffset() const { return regoffset_; } - ptrdiff_t offset() const { return offset_; } - AddrMode addrmode() const { return addrmode_; } - Shift shift() const { return shift_; } - Extend extend() const { return extend_; } - unsigned shift_amount() const { return shift_amount_; } - inline bool IsImmediateOffset() const; - inline bool IsRegisterOffset() const; - inline bool IsPreIndex() const; - inline bool IsPostIndex() const; - - // For offset modes, return the offset as an Operand. This helper cannot - // handle indexed modes. - inline Operand OffsetAsOperand() const; - - private: - Register base_; - Register regoffset_; - ptrdiff_t offset_; - AddrMode addrmode_; - Shift shift_; - Extend extend_; - unsigned shift_amount_; -}; - - -// ----------------------------------------------------------------------------- -// Assembler. - -class Assembler : public AssemblerBase { - 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* arg_isolate, void* buffer, int buffer_size); - - virtual ~Assembler(); - - // System functions --------------------------------------------------------- - // Start generating code from the beginning of the buffer, discarding any code - // and data that has already been emitted into the buffer. - // - // In order to avoid any accidental transfer of state, Reset ASSERTs that the - // constant pool is not blocked. - void Reset(); - - // 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. - // - // The descriptor (desc) can be NULL. In that case, the code is finalized as - // usual, but the descriptor is not populated. - void GetCode(CodeDesc* desc); - - // 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 (>= 4). - void Align(int m); - - inline void Unreachable(); - - // Label -------------------------------------------------------------------- - // Bind a label to the current pc. Note that labels can only be bound once, - // and if labels are linked to other instructions, they _must_ be bound - // before they go out of scope. - void bind(Label* label); - - - // RelocInfo and constant pool ---------------------------------------------- - - // Record relocation information for current pc_. - void RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data = 0); - - // Return the address in the constant pool of the code target address used by - // the branch/call instruction at pc. - inline static Address target_pointer_address_at(Address pc); - - // Read/Modify the code target address in the branch/call instruction at pc. - inline static Address target_address_at(Address pc); - inline static void set_target_address_at(Address pc, Address target); - - // 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); - - // Given the address of the beginning of a call, return the address in the - // instruction stream that call will return from. - inline static Address return_address_from_call_start(Address pc); - - // This sets the branch destination (which is in the constant pool on ARM). - // This is for calls and branches within generated code. - inline static void deserialization_set_special_target_at( - Address constant_pool_entry, Address target); - - // All addresses in the constant pool are the same size as pointers. - static const int kSpecialTargetSize = kPointerSize; - - // The sizes of the call sequences emitted by MacroAssembler::Call. - // Wherever possible, use MacroAssembler::CallSize instead of these constants, - // as it will choose the correct value for a given relocation mode. - // - // Without relocation: - // movz ip0, #(target & 0x000000000000ffff) - // movk ip0, #(target & 0x00000000ffff0000) - // movk ip0, #(target & 0x0000ffff00000000) - // movk ip0, #(target & 0xffff000000000000) - // blr ip0 - // - // With relocation: - // ldr ip0, =target - // blr ip0 - static const int kCallSizeWithoutRelocation = 5 * kInstructionSize; - static const int kCallSizeWithRelocation = 2 * kInstructionSize; - - // Size of the generated code in bytes - uint64_t SizeOfGeneratedCode() const { - ASSERT((pc_ >= buffer_) && (pc_ < (buffer_ + buffer_size_))); - return pc_ - buffer_; - } - - // Return the code size generated from label to the current position. - uint64_t SizeOfCodeGeneratedSince(const Label* label) { - ASSERT(label->is_bound()); - ASSERT(pc_offset() >= label->pos()); - ASSERT(pc_offset() < buffer_size_); - return pc_offset() - label->pos(); - } - - // Check the size of the code generated since the given label. This function - // is used primarily to work around comparisons between signed and unsigned - // quantities, since V8 uses both. - // TODO(jbramley): Work out what sign to use for these things and if possible, - // change things to be consistent. - void AssertSizeOfCodeGeneratedSince(const Label* label, ptrdiff_t size) { - ASSERT(size >= 0); - ASSERT(static_cast(size) == SizeOfCodeGeneratedSince(label)); - } - - // Return the number of instructions generated from label to the - // current position. - int InstructionsGeneratedSince(const Label* label) { - return SizeOfCodeGeneratedSince(label) / kInstructionSize; - } - - // TODO(all): Initialize these constants related with code patching. - // TODO(all): Set to -1 to hopefully crash if mistakenly used. - - // Number of instructions generated for the return sequence in - // FullCodeGenerator::EmitReturnSequence. - static const int kJSRetSequenceInstructions = 7; - // Distance between start of patched return sequence and the emitted address - // to jump to. - static const int kPatchReturnSequenceAddressOffset = 0; - static const int kPatchDebugBreakSlotAddressOffset = 0; - - // Number of instructions necessary to be able to later patch it to a call. - // See Debug::GenerateSlot() and BreakLocationIterator::SetDebugBreakAtSlot(). - static const int kDebugBreakSlotInstructions = 4; - static const int kDebugBreakSlotLength = - kDebugBreakSlotInstructions * kInstructionSize; - - static const int kPatchDebugBreakSlotReturnOffset = 2 * kInstructionSize; - - // Prevent contant pool emission until EndBlockConstPool is called. - // Call to this function can be nested but must be followed by an equal - // number of call to EndBlockConstpool. - void StartBlockConstPool(); - - // Resume constant pool emission. Need to be called as many time as - // StartBlockConstPool to have an effect. - void EndBlockConstPool(); - - bool is_const_pool_blocked() const; - static bool IsConstantPoolAt(Instruction* instr); - static int ConstantPoolSizeAt(Instruction* instr); - // See Assembler::CheckConstPool for more info. - void ConstantPoolMarker(uint32_t size); - void ConstantPoolGuard(); - - - // Debugging ---------------------------------------------------------------- - PositionsRecorder* positions_recorder() { return &positions_recorder_; } - void RecordComment(const char* msg); - int buffer_space() const; - - // Mark address of the ExitJSFrame code. - void RecordJSReturn(); - - // Mark address of a debug break slot. - void RecordDebugBreakSlot(); - - // Record the emission of a constant pool. - // - // The emission of constant pool depends on the size of the code generated and - // the number of RelocInfo recorded. - // The Debug mechanism needs to map code offsets between two versions of a - // function, compiled with and without debugger support (see for example - // Debug::PrepareForBreakPoints()). - // Compiling functions with debugger support generates additional code - // (Debug::GenerateSlot()). This may affect the emission of the constant - // pools and cause the version of the code with debugger support to have - // constant pools generated in different places. - // Recording the position and size of emitted constant pools allows to - // correctly compute the offset mappings between the different versions of a - // function in all situations. - // - // The parameter indicates the size of the constant pool (in bytes), including - // the marker and branch over the data. - void RecordConstPool(int size); - - - // Instruction set functions ------------------------------------------------ - - // Branch / Jump instructions. - // For branches offsets are scaled, i.e. they in instrcutions not in bytes. - // Branch to register. - void br(const Register& xn); - - // Branch-link to register. - void blr(const Register& xn); - - // Branch to register with return hint. - void ret(const Register& xn = lr); - - // Unconditional branch to label. - void b(Label* label); - - // Conditional branch to label. - void b(Label* label, Condition cond); - - // Unconditional branch to PC offset. - void b(int imm26); - - // Conditional branch to PC offset. - void b(int imm19, Condition cond); - - // Branch-link to label / pc offset. - void bl(Label* label); - void bl(int imm26); - - // Compare and branch to label / pc offset if zero. - void cbz(const Register& rt, Label* label); - void cbz(const Register& rt, int imm19); - - // Compare and branch to label / pc offset if not zero. - void cbnz(const Register& rt, Label* label); - void cbnz(const Register& rt, int imm19); - - // Test bit and branch to label / pc offset if zero. - void tbz(const Register& rt, unsigned bit_pos, Label* label); - void tbz(const Register& rt, unsigned bit_pos, int imm14); - - // Test bit and branch to label / pc offset if not zero. - void tbnz(const Register& rt, unsigned bit_pos, Label* label); - void tbnz(const Register& rt, unsigned bit_pos, int imm14); - - // Address calculation instructions. - // Calculate a PC-relative address. Unlike for branches the offset in adr is - // unscaled (i.e. the result can be unaligned). - void adr(const Register& rd, Label* label); - void adr(const Register& rd, int imm21); - - // Data Processing instructions. - // Add. - void add(const Register& rd, - const Register& rn, - const Operand& operand); - - // Add and update status flags. - void adds(const Register& rd, - const Register& rn, - const Operand& operand); - - // Compare negative. - void cmn(const Register& rn, const Operand& operand); - - // Subtract. - void sub(const Register& rd, - const Register& rn, - const Operand& operand); - - // Subtract and update status flags. - void subs(const Register& rd, - const Register& rn, - const Operand& operand); - - // Compare. - void cmp(const Register& rn, const Operand& operand); - - // Negate. - void neg(const Register& rd, - const Operand& operand); - - // Negate and update status flags. - void negs(const Register& rd, - const Operand& operand); - - // Add with carry bit. - void adc(const Register& rd, - const Register& rn, - const Operand& operand); - - // Add with carry bit and update status flags. - void adcs(const Register& rd, - const Register& rn, - const Operand& operand); - - // Subtract with carry bit. - void sbc(const Register& rd, - const Register& rn, - const Operand& operand); - - // Subtract with carry bit and update status flags. - void sbcs(const Register& rd, - const Register& rn, - const Operand& operand); - - // Negate with carry bit. - void ngc(const Register& rd, - const Operand& operand); - - // Negate with carry bit and update status flags. - void ngcs(const Register& rd, - const Operand& operand); - - // Logical instructions. - // Bitwise and (A & B). - void and_(const Register& rd, - const Register& rn, - const Operand& operand); - - // Bitwise and (A & B) and update status flags. - void ands(const Register& rd, - const Register& rn, - const Operand& operand); - - // Bit test, and set flags. - void tst(const Register& rn, const Operand& operand); - - // Bit clear (A & ~B). - void bic(const Register& rd, - const Register& rn, - const Operand& operand); - - // Bit clear (A & ~B) and update status flags. - void bics(const Register& rd, - const Register& rn, - const Operand& operand); - - // Bitwise or (A | B). - void orr(const Register& rd, const Register& rn, const Operand& operand); - - // Bitwise nor (A | ~B). - void orn(const Register& rd, const Register& rn, const Operand& operand); - - // Bitwise eor/xor (A ^ B). - void eor(const Register& rd, const Register& rn, const Operand& operand); - - // Bitwise enor/xnor (A ^ ~B). - void eon(const Register& rd, const Register& rn, const Operand& operand); - - // Logical shift left variable. - void lslv(const Register& rd, const Register& rn, const Register& rm); - - // Logical shift right variable. - void lsrv(const Register& rd, const Register& rn, const Register& rm); - - // Arithmetic shift right variable. - void asrv(const Register& rd, const Register& rn, const Register& rm); - - // Rotate right variable. - void rorv(const Register& rd, const Register& rn, const Register& rm); - - // Bitfield instructions. - // Bitfield move. - void bfm(const Register& rd, - const Register& rn, - unsigned immr, - unsigned imms); - - // Signed bitfield move. - void sbfm(const Register& rd, - const Register& rn, - unsigned immr, - unsigned imms); - - // Unsigned bitfield move. - void ubfm(const Register& rd, - const Register& rn, - unsigned immr, - unsigned imms); - - // Bfm aliases. - // Bitfield insert. - void bfi(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width) { - ASSERT(width >= 1); - ASSERT(lsb + width <= rn.SizeInBits()); - bfm(rd, rn, (rd.SizeInBits() - lsb) & (rd.SizeInBits() - 1), width - 1); - } - - // Bitfield extract and insert low. - void bfxil(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width) { - ASSERT(width >= 1); - ASSERT(lsb + width <= rn.SizeInBits()); - bfm(rd, rn, lsb, lsb + width - 1); - } - - // Sbfm aliases. - // Arithmetic shift right. - void asr(const Register& rd, const Register& rn, unsigned shift) { - ASSERT(shift < rd.SizeInBits()); - sbfm(rd, rn, shift, rd.SizeInBits() - 1); - } - - // Signed bitfield insert in zero. - void sbfiz(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width) { - ASSERT(width >= 1); - ASSERT(lsb + width <= rn.SizeInBits()); - sbfm(rd, rn, (rd.SizeInBits() - lsb) & (rd.SizeInBits() - 1), width - 1); - } - - // Signed bitfield extract. - void sbfx(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width) { - ASSERT(width >= 1); - ASSERT(lsb + width <= rn.SizeInBits()); - sbfm(rd, rn, lsb, lsb + width - 1); - } - - // Signed extend byte. - void sxtb(const Register& rd, const Register& rn) { - sbfm(rd, rn, 0, 7); - } - - // Signed extend halfword. - void sxth(const Register& rd, const Register& rn) { - sbfm(rd, rn, 0, 15); - } - - // Signed extend word. - void sxtw(const Register& rd, const Register& rn) { - sbfm(rd, rn, 0, 31); - } - - // Ubfm aliases. - // Logical shift left. - void lsl(const Register& rd, const Register& rn, unsigned shift) { - unsigned reg_size = rd.SizeInBits(); - ASSERT(shift < reg_size); - ubfm(rd, rn, (reg_size - shift) % reg_size, reg_size - shift - 1); - } - - // Logical shift right. - void lsr(const Register& rd, const Register& rn, unsigned shift) { - ASSERT(shift < rd.SizeInBits()); - ubfm(rd, rn, shift, rd.SizeInBits() - 1); - } - - // Unsigned bitfield insert in zero. - void ubfiz(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width) { - ASSERT(width >= 1); - ASSERT(lsb + width <= rn.SizeInBits()); - ubfm(rd, rn, (rd.SizeInBits() - lsb) & (rd.SizeInBits() - 1), width - 1); - } - - // Unsigned bitfield extract. - void ubfx(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width) { - ASSERT(width >= 1); - ASSERT(lsb + width <= rn.SizeInBits()); - ubfm(rd, rn, lsb, lsb + width - 1); - } - - // Unsigned extend byte. - void uxtb(const Register& rd, const Register& rn) { - ubfm(rd, rn, 0, 7); - } - - // Unsigned extend halfword. - void uxth(const Register& rd, const Register& rn) { - ubfm(rd, rn, 0, 15); - } - - // Unsigned extend word. - void uxtw(const Register& rd, const Register& rn) { - ubfm(rd, rn, 0, 31); - } - - // Extract. - void extr(const Register& rd, - const Register& rn, - const Register& rm, - unsigned lsb); - - // Conditional select: rd = cond ? rn : rm. - void csel(const Register& rd, - const Register& rn, - const Register& rm, - Condition cond); - - // Conditional select increment: rd = cond ? rn : rm + 1. - void csinc(const Register& rd, - const Register& rn, - const Register& rm, - Condition cond); - - // Conditional select inversion: rd = cond ? rn : ~rm. - void csinv(const Register& rd, - const Register& rn, - const Register& rm, - Condition cond); - - // Conditional select negation: rd = cond ? rn : -rm. - void csneg(const Register& rd, - const Register& rn, - const Register& rm, - Condition cond); - - // Conditional set: rd = cond ? 1 : 0. - void cset(const Register& rd, Condition cond); - - // Conditional set minus: rd = cond ? -1 : 0. - void csetm(const Register& rd, Condition cond); - - // Conditional increment: rd = cond ? rn + 1 : rn. - void cinc(const Register& rd, const Register& rn, Condition cond); - - // Conditional invert: rd = cond ? ~rn : rn. - void cinv(const Register& rd, const Register& rn, Condition cond); - - // Conditional negate: rd = cond ? -rn : rn. - void cneg(const Register& rd, const Register& rn, Condition cond); - - // Extr aliases. - void ror(const Register& rd, const Register& rs, unsigned shift) { - extr(rd, rs, rs, shift); - } - - // Conditional comparison. - // Conditional compare negative. - void ccmn(const Register& rn, - const Operand& operand, - StatusFlags nzcv, - Condition cond); - - // Conditional compare. - void ccmp(const Register& rn, - const Operand& operand, - StatusFlags nzcv, - Condition cond); - - // Multiplication. - // 32 x 32 -> 32-bit and 64 x 64 -> 64-bit multiply. - void mul(const Register& rd, const Register& rn, const Register& rm); - - // 32 + 32 x 32 -> 32-bit and 64 + 64 x 64 -> 64-bit multiply accumulate. - void madd(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra); - - // -(32 x 32) -> 32-bit and -(64 x 64) -> 64-bit multiply. - void mneg(const Register& rd, const Register& rn, const Register& rm); - - // 32 - 32 x 32 -> 32-bit and 64 - 64 x 64 -> 64-bit multiply subtract. - void msub(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra); - - // 32 x 32 -> 64-bit multiply. - void smull(const Register& rd, const Register& rn, const Register& rm); - - // Xd = bits<127:64> of Xn * Xm. - void smulh(const Register& rd, const Register& rn, const Register& rm); - - // Signed 32 x 32 -> 64-bit multiply and accumulate. - void smaddl(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra); - - // Unsigned 32 x 32 -> 64-bit multiply and accumulate. - void umaddl(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra); - - // Signed 32 x 32 -> 64-bit multiply and subtract. - void smsubl(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra); - - // Unsigned 32 x 32 -> 64-bit multiply and subtract. - void umsubl(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra); - - // Signed integer divide. - void sdiv(const Register& rd, const Register& rn, const Register& rm); - - // Unsigned integer divide. - void udiv(const Register& rd, const Register& rn, const Register& rm); - - // Bit count, bit reverse and endian reverse. - void rbit(const Register& rd, const Register& rn); - void rev16(const Register& rd, const Register& rn); - void rev32(const Register& rd, const Register& rn); - void rev(const Register& rd, const Register& rn); - void clz(const Register& rd, const Register& rn); - void cls(const Register& rd, const Register& rn); - - // Memory instructions. - - // Load literal from pc + offset_from_pc. - void LoadLiteral(const CPURegister& rt, int offset_from_pc); - - // Load integer or FP register. - void ldr(const CPURegister& rt, const MemOperand& src); - - // Store integer or FP register. - void str(const CPURegister& rt, const MemOperand& dst); - - // Load word with sign extension. - void ldrsw(const Register& rt, const MemOperand& src); - - // Load byte. - void ldrb(const Register& rt, const MemOperand& src); - - // Store byte. - void strb(const Register& rt, const MemOperand& dst); - - // Load byte with sign extension. - void ldrsb(const Register& rt, const MemOperand& src); - - // Load half-word. - void ldrh(const Register& rt, const MemOperand& src); - - // Store half-word. - void strh(const Register& rt, const MemOperand& dst); - - // Load half-word with sign extension. - void ldrsh(const Register& rt, const MemOperand& src); - - // Load integer or FP register pair. - void ldp(const CPURegister& rt, const CPURegister& rt2, - const MemOperand& src); - - // Store integer or FP register pair. - void stp(const CPURegister& rt, const CPURegister& rt2, - const MemOperand& dst); - - // Load word pair with sign extension. - void ldpsw(const Register& rt, const Register& rt2, const MemOperand& src); - - // Load integer or FP register pair, non-temporal. - void ldnp(const CPURegister& rt, const CPURegister& rt2, - const MemOperand& src); - - // Store integer or FP register pair, non-temporal. - void stnp(const CPURegister& rt, const CPURegister& rt2, - const MemOperand& dst); - - // Load literal to register. - void ldr(const Register& rt, uint64_t imm); - - // Load literal to FP register. - void ldr(const FPRegister& ft, double imm); - - // Move instructions. The default shift of -1 indicates that the move - // instruction will calculate an appropriate 16-bit immediate and left shift - // that is equal to the 64-bit immediate argument. If an explicit left shift - // is specified (0, 16, 32 or 48), the immediate must be a 16-bit value. - // - // For movk, an explicit shift can be used to indicate which half word should - // be overwritten, eg. movk(x0, 0, 0) will overwrite the least-significant - // half word with zero, whereas movk(x0, 0, 48) will overwrite the - // most-significant. - - // Move and keep. - void movk(const Register& rd, uint64_t imm, int shift = -1) { - MoveWide(rd, imm, shift, MOVK); - } - - // Move with non-zero. - void movn(const Register& rd, uint64_t imm, int shift = -1) { - MoveWide(rd, imm, shift, MOVN); - } - - // Move with zero. - void movz(const Register& rd, uint64_t imm, int shift = -1) { - MoveWide(rd, imm, shift, MOVZ); - } - - // Misc instructions. - // Monitor debug-mode breakpoint. - void brk(int code); - - // Halting debug-mode breakpoint. - void hlt(int code); - - // Move register to register. - void mov(const Register& rd, const Register& rn); - - // Move NOT(operand) to register. - void mvn(const Register& rd, const Operand& operand); - - // System instructions. - // Move to register from system register. - void mrs(const Register& rt, SystemRegister sysreg); - - // Move from register to system register. - void msr(SystemRegister sysreg, const Register& rt); - - // System hint. - void hint(SystemHint code); - - // Data memory barrier - void dmb(BarrierDomain domain, BarrierType type); - - // Data synchronization barrier - void dsb(BarrierDomain domain, BarrierType type); - - // Instruction synchronization barrier - void isb(); - - // Alias for system instructions. - void nop() { hint(NOP); } - - // Different nop operations are used by the code generator to detect certain - // states of the generated code. - enum NopMarkerTypes { - DEBUG_BREAK_NOP, - INTERRUPT_CODE_NOP, - FIRST_NOP_MARKER = DEBUG_BREAK_NOP, - LAST_NOP_MARKER = INTERRUPT_CODE_NOP - }; - - void nop(NopMarkerTypes n) { - ASSERT((FIRST_NOP_MARKER <= n) && (n <= LAST_NOP_MARKER)); - mov(Register::XRegFromCode(n), Register::XRegFromCode(n)); - } - - // FP instructions. - // Move immediate to FP register. - void fmov(FPRegister fd, double imm); - - // Move FP register to register. - void fmov(Register rd, FPRegister fn); - - // Move register to FP register. - void fmov(FPRegister fd, Register rn); - - // Move FP register to FP register. - void fmov(FPRegister fd, FPRegister fn); - - // FP add. - void fadd(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm); - - // FP subtract. - void fsub(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm); - - // FP multiply. - void fmul(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm); - - // FP fused multiply and add. - void fmadd(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm, - const FPRegister& fa); - - // FP fused multiply and subtract. - void fmsub(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm, - const FPRegister& fa); - - // FP fused multiply, add and negate. - void fnmadd(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm, - const FPRegister& fa); - - // FP fused multiply, subtract and negate. - void fnmsub(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm, - const FPRegister& fa); - - // FP divide. - void fdiv(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm); - - // FP maximum. - void fmax(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm); - - // FP minimum. - void fmin(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm); - - // FP maximum. - void fmaxnm(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm); - - // FP minimum. - void fminnm(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm); - - // FP absolute. - void fabs(const FPRegister& fd, const FPRegister& fn); - - // FP negate. - void fneg(const FPRegister& fd, const FPRegister& fn); - - // FP square root. - void fsqrt(const FPRegister& fd, const FPRegister& fn); - - // FP round to integer (nearest with ties to away). - void frinta(const FPRegister& fd, const FPRegister& fn); - - // FP round to integer (nearest with ties to even). - void frintn(const FPRegister& fd, const FPRegister& fn); - - // FP round to integer (towards zero.) - void frintz(const FPRegister& fd, const FPRegister& fn); - - // FP compare registers. - void fcmp(const FPRegister& fn, const FPRegister& fm); - - // FP compare immediate. - void fcmp(const FPRegister& fn, double value); - - // FP conditional compare. - void fccmp(const FPRegister& fn, - const FPRegister& fm, - StatusFlags nzcv, - Condition cond); - - // FP conditional select. - void fcsel(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm, - Condition cond); - - // Common FP Convert function - void FPConvertToInt(const Register& rd, - const FPRegister& fn, - FPIntegerConvertOp op); - - // FP convert between single and double precision. - void fcvt(const FPRegister& fd, const FPRegister& fn); - - // Convert FP to unsigned integer (nearest with ties to away). - void fcvtau(const Register& rd, const FPRegister& fn); - - // Convert FP to signed integer (nearest with ties to away). - void fcvtas(const Register& rd, const FPRegister& fn); - - // Convert FP to unsigned integer (round towards -infinity). - void fcvtmu(const Register& rd, const FPRegister& fn); - - // Convert FP to signed integer (round towards -infinity). - void fcvtms(const Register& rd, const FPRegister& fn); - - // Convert FP to unsigned integer (nearest with ties to even). - void fcvtnu(const Register& rd, const FPRegister& fn); - - // Convert FP to signed integer (nearest with ties to even). - void fcvtns(const Register& rd, const FPRegister& fn); - - // Convert FP to unsigned integer (round towards zero). - void fcvtzu(const Register& rd, const FPRegister& fn); - - // Convert FP to signed integer (rounf towards zero). - void fcvtzs(const Register& rd, const FPRegister& fn); - - // Convert signed integer or fixed point to FP. - void scvtf(const FPRegister& fd, const Register& rn, unsigned fbits = 0); - - // Convert unsigned integer or fixed point to FP. - void ucvtf(const FPRegister& fd, const Register& rn, unsigned fbits = 0); - - // Instruction functions used only for test, debug, and patching. - // Emit raw instructions in the instruction stream. - void dci(Instr raw_inst) { Emit(raw_inst); } - - // Emit 8 bits of data in the instruction stream. - void dc8(uint8_t data) { EmitData(&data, sizeof(data)); } - - // Emit 32 bits of data in the instruction stream. - void dc32(uint32_t data) { EmitData(&data, sizeof(data)); } - - // Emit 64 bits of data in the instruction stream. - void dc64(uint64_t data) { EmitData(&data, sizeof(data)); } - - // Copy a string into the instruction stream, including the terminating NULL - // character. The instruction pointer (pc_) is then aligned correctly for - // subsequent instructions. - void EmitStringData(const char * string) { - size_t len = strlen(string) + 1; - ASSERT(RoundUp(len, kInstructionSize) <= static_cast(kGap)); - EmitData(string, len); - // Pad with NULL characters until pc_ is aligned. - const char pad[] = {'\0', '\0', '\0', '\0'}; - STATIC_ASSERT(sizeof(pad) == kInstructionSize); - byte* next_pc = AlignUp(pc_, kInstructionSize); - EmitData(&pad, next_pc - pc_); - } - - // Pseudo-instructions ------------------------------------------------------ - - // Parameters are described in a64/instructions-a64.h. - void debug(const char* message, uint32_t code, Instr params = BREAK); - - // Required by V8. - void dd(uint32_t data) { dc32(data); } - void db(uint8_t data) { dc8(data); } - - // Code generation helpers -------------------------------------------------- - - unsigned num_pending_reloc_info() const { return num_pending_reloc_info_; } - - Instruction* InstructionAt(int offset) const { - return reinterpret_cast(buffer_ + offset); - } - - // Register encoding. - static Instr Rd(CPURegister rd) { - ASSERT(rd.code() != kSPRegInternalCode); - return rd.code() << Rd_offset; - } - - static Instr Rn(CPURegister rn) { - ASSERT(rn.code() != kSPRegInternalCode); - return rn.code() << Rn_offset; - } - - static Instr Rm(CPURegister rm) { - ASSERT(rm.code() != kSPRegInternalCode); - return rm.code() << Rm_offset; - } - - static Instr Ra(CPURegister ra) { - ASSERT(ra.code() != kSPRegInternalCode); - return ra.code() << Ra_offset; - } - - static Instr Rt(CPURegister rt) { - ASSERT(rt.code() != kSPRegInternalCode); - return rt.code() << Rt_offset; - } - - static Instr Rt2(CPURegister rt2) { - ASSERT(rt2.code() != kSPRegInternalCode); - return rt2.code() << Rt2_offset; - } - - // These encoding functions allow the stack pointer to be encoded, and - // disallow the zero register. - static Instr RdSP(Register rd) { - ASSERT(!rd.IsZero()); - return (rd.code() & kRegCodeMask) << Rd_offset; - } - - static Instr RnSP(Register rn) { - ASSERT(!rn.IsZero()); - return (rn.code() & kRegCodeMask) << Rn_offset; - } - - // Flags encoding. - inline static Instr Flags(FlagsUpdate S); - inline static Instr Cond(Condition cond); - - // PC-relative address encoding. - inline static Instr ImmPCRelAddress(int imm21); - - // Branch encoding. - inline static Instr ImmUncondBranch(int imm26); - inline static Instr ImmCondBranch(int imm19); - inline static Instr ImmCmpBranch(int imm19); - inline static Instr ImmTestBranch(int imm14); - inline static Instr ImmTestBranchBit(unsigned bit_pos); - - // Data Processing encoding. - inline static Instr SF(Register rd); - inline static Instr ImmAddSub(int64_t imm); - inline static Instr ImmS(unsigned imms, unsigned reg_size); - inline static Instr ImmR(unsigned immr, unsigned reg_size); - inline static Instr ImmSetBits(unsigned imms, unsigned reg_size); - inline static Instr ImmRotate(unsigned immr, unsigned reg_size); - inline static Instr ImmLLiteral(int imm19); - inline static Instr BitN(unsigned bitn, unsigned reg_size); - inline static Instr ShiftDP(Shift shift); - inline static Instr ImmDPShift(unsigned amount); - inline static Instr ExtendMode(Extend extend); - inline static Instr ImmExtendShift(unsigned left_shift); - inline static Instr ImmCondCmp(unsigned imm); - inline static Instr Nzcv(StatusFlags nzcv); - - // MemOperand offset encoding. - inline static Instr ImmLSUnsigned(int imm12); - inline static Instr ImmLS(int imm9); - inline static Instr ImmLSPair(int imm7, LSDataSize size); - inline static Instr ImmShiftLS(unsigned shift_amount); - inline static Instr ImmException(int imm16); - inline static Instr ImmSystemRegister(int imm15); - inline static Instr ImmHint(int imm7); - inline static Instr ImmBarrierDomain(int imm2); - inline static Instr ImmBarrierType(int imm2); - inline static LSDataSize CalcLSDataSize(LoadStoreOp op); - - // Move immediates encoding. - inline static Instr ImmMoveWide(uint64_t imm); - inline static Instr ShiftMoveWide(int64_t shift); - - // FP Immediates. - static Instr ImmFP32(float imm); - static Instr ImmFP64(double imm); - inline static Instr FPScale(unsigned scale); - - // FP register type. - inline static Instr FPType(FPRegister fd); - - // Class for scoping postponing the constant pool generation. - class BlockConstPoolScope { - public: - explicit BlockConstPoolScope(Assembler* assem) : assem_(assem) { - assem_->StartBlockConstPool(); - } - ~BlockConstPoolScope() { - assem_->EndBlockConstPool(); - } - - private: - Assembler* assem_; - - DISALLOW_IMPLICIT_CONSTRUCTORS(BlockConstPoolScope); - }; - - // Check if is time to emit a constant pool. - void CheckConstPool(bool force_emit, bool require_jump); - - // Available for constrained code generation scopes. Prefer - // MacroAssembler::Mov() when possible. - inline void LoadRelocated(const CPURegister& rt, const Operand& operand); - - protected: - inline const Register& AppropriateZeroRegFor(const CPURegister& reg) const; - - void LoadStore(const CPURegister& rt, - const MemOperand& addr, - LoadStoreOp op); - static bool IsImmLSUnscaled(ptrdiff_t offset); - static bool IsImmLSScaled(ptrdiff_t offset, LSDataSize size); - - void Logical(const Register& rd, - const Register& rn, - const Operand& operand, - LogicalOp op); - void LogicalImmediate(const Register& rd, - const Register& rn, - unsigned n, - unsigned imm_s, - unsigned imm_r, - LogicalOp op); - static bool IsImmLogical(uint64_t value, - unsigned width, - unsigned* n, - unsigned* imm_s, - unsigned* imm_r); - - void ConditionalCompare(const Register& rn, - const Operand& operand, - StatusFlags nzcv, - Condition cond, - ConditionalCompareOp op); - static bool IsImmConditionalCompare(int64_t immediate); - - void AddSubWithCarry(const Register& rd, - const Register& rn, - const Operand& operand, - FlagsUpdate S, - AddSubWithCarryOp op); - - // Functions for emulating operands not directly supported by the instruction - // set. - void EmitShift(const Register& rd, - const Register& rn, - Shift shift, - unsigned amount); - void EmitExtendShift(const Register& rd, - const Register& rn, - Extend extend, - unsigned left_shift); - - void AddSub(const Register& rd, - const Register& rn, - const Operand& operand, - FlagsUpdate S, - AddSubOp op); - static bool IsImmAddSub(int64_t immediate); - - static bool IsImmFP32(float imm); - static bool IsImmFP64(double imm); - - // Find an appropriate LoadStoreOp or LoadStorePairOp for the specified - // registers. Only simple loads are supported; sign- and zero-extension (such - // as in LDPSW_x or LDRB_w) are not supported. - static inline LoadStoreOp LoadOpFor(const CPURegister& rt); - static inline LoadStorePairOp LoadPairOpFor(const CPURegister& rt, - const CPURegister& rt2); - static inline LoadStoreOp StoreOpFor(const CPURegister& rt); - static inline LoadStorePairOp StorePairOpFor(const CPURegister& rt, - const CPURegister& rt2); - static inline LoadStorePairNonTemporalOp LoadPairNonTemporalOpFor( - const CPURegister& rt, const CPURegister& rt2); - static inline LoadStorePairNonTemporalOp StorePairNonTemporalOpFor( - const CPURegister& rt, const CPURegister& rt2); - - // Remove the specified branch from the unbound label link chain. - // If available, a veneer for this label can be used for other branches in the - // chain if the link chain cannot be fixed up without this branch. - void RemoveBranchFromLabelLinkChain(Instruction* branch, - Label* label, - Instruction* label_veneer = NULL); - - private: - // Instruction helpers. - void MoveWide(const Register& rd, - uint64_t imm, - int shift, - MoveWideImmediateOp mov_op); - void DataProcShiftedRegister(const Register& rd, - const Register& rn, - const Operand& operand, - FlagsUpdate S, - Instr op); - void DataProcExtendedRegister(const Register& rd, - const Register& rn, - const Operand& operand, - FlagsUpdate S, - Instr op); - void LoadStorePair(const CPURegister& rt, - const CPURegister& rt2, - const MemOperand& addr, - LoadStorePairOp op); - void LoadStorePairNonTemporal(const CPURegister& rt, - const CPURegister& rt2, - const MemOperand& addr, - LoadStorePairNonTemporalOp op); - // Register the relocation information for the operand and load its value - // into rt. - void LoadRelocatedValue(const CPURegister& rt, - const Operand& operand, - LoadLiteralOp op); - void ConditionalSelect(const Register& rd, - const Register& rn, - const Register& rm, - Condition cond, - ConditionalSelectOp op); - void DataProcessing1Source(const Register& rd, - const Register& rn, - DataProcessing1SourceOp op); - void DataProcessing3Source(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra, - DataProcessing3SourceOp op); - void FPDataProcessing1Source(const FPRegister& fd, - const FPRegister& fn, - FPDataProcessing1SourceOp op); - void FPDataProcessing2Source(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm, - FPDataProcessing2SourceOp op); - void FPDataProcessing3Source(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm, - const FPRegister& fa, - FPDataProcessing3SourceOp op); - - // Label helpers. - - // Return an offset for a label-referencing instruction, typically a branch. - int LinkAndGetByteOffsetTo(Label* label); - - // This is the same as LinkAndGetByteOffsetTo, but return an offset - // suitable for fields that take instruction offsets. - inline int LinkAndGetInstructionOffsetTo(Label* label); - - static const int kStartOfLabelLinkChain = 0; - - // Verify that a label's link chain is intact. - void CheckLabelLinkChain(Label const * label); - - void RecordLiteral(int64_t imm, unsigned size); - - // Postpone the generation of the constant pool for the specified number of - // instructions. - void BlockConstPoolFor(int instructions); - - // Emit the instruction at pc_. - void Emit(Instr instruction) { - STATIC_ASSERT(sizeof(*pc_) == 1); - STATIC_ASSERT(sizeof(instruction) == kInstructionSize); - ASSERT((pc_ + sizeof(instruction)) <= (buffer_ + buffer_size_)); - - memcpy(pc_, &instruction, sizeof(instruction)); - pc_ += sizeof(instruction); - CheckBuffer(); - } - - // Emit data inline in the instruction stream. - void EmitData(void const * data, unsigned size) { - ASSERT(sizeof(*pc_) == 1); - ASSERT((pc_ + size) <= (buffer_ + buffer_size_)); - - // TODO(all): Somehow register we have some data here. Then we can - // disassemble it correctly. - memcpy(pc_, data, size); - pc_ += size; - CheckBuffer(); - } - - void GrowBuffer(); - void CheckBuffer(); - - // Pc offset of the next buffer check. - int next_buffer_check_; - - // Constant pool generation - // Pools are emitted in the instruction stream, preferably after unconditional - // jumps or after returns from functions (in dead code locations). - // If a long code sequence does not contain unconditional jumps, it is - // necessary to emit the constant pool before the pool gets too far from the - // location it is accessed from. In this case, we emit a jump over the emitted - // constant pool. - // Constants in the pool may be addresses of functions that gets relocated; - // if so, a relocation info entry is associated to the constant pool entry. - - // Repeated checking whether the constant pool should be emitted is rather - // expensive. By default we only check again once a number of instructions - // has been generated. That also means that the sizing of the buffers is not - // an exact science, and that we rely on some slop to not overrun buffers. - static const int kCheckPoolIntervalInst = 128; - static const int kCheckPoolInterval = - kCheckPoolIntervalInst * kInstructionSize; - - // Constants in pools are accessed via pc relative addressing, which can - // reach +/-4KB thereby defining a maximum distance between the instruction - // and the accessed constant. - static const int kMaxDistToPool = 4 * KB; - static const int kMaxNumPendingRelocInfo = kMaxDistToPool / kInstructionSize; - - - // Average distance beetween a constant pool and the first instruction - // accessing the constant pool. Longer distance should result in less I-cache - // pollution. - // In practice the distance will be smaller since constant pool emission is - // forced after function return and sometimes after unconditional branches. - static const int kAvgDistToPool = kMaxDistToPool - kCheckPoolInterval; - - // Emission of the constant pool may be blocked in some code sequences. - int const_pool_blocked_nesting_; // Block emission if this is not zero. - int no_const_pool_before_; // Block emission before this pc offset. - - // Keep track of the first instruction requiring a constant pool entry - // since the previous constant pool was emitted. - int first_const_pool_use_; - - // Relocation info generation - // Each relocation is encoded as a variable size value - static const int kMaxRelocSize = RelocInfoWriter::kMaxSize; - RelocInfoWriter reloc_info_writer; - - // Relocation info records are also used during code generation as temporary - // containers for constants and code target addresses until they are emitted - // to the constant pool. These pending relocation info records are temporarily - // stored in a separate buffer until a constant pool is emitted. - // If every instruction in a long sequence is accessing the pool, we need one - // pending relocation entry per instruction. - - // the buffer of pending relocation info - RelocInfo pending_reloc_info_[kMaxNumPendingRelocInfo]; - // number of pending reloc info entries in the buffer - int num_pending_reloc_info_; - - // Relocation for a type-recording IC has the AST id added to it. This - // member variable is a way to pass the information from the call site to - // the relocation info. - TypeFeedbackId recorded_ast_id_; - - inline TypeFeedbackId RecordedAstId(); - inline void ClearRecordedAstId(); - - protected: - // Record the AST id of the CallIC being compiled, so that it can be placed - // in the relocation information. - void SetRecordedAstId(TypeFeedbackId ast_id) { - ASSERT(recorded_ast_id_.IsNone()); - recorded_ast_id_ = ast_id; - } - - // Code generation - // The relocation writer's position is at least kGap bytes below the end of - // the generated instructions. This is so that multi-instruction sequences do - // not have to check for overflow. The same is true for writes of large - // relocation info entries, and debug strings encoded in the instruction - // stream. - static const int kGap = 128; - - public: - class FarBranchInfo { - public: - FarBranchInfo(int offset, Label* label) - : pc_offset_(offset), label_(label) {} - // Offset of the branch in the code generation buffer. - int pc_offset_; - // The label branched to. - Label* label_; - }; - - protected: - // Information about unresolved (forward) branches. - // The Assembler is only allowed to delete out-of-date information from here - // after a label is bound. The MacroAssembler uses this information to - // generate veneers. - // - // The second member gives information about the unresolved branch. The first - // member of the pair is the maximum offset that the branch can reach in the - // buffer. The map is sorted according to this reachable offset, allowing to - // easily check when veneers need to be emitted. - // Note that the maximum reachable offset (first member of the pairs) should - // always be positive but has the same type as the return value for - // pc_offset() for convenience. - std::multimap unresolved_branches_; - - private: - // If a veneer is emitted for a branch instruction, that instruction must be - // removed from the associated label's link chain so that the assembler does - // not later attempt (likely unsuccessfully) to patch it to branch directly to - // the label. - void DeleteUnresolvedBranchInfoForLabel(Label* label); - - private: - // TODO(jbramley): VIXL uses next_literal_pool_check_ and - // literal_pool_monitor_ to determine when to consider emitting a literal - // pool. V8 doesn't use them, so they should either not be here at all, or - // should replace or be merged with next_buffer_check_ and - // const_pool_blocked_nesting_. - Instruction* next_literal_pool_check_; - unsigned literal_pool_monitor_; - - PositionsRecorder positions_recorder_; - friend class PositionsRecorder; - friend class EnsureSpace; -}; - -class PatchingAssembler : public Assembler { - public: - // Create an Assembler with a buffer starting at 'start'. - // The buffer size is - // size of instructions to patch + kGap - // Where kGap is the distance from which the Assembler tries to grow the - // buffer. - // If more or fewer instructions than expected are generated or if some - // relocation information takes space in the buffer, the PatchingAssembler - // will crash trying to grow the buffer. - PatchingAssembler(Instruction* start, unsigned count) - : Assembler(NULL, - reinterpret_cast(start), - count * kInstructionSize + kGap) { - // Block constant pool emission. - StartBlockConstPool(); - } - - PatchingAssembler(byte* start, unsigned count) - : Assembler(NULL, start, count * kInstructionSize + kGap) { - // Block constant pool emission. - StartBlockConstPool(); - } - - ~PatchingAssembler() { - // Const pool should still be blocked. - ASSERT(is_const_pool_blocked()); - EndBlockConstPool(); - // Verify we have generated the number of instruction we expected. - ASSERT((pc_offset() + kGap) == buffer_size_); - // Verify no relocation information has been emitted. - ASSERT(num_pending_reloc_info() == 0); - // Flush the Instruction cache. - size_t length = buffer_size_ - kGap; - CPU::FlushICache(buffer_, length); - } -}; - - -class EnsureSpace BASE_EMBEDDED { - public: - explicit EnsureSpace(Assembler* assembler) { - assembler->CheckBuffer(); - } -}; - -} } // namespace v8::internal - -#endif // V8_A64_ASSEMBLER_A64_H_ diff --git a/deps/v8/src/a64/builtins-a64.cc b/deps/v8/src/a64/builtins-a64.cc deleted file mode 100644 index 797fbc3a54..0000000000 --- a/deps/v8/src/a64/builtins-a64.cc +++ /dev/null @@ -1,1479 +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 "v8.h" - -#if V8_TARGET_ARCH_A64 - -#include "codegen.h" -#include "debug.h" -#include "deoptimizer.h" -#include "full-codegen.h" -#include "runtime.h" -#include "stub-cache.h" - -namespace v8 { -namespace internal { - - -#define __ ACCESS_MASM(masm) - - -// Load the built-in Array function from the current context. -static void GenerateLoadArrayFunction(MacroAssembler* masm, Register result) { - // Load the native context. - __ Ldr(result, GlobalObjectMemOperand()); - __ Ldr(result, - FieldMemOperand(result, GlobalObject::kNativeContextOffset)); - // Load the InternalArray function from the native context. - __ Ldr(result, - MemOperand(result, - Context::SlotOffset(Context::ARRAY_FUNCTION_INDEX))); -} - - -// Load the built-in InternalArray function from the current context. -static void GenerateLoadInternalArrayFunction(MacroAssembler* masm, - Register result) { - // Load the native context. - __ Ldr(result, GlobalObjectMemOperand()); - __ Ldr(result, - FieldMemOperand(result, GlobalObject::kNativeContextOffset)); - // Load the InternalArray function from the native context. - __ Ldr(result, ContextMemOperand(result, - Context::INTERNAL_ARRAY_FUNCTION_INDEX)); -} - - -void Builtins::Generate_Adaptor(MacroAssembler* masm, - CFunctionId id, - BuiltinExtraArguments extra_args) { - // ----------- S t a t e ------------- - // -- x0 : number of arguments excluding receiver - // -- x1 : called function (only guaranteed when - // extra_args requires it) - // -- cp : context - // -- sp[0] : last argument - // -- ... - // -- sp[4 * (argc - 1)] : first argument (argc == x0) - // -- sp[4 * argc] : receiver - // ----------------------------------- - - // Insert extra arguments. - int num_extra_args = 0; - if (extra_args == NEEDS_CALLED_FUNCTION) { - num_extra_args = 1; - __ Push(x1); - } else { - ASSERT(extra_args == NO_EXTRA_ARGUMENTS); - } - - // JumpToExternalReference expects x0 to contain the number of arguments - // including the receiver and the extra arguments. - __ Add(x0, x0, num_extra_args + 1); - __ JumpToExternalReference(ExternalReference(id, masm->isolate())); -} - - -void Builtins::Generate_InternalArrayCode(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- x0 : number of arguments - // -- lr : return address - // -- sp[...]: constructor arguments - // ----------------------------------- - ASM_LOCATION("Builtins::Generate_InternalArrayCode"); - Label generic_array_code; - - // Get the InternalArray function. - GenerateLoadInternalArrayFunction(masm, x1); - - if (FLAG_debug_code) { - // Initial map for the builtin InternalArray functions should be maps. - __ Ldr(x10, FieldMemOperand(x1, JSFunction::kPrototypeOrInitialMapOffset)); - __ Tst(x10, kSmiTagMask); - __ Assert(ne, kUnexpectedInitialMapForInternalArrayFunction); - __ CompareObjectType(x10, x11, x12, MAP_TYPE); - __ Assert(eq, kUnexpectedInitialMapForInternalArrayFunction); - } - - // Run the native code for the InternalArray function called as a normal - // function. - InternalArrayConstructorStub stub(masm->isolate()); - __ TailCallStub(&stub); -} - - -void Builtins::Generate_ArrayCode(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- x0 : number of arguments - // -- lr : return address - // -- sp[...]: constructor arguments - // ----------------------------------- - ASM_LOCATION("Builtins::Generate_ArrayCode"); - Label generic_array_code, one_or_more_arguments, two_or_more_arguments; - - // Get the Array function. - GenerateLoadArrayFunction(masm, x1); - - if (FLAG_debug_code) { - // Initial map for the builtin Array functions should be maps. - __ Ldr(x10, FieldMemOperand(x1, JSFunction::kPrototypeOrInitialMapOffset)); - __ Tst(x10, kSmiTagMask); - __ Assert(ne, kUnexpectedInitialMapForArrayFunction); - __ CompareObjectType(x10, x11, x12, MAP_TYPE); - __ Assert(eq, kUnexpectedInitialMapForArrayFunction); - } - - // Run the native code for the Array function called as a normal function. - Handle undefined_sentinel( - masm->isolate()->heap()->undefined_value(), - masm->isolate()); - __ Mov(x2, Operand(undefined_sentinel)); - ArrayConstructorStub stub(masm->isolate()); - __ TailCallStub(&stub); -} - - -void Builtins::Generate_StringConstructCode(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- x0 : number of arguments - // -- x1 : constructor function - // -- lr : return address - // -- sp[(argc - n - 1) * 8] : arg[n] (zero based) - // -- sp[argc * 8] : receiver - // ----------------------------------- - ASM_LOCATION("Builtins::Generate_StringConstructCode"); - Counters* counters = masm->isolate()->counters(); - __ IncrementCounter(counters->string_ctor_calls(), 1, x10, x11); - - Register argc = x0; - Register function = x1; - if (FLAG_debug_code) { - __ LoadGlobalFunction(Context::STRING_FUNCTION_INDEX, x10); - __ Cmp(function, x10); - __ Assert(eq, kUnexpectedStringFunction); - } - - // Load the first arguments in x0 and get rid of the rest. - Label no_arguments; - __ Cbz(argc, &no_arguments); - // First args = sp[(argc - 1) * 8]. - __ Sub(argc, argc, 1); - __ Claim(argc, kXRegSizeInBytes); - // jssp now point to args[0], load and drop args[0] + receiver. - // TODO(jbramley): Consider adding ClaimAndPoke. - __ Ldr(argc, MemOperand(jssp, 2 * kPointerSize, PostIndex)); - - Register argument = x2; - Label not_cached, argument_is_string; - __ LookupNumberStringCache(argc, // Input. - argument, // Result. - x10, // Scratch. - x11, // Scratch. - x12, // Scratch. - ¬_cached); - __ IncrementCounter(counters->string_ctor_cached_number(), 1, x10, x11); - __ Bind(&argument_is_string); - - // ----------- S t a t e ------------- - // -- x2 : argument converted to string - // -- x1 : constructor function - // -- lr : return address - // ----------------------------------- - - Label gc_required; - Register new_obj = x0; - __ Allocate(JSValue::kSize, new_obj, x10, x11, &gc_required, TAG_OBJECT); - - // Initialize the String object. - Register map = x3; - __ LoadGlobalFunctionInitialMap(function, map, x10); - if (FLAG_debug_code) { - __ Ldrb(x4, FieldMemOperand(map, Map::kInstanceSizeOffset)); - __ Cmp(x4, JSValue::kSize >> kPointerSizeLog2); - __ Assert(eq, kUnexpectedStringWrapperInstanceSize); - __ Ldrb(x4, FieldMemOperand(map, Map::kUnusedPropertyFieldsOffset)); - __ Cmp(x4, 0); - __ Assert(eq, kUnexpectedUnusedPropertiesOfStringWrapper); - } - __ Str(map, FieldMemOperand(new_obj, HeapObject::kMapOffset)); - - Register empty = x3; - __ LoadRoot(empty, Heap::kEmptyFixedArrayRootIndex); - __ Str(empty, FieldMemOperand(new_obj, JSObject::kPropertiesOffset)); - __ Str(empty, FieldMemOperand(new_obj, JSObject::kElementsOffset)); - - __ Str(argument, FieldMemOperand(new_obj, JSValue::kValueOffset)); - - // Ensure the object is fully initialized. - STATIC_ASSERT(JSValue::kSize == (4 * kPointerSize)); - - __ Ret(); - - // The argument was not found in the number to string cache. Check - // if it's a string already before calling the conversion builtin. - Label convert_argument; - __ Bind(¬_cached); - __ JumpIfSmi(argc, &convert_argument); - - // Is it a String? - __ Ldr(x10, FieldMemOperand(x0, HeapObject::kMapOffset)); - __ Ldrb(x11, FieldMemOperand(x10, Map::kInstanceTypeOffset)); - __ Tbnz(x11, MaskToBit(kIsNotStringMask), &convert_argument); - __ Mov(argument, argc); - __ IncrementCounter(counters->string_ctor_string_value(), 1, x10, x11); - __ B(&argument_is_string); - - // Invoke the conversion builtin and put the result into x2. - __ Bind(&convert_argument); - __ Push(function); // Preserve the function. - __ IncrementCounter(counters->string_ctor_conversions(), 1, x10, x11); - { - FrameScope scope(masm, StackFrame::INTERNAL); - __ Push(argc); - __ InvokeBuiltin(Builtins::TO_STRING, CALL_FUNCTION); - } - __ Pop(function); - __ Mov(argument, x0); - __ B(&argument_is_string); - - // Load the empty string into x2, remove the receiver from the - // stack, and jump back to the case where the argument is a string. - __ Bind(&no_arguments); - __ LoadRoot(argument, Heap::kempty_stringRootIndex); - __ Drop(1); - __ B(&argument_is_string); - - // At this point the argument is already a string. Call runtime to create a - // string wrapper. - __ Bind(&gc_required); - __ IncrementCounter(counters->string_ctor_gc_required(), 1, x10, x11); - { - FrameScope scope(masm, StackFrame::INTERNAL); - __ Push(argument); - __ CallRuntime(Runtime::kNewStringWrapper, 1); - } - __ Ret(); -} - - -static void CallRuntimePassFunction(MacroAssembler* masm, - Runtime::FunctionId function_id) { - FrameScope scope(masm, StackFrame::INTERNAL); - // - Push a copy of the function onto the stack. - // - Push another copy as a parameter to the runtime call. - __ Push(x1, x1); - - __ CallRuntime(function_id, 1); - - // - Restore receiver. - __ Pop(x1); -} - - -static void GenerateTailCallToSharedCode(MacroAssembler* masm) { - __ Ldr(x2, FieldMemOperand(x1, JSFunction::kSharedFunctionInfoOffset)); - __ Ldr(x2, FieldMemOperand(x2, SharedFunctionInfo::kCodeOffset)); - __ Add(x2, x2, Code::kHeaderSize - kHeapObjectTag); - __ Br(x2); -} - - -static void GenerateTailCallToReturnedCode(MacroAssembler* masm) { - __ Add(x0, x0, Code::kHeaderSize - kHeapObjectTag); - __ Br(x0); -} - - -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; - __ CompareRoot(masm->StackPointer(), Heap::kStackLimitRootIndex); - __ B(hs, &ok); - - CallRuntimePassFunction(masm, Runtime::kTryInstallOptimizedCode); - GenerateTailCallToReturnedCode(masm); - - __ Bind(&ok); - GenerateTailCallToSharedCode(masm); -} - - -static void Generate_JSConstructStubHelper(MacroAssembler* masm, - bool is_api_function, - bool count_constructions) { - // ----------- S t a t e ------------- - // -- x0 : number of arguments - // -- x1 : constructor function - // -- lr : return address - // -- sp[...]: constructor arguments - // ----------------------------------- - - ASM_LOCATION("Builtins::Generate_JSConstructStubHelper"); - // Should never count constructions for api objects. - ASSERT(!is_api_function || !count_constructions); - - Isolate* isolate = masm->isolate(); - - // Enter a construct frame. - { - FrameScope scope(masm, StackFrame::CONSTRUCT); - - // Preserve the two incoming parameters on the stack. - Register argc = x0; - Register constructor = x1; - // x1: constructor function - __ SmiTag(argc); - __ Push(argc, constructor); - // sp[0] : Constructor function. - // sp[1]: number of arguments (smi-tagged) - - // Try to allocate the object without transitioning into C code. If any of - // the preconditions is not met, the code bails out to the runtime call. - Label rt_call, allocated; - if (FLAG_inline_new) { - Label undo_allocation; -#if ENABLE_DEBUGGER_SUPPORT - ExternalReference debug_step_in_fp = - ExternalReference::debug_step_in_fp_address(isolate); - __ Mov(x2, Operand(debug_step_in_fp)); - __ Ldr(x2, MemOperand(x2)); - __ Cbnz(x2, &rt_call); -#endif - // Load the initial map and verify that it is in fact a map. - Register init_map = x2; - __ Ldr(init_map, - FieldMemOperand(constructor, - JSFunction::kPrototypeOrInitialMapOffset)); - __ JumpIfSmi(init_map, &rt_call); - __ JumpIfNotObjectType(init_map, x10, x11, MAP_TYPE, &rt_call); - - // Check that the constructor is not constructing a JSFunction (see - // comments in Runtime_NewObject in runtime.cc). In which case the initial - // map's instance type would be JS_FUNCTION_TYPE. - __ CompareInstanceType(init_map, x10, JS_FUNCTION_TYPE); - __ B(eq, &rt_call); - - if (count_constructions) { - Label allocate; - // Decrease generous allocation count. - __ Ldr(x3, FieldMemOperand(constructor, - JSFunction::kSharedFunctionInfoOffset)); - MemOperand constructor_count = - FieldMemOperand(x3, SharedFunctionInfo::kConstructionCountOffset); - __ Ldrb(x4, constructor_count); - __ Subs(x4, x4, 1); - __ Strb(x4, constructor_count); - __ B(ne, &allocate); - - // Push the constructor and map to the stack, and the constructor again - // as argument to the runtime call. - __ Push(constructor, init_map, constructor); - // The call will replace the stub, so the countdown is only done once. - __ CallRuntime(Runtime::kFinalizeInstanceSize, 1); - __ Pop(init_map, constructor); - __ Bind(&allocate); - } - - // Now allocate the JSObject on the heap. - Register obj_size = x3; - Register new_obj = x4; - __ Ldrb(obj_size, FieldMemOperand(init_map, Map::kInstanceSizeOffset)); - __ Allocate(obj_size, new_obj, x10, x11, &rt_call, SIZE_IN_WORDS); - - // Allocated the JSObject, now initialize the fields. Map is set to - // initial map and properties and elements are set to empty fixed array. - // NB. the object pointer is not tagged, so MemOperand is used. - Register empty = x5; - __ LoadRoot(empty, Heap::kEmptyFixedArrayRootIndex); - __ Str(init_map, MemOperand(new_obj, JSObject::kMapOffset)); - __ Str(empty, MemOperand(new_obj, JSObject::kPropertiesOffset)); - __ Str(empty, MemOperand(new_obj, JSObject::kElementsOffset)); - - Register first_prop = x5; - __ Add(first_prop, new_obj, JSObject::kHeaderSize); - - // Fill all of the in-object properties with the appropriate filler. - Register obj_end = x6; - __ Add(obj_end, new_obj, Operand(obj_size, LSL, kPointerSizeLog2)); - Register undef = x7; - __ LoadRoot(undef, Heap::kUndefinedValueRootIndex); - - // Obtain number of pre-allocated property fields and in-object - // properties. - Register prealloc_fields = x10; - Register inobject_props = x11; - Register inst_sizes = x11; - __ Ldr(inst_sizes, FieldMemOperand(init_map, Map::kInstanceSizesOffset)); - __ Ubfx(prealloc_fields, inst_sizes, - Map::kPreAllocatedPropertyFieldsByte * kBitsPerByte, - kBitsPerByte); - __ Ubfx(inobject_props, inst_sizes, - Map::kInObjectPropertiesByte * kBitsPerByte, kBitsPerByte); - - if (count_constructions) { - // Register first_non_prealloc is the offset of the first field after - // pre-allocated fields. - Register first_non_prealloc = x12; - __ Add(first_non_prealloc, first_prop, - Operand(prealloc_fields, LSL, kPointerSizeLog2)); - - if (FLAG_debug_code) { - __ Cmp(first_non_prealloc, obj_end); - __ Assert(le, kUnexpectedNumberOfPreAllocatedPropertyFields); - } - __ InitializeFieldsWithFiller(first_prop, first_non_prealloc, undef); - // To allow for truncation. - __ LoadRoot(x12, Heap::kOnePointerFillerMapRootIndex); - __ InitializeFieldsWithFiller(first_prop, obj_end, x12); - } else { - __ InitializeFieldsWithFiller(first_prop, obj_end, undef); - } - - // Add the object tag to make the JSObject real, so that we can continue - // and jump into the continuation code at any time from now on. Any - // failures need to undo the allocation, so that the heap is in a - // consistent state and verifiable. - __ Add(new_obj, new_obj, kHeapObjectTag); - - // Check if a non-empty properties array is needed. Continue with - // allocated object if not, or fall through to runtime call if it is. - Register element_count = x3; - __ Ldrb(x3, FieldMemOperand(init_map, Map::kUnusedPropertyFieldsOffset)); - // The field instance sizes contains both pre-allocated property fields - // and in-object properties. - __ Add(x3, x3, prealloc_fields); - __ Subs(element_count, x3, inobject_props); - - // Done if no extra properties are to be allocated. - __ B(eq, &allocated); - __ Assert(pl, kPropertyAllocationCountFailed); - - // Scale the number of elements by pointer size and add the header for - // FixedArrays to the start of the next object calculation from above. - Register new_array = x5; - Register array_size = x6; - __ Add(array_size, element_count, FixedArray::kHeaderSize / kPointerSize); - __ Allocate(array_size, new_array, x11, x12, &undo_allocation, - static_cast(RESULT_CONTAINS_TOP | - SIZE_IN_WORDS)); - - Register array_map = x10; - __ LoadRoot(array_map, Heap::kFixedArrayMapRootIndex); - __ Str(array_map, MemOperand(new_array, FixedArray::kMapOffset)); - __ SmiTag(x0, element_count); - __ Str(x0, MemOperand(new_array, FixedArray::kLengthOffset)); - - // Initialize the fields to undefined. - Register elements = x10; - Register elements_end = x11; - __ Add(elements, new_array, FixedArray::kHeaderSize); - __ Add(elements_end, elements, - Operand(element_count, LSL, kPointerSizeLog2)); - __ InitializeFieldsWithFiller(elements, elements_end, undef); - - // Store the initialized FixedArray into the properties field of the - // JSObject. - __ Add(new_array, new_array, kHeapObjectTag); - __ Str(new_array, FieldMemOperand(new_obj, JSObject::kPropertiesOffset)); - - // Continue with JSObject being successfully allocated. - __ B(&allocated); - - // Undo the setting of the new top so that the heap is verifiable. For - // example, the map's unused properties potentially do not match the - // allocated objects unused properties. - __ Bind(&undo_allocation); - __ UndoAllocationInNewSpace(new_obj, x14); - } - - // Allocate the new receiver object using the runtime call. - __ Bind(&rt_call); - __ Push(constructor); // Argument for Runtime_NewObject. - __ CallRuntime(Runtime::kNewObject, 1); - __ Mov(x4, x0); - - // Receiver for constructor call allocated. - // x4: JSObject - __ Bind(&allocated); - __ Push(x4, x4); - - // Reload the number of arguments from the stack. - // Set it up in x0 for the function call below. - // jssp[0]: receiver - // jssp[1]: receiver - // jssp[2]: constructor function - // jssp[3]: number of arguments (smi-tagged) - __ Peek(constructor, 2 * kXRegSizeInBytes); // Load constructor. - __ Peek(argc, 3 * kXRegSizeInBytes); // Load number of arguments. - __ SmiUntag(argc); - - // Set up pointer to last argument. - __ Add(x2, fp, StandardFrameConstants::kCallerSPOffset); - - // Copy arguments and receiver to the expression stack. - // Copy 2 values every loop to use ldp/stp. - // x0: number of arguments - // x1: constructor function - // x2: address of last argument (caller sp) - // jssp[0]: receiver - // jssp[1]: receiver - // jssp[2]: constructor function - // jssp[3]: number of arguments (smi-tagged) - // Compute the start address of the copy in x3. - __ Add(x3, x2, Operand(argc, LSL, kPointerSizeLog2)); - Label loop, entry, done_copying_arguments; - __ B(&entry); - __ Bind(&loop); - __ Ldp(x10, x11, MemOperand(x3, -2 * kPointerSize, PreIndex)); - __ Push(x11, x10); - __ Bind(&entry); - __ Cmp(x3, x2); - __ B(gt, &loop); - // Because we copied values 2 by 2 we may have copied one extra value. - // Drop it if that is the case. - __ B(eq, &done_copying_arguments); - __ Drop(1); - __ Bind(&done_copying_arguments); - - // Call the function. - // x0: number of arguments - // x1: constructor function - if (is_api_function) { - __ Ldr(cp, FieldMemOperand(constructor, JSFunction::kContextOffset)); - Handle code = - masm->isolate()->builtins()->HandleApiCallConstruct(); - __ Call(code, RelocInfo::CODE_TARGET); - } else { - ParameterCount actual(argc); - __ InvokeFunction(constructor, actual, CALL_FUNCTION, NullCallWrapper()); - } - - // Store offset of return address for deoptimizer. - if (!is_api_function && !count_constructions) { - masm->isolate()->heap()->SetConstructStubDeoptPCOffset(masm->pc_offset()); - } - - // Restore the context from the frame. - // x0: result - // jssp[0]: receiver - // jssp[1]: constructor function - // jssp[2]: number of arguments (smi-tagged) - __ Ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); - - // 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. - // x0: result - // jssp[0]: receiver (newly allocated object) - // jssp[1]: constructor function - // jssp[2]: number of arguments (smi-tagged) - __ JumpIfSmi(x0, &use_receiver); - - // If the type of the result (stored in its map) is less than - // FIRST_SPEC_OBJECT_TYPE, it is not an object in the ECMA sense. - __ JumpIfObjectType(x0, x1, x3, FIRST_SPEC_OBJECT_TYPE, &exit, ge); - - // Throw away the result of the constructor invocation and use the - // on-stack receiver as the result. - __ Bind(&use_receiver); - __ Peek(x0, 0); - - // Remove the receiver from the stack, remove caller arguments, and - // return. - __ Bind(&exit); - // x0: result - // jssp[0]: receiver (newly allocated object) - // jssp[1]: constructor function - // jssp[2]: number of arguments (smi-tagged) - __ Peek(x1, 2 * kXRegSizeInBytes); - - // Leave construct frame. - } - - __ DropBySMI(x1); - __ Drop(1); - __ IncrementCounter(isolate->counters()->constructed_objects(), 1, x1, x2); - __ Ret(); -} - - -void Builtins::Generate_JSConstructStubCountdown(MacroAssembler* masm) { - Generate_JSConstructStubHelper(masm, false, true); -} - - -void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) { - Generate_JSConstructStubHelper(masm, false, false); -} - - -void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) { - Generate_JSConstructStubHelper(masm, true, false); -} - - -// Input: -// x0: code entry. -// x1: function. -// x2: receiver. -// x3: argc. -// x4: argv. -// Output: -// x0: result. -static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, - bool is_construct) { - // Called from JSEntryStub::GenerateBody(). - Register function = x1; - Register receiver = x2; - Register argc = x3; - Register argv = x4; - - ProfileEntryHookStub::MaybeCallEntryHook(masm); - - // Clear the context before we push it when entering the internal frame. - __ Mov(cp, 0); - - { - // Enter an internal frame. - FrameScope scope(masm, StackFrame::INTERNAL); - - // Set up the context from the function argument. - __ Ldr(cp, FieldMemOperand(function, JSFunction::kContextOffset)); - - __ InitializeRootRegister(); - - // Push the function and the receiver onto the stack. - __ Push(function, receiver); - - // Copy arguments to the stack in a loop, in reverse order. - // x3: argc. - // x4: argv. - Label loop, entry; - // Compute the copy end address. - __ Add(x10, argv, Operand(argc, LSL, kPointerSizeLog2)); - - __ B(&entry); - __ Bind(&loop); - __ Ldr(x11, MemOperand(argv, kPointerSize, PostIndex)); - __ Ldr(x12, MemOperand(x11)); // Dereference the handle. - __ Push(x12); // Push the argument. - __ Bind(&entry); - __ Cmp(x10, argv); - __ B(ne, &loop); - - // Initialize all JavaScript callee-saved registers, since they will be seen - // by the garbage collector as part of handlers. - // The original values have been saved in JSEntryStub::GenerateBody(). - __ LoadRoot(x19, Heap::kUndefinedValueRootIndex); - __ Mov(x20, x19); - __ Mov(x21, x19); - __ Mov(x22, x19); - __ Mov(x23, x19); - __ Mov(x24, x19); - __ Mov(x25, x19); - // Don't initialize the reserved registers. - // x26 : root register (root). - // x27 : context pointer (cp). - // x28 : JS stack pointer (jssp). - // x29 : frame pointer (fp). - - // TODO(alexandre): Revisit the MAsm function invocation mechanisms. - // Currently there is a mix of statically and dynamically allocated - // registers. - __ Mov(x0, argc); - if (is_construct) { - // No type feedback cell is available. - Handle undefined_sentinel( - masm->isolate()->heap()->undefined_value(), masm->isolate()); - __ Mov(x2, Operand(undefined_sentinel)); - - CallConstructStub stub(NO_CALL_FUNCTION_FLAGS); - __ CallStub(&stub); - } else { - ParameterCount actual(x0); - __ InvokeFunction(function, actual, CALL_FUNCTION, NullCallWrapper()); - } - // Exit the JS internal frame and remove the parameters (except function), - // and return. - } - - // Result is in x0. Return. - __ Ret(); -} - - -void Builtins::Generate_JSEntryTrampoline(MacroAssembler* masm) { - Generate_JSEntryTrampolineHelper(masm, false); -} - - -void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) { - Generate_JSEntryTrampolineHelper(masm, true); -} - - -void Builtins::Generate_CompileUnoptimized(MacroAssembler* masm) { - CallRuntimePassFunction(masm, Runtime::kCompileUnoptimized); - GenerateTailCallToReturnedCode(masm); -} - - -static void CallCompileOptimized(MacroAssembler* masm, bool concurrent) { - FrameScope scope(masm, StackFrame::INTERNAL); - Register function = x1; - - // Preserve function. At the same time, push arguments for - // kCompileOptimized. - __ LoadObject(x10, masm->isolate()->factory()->ToBoolean(concurrent)); - __ Push(function, function, x10); - - __ CallRuntime(Runtime::kCompileOptimized, 2); - - // Restore receiver. - __ Pop(function); -} - - -void Builtins::Generate_CompileOptimized(MacroAssembler* masm) { - CallCompileOptimized(masm, false); - GenerateTailCallToReturnedCode(masm); -} - - -void Builtins::Generate_CompileOptimizedConcurrent(MacroAssembler* masm) { - CallCompileOptimized(masm, true); - GenerateTailCallToReturnedCode(masm); -} - - -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 fast, since we shouldn't have to do stack - // crawls in MakeCodeYoung. This seems a bit fragile. - - // The following caller-saved registers must be saved and restored when - // calling through to the runtime: - // x0 - The address from which to resume execution. - // x1 - isolate - // lr - The return address for the JSFunction itself. It has not yet been - // preserved on the stack because the frame setup code was replaced - // with a call to this stub, to handle code ageing. - { - FrameScope scope(masm, StackFrame::MANUAL); - __ Push(x0, x1, fp, lr); - __ Mov(x1, Operand(ExternalReference::isolate_address(masm->isolate()))); - __ CallCFunction( - ExternalReference::get_make_code_young_function(masm->isolate()), 2); - __ Pop(lr, fp, x1, x0); - } - - // The calling function has been made young again, so return to execute the - // real frame set-up code. - __ Br(x0); -} - -#define DEFINE_CODE_AGE_BUILTIN_GENERATOR(C) \ -void Builtins::Generate_Make##C##CodeYoungAgainEvenMarking( \ - MacroAssembler* masm) { \ - GenerateMakeCodeYoungAgainCommon(masm); \ -} \ -void Builtins::Generate_Make##C##CodeYoungAgainOddMarking( \ - 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. - - // The following caller-saved registers must be saved and restored when - // calling through to the runtime: - // x0 - The address from which to resume execution. - // x1 - isolate - // lr - The return address for the JSFunction itself. It has not yet been - // preserved on the stack because the frame setup code was replaced - // with a call to this stub, to handle code ageing. - { - FrameScope scope(masm, StackFrame::MANUAL); - __ Push(x0, x1, fp, lr); - __ Mov(x1, Operand(ExternalReference::isolate_address(masm->isolate()))); - __ CallCFunction( - ExternalReference::get_mark_code_as_executed_function( - masm->isolate()), 2); - __ Pop(lr, fp, x1, x0); - - // Perform prologue operations usually performed by the young code stub. - __ EmitFrameSetupForCodeAgePatching(masm); - } - - // Jump to point after the code-age stub. - __ Add(x0, x0, kCodeAgeSequenceSize); - __ Br(x0); -} - - -void Builtins::Generate_MarkCodeAsExecutedTwice(MacroAssembler* masm) { - GenerateMakeCodeYoungAgainCommon(masm); -} - - -static void Generate_NotifyStubFailureHelper(MacroAssembler* masm, - SaveFPRegsMode save_doubles) { - { - FrameScope scope(masm, StackFrame::INTERNAL); - - // Preserve registers across notification, this is important for compiled - // stubs that tail call the runtime on deopts passing their parameters in - // registers. - // TODO(jbramley): Is it correct (and appropriate) to use safepoint - // registers here? According to the comment above, we should only need to - // preserve the registers with parameters. - __ PushXRegList(kSafepointSavedRegisters); - // Pass the function and deoptimization type to the runtime system. - __ CallRuntime(Runtime::kNotifyStubFailure, 0, save_doubles); - __ PopXRegList(kSafepointSavedRegisters); - } - - // Ignore state (pushed by Deoptimizer::EntryGenerator::Generate). - __ Drop(1); - - // Jump to the miss handler. Deoptimizer::EntryGenerator::Generate loads this - // into lr before it jumps here. - __ Br(lr); -} - - -void Builtins::Generate_NotifyStubFailure(MacroAssembler* masm) { - Generate_NotifyStubFailureHelper(masm, kDontSaveFPRegs); -} - - -void Builtins::Generate_NotifyStubFailureSaveDoubles(MacroAssembler* masm) { - Generate_NotifyStubFailureHelper(masm, kSaveFPRegs); -} - - -static void Generate_NotifyDeoptimizedHelper(MacroAssembler* masm, - Deoptimizer::BailoutType type) { - { - FrameScope scope(masm, StackFrame::INTERNAL); - // Pass the deoptimization type to the runtime system. - __ Mov(x0, Operand(Smi::FromInt(static_cast(type)))); - __ Push(x0); - __ CallRuntime(Runtime::kNotifyDeoptimized, 1); - } - - // Get the full codegen state from the stack and untag it. - Register state = x6; - __ Peek(state, 0); - __ SmiUntag(state); - - // Switch on the state. - Label with_tos_register, unknown_state; - __ CompareAndBranch( - state, FullCodeGenerator::NO_REGISTERS, ne, &with_tos_register); - __ Drop(1); // Remove state. - __ Ret(); - - __ Bind(&with_tos_register); - // Reload TOS register. - __ Peek(x0, kPointerSize); - __ CompareAndBranch(state, FullCodeGenerator::TOS_REG, ne, &unknown_state); - __ Drop(2); // Remove state and TOS. - __ Ret(); - - __ Bind(&unknown_state); - __ Abort(kInvalidFullCodegenState); -} - - -void Builtins::Generate_NotifyDeoptimized(MacroAssembler* masm) { - Generate_NotifyDeoptimizedHelper(masm, Deoptimizer::EAGER); -} - - -void Builtins::Generate_NotifyLazyDeoptimized(MacroAssembler* masm) { - Generate_NotifyDeoptimizedHelper(masm, Deoptimizer::LAZY); -} - - -void Builtins::Generate_NotifySoftDeoptimized(MacroAssembler* masm) { - Generate_NotifyDeoptimizedHelper(masm, Deoptimizer::SOFT); -} - - -void Builtins::Generate_OnStackReplacement(MacroAssembler* masm) { - // Lookup the function in the JavaScript frame. - __ Ldr(x0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); - { - FrameScope scope(masm, StackFrame::INTERNAL); - // Pass function as argument. - __ Push(x0); - __ CallRuntime(Runtime::kCompileForOnStackReplacement, 1); - } - - // If the code object is null, just return to the unoptimized code. - Label skip; - __ CompareAndBranch(x0, Operand(Smi::FromInt(0)), ne, &skip); - __ Ret(); - - __ Bind(&skip); - - // Load deoptimization data from the code object. - // = [#deoptimization_data_offset] - __ Ldr(x1, MemOperand(x0, Code::kDeoptimizationDataOffset - kHeapObjectTag)); - - // Load the OSR entrypoint offset from the deoptimization data. - // = [#header_size + #osr_pc_offset] - __ Ldrsw(w1, UntagSmiFieldMemOperand(x1, FixedArray::OffsetOfElementAt( - DeoptimizationInputData::kOsrPcOffsetIndex))); - - // Compute the target address = code_obj + header_size + osr_offset - // = + #header_size + - __ Add(x0, x0, x1); - __ Add(lr, x0, Code::kHeaderSize - kHeapObjectTag); - - // And "return" to the OSR entry point of the function. - __ Ret(); -} - - -void Builtins::Generate_OsrAfterStackCheck(MacroAssembler* masm) { - // We check the stack limit as indicator that recompilation might be done. - Label ok; - __ CompareRoot(jssp, Heap::kStackLimitRootIndex); - __ B(hs, &ok); - { - FrameScope scope(masm, StackFrame::INTERNAL); - __ CallRuntime(Runtime::kStackGuard, 0); - } - __ Jump(masm->isolate()->builtins()->OnStackReplacement(), - RelocInfo::CODE_TARGET); - - __ Bind(&ok); - __ Ret(); -} - - -void Builtins::Generate_FunctionCall(MacroAssembler* masm) { - enum { - call_type_JS_func = 0, - call_type_func_proxy = 1, - call_type_non_func = 2 - }; - Register argc = x0; - Register function = x1; - Register call_type = x4; - Register scratch1 = x10; - Register scratch2 = x11; - Register receiver_type = x13; - - ASM_LOCATION("Builtins::Generate_FunctionCall"); - // 1. Make sure we have at least one argument. - { Label done; - __ Cbnz(argc, &done); - __ LoadRoot(scratch1, Heap::kUndefinedValueRootIndex); - __ Push(scratch1); - __ Mov(argc, 1); - __ Bind(&done); - } - - // 2. Get the function to call (passed as receiver) from the stack, check - // if it is a function. - Label slow, non_function; - __ Peek(function, Operand(argc, LSL, kXRegSizeInBytesLog2)); - __ JumpIfSmi(function, &non_function); - __ JumpIfNotObjectType(function, scratch1, receiver_type, - JS_FUNCTION_TYPE, &slow); - - // 3a. Patch the first argument if necessary when calling a function. - Label shift_arguments; - __ Mov(call_type, static_cast(call_type_JS_func)); - { Label convert_to_object, use_global_receiver, patch_receiver; - // Change context eagerly in case we need the global receiver. - __ Ldr(cp, FieldMemOperand(function, JSFunction::kContextOffset)); - - // Do not transform the receiver for strict mode functions. - // Also do not transform the receiver for native (Compilerhints already in - // x3). - __ Ldr(scratch1, - FieldMemOperand(function, JSFunction::kSharedFunctionInfoOffset)); - __ Ldr(scratch2.W(), - FieldMemOperand(scratch1, SharedFunctionInfo::kCompilerHintsOffset)); - __ TestAndBranchIfAnySet( - scratch2.W(), - (1 << SharedFunctionInfo::kStrictModeFunction) | - (1 << SharedFunctionInfo::kNative), - &shift_arguments); - - // Compute the receiver in non-strict mode. - Register receiver = x2; - __ Sub(scratch1, argc, 1); - __ Peek(receiver, Operand(scratch1, LSL, kXRegSizeInBytesLog2)); - __ JumpIfSmi(receiver, &convert_to_object); - - __ JumpIfRoot(receiver, Heap::kUndefinedValueRootIndex, - &use_global_receiver); - __ JumpIfRoot(receiver, Heap::kNullValueRootIndex, &use_global_receiver); - - STATIC_ASSERT(LAST_SPEC_OBJECT_TYPE == LAST_TYPE); - __ JumpIfObjectType(receiver, scratch1, scratch2, - FIRST_SPEC_OBJECT_TYPE, &shift_arguments, ge); - - __ Bind(&convert_to_object); - - { - // Enter an internal frame in order to preserve argument count. - FrameScope scope(masm, StackFrame::INTERNAL); - __ SmiTag(argc); - - __ Push(argc, receiver); - __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); - __ Mov(receiver, x0); - - __ Pop(argc); - __ SmiUntag(argc); - - // Exit the internal frame. - } - - // Restore the function and flag in the registers. - __ Peek(function, Operand(argc, LSL, kXRegSizeInBytesLog2)); - __ Mov(call_type, static_cast(call_type_JS_func)); - __ B(&patch_receiver); - - __ Bind(&use_global_receiver); - __ Ldr(receiver, GlobalObjectMemOperand()); - __ Ldr(receiver, - FieldMemOperand(receiver, GlobalObject::kGlobalReceiverOffset)); - - - __ Bind(&patch_receiver); - __ Sub(scratch1, argc, 1); - __ Poke(receiver, Operand(scratch1, LSL, kXRegSizeInBytesLog2)); - - __ B(&shift_arguments); - } - - // 3b. Check for function proxy. - __ Bind(&slow); - __ Mov(call_type, static_cast(call_type_func_proxy)); - __ Cmp(receiver_type, JS_FUNCTION_PROXY_TYPE); - __ B(eq, &shift_arguments); - __ Bind(&non_function); - __ Mov(call_type, static_cast(call_type_non_func)); - - // 3c. Patch the first argument when calling a non-function. The - // CALL_NON_FUNCTION builtin expects the non-function callee as - // receiver, so overwrite the first argument which will ultimately - // become the receiver. - // call type (0: JS function, 1: function proxy, 2: non-function) - __ Sub(scratch1, argc, 1); - __ Poke(function, Operand(scratch1, LSL, kXRegSizeInBytesLog2)); - - // 4. 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. - // call type (0: JS function, 1: function proxy, 2: non-function) - __ Bind(&shift_arguments); - { Label loop; - // Calculate the copy start address (destination). Copy end address is jssp. - __ Add(scratch2, jssp, Operand(argc, LSL, kPointerSizeLog2)); - __ Sub(scratch1, scratch2, kPointerSize); - - __ Bind(&loop); - __ Ldr(x12, MemOperand(scratch1, -kPointerSize, PostIndex)); - __ Str(x12, MemOperand(scratch2, -kPointerSize, PostIndex)); - __ Cmp(scratch1, jssp); - __ B(ge, &loop); - // Adjust the actual number of arguments and remove the top element - // (which is a copy of the last argument). - __ Sub(argc, argc, 1); - __ Drop(1); - } - - // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin, - // or a function proxy via CALL_FUNCTION_PROXY. - // call type (0: JS function, 1: function proxy, 2: non-function) - { Label js_function, non_proxy; - __ Cbz(call_type, &js_function); - // Expected number of arguments is 0 for CALL_NON_FUNCTION. - __ Mov(x2, 0); - __ Cmp(call_type, static_cast(call_type_func_proxy)); - __ B(ne, &non_proxy); - - __ Push(function); // Re-add proxy object as additional argument. - __ Add(argc, argc, 1); - __ GetBuiltinFunction(function, Builtins::CALL_FUNCTION_PROXY); - __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(), - RelocInfo::CODE_TARGET); - - __ Bind(&non_proxy); - __ GetBuiltinFunction(function, Builtins::CALL_NON_FUNCTION); - __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(), - RelocInfo::CODE_TARGET); - __ Bind(&js_function); - } - - // 5b. Get the code to call from the function and check that the number of - // expected arguments matches what we're providing. If so, jump - // (tail-call) to the code in register edx without checking arguments. - __ Ldr(x3, FieldMemOperand(function, JSFunction::kSharedFunctionInfoOffset)); - __ Ldrsw(x2, - FieldMemOperand(x3, - SharedFunctionInfo::kFormalParameterCountOffset)); - Label dont_adapt_args; - __ Cmp(x2, argc); // Check formal and actual parameter counts. - __ B(eq, &dont_adapt_args); - __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(), - RelocInfo::CODE_TARGET); - __ Bind(&dont_adapt_args); - - __ Ldr(x3, FieldMemOperand(function, JSFunction::kCodeEntryOffset)); - ParameterCount expected(0); - __ InvokeCode(x3, expected, expected, JUMP_FUNCTION, NullCallWrapper()); -} - - -void Builtins::Generate_FunctionApply(MacroAssembler* masm) { - ASM_LOCATION("Builtins::Generate_FunctionApply"); - const int kIndexOffset = - StandardFrameConstants::kExpressionsOffset - (2 * kPointerSize); - const int kLimitOffset = - StandardFrameConstants::kExpressionsOffset - (1 * kPointerSize); - const int kArgsOffset = 2 * kPointerSize; - const int kReceiverOffset = 3 * kPointerSize; - const int kFunctionOffset = 4 * kPointerSize; - - { - FrameScope frame_scope(masm, StackFrame::INTERNAL); - - Register args = x12; - Register receiver = x14; - Register function = x15; - - // Get the length of the arguments via a builtin call. - __ Ldr(function, MemOperand(fp, kFunctionOffset)); - __ Ldr(args, MemOperand(fp, kArgsOffset)); - __ Push(function, args); - __ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION); - Register argc = x0; - - // 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 enough_stack_space; - __ LoadRoot(x10, Heap::kRealStackLimitRootIndex); - __ Ldr(function, MemOperand(fp, kFunctionOffset)); - // Make x10 the space we have left. The stack might already be overflowed - // here which will cause x10 to become negative. - // TODO(jbramley): Check that the stack usage here is safe. - __ Sub(x10, jssp, x10); - // Check if the arguments will overflow the stack. - __ Cmp(x10, Operand(argc, LSR, kSmiShift - kPointerSizeLog2)); - __ B(gt, &enough_stack_space); - // There is not enough stack space, so use a builtin to throw an appropriate - // error. - __ Push(function, argc); - __ InvokeBuiltin(Builtins::APPLY_OVERFLOW, CALL_FUNCTION); - // We should never return from the APPLY_OVERFLOW builtin. - if (__ emit_debug_code()) { - __ Unreachable(); - } - - __ Bind(&enough_stack_space); - // Push current limit and index. - __ Mov(x1, 0); // Initial index. - __ Push(argc, x1); - - Label push_receiver; - __ Ldr(receiver, MemOperand(fp, kReceiverOffset)); - - // Check that the function is a JS function. Otherwise it must be a proxy. - // When it is not the function proxy will be invoked later. - __ JumpIfNotObjectType(function, x10, x11, JS_FUNCTION_TYPE, - &push_receiver); - - // Change context eagerly to get the right global object if necessary. - __ Ldr(cp, FieldMemOperand(function, JSFunction::kContextOffset)); - // Load the shared function info. - __ Ldr(x2, FieldMemOperand(function, - JSFunction::kSharedFunctionInfoOffset)); - - // Compute and push the receiver. - // Do not transform the receiver for strict mode functions. - Label convert_receiver_to_object, use_global_receiver; - __ Ldr(w10, FieldMemOperand(x2, SharedFunctionInfo::kCompilerHintsOffset)); - __ Tbnz(x10, SharedFunctionInfo::kStrictModeFunction, &push_receiver); - // Do not transform the receiver for native functions. - __ Tbnz(x10, SharedFunctionInfo::kNative, &push_receiver); - - // Compute the receiver in non-strict mode. - __ JumpIfSmi(receiver, &convert_receiver_to_object); - __ JumpIfRoot(receiver, Heap::kNullValueRootIndex, &use_global_receiver); - __ JumpIfRoot(receiver, Heap::kUndefinedValueRootIndex, - &use_global_receiver); - - // Check if the receiver is already a JavaScript object. - STATIC_ASSERT(LAST_SPEC_OBJECT_TYPE == LAST_TYPE); - __ JumpIfObjectType(receiver, x10, x11, FIRST_SPEC_OBJECT_TYPE, - &push_receiver, ge); - - // Call a builtin to convert the receiver to a regular object. - __ Bind(&convert_receiver_to_object); - __ Push(receiver); - __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); - __ Mov(receiver, x0); - __ B(&push_receiver); - - __ Bind(&use_global_receiver); - __ Ldr(x10, GlobalObjectMemOperand()); - __ Ldr(receiver, FieldMemOperand(x10, GlobalObject::kGlobalReceiverOffset)); - - // Push the receiver - __ Bind(&push_receiver); - __ Push(receiver); - - // Copy all arguments from the array to the stack. - Label entry, loop; - Register current = x0; - __ Ldr(current, MemOperand(fp, kIndexOffset)); - __ B(&entry); - - __ Bind(&loop); - // Load the current argument from the arguments array and push it. - // TODO(all): Couldn't we optimize this for JS arrays? - - __ Ldr(x1, MemOperand(fp, kArgsOffset)); - __ Push(x1, current); - - // Call the runtime to access the property in the arguments array. - __ CallRuntime(Runtime::kGetProperty, 2); - __ Push(x0); - - // Use inline caching to access the arguments. - __ Ldr(current, MemOperand(fp, kIndexOffset)); - __ Add(current, current, Operand(Smi::FromInt(1))); - __ Str(current, MemOperand(fp, kIndexOffset)); - - // Test if the copy loop has finished copying all the elements from the - // arguments object. - __ Bind(&entry); - __ Ldr(x1, MemOperand(fp, kLimitOffset)); - __ Cmp(current, x1); - __ B(ne, &loop); - - // At the end of the loop, the number of arguments is stored in 'current', - // represented as a smi. - - function = x1; // From now on we want the function to be kept in x1; - __ Ldr(function, MemOperand(fp, kFunctionOffset)); - - // Call the function. - Label call_proxy; - ParameterCount actual(current); - __ SmiUntag(current); - __ JumpIfNotObjectType(function, x10, x11, JS_FUNCTION_TYPE, &call_proxy); - __ InvokeFunction(function, actual, CALL_FUNCTION, NullCallWrapper()); - frame_scope.GenerateLeaveFrame(); - __ Drop(3); - __ Ret(); - - // Call the function proxy. - __ Bind(&call_proxy); - // x0 : argc - // x1 : function - __ Push(function); // Add function proxy as last argument. - __ Add(x0, x0, 1); - __ Mov(x2, 0); - __ GetBuiltinFunction(x1, Builtins::CALL_FUNCTION_PROXY); - __ Call(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(), - RelocInfo::CODE_TARGET); - } - __ Drop(3); - __ Ret(); -} - - -static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) { - __ SmiTag(x10, x0); - __ Mov(x11, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); - __ Push(lr, fp); - __ Push(x11, x1, x10); - __ Add(fp, jssp, - StandardFrameConstants::kFixedFrameSizeFromFp + kPointerSize); -} - - -static void LeaveArgumentsAdaptorFrame(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- x0 : result being passed through - // ----------------------------------- - // Get the number of arguments passed (as a smi), tear down the frame and - // then drop the parameters and the receiver. - __ Ldr(x10, MemOperand(fp, -(StandardFrameConstants::kFixedFrameSizeFromFp + - kPointerSize))); - __ Mov(jssp, fp); - __ Pop(fp, lr); - __ DropBySMI(x10, kXRegSizeInBytes); - __ Drop(1); -} - - -void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { - ASM_LOCATION("Builtins::Generate_ArgumentsAdaptorTrampoline"); - // ----------- S t a t e ------------- - // -- x0 : actual number of arguments - // -- x1 : function (passed through to callee) - // -- x2 : expected number of arguments - // ----------------------------------- - - Label invoke, dont_adapt_arguments; - - Label enough, too_few; - __ Ldr(x3, FieldMemOperand(x1, JSFunction::kCodeEntryOffset)); - __ Cmp(x0, x2); - __ B(lt, &too_few); - __ Cmp(x2, SharedFunctionInfo::kDontAdaptArgumentsSentinel); - __ B(eq, &dont_adapt_arguments); - - { // Enough parameters: actual >= expected - EnterArgumentsAdaptorFrame(masm); - - // Calculate copy start address into x10 and end address into x11. - // x0: actual number of arguments - // x1: function - // x2: expected number of arguments - // x3: code entry to call - __ Add(x10, fp, Operand(x0, LSL, kPointerSizeLog2)); - // Adjust for return address and receiver - __ Add(x10, x10, 2 * kPointerSize); - __ Sub(x11, x10, Operand(x2, LSL, kPointerSizeLog2)); - - // Copy the arguments (including the receiver) to the new stack frame. - // x0: actual number of arguments - // x1: function - // x2: expected number of arguments - // x3: code entry to call - // x10: copy start address - // x11: copy end address - - // TODO(all): Should we push values 2 by 2? - Label copy; - __ Bind(©); - __ Cmp(x10, x11); - __ Ldr(x12, MemOperand(x10, -kPointerSize, PostIndex)); - __ Push(x12); - __ B(gt, ©); - - __ B(&invoke); - } - - { // Too few parameters: Actual < expected - __ Bind(&too_few); - EnterArgumentsAdaptorFrame(masm); - - // Calculate copy start address into x10 and copy end address into x11. - // x0: actual number of arguments - // x1: function - // x2: expected number of arguments - // x3: code entry to call - // Adjust for return address. - __ Add(x11, fp, 1 * kPointerSize); - __ Add(x10, x11, Operand(x0, LSL, kPointerSizeLog2)); - __ Add(x10, x10, 1 * kPointerSize); - - // Copy the arguments (including the receiver) to the new stack frame. - // x0: actual number of arguments - // x1: function - // x2: expected number of arguments - // x3: code entry to call - // x10: copy start address - // x11: copy end address - Label copy; - __ Bind(©); - __ Ldr(x12, MemOperand(x10, -kPointerSize, PostIndex)); - __ Push(x12); - __ Cmp(x10, x11); // Compare before moving to next argument. - __ B(ne, ©); - - // Fill the remaining expected arguments with undefined. - // x0: actual number of arguments - // x1: function - // x2: expected number of arguments - // x3: code entry to call - __ LoadRoot(x10, Heap::kUndefinedValueRootIndex); - __ Sub(x11, fp, Operand(x2, LSL, kPointerSizeLog2)); - // Adjust for the arguments adaptor frame and already pushed receiver. - __ Sub(x11, x11, - StandardFrameConstants::kFixedFrameSizeFromFp + (2 * kPointerSize)); - - // TODO(all): Optimize this to use ldp? - Label fill; - __ Bind(&fill); - __ Push(x10); - __ Cmp(jssp, x11); - __ B(ne, &fill); - } - - // Arguments have been adapted. Now call the entry point. - __ Bind(&invoke); - __ Call(x3); - - // Store offset of return address for deoptimizer. - masm->isolate()->heap()->SetArgumentsAdaptorDeoptPCOffset(masm->pc_offset()); - - // Exit frame and return. - LeaveArgumentsAdaptorFrame(masm); - __ Ret(); - - // Call the entry point without adapting the arguments. - __ Bind(&dont_adapt_arguments); - __ Jump(x3); -} - - -#undef __ - -} } // namespace v8::internal - -#endif // V8_TARGET_ARCH_ARM diff --git a/deps/v8/src/a64/code-stubs-a64.cc b/deps/v8/src/a64/code-stubs-a64.cc deleted file mode 100644 index b640677cae..0000000000 --- a/deps/v8/src/a64/code-stubs-a64.cc +++ /dev/null @@ -1,5809 +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 "v8.h" - -#if V8_TARGET_ARCH_A64 - -#include "bootstrapper.h" -#include "code-stubs.h" -#include "regexp-macro-assembler.h" -#include "stub-cache.h" - -namespace v8 { -namespace internal { - - -void FastNewClosureStub::InitializeInterfaceDescriptor( - Isolate* isolate, - CodeStubInterfaceDescriptor* descriptor) { - // x2: function info - static Register registers[] = { x2 }; - descriptor->register_param_count_ = sizeof(registers) / sizeof(registers[0]); - descriptor->register_params_ = registers; - descriptor->deoptimization_handler_ = - Runtime::FunctionForId(Runtime::kNewClosureFromStubFailure)->entry; -} - - -void FastNewContextStub::InitializeInterfaceDescriptor( - Isolate* isolate, - CodeStubInterfaceDescriptor* descriptor) { - // x1: function - static Register registers[] = { x1 }; - descriptor->register_param_count_ = sizeof(registers) / sizeof(registers[0]); - descriptor->register_params_ = registers; - descriptor->deoptimization_handler_ = NULL; -} - - -void ToNumberStub::InitializeInterfaceDescriptor( - Isolate* isolate, - CodeStubInterfaceDescriptor* descriptor) { - // x0: value - static Register registers[] = { x0 }; - descriptor->register_param_count_ = sizeof(registers) / sizeof(registers[0]); - descriptor->register_params_ = registers; - descriptor->deoptimization_handler_ = NULL; -} - - -void NumberToStringStub::InitializeInterfaceDescriptor( - Isolate* isolate, - CodeStubInterfaceDescriptor* descriptor) { - // x0: value - static Register registers[] = { x0 }; - descriptor->register_param_count_ = sizeof(registers) / sizeof(registers[0]); - descriptor->register_params_ = registers; - descriptor->deoptimization_handler_ = - Runtime::FunctionForId(Runtime::kNumberToString)->entry; -} - - -void FastCloneShallowArrayStub::InitializeInterfaceDescriptor( - Isolate* isolate, - CodeStubInterfaceDescriptor* descriptor) { - // x3: array literals array - // x2: array literal index - // x1: constant elements - static Register registers[] = { x3, x2, x1 }; - descriptor->register_param_count_ = sizeof(registers) / sizeof(registers[0]); - descriptor->register_params_ = registers; - descriptor->deoptimization_handler_ = - Runtime::FunctionForId(Runtime::kCreateArrayLiteralStubBailout)->entry; -} - - -void FastCloneShallowObjectStub::InitializeInterfaceDescriptor( - Isolate* isolate, - CodeStubInterfaceDescriptor* descriptor) { - // x3: object literals array - // x2: object literal index - // x1: constant properties - // x0: object literal flags - static Register registers[] = { x3, x2, x1, x0 }; - descriptor->register_param_count_ = sizeof(registers) / sizeof(registers[0]); - descriptor->register_params_ = registers; - descriptor->deoptimization_handler_ = - Runtime::FunctionForId(Runtime::kCreateObjectLiteral)->entry; -} - - -void CreateAllocationSiteStub::InitializeInterfaceDescriptor( - Isolate* isolate, - CodeStubInterfaceDescriptor* descriptor) { - // x2: feedback vector - // x3: call feedback slot - static Register registers[] = { x2, x3 }; - descriptor->register_param_count_ = sizeof(registers) / sizeof(registers[0]); - descriptor->register_params_ = registers; - descriptor->deoptimization_handler_ = NULL; -} - - -void KeyedLoadFastElementStub::InitializeInterfaceDescriptor( - Isolate* isolate, - CodeStubInterfaceDescriptor* descriptor) { - // x1: receiver - // x0: key - static Register registers[] = { x1, x0 }; - descriptor->register_param_count_ = sizeof(registers) / sizeof(registers[0]); - descriptor->register_params_ = registers; - descriptor->deoptimization_handler_ = - FUNCTION_ADDR(KeyedLoadIC_MissFromStubFailure); -} - - -void KeyedLoadDictionaryElementStub::InitializeInterfaceDescriptor( - Isolate* isolate, - CodeStubInterfaceDescriptor* descriptor) { - // x1: receiver - // x0: key - static Register registers[] = { x1, x0 }; - descriptor->register_param_count_ = sizeof(registers) / sizeof(registers[0]); - descriptor->register_params_ = registers; - descriptor->deoptimization_handler_ = - FUNCTION_ADDR(KeyedLoadIC_MissFromStubFailure); -} - - -void RegExpConstructResultStub::InitializeInterfaceDescriptor( - Isolate* isolate, - CodeStubInterfaceDescriptor* descriptor) { - // x2: length - // x1: index (of last match) - // x0: string - static Register registers[] = { x2, x1, x0 }; - descriptor->register_param_count_ = sizeof(registers) / sizeof(registers[0]); - descriptor->register_params_ = registers; - descriptor->deoptimization_handler_ = - Runtime::FunctionForId(Runtime::kRegExpConstructResult)->entry; -} - - -void LoadFieldStub::InitializeInterfaceDescriptor( - Isolate* isolate, - CodeStubInterfaceDescriptor* descriptor) { - // x0: receiver - static Register registers[] = { x0 }; - descriptor->register_param_count_ = sizeof(registers) / sizeof(registers[0]); - descriptor->register_params_ = registers; - descriptor->deoptimization_handler_ = NULL; -} - - -void KeyedLoadFieldStub::InitializeInterfaceDescriptor( - Isolate* isolate, - CodeStubInterfaceDescriptor* descriptor) { - // x1: receiver - static Register registers[] = { x1 }; - descriptor->register_param_count_ = sizeof(registers) / sizeof(registers[0]); - descriptor->register_params_ = registers; - descriptor->deoptimization_handler_ = NULL; -} - - -void KeyedStoreFastElementStub::InitializeInterfaceDescriptor( - Isolate* isolate, - CodeStubInterfaceDescriptor* descriptor) { - // x2: receiver - // x1: key - // x0: value - static Register registers[] = { x2, x1, x0 }; - descriptor->register_param_count_ = sizeof(registers) / sizeof(registers[0]); - descriptor->register_params_ = registers; - descriptor->deoptimization_handler_ = - FUNCTION_ADDR(KeyedStoreIC_MissFromStubFailure); -} - - -void TransitionElementsKindStub::InitializeInterfaceDescriptor( - Isolate* isolate, - CodeStubInterfaceDescriptor* descriptor) { - // x0: value (js_array) - // x1: to_map - static Register registers[] = { x0, x1 }; - descriptor->register_param_count_ = sizeof(registers) / sizeof(registers[0]); - descriptor->register_params_ = registers; - Address entry = - Runtime::FunctionForId(Runtime::kTransitionElementsKind)->entry; - descriptor->deoptimization_handler_ = FUNCTION_ADDR(entry); -} - - -void CompareNilICStub::InitializeInterfaceDescriptor( - Isolate* isolate, - CodeStubInterfaceDescriptor* descriptor) { - // x0: value to compare - static Register registers[] = { x0 }; - descriptor->register_param_count_ = sizeof(registers) / sizeof(registers[0]); - descriptor->register_params_ = registers; - descriptor->deoptimization_handler_ = - FUNCTION_ADDR(CompareNilIC_Miss); - descriptor->SetMissHandler( - ExternalReference(IC_Utility(IC::kCompareNilIC_Miss), isolate)); -} - - -static void InitializeArrayConstructorDescriptor( - Isolate* isolate, - CodeStubInterfaceDescriptor* descriptor, - int constant_stack_parameter_count) { - // x1: function - // x2: allocation site with elements kind - // x0: number of arguments to the constructor function - static Register registers_variable_args[] = { x1, x2, x0 }; - static Register registers_no_args[] = { x1, x2 }; - - if (constant_stack_parameter_count == 0) { - descriptor->register_param_count_ = - sizeof(registers_no_args) / sizeof(registers_no_args[0]); - descriptor->register_params_ = registers_no_args; - } else { - // stack param count needs (constructor pointer, and single argument) - descriptor->handler_arguments_mode_ = PASS_ARGUMENTS; - descriptor->stack_parameter_count_ = x0; - descriptor->register_param_count_ = - sizeof(registers_variable_args) / sizeof(registers_variable_args[0]); - descriptor->register_params_ = registers_variable_args; - } - - descriptor->hint_stack_parameter_count_ = constant_stack_parameter_count; - descriptor->function_mode_ = JS_FUNCTION_STUB_MODE; - descriptor->deoptimization_handler_ = - Runtime::FunctionForId(Runtime::kArrayConstructor)->entry; -} - - -void ArrayNoArgumentConstructorStub::InitializeInterfaceDescriptor( - Isolate* isolate, - CodeStubInterfaceDescriptor* descriptor) { - InitializeArrayConstructorDescriptor(isolate, descriptor, 0); -} - - -void ArraySingleArgumentConstructorStub::InitializeInterfaceDescriptor( - Isolate* isolate, - CodeStubInterfaceDescriptor* descriptor) { - InitializeArrayConstructorDescriptor(isolate, descriptor, 1); -} - - -void ArrayNArgumentsConstructorStub::InitializeInterfaceDescriptor( - Isolate* isolate, - CodeStubInterfaceDescriptor* descriptor) { - InitializeArrayConstructorDescriptor(isolate, descriptor, -1); -} - - -static void InitializeInternalArrayConstructorDescriptor( - Isolate* isolate, - CodeStubInterfaceDescriptor* descriptor, - int constant_stack_parameter_count) { - // x1: constructor function - // x0: number of arguments to the constructor function - static Register registers_variable_args[] = { x1, x0 }; - static Register registers_no_args[] = { x1 }; - - if (constant_stack_parameter_count == 0) { - descriptor->register_param_count_ = - sizeof(registers_no_args) / sizeof(registers_no_args[0]); - descriptor->register_params_ = registers_no_args; - } else { - // stack param count needs (constructor pointer, and single argument) - descriptor->handler_arguments_mode_ = PASS_ARGUMENTS; - descriptor->stack_parameter_count_ = x0; - descriptor->register_param_count_ = - sizeof(registers_variable_args) / sizeof(registers_variable_args[0]); - descriptor->register_params_ = registers_variable_args; - } - - descriptor->hint_stack_parameter_count_ = constant_stack_parameter_count; - descriptor->function_mode_ = JS_FUNCTION_STUB_MODE; - descriptor->deoptimization_handler_ = - Runtime::FunctionForId(Runtime::kInternalArrayConstructor)->entry; -} - - -void InternalArrayNoArgumentConstructorStub::InitializeInterfaceDescriptor( - Isolate* isolate, - CodeStubInterfaceDescriptor* descriptor) { - InitializeInternalArrayConstructorDescriptor(isolate, descriptor, 0); -} - - -void InternalArraySingleArgumentConstructorStub::InitializeInterfaceDescriptor( - Isolate* isolate, - CodeStubInterfaceDescriptor* descriptor) { - InitializeInternalArrayConstructorDescriptor(isolate, descriptor, 1); -} - - -void InternalArrayNArgumentsConstructorStub::InitializeInterfaceDescriptor( - Isolate* isolate, - CodeStubInterfaceDescriptor* descriptor) { - InitializeInternalArrayConstructorDescriptor(isolate, descriptor, -1); -} - - -void ToBooleanStub::InitializeInterfaceDescriptor( - Isolate* isolate, - CodeStubInterfaceDescriptor* descriptor) { - // x0: value - static Register registers[] = { x0 }; - descriptor->register_param_count_ = sizeof(registers) / sizeof(registers[0]); - descriptor->register_params_ = registers; - descriptor->deoptimization_handler_ = FUNCTION_ADDR(ToBooleanIC_Miss); - descriptor->SetMissHandler( - ExternalReference(IC_Utility(IC::kToBooleanIC_Miss), isolate)); -} - - -void StoreGlobalStub::InitializeInterfaceDescriptor( - Isolate* isolate, - CodeStubInterfaceDescriptor* descriptor) { - // x1: receiver - // x2: key (unused) - // x0: value - static Register registers[] = { x1, x2, x0 }; - descriptor->register_param_count_ = sizeof(registers) / sizeof(registers[0]); - descriptor->register_params_ = registers; - descriptor->deoptimization_handler_ = - FUNCTION_ADDR(StoreIC_MissFromStubFailure); -} - - -void ElementsTransitionAndStoreStub::InitializeInterfaceDescriptor( - Isolate* isolate, - CodeStubInterfaceDescriptor* descriptor) { - // x0: value - // x3: target map - // x1: key - // x2: receiver - static Register registers[] = { x0, x3, x1, x2 }; - descriptor->register_param_count_ = sizeof(registers) / sizeof(registers[0]); - descriptor->register_params_ = registers; - descriptor->deoptimization_handler_ = - FUNCTION_ADDR(ElementsTransitionAndStoreIC_Miss); -} - - -void BinaryOpICStub::InitializeInterfaceDescriptor( - Isolate* isolate, - CodeStubInterfaceDescriptor* descriptor) { - // x1: left operand - // x0: right operand - static Register registers[] = { x1, x0 }; - descriptor->register_param_count_ = sizeof(registers) / sizeof(registers[0]); - descriptor->register_params_ = registers; - descriptor->deoptimization_handler_ = FUNCTION_ADDR(BinaryOpIC_Miss); - descriptor->SetMissHandler( - ExternalReference(IC_Utility(IC::kBinaryOpIC_Miss), isolate)); -} - - -void BinaryOpWithAllocationSiteStub::InitializeInterfaceDescriptor( - Isolate* isolate, - CodeStubInterfaceDescriptor* descriptor) { - // x2: allocation site - // x1: left operand - // x0: right operand - static Register registers[] = { x2, x1, x0 }; - descriptor->register_param_count_ = sizeof(registers) / sizeof(registers[0]); - descriptor->register_params_ = registers; - descriptor->deoptimization_handler_ = - FUNCTION_ADDR(BinaryOpIC_MissWithAllocationSite); -} - - -void StringAddStub::InitializeInterfaceDescriptor( - Isolate* isolate, - CodeStubInterfaceDescriptor* descriptor) { - // x1: left operand - // x0: right operand - static Register registers[] = { x1, x0 }; - descriptor->register_param_count_ = sizeof(registers) / sizeof(registers[0]); - descriptor->register_params_ = registers; - descriptor->deoptimization_handler_ = - Runtime::FunctionForId(Runtime::kStringAdd)->entry; -} - - -void CallDescriptors::InitializeForIsolate(Isolate* isolate) { - static PlatformCallInterfaceDescriptor default_descriptor = - PlatformCallInterfaceDescriptor(CAN_INLINE_TARGET_ADDRESS); - - static PlatformCallInterfaceDescriptor noInlineDescriptor = - PlatformCallInterfaceDescriptor(NEVER_INLINE_TARGET_ADDRESS); - - { - CallInterfaceDescriptor* descriptor = - isolate->call_descriptor(Isolate::ArgumentAdaptorCall); - static Register registers[] = { x1, // JSFunction - cp, // context - x0, // actual number of arguments - x2, // expected number of arguments - }; - static Representation representations[] = { - Representation::Tagged(), // JSFunction - Representation::Tagged(), // context - Representation::Integer32(), // actual number of arguments - Representation::Integer32(), // expected number of arguments - }; - descriptor->register_param_count_ = 4; - descriptor->register_params_ = registers; - descriptor->param_representations_ = representations; - descriptor->platform_specific_descriptor_ = &default_descriptor; - } - { - CallInterfaceDescriptor* descriptor = - isolate->call_descriptor(Isolate::KeyedCall); - static Register registers[] = { cp, // context - x2, // key - }; - static Representation representations[] = { - Representation::Tagged(), // context - Representation::Tagged(), // key - }; - descriptor->register_param_count_ = 2; - descriptor->register_params_ = registers; - descriptor->param_representations_ = representations; - descriptor->platform_specific_descriptor_ = &noInlineDescriptor; - } - { - CallInterfaceDescriptor* descriptor = - isolate->call_descriptor(Isolate::NamedCall); - static Register registers[] = { cp, // context - x2, // name - }; - static Representation representations[] = { - Representation::Tagged(), // context - Representation::Tagged(), // name - }; - descriptor->register_param_count_ = 2; - descriptor->register_params_ = registers; - descriptor->param_representations_ = representations; - descriptor->platform_specific_descriptor_ = &noInlineDescriptor; - } - { - CallInterfaceDescriptor* descriptor = - isolate->call_descriptor(Isolate::CallHandler); - static Register registers[] = { cp, // context - x0, // receiver - }; - static Representation representations[] = { - Representation::Tagged(), // context - Representation::Tagged(), // receiver - }; - descriptor->register_param_count_ = 2; - descriptor->register_params_ = registers; - descriptor->param_representations_ = representations; - descriptor->platform_specific_descriptor_ = &default_descriptor; - } - { - CallInterfaceDescriptor* descriptor = - isolate->call_descriptor(Isolate::ApiFunctionCall); - static Register registers[] = { x0, // callee - x4, // call_data - x2, // holder - x1, // api_function_address - cp, // context - }; - static Representation representations[] = { - Representation::Tagged(), // callee - Representation::Tagged(), // call_data - Representation::Tagged(), // holder - Representation::External(), // api_function_address - Representation::Tagged(), // context - }; - descriptor->register_param_count_ = 5; - descriptor->register_params_ = registers; - descriptor->param_representations_ = representations; - descriptor->platform_specific_descriptor_ = &default_descriptor; - } -} - - -#define __ ACCESS_MASM(masm) - - -void HydrogenCodeStub::GenerateLightweightMiss(MacroAssembler* masm) { - // Update the static counter each time a new code stub is generated. - Isolate* isolate = masm->isolate(); - isolate->counters()->code_stubs()->Increment(); - - CodeStubInterfaceDescriptor* descriptor = GetInterfaceDescriptor(isolate); - int param_count = descriptor->register_param_count_; - { - // Call the runtime system in a fresh internal frame. - FrameScope scope(masm, StackFrame::INTERNAL); - ASSERT((descriptor->register_param_count_ == 0) || - x0.Is(descriptor->register_params_[param_count - 1])); - // Push arguments - // TODO(jbramley): Try to push these in blocks. - for (int i = 0; i < param_count; ++i) { - __ Push(descriptor->register_params_[i]); - } - ExternalReference miss = descriptor->miss_handler(); - __ CallExternalReference(miss, descriptor->register_param_count_); - } - - __ Ret(); -} - - -void DoubleToIStub::Generate(MacroAssembler* masm) { - Label done; - Register input = source(); - Register result = destination(); - ASSERT(is_truncating()); - - ASSERT(result.Is64Bits()); - ASSERT(jssp.Is(masm->StackPointer())); - - int double_offset = offset(); - - DoubleRegister double_scratch = d0; // only used if !skip_fastpath() - Register scratch1 = GetAllocatableRegisterThatIsNotOneOf(input, result); - Register scratch2 = - GetAllocatableRegisterThatIsNotOneOf(input, result, scratch1); - - __ Push(scratch1, scratch2); - // Account for saved regs if input is jssp. - if (input.is(jssp)) double_offset += 2 * kPointerSize; - - if (!skip_fastpath()) { - __ Push(double_scratch); - if (input.is(jssp)) double_offset += 1 * kDoubleSize; - __ Ldr(double_scratch, MemOperand(input, double_offset)); - // Try to convert with a FPU convert instruction. This handles all - // non-saturating cases. - __ TryInlineTruncateDoubleToI(result, double_scratch, &done); - __ Fmov(result, double_scratch); - } else { - __ Ldr(result, MemOperand(input, double_offset)); - } - - // If we reach here we need to manually convert the input to an int32. - - // Extract the exponent. - Register exponent = scratch1; - __ Ubfx(exponent, result, HeapNumber::kMantissaBits, - HeapNumber::kExponentBits); - - // It the exponent is >= 84 (kMantissaBits + 32), the result is always 0 since - // the mantissa gets shifted completely out of the int32_t result. - __ Cmp(exponent, HeapNumber::kExponentBias + HeapNumber::kMantissaBits + 32); - __ CzeroX(result, ge); - __ B(ge, &done); - - // The Fcvtzs sequence handles all cases except where the conversion causes - // signed overflow in the int64_t target. Since we've already handled - // exponents >= 84, we can guarantee that 63 <= exponent < 84. - - if (masm->emit_debug_code()) { - __ Cmp(exponent, HeapNumber::kExponentBias + 63); - // Exponents less than this should have been handled by the Fcvt case. - __ Check(ge, kUnexpectedValue); - } - - // Isolate the mantissa bits, and set the implicit '1'. - Register mantissa = scratch2; - __ Ubfx(mantissa, result, 0, HeapNumber::kMantissaBits); - __ Orr(mantissa, mantissa, 1UL << HeapNumber::kMantissaBits); - - // Negate the mantissa if necessary. - __ Tst(result, kXSignMask); - __ Cneg(mantissa, mantissa, ne); - - // Shift the mantissa bits in the correct place. We know that we have to shift - // it left here, because exponent >= 63 >= kMantissaBits. - __ Sub(exponent, exponent, - HeapNumber::kExponentBias + HeapNumber::kMantissaBits); - __ Lsl(result, mantissa, exponent); - - __ Bind(&done); - if (!skip_fastpath()) { - __ Pop(double_scratch); - } - __ Pop(scratch2, scratch1); - __ Ret(); -} - - -// See call site for description. -static void EmitIdenticalObjectComparison(MacroAssembler* masm, - Register left, - Register right, - Register scratch, - FPRegister double_scratch, - Label* slow, - Condition cond) { - ASSERT(!AreAliased(left, right, scratch)); - Label not_identical, return_equal, heap_number; - Register result = x0; - - __ Cmp(right, left); - __ B(ne, ¬_identical); - - // Test for NaN. Sadly, we can't just compare to factory::nan_value(), - // so we do the second best thing - test it ourselves. - // They are both equal and they are not both Smis so both of them are not - // Smis. If it's not a heap number, then return equal. - if ((cond == lt) || (cond == gt)) { - __ JumpIfObjectType(right, scratch, scratch, FIRST_SPEC_OBJECT_TYPE, slow, - ge); - } else { - Register right_type = scratch; - __ JumpIfObjectType(right, right_type, right_type, HEAP_NUMBER_TYPE, - &heap_number); - // Comparing JS objects with <=, >= is complicated. - if (cond != eq) { - __ Cmp(right_type, FIRST_SPEC_OBJECT_TYPE); - __ B(ge, slow); - // Normally here we fall through to return_equal, but undefined is - // special: (undefined == undefined) == true, but - // (undefined <= undefined) == false! See ECMAScript 11.8.5. - if ((cond == le) || (cond == ge)) { - __ Cmp(right_type, ODDBALL_TYPE); - __ B(ne, &return_equal); - __ JumpIfNotRoot(right, Heap::kUndefinedValueRootIndex, &return_equal); - if (cond == le) { - // undefined <= undefined should fail. - __ Mov(result, GREATER); - } else { - // undefined >= undefined should fail. - __ Mov(result, LESS); - } - __ Ret(); - } - } - } - - __ Bind(&return_equal); - if (cond == lt) { - __ Mov(result, GREATER); // Things aren't less than themselves. - } else if (cond == gt) { - __ Mov(result, LESS); // Things aren't greater than themselves. - } else { - __ Mov(result, EQUAL); // Things are <=, >=, ==, === themselves. - } - __ Ret(); - - // Cases lt and gt have been handled earlier, and case ne is never seen, as - // it is handled in the parser (see Parser::ParseBinaryExpression). We are - // only concerned with cases ge, le and eq here. - if ((cond != lt) && (cond != gt)) { - ASSERT((cond == ge) || (cond == le) || (cond == eq)); - __ Bind(&heap_number); - // Left and right are identical pointers to a heap number object. Return - // non-equal if the heap number is a NaN, and equal otherwise. Comparing - // the number to itself will set the overflow flag iff the number is NaN. - __ Ldr(double_scratch, FieldMemOperand(right, HeapNumber::kValueOffset)); - __ Fcmp(double_scratch, double_scratch); - __ B(vc, &return_equal); // Not NaN, so treat as normal heap number. - - if (cond == le) { - __ Mov(result, GREATER); - } else { - __ Mov(result, LESS); - } - __ Ret(); - } - - // No fall through here. - if (FLAG_debug_code) { - __ Unreachable(); - } - - __ Bind(¬_identical); -} - - -// See call site for description. -static void EmitStrictTwoHeapObjectCompare(MacroAssembler* masm, - Register left, - Register right, - Register left_type, - Register right_type, - Register scratch) { - ASSERT(!AreAliased(left, right, left_type, right_type, scratch)); - - if (masm->emit_debug_code()) { - // We assume that the arguments are not identical. - __ Cmp(left, right); - __ Assert(ne, kExpectedNonIdenticalObjects); - } - - // If either operand is a JS object or an oddball value, then they are not - // equal since their pointers are different. - // There is no test for undetectability in strict equality. - STATIC_ASSERT(LAST_TYPE == LAST_SPEC_OBJECT_TYPE); - Label right_non_object; - - __ Cmp(right_type, FIRST_SPEC_OBJECT_TYPE); - __ B(lt, &right_non_object); - - // Return non-zero - x0 already contains a non-zero pointer. - ASSERT(left.is(x0) || right.is(x0)); - Label return_not_equal; - __ Bind(&return_not_equal); - __ Ret(); - - __ Bind(&right_non_object); - - // Check for oddballs: true, false, null, undefined. - __ Cmp(right_type, ODDBALL_TYPE); - - // If right is not ODDBALL, test left. Otherwise, set eq condition. - __ Ccmp(left_type, ODDBALL_TYPE, ZFlag, ne); - - // If right or left is not ODDBALL, test left >= FIRST_SPEC_OBJECT_TYPE. - // Otherwise, right or left is ODDBALL, so set a ge condition. - __ Ccmp(left_type, FIRST_SPEC_OBJECT_TYPE, NVFlag, ne); - - __ B(ge, &return_not_equal); - - // Internalized strings are unique, so they can only be equal if they are the - // same object. We have already tested that case, so if left and right are - // both internalized strings, they cannot be equal. - STATIC_ASSERT((kInternalizedTag == 0) && (kStringTag == 0)); - __ Orr(scratch, left_type, right_type); - __ TestAndBranchIfAllClear( - scratch, kIsNotStringMask | kIsNotInternalizedMask, &return_not_equal); -} - - -// See call site for description. -static void EmitSmiNonsmiComparison(MacroAssembler* masm, - Register left, - Register right, - FPRegister left_d, - FPRegister right_d, - Register scratch, - Label* slow, - bool strict) { - ASSERT(!AreAliased(left, right, scratch)); - ASSERT(!AreAliased(left_d, right_d)); - ASSERT((left.is(x0) && right.is(x1)) || - (right.is(x0) && left.is(x1))); - Register result = x0; - - Label right_is_smi, done; - __ JumpIfSmi(right, &right_is_smi); - - // Left is the smi. Check whether right is a heap number. - if (strict) { - // If right is not a number and left is a smi, then strict equality cannot - // succeed. Return non-equal. - Label is_heap_number; - __ JumpIfObjectType(right, scratch, scratch, HEAP_NUMBER_TYPE, - &is_heap_number); - // Register right is a non-zero pointer, which is a valid NOT_EQUAL result. - if (!right.is(result)) { - __ Mov(result, NOT_EQUAL); - } - __ Ret(); - __ Bind(&is_heap_number); - } else { - // Smi compared non-strictly with a non-smi, non-heap-number. Call the - // runtime. - __ JumpIfNotObjectType(right, scratch, scratch, HEAP_NUMBER_TYPE, slow); - } - - // Left is the smi. Right is a heap number. Load right value into right_d, and - // convert left smi into double in left_d. - __ Ldr(right_d, FieldMemOperand(right, HeapNumber::kValueOffset)); - __ SmiUntagToDouble(left_d, left); - __ B(&done); - - __ Bind(&right_is_smi); - // Right is a smi. Check whether the non-smi left is a heap number. - if (strict) { - // If left is not a number and right is a smi then strict equality cannot - // succeed. Return non-equal. - Label is_heap_number; - __ JumpIfObjectType(left, scratch, scratch, HEAP_NUMBER_TYPE, - &is_heap_number); - // Register left is a non-zero pointer, which is a valid NOT_EQUAL result. - if (!left.is(result)) { - __ Mov(result, NOT_EQUAL); - } - __ Ret(); - __ Bind(&is_heap_number); - } else { - // Smi compared non-strictly with a non-smi, non-heap-number. Call the - // runtime. - __ JumpIfNotObjectType(left, scratch, scratch, HEAP_NUMBER_TYPE, slow); - } - - // Right is the smi. Left is a heap number. Load left value into left_d, and - // convert right smi into double in right_d. - __ Ldr(left_d, FieldMemOperand(left, HeapNumber::kValueOffset)); - __ SmiUntagToDouble(right_d, right); - - // Fall through to both_loaded_as_doubles. - __ Bind(&done); -} - - -// Fast negative check for internalized-to-internalized equality. -// See call site for description. -static void EmitCheckForInternalizedStringsOrObjects(MacroAssembler* masm, - Register left, - Register right, - Register left_map, - Register right_map, - Register left_type, - Register right_type, - Label* possible_strings, - Label* not_both_strings) { - ASSERT(!AreAliased(left, right, left_map, right_map, left_type, right_type)); - Register result = x0; - - Label object_test; - STATIC_ASSERT((kInternalizedTag == 0) && (kStringTag == 0)); - // TODO(all): reexamine this branch sequence for optimisation wrt branch - // prediction. - __ Tbnz(right_type, MaskToBit(kIsNotStringMask), &object_test); - __ Tbnz(right_type, MaskToBit(kIsNotInternalizedMask), possible_strings); - __ Tbnz(left_type, MaskToBit(kIsNotStringMask), not_both_strings); - __ Tbnz(left_type, MaskToBit(kIsNotInternalizedMask), possible_strings); - - // Both are internalized. We already checked that they weren't the same - // pointer, so they are not equal. - __ Mov(result, NOT_EQUAL); - __ Ret(); - - __ Bind(&object_test); - - __ Cmp(right_type, FIRST_SPEC_OBJECT_TYPE); - - // If right >= FIRST_SPEC_OBJECT_TYPE, test left. - // Otherwise, right < FIRST_SPEC_OBJECT_TYPE, so set lt condition. - __ Ccmp(left_type, FIRST_SPEC_OBJECT_TYPE, NFlag, ge); - - __ B(lt, not_both_strings); - - // If both objects are undetectable, they are equal. Otherwise, they are not - // equal, since they are different objects and an object is not equal to - // undefined. - - // Returning here, so we can corrupt right_type and left_type. - Register right_bitfield = right_type; - Register left_bitfield = left_type; - __ Ldrb(right_bitfield, FieldMemOperand(right_map, Map::kBitFieldOffset)); - __ Ldrb(left_bitfield, FieldMemOperand(left_map, Map::kBitFieldOffset)); - __ And(result, right_bitfield, left_bitfield); - __ And(result, result, 1 << Map::kIsUndetectable); - __ Eor(result, result, 1 << Map::kIsUndetectable); - __ Ret(); -} - - -static void ICCompareStub_CheckInputType(MacroAssembler* masm, - Register input, - Register scratch, - CompareIC::State expected, - Label* fail) { - Label ok; - if (expected == CompareIC::SMI) { - __ JumpIfNotSmi(input, fail); - } else if (expected == CompareIC::NUMBER) { - __ JumpIfSmi(input, &ok); - __ CheckMap(input, scratch, Heap::kHeapNumberMapRootIndex, fail, - DONT_DO_SMI_CHECK); - } - // 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); -} - - -void ICCompareStub::GenerateGeneric(MacroAssembler* masm) { - Register lhs = x1; - Register rhs = x0; - Register result = x0; - Condition cond = GetCondition(); - - Label miss; - ICCompareStub_CheckInputType(masm, lhs, x2, left_, &miss); - ICCompareStub_CheckInputType(masm, rhs, x3, right_, &miss); - - Label slow; // Call builtin. - Label not_smis, both_loaded_as_doubles; - Label not_two_smis, smi_done; - __ JumpIfEitherNotSmi(lhs, rhs, ¬_two_smis); - __ SmiUntag(lhs); - __ Sub(result, lhs, Operand::UntagSmi(rhs)); - __ Ret(); - - __ Bind(¬_two_smis); - - // 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. - - // Handle the case where the objects are identical. Either returns the answer - // or goes to slow. Only falls through if the objects were not identical. - EmitIdenticalObjectComparison(masm, lhs, rhs, x10, d0, &slow, cond); - - // If either is a smi (we know that at least one is not a smi), then they can - // only be strictly equal if the other is a HeapNumber. - __ JumpIfBothNotSmi(lhs, rhs, ¬_smis); - - // Exactly one operand is a smi. EmitSmiNonsmiComparison generates code that - // can: - // 1) Return the answer. - // 2) Branch to the slow case. - // 3) Fall through to both_loaded_as_doubles. - // In case 3, we have found out that we were dealing with a number-number - // comparison. The double values of the numbers have been loaded, right into - // rhs_d, left into lhs_d. - FPRegister rhs_d = d0; - FPRegister lhs_d = d1; - EmitSmiNonsmiComparison(masm, lhs, rhs, lhs_d, rhs_d, x10, &slow, strict()); - - __ Bind(&both_loaded_as_doubles); - // The arguments have been converted to doubles and stored in rhs_d and - // lhs_d. - Label nan; - __ Fcmp(lhs_d, rhs_d); - __ B(vs, &nan); // Overflow flag set if either is NaN. - STATIC_ASSERT((LESS == -1) && (EQUAL == 0) && (GREATER == 1)); - __ Cset(result, gt); // gt => 1, otherwise (lt, eq) => 0 (EQUAL). - __ Csinv(result, result, xzr, ge); // lt => -1, gt => 1, eq => 0. - __ Ret(); - - __ Bind(&nan); - // Left and/or right is a NaN. Load the result register with whatever makes - // the comparison fail, since comparisons with NaN always fail (except ne, - // which is filtered out at a higher level.) - ASSERT(cond != ne); - if ((cond == lt) || (cond == le)) { - __ Mov(result, GREATER); - } else { - __ Mov(result, LESS); - } - __ Ret(); - - __ Bind(¬_smis); - // At this point we know we are dealing with two different objects, and - // neither of them is a smi. The objects are in rhs_ and lhs_. - - // Load the maps and types of the objects. - Register rhs_map = x10; - Register rhs_type = x11; - Register lhs_map = x12; - Register lhs_type = x13; - __ Ldr(rhs_map, FieldMemOperand(rhs, HeapObject::kMapOffset)); - __ Ldr(lhs_map, FieldMemOperand(lhs, HeapObject::kMapOffset)); - __ Ldrb(rhs_type, FieldMemOperand(rhs_map, Map::kInstanceTypeOffset)); - __ Ldrb(lhs_type, FieldMemOperand(lhs_map, Map::kInstanceTypeOffset)); - - if (strict()) { - // This emits a non-equal return sequence for some object types, or falls - // through if it was not lucky. - EmitStrictTwoHeapObjectCompare(masm, lhs, rhs, lhs_type, rhs_type, x14); - } - - Label check_for_internalized_strings; - Label flat_string_check; - // Check for heap number comparison. Branch to earlier double comparison code - // if they are heap numbers, otherwise, branch to internalized string check. - __ Cmp(rhs_type, HEAP_NUMBER_TYPE); - __ B(ne, &check_for_internalized_strings); - __ Cmp(lhs_map, rhs_map); - - // If maps aren't equal, lhs_ and rhs_ are not heap numbers. Branch to flat - // string check. - __ B(ne, &flat_string_check); - - // Both lhs_ and rhs_ are heap numbers. Load them and branch to the double - // comparison code. - __ Ldr(lhs_d, FieldMemOperand(lhs, HeapNumber::kValueOffset)); - __ Ldr(rhs_d, FieldMemOperand(rhs, HeapNumber::kValueOffset)); - __ B(&both_loaded_as_doubles); - - __ Bind(&check_for_internalized_strings); - // In the strict case, the EmitStrictTwoHeapObjectCompare already took care - // of internalized strings. - if ((cond == eq) && !strict()) { - // Returns an answer for two internalized strings or two detectable objects. - // Otherwise branches to the string case or not both strings case. - EmitCheckForInternalizedStringsOrObjects(masm, lhs, rhs, lhs_map, rhs_map, - lhs_type, rhs_type, - &flat_string_check, &slow); - } - - // Check for both being sequential ASCII strings, and inline if that is the - // case. - __ Bind(&flat_string_check); - __ JumpIfBothInstanceTypesAreNotSequentialAscii(lhs_type, rhs_type, x14, - x15, &slow); - - Isolate* isolate = masm->isolate(); - __ IncrementCounter(isolate->counters()->string_compare_native(), 1, x10, - x11); - if (cond == eq) { - StringCompareStub::GenerateFlatAsciiStringEquals(masm, lhs, rhs, - x10, x11, x12); - } else { - StringCompareStub::GenerateCompareFlatAsciiStrings(masm, lhs, rhs, - x10, x11, x12, x13); - } - - // Never fall through to here. - if (FLAG_debug_code) { - __ Unreachable(); - } - - __ Bind(&slow); - - __ Push(lhs, rhs); - // Figure out which native to call and setup the arguments. - Builtins::JavaScript native; - if (cond == eq) { - native = strict() ? Builtins::STRICT_EQUALS : Builtins::EQUALS; - } else { - native = Builtins::COMPARE; - int ncr; // NaN compare result - if ((cond == lt) || (cond == le)) { - ncr = GREATER; - } else { - ASSERT((cond == gt) || (cond == ge)); // remaining cases - ncr = LESS; - } - __ Mov(x10, Operand(Smi::FromInt(ncr))); - __ Push(x10); - } - - // Call the native; it returns -1 (less), 0 (equal), or 1 (greater) - // tagged as a small integer. - __ InvokeBuiltin(native, JUMP_FUNCTION); - - __ Bind(&miss); - GenerateMiss(masm); -} - - -void StoreBufferOverflowStub::Generate(MacroAssembler* masm) { - // Preserve caller-saved registers x0-x7 and x10-x15. We don't care if x8, x9, - // ip0 and ip1 are corrupted by the call into C. - CPURegList saved_regs = kCallerSaved; - saved_regs.Remove(ip0); - saved_regs.Remove(ip1); - saved_regs.Remove(x8); - saved_regs.Remove(x9); - - // 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. - __ PushCPURegList(saved_regs); - if (save_doubles_ == kSaveFPRegs) { - __ PushCPURegList(kCallerSavedFP); - } - - AllowExternalCallThatCantCauseGC scope(masm); - __ Mov(x0, Operand(ExternalReference::isolate_address(masm->isolate()))); - __ CallCFunction( - ExternalReference::store_buffer_overflow_function(masm->isolate()), - 1, 0); - - if (save_doubles_ == kSaveFPRegs) { - __ PopCPURegList(kCallerSavedFP); - } - __ PopCPURegList(saved_regs); - __ Ret(); -} - - -void StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime( - Isolate* isolate) { - StoreBufferOverflowStub stub1(kDontSaveFPRegs); - stub1.GetCode(isolate); - StoreBufferOverflowStub stub2(kSaveFPRegs); - stub2.GetCode(isolate); -} - - -void MathPowStub::Generate(MacroAssembler* masm) { - // Stack on entry: - // jssp[0]: Exponent (as a tagged value). - // jssp[1]: Base (as a tagged value). - // - // The (tagged) result will be returned in x0, as a heap number. - - Register result_tagged = x0; - Register base_tagged = x10; - Register exponent_tagged = x11; - Register exponent_integer = x12; - Register scratch1 = x14; - Register scratch0 = x15; - Register saved_lr = x19; - FPRegister result_double = d0; - FPRegister base_double = d0; - FPRegister exponent_double = d1; - FPRegister base_double_copy = d2; - FPRegister scratch1_double = d6; - FPRegister scratch0_double = d7; - - // A fast-path for integer exponents. - Label exponent_is_smi, exponent_is_integer; - // Bail out to runtime. - Label call_runtime; - // Allocate a heap number for the result, and return it. - Label done; - - // Unpack the inputs. - if (exponent_type_ == ON_STACK) { - Label base_is_smi; - Label unpack_exponent; - - __ Pop(exponent_tagged, base_tagged); - - __ JumpIfSmi(base_tagged, &base_is_smi); - __ JumpIfNotHeapNumber(base_tagged, &call_runtime); - // base_tagged is a heap number, so load its double value. - __ Ldr(base_double, FieldMemOperand(base_tagged, HeapNumber::kValueOffset)); - __ B(&unpack_exponent); - __ Bind(&base_is_smi); - // base_tagged is a SMI, so untag it and convert it to a double. - __ SmiUntagToDouble(base_double, base_tagged); - - __ Bind(&unpack_exponent); - // x10 base_tagged The tagged base (input). - // x11 exponent_tagged The tagged exponent (input). - // d1 base_double The base as a double. - __ JumpIfSmi(exponent_tagged, &exponent_is_smi); - __ JumpIfNotHeapNumber(exponent_tagged, &call_runtime); - // exponent_tagged is a heap number, so load its double value. - __ Ldr(exponent_double, - FieldMemOperand(exponent_tagged, HeapNumber::kValueOffset)); - } else if (exponent_type_ == TAGGED) { - __ JumpIfSmi(exponent_tagged, &exponent_is_smi); - __ Ldr(exponent_double, - FieldMemOperand(exponent_tagged, HeapNumber::kValueOffset)); - } - - // Handle double (heap number) exponents. - if (exponent_type_ != INTEGER) { - // Detect integer exponents stored as doubles and handle those in the - // integer fast-path. - __ TryConvertDoubleToInt64(exponent_integer, exponent_double, - scratch0_double, &exponent_is_integer); - - if (exponent_type_ == ON_STACK) { - FPRegister half_double = d3; - FPRegister minus_half_double = d4; - FPRegister zero_double = d5; - // Detect square root case. Crankshaft detects constant +/-0.5 at compile - // time and uses DoMathPowHalf instead. We then skip this check for - // non-constant cases of +/-0.5 as these hardly occur. - - __ Fmov(minus_half_double, -0.5); - __ Fmov(half_double, 0.5); - __ Fcmp(minus_half_double, exponent_double); - __ Fccmp(half_double, exponent_double, NZFlag, ne); - // Condition flags at this point: - // 0.5; nZCv // Identified by eq && pl - // -0.5: NZcv // Identified by eq && mi - // other: ?z?? // Identified by ne - __ B(ne, &call_runtime); - - // The exponent is 0.5 or -0.5. - - // Given that exponent is known to be either 0.5 or -0.5, the following - // special cases could apply (according to ECMA-262 15.8.2.13): - // - // base.isNaN(): The result is NaN. - // (base == +INFINITY) || (base == -INFINITY) - // exponent == 0.5: The result is +INFINITY. - // exponent == -0.5: The result is +0. - // (base == +0) || (base == -0) - // exponent == 0.5: The result is +0. - // exponent == -0.5: The result is +INFINITY. - // (base < 0) && base.isFinite(): The result is NaN. - // - // Fsqrt (and Fdiv for the -0.5 case) can handle all of those except - // where base is -INFINITY or -0. - - // Add +0 to base. This has no effect other than turning -0 into +0. - __ Fmov(zero_double, 0.0); - __ Fadd(base_double, base_double, zero_double); - // The operation -0+0 results in +0 in all cases except where the - // FPCR rounding mode is 'round towards minus infinity' (RM). The - // A64 simulator does not currently simulate FPCR (where the rounding - // mode is set), so test the operation with some debug code. - if (masm->emit_debug_code()) { - Register temp = masm->Tmp1(); - // d5 zero_double The value +0.0 as a double. - __ Fneg(scratch0_double, zero_double); - // Verify that we correctly generated +0.0 and -0.0. - // bits(+0.0) = 0x0000000000000000 - // bits(-0.0) = 0x8000000000000000 - __ Fmov(temp, zero_double); - __ CheckRegisterIsClear(temp, kCouldNotGenerateZero); - __ Fmov(temp, scratch0_double); - __ Eor(temp, temp, kDSignMask); - __ CheckRegisterIsClear(temp, kCouldNotGenerateNegativeZero); - // Check that -0.0 + 0.0 == +0.0. - __ Fadd(scratch0_double, scratch0_double, zero_double); - __ Fmov(temp, scratch0_double); - __ CheckRegisterIsClear(temp, kExpectedPositiveZero); - } - - // If base is -INFINITY, make it +INFINITY. - // * Calculate base - base: All infinities will become NaNs since both - // -INFINITY+INFINITY and +INFINITY-INFINITY are NaN in A64. - // * If the result is NaN, calculate abs(base). - __ Fsub(scratch0_double, base_double, base_double); - __ Fcmp(scratch0_double, 0.0); - __ Fabs(scratch1_double, base_double); - __ Fcsel(base_double, scratch1_double, base_double, vs); - - // Calculate the square root of base. - __ Fsqrt(result_double, base_double); - __ Fcmp(exponent_double, 0.0); - __ B(ge, &done); // Finish now for exponents of 0.5. - // Find the inverse for exponents of -0.5. - __ Fmov(scratch0_double, 1.0); - __ Fdiv(result_double, scratch0_double, result_double); - __ B(&done); - } - - { - AllowExternalCallThatCantCauseGC scope(masm); - __ Mov(saved_lr, lr); - __ CallCFunction( - ExternalReference::power_double_double_function(masm->isolate()), - 0, 2); - __ Mov(lr, saved_lr); - __ B(&done); - } - - // Handle SMI exponents. - __ Bind(&exponent_is_smi); - // x10 base_tagged The tagged base (input). - // x11 exponent_tagged The tagged exponent (input). - // d1 base_double The base as a double. - __ SmiUntag(exponent_integer, exponent_tagged); - } - - __ Bind(&exponent_is_integer); - // x10 base_tagged The tagged base (input). - // x11 exponent_tagged The tagged exponent (input). - // x12 exponent_integer The exponent as an integer. - // d1 base_double The base as a double. - - // Find abs(exponent). For negative exponents, we can find the inverse later. - Register exponent_abs = x13; - __ Cmp(exponent_integer, 0); - __ Cneg(exponent_abs, exponent_integer, mi); - // x13 exponent_abs The value of abs(exponent_integer). - - // Repeatedly multiply to calculate the power. - // result = 1.0; - // For each bit n (exponent_integer{n}) { - // if (exponent_integer{n}) { - // result *= base; - // } - // base *= base; - // if (remaining bits in exponent_integer are all zero) { - // break; - // } - // } - Label power_loop, power_loop_entry, power_loop_exit; - __ Fmov(scratch1_double, base_double); - __ Fmov(base_double_copy, base_double); - __ Fmov(result_double, 1.0); - __ B(&power_loop_entry); - - __ Bind(&power_loop); - __ Fmul(scratch1_double, scratch1_double, scratch1_double); - __ Lsr(exponent_abs, exponent_abs, 1); - __ Cbz(exponent_abs, &power_loop_exit); - - __ Bind(&power_loop_entry); - __ Tbz(exponent_abs, 0, &power_loop); - __ Fmul(result_double, result_double, scratch1_double); - __ B(&power_loop); - - __ Bind(&power_loop_exit); - - // If the exponent was positive, result_double holds the result. - __ Tbz(exponent_integer, kXSignBit, &done); - - // The exponent was negative, so find the inverse. - __ Fmov(scratch0_double, 1.0); - __ Fdiv(result_double, scratch0_double, result_double); - // ECMA-262 only requires Math.pow to return an 'implementation-dependent - // approximation' of base^exponent. However, mjsunit/math-pow uses Math.pow - // to calculate the subnormal value 2^-1074. This method of calculating - // negative powers doesn't work because 2^1074 overflows to infinity. To - // catch this corner-case, we bail out if the result was 0. (This can only - // occur if the divisor is infinity or the base is zero.) - __ Fcmp(result_double, 0.0); - __ B(&done, ne); - - if (exponent_type_ == ON_STACK) { - // Bail out to runtime code. - __ Bind(&call_runtime); - // Put the arguments back on the stack. - __ Push(base_tagged, exponent_tagged); - __ TailCallRuntime(Runtime::kMath_pow_cfunction, 2, 1); - - // Return. - __ Bind(&done); - __ AllocateHeapNumber(result_tagged, &call_runtime, scratch0, scratch1); - __ Str(result_double, - FieldMemOperand(result_tagged, HeapNumber::kValueOffset)); - ASSERT(result_tagged.is(x0)); - __ IncrementCounter( - masm->isolate()->counters()->math_pow(), 1, scratch0, scratch1); - __ Ret(); - } else { - AllowExternalCallThatCantCauseGC scope(masm); - __ Mov(saved_lr, lr); - __ Fmov(base_double, base_double_copy); - __ Scvtf(exponent_double, exponent_integer); - __ CallCFunction( - ExternalReference::power_double_double_function(masm->isolate()), - 0, 2); - __ Mov(lr, saved_lr); - __ Bind(&done); - __ IncrementCounter( - masm->isolate()->counters()->math_pow(), 1, scratch0, scratch1); - __ Ret(); - } -} - - -void CodeStub::GenerateStubsAheadOfTime(Isolate* isolate) { - // It is important that the following stubs are generated in this order - // because pregenerated stubs can only call other pregenerated stubs. - // RecordWriteStub uses StoreBufferOverflowStub, which in turn uses - // CEntryStub. - CEntryStub::GenerateAheadOfTime(isolate); - StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime(isolate); - StubFailureTrampolineStub::GenerateAheadOfTime(isolate); - ArrayConstructorStubBase::GenerateStubsAheadOfTime(isolate); - CreateAllocationSiteStub::GenerateAheadOfTime(isolate); - BinaryOpICStub::GenerateAheadOfTime(isolate); - BinaryOpICWithAllocationSiteStub::GenerateAheadOfTime(isolate); -} - - -void CodeStub::GenerateFPStubs(Isolate* isolate) { - // Floating-point code doesn't get special handling in A64, so there's - // nothing to do here. - USE(isolate); -} - - -static void JumpIfOOM(MacroAssembler* masm, - Register value, - Register scratch, - Label* oom_label) { - STATIC_ASSERT(Failure::OUT_OF_MEMORY_EXCEPTION == 3); - STATIC_ASSERT(kFailureTag == 3); - __ And(scratch, value, 0xf); - __ Cmp(scratch, 0xf); - __ B(eq, oom_label); -} - - -bool CEntryStub::NeedsImmovableCode() { - // CEntryStub stores the return address on the stack before calling into - // C++ code. In some cases, the VM accesses this address, but it is not used - // when the C++ code returns to the stub because LR holds the return address - // in AAPCS64. If the stub is moved (perhaps during a GC), we could end up - // returning to dead code. - // TODO(jbramley): Whilst this is the only analysis that makes sense, I can't - // find any comment to confirm this, and I don't hit any crashes whatever - // this function returns. The anaylsis should be properly confirmed. - return true; -} - - -void CEntryStub::GenerateAheadOfTime(Isolate* isolate) { - CEntryStub stub(1, kDontSaveFPRegs); - stub.GetCode(isolate); - CEntryStub stub_fp(1, kSaveFPRegs); - stub_fp.GetCode(isolate); -} - - -void CEntryStub::GenerateCore(MacroAssembler* masm, - Label* throw_normal, - Label* throw_termination, - Label* throw_out_of_memory, - bool do_gc, - bool always_allocate) { - // x0 : Result parameter for PerformGC, if do_gc is true. - // x21 : argv - // x22 : argc - // x23 : target - // - // The stack (on entry) holds the arguments and the receiver, with the - // receiver at the highest address: - // - // argv[8]: receiver - // argv -> argv[0]: arg[argc-2] - // ... ... - // argv[...]: arg[1] - // argv[...]: arg[0] - // - // Immediately below (after) this is the exit frame, as constructed by - // EnterExitFrame: - // fp[8]: CallerPC (lr) - // fp -> fp[0]: CallerFP (old fp) - // fp[-8]: Space reserved for SPOffset. - // fp[-16]: CodeObject() - // csp[...]: Saved doubles, if saved_doubles is true. - // csp[32]: Alignment padding, if necessary. - // csp[24]: Preserved x23 (used for target). - // csp[16]: Preserved x22 (used for argc). - // csp[8]: Preserved x21 (used for argv). - // csp -> csp[0]: Space reserved for the return address. - // - // After a successful call, the exit frame, preserved registers (x21-x23) and - // the arguments (including the receiver) are dropped or popped as - // appropriate. The stub then returns. - // - // After an unsuccessful call, the exit frame and suchlike are left - // untouched, and the stub either throws an exception by jumping to one of - // the provided throw_ labels, or it falls through. The failure details are - // passed through in x0. - ASSERT(csp.Is(__ StackPointer())); - - Isolate* isolate = masm->isolate(); - - const Register& argv = x21; - const Register& argc = x22; - const Register& target = x23; - - if (do_gc) { - // Call Runtime::PerformGC, passing x0 (the result parameter for - // PerformGC) and x1 (the isolate). - __ Mov(x1, Operand(ExternalReference::isolate_address(masm->isolate()))); - __ CallCFunction( - ExternalReference::perform_gc_function(isolate), 2, 0); - } - - ExternalReference scope_depth = - ExternalReference::heap_always_allocate_scope_depth(isolate); - if (always_allocate) { - __ Mov(x10, Operand(scope_depth)); - __ Ldr(x11, MemOperand(x10)); - __ Add(x11, x11, 1); - __ Str(x11, MemOperand(x10)); - } - - // Prepare AAPCS64 arguments to pass to the builtin. - __ Mov(x0, argc); - __ Mov(x1, argv); - __ Mov(x2, Operand(ExternalReference::isolate_address(isolate))); - - // Store the return address on the stack, in the space previously allocated - // by EnterExitFrame. The return address is queried by - // ExitFrame::GetStateForFramePointer. - Label return_location; - __ Adr(x12, &return_location); - __ Poke(x12, 0); - if (__ emit_debug_code()) { - // Verify that the slot below fp[kSPOffset]-8 points to the return location - // (currently in x12). - Register temp = masm->Tmp1(); - __ Ldr(temp, MemOperand(fp, ExitFrameConstants::kSPOffset)); - __ Ldr(temp, MemOperand(temp, -static_cast(kXRegSizeInBytes))); - __ Cmp(temp, x12); - __ Check(eq, kReturnAddressNotFoundInFrame); - } - - // Call the builtin. - __ Blr(target); - __ Bind(&return_location); - const Register& result = x0; - - if (always_allocate) { - __ Mov(x10, Operand(scope_depth)); - __ Ldr(x11, MemOperand(x10)); - __ Sub(x11, x11, 1); - __ Str(x11, MemOperand(x10)); - } - - // x0 result The return code from the call. - // x21 argv - // x22 argc - // x23 target - // - // If all of the result bits matching kFailureTagMask are '1', the result is - // a failure. Otherwise, it's an ordinary tagged object and the call was a - // success. - Label failure; - __ And(x10, result, kFailureTagMask); - __ Cmp(x10, kFailureTagMask); - __ B(&failure, eq); - - // The call succeeded, so unwind the stack and return. - - // Restore callee-saved registers x21-x23. - __ Mov(x11, argc); - - __ Peek(argv, 1 * kPointerSize); - __ Peek(argc, 2 * kPointerSize); - __ Peek(target, 3 * kPointerSize); - - __ LeaveExitFrame(save_doubles_, x10, true); - ASSERT(jssp.Is(__ StackPointer())); - // Pop or drop the remaining stack slots and return from the stub. - // jssp[24]: Arguments array (of size argc), including receiver. - // jssp[16]: Preserved x23 (used for target). - // jssp[8]: Preserved x22 (used for argc). - // jssp[0]: Preserved x21 (used for argv). - __ Drop(x11); - __ Ret(); - - // The stack pointer is still csp if we aren't returning, and the frame - // hasn't changed (except for the return address). - __ SetStackPointer(csp); - - __ Bind(&failure); - // The call failed, so check if we need to throw an exception, and fall - // through (to retry) otherwise. - - Label retry; - // x0 result The return code from the call, including the failure - // code and details. - // x21 argv - // x22 argc - // x23 target - // Refer to the Failure class for details of the bit layout. - STATIC_ASSERT(Failure::RETRY_AFTER_GC == 0); - __ Tst(result, kFailureTypeTagMask << kFailureTagSize); - __ B(eq, &retry); // RETRY_AFTER_GC - - // Special handling of out-of-memory exceptions: Pass the failure result, - // rather than the exception descriptor. - JumpIfOOM(masm, result, x10, throw_out_of_memory); - - // Retrieve the pending exception. - const Register& exception = result; - const Register& exception_address = x11; - __ Mov(exception_address, - Operand(ExternalReference(Isolate::kPendingExceptionAddress, - isolate))); - __ Ldr(exception, MemOperand(exception_address)); - - // See if we just retrieved an OOM exception. - JumpIfOOM(masm, exception, x10, throw_out_of_memory); - - // Clear the pending exception. - __ Mov(x10, Operand(isolate->factory()->the_hole_value())); - __ Str(x10, MemOperand(exception_address)); - - // x0 exception The exception descriptor. - // x21 argv - // x22 argc - // x23 target - - // Special handling of termination exceptions, which are uncatchable by - // JavaScript code. - __ Cmp(exception, Operand(isolate->factory()->termination_exception())); - __ B(eq, throw_termination); - - // Handle normal exception. - __ B(throw_normal); - - __ Bind(&retry); - // The result (x0) is passed through as the next PerformGC parameter. -} - - -void CEntryStub::Generate(MacroAssembler* masm) { - // The Abort mechanism relies on CallRuntime, which in turn relies on - // CEntryStub, so until this stub has been generated, we have to use a - // fall-back Abort mechanism. - // - // Note that this stub must be generated before any use of Abort. - MacroAssembler::NoUseRealAbortsScope no_use_real_aborts(masm); - - ASM_LOCATION("CEntryStub::Generate entry"); - ProfileEntryHookStub::MaybeCallEntryHook(masm); - - // Register parameters: - // x0: argc (including receiver, untagged) - // x1: target - // - // The stack on entry holds the arguments and the receiver, with the receiver - // at the highest address: - // - // jssp]argc-1]: receiver - // jssp[argc-2]: arg[argc-2] - // ... ... - // jssp[1]: arg[1] - // jssp[0]: arg[0] - // - // The arguments are in reverse order, so that arg[argc-2] is actually the - // first argument to the target function and arg[0] is the last. - ASSERT(jssp.Is(__ StackPointer())); - const Register& argc_input = x0; - const Register& target_input = x1; - - // Calculate argv, argc and the target address, and store them in - // callee-saved registers so we can retry the call without having to reload - // these arguments. - // TODO(jbramley): If the first call attempt succeeds in the common case (as - // it should), then we might be better off putting these parameters directly - // into their argument registers, rather than using callee-saved registers and - // preserving them on the stack. - const Register& argv = x21; - const Register& argc = x22; - const Register& target = x23; - - // Derive argv from the stack pointer so that it points to the first argument - // (arg[argc-2]), or just below the receiver in case there are no arguments. - // - Adjust for the arg[] array. - Register temp_argv = x11; - __ Add(temp_argv, jssp, Operand(x0, LSL, kPointerSizeLog2)); - // - Adjust for the receiver. - __ Sub(temp_argv, temp_argv, 1 * kPointerSize); - - // Enter the exit frame. Reserve three slots to preserve x21-x23 callee-saved - // registers. - FrameScope scope(masm, StackFrame::MANUAL); - __ EnterExitFrame(save_doubles_, x10, 3); - ASSERT(csp.Is(__ StackPointer())); - - // Poke callee-saved registers into reserved space. - __ Poke(argv, 1 * kPointerSize); - __ Poke(argc, 2 * kPointerSize); - __ Poke(target, 3 * kPointerSize); - - // We normally only keep tagged values in callee-saved registers, as they - // could be pushed onto the stack by called stubs and functions, and on the - // stack they can confuse the GC. However, we're only calling C functions - // which can push arbitrary data onto the stack anyway, and so the GC won't - // examine that part of the stack. - __ Mov(argc, argc_input); - __ Mov(target, target_input); - __ Mov(argv, temp_argv); - - Label throw_normal; - Label throw_termination; - Label throw_out_of_memory; - - // Call the runtime function. - GenerateCore(masm, - &throw_normal, - &throw_termination, - &throw_out_of_memory, - false, - false); - - // If successful, the previous GenerateCore will have returned to the - // calling code. Otherwise, we fall through into the following. - - // Do space-specific GC and retry runtime call. - GenerateCore(masm, - &throw_normal, - &throw_termination, - &throw_out_of_memory, - true, - false); - - // Do full GC and retry runtime call one final time. - __ Mov(x0, reinterpret_cast(Failure::InternalError())); - GenerateCore(masm, - &throw_normal, - &throw_termination, - &throw_out_of_memory, - true, - true); - - // We didn't execute a return case, so the stack frame hasn't been updated - // (except for the return address slot). However, we don't need to initialize - // jssp because the throw method will immediately overwrite it when it - // unwinds the stack. - if (__ emit_debug_code()) { - __ Mov(jssp, kDebugZapValue); - } - __ SetStackPointer(jssp); - - // Throw exceptions. - // If we throw an exception, we can end up re-entering CEntryStub before we - // pop the exit frame, so need to ensure that x21-x23 contain GC-safe values - // here. - __ Bind(&throw_out_of_memory); - ASM_LOCATION("Throw out of memory"); - __ Mov(argv, 0); - __ Mov(argc, 0); - __ Mov(target, 0); - // Set external caught exception to false. - Isolate* isolate = masm->isolate(); - __ Mov(x2, Operand(ExternalReference(Isolate::kExternalCaughtExceptionAddress, - isolate))); - __ Str(xzr, MemOperand(x2)); - - // Set pending exception and x0 to out of memory exception. - Label already_have_failure; - JumpIfOOM(masm, x0, x10, &already_have_failure); - Failure* out_of_memory = Failure::OutOfMemoryException(0x1); - __ Mov(x0, Operand(reinterpret_cast(out_of_memory))); - __ Bind(&already_have_failure); - __ Mov(x2, Operand(ExternalReference(Isolate::kPendingExceptionAddress, - isolate))); - __ Str(x0, MemOperand(x2)); - // Fall through to the next label. - - __ Bind(&throw_termination); - ASM_LOCATION("Throw termination"); - __ Mov(argv, 0); - __ Mov(argc, 0); - __ Mov(target, 0); - __ ThrowUncatchable(x0, x10, x11, x12, x13); - - __ Bind(&throw_normal); - ASM_LOCATION("Throw normal"); - __ Mov(argv, 0); - __ Mov(argc, 0); - __ Mov(target, 0); - __ Throw(x0, x10, x11, x12, x13); -} - - -// This is the entry point from C++. 5 arguments are provided in x0-x4. -// See use of the CALL_GENERATED_CODE macro for example in src/execution.cc. -// Input: -// x0: code entry. -// x1: function. -// x2: receiver. -// x3: argc. -// x4: argv. -// Output: -// x0: result. -void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { - ASSERT(jssp.Is(__ StackPointer())); - Register code_entry = x0; - - // Enable instruction instrumentation. This only works on the simulator, and - // will have no effect on the model or real hardware. - __ EnableInstrumentation(); - - Label invoke, handler_entry, exit; - - // Push callee-saved registers and synchronize the system stack pointer (csp) - // and the JavaScript stack pointer (jssp). - // - // We must not write to jssp until after the PushCalleeSavedRegisters() - // call, since jssp is itself a callee-saved register. - __ SetStackPointer(csp); - __ PushCalleeSavedRegisters(); - __ Mov(jssp, csp); - __ SetStackPointer(jssp); - - ProfileEntryHookStub::MaybeCallEntryHook(masm); - - // Build an entry frame (see layout below). - Isolate* isolate = masm->isolate(); - - // Build an entry frame. - int marker = is_construct ? StackFrame::ENTRY_CONSTRUCT : StackFrame::ENTRY; - int64_t bad_frame_pointer = -1L; // Bad frame pointer to fail if it is used. - __ Mov(x13, bad_frame_pointer); - __ Mov(x12, Operand(Smi::FromInt(marker))); - __ Mov(x11, Operand(ExternalReference(Isolate::kCEntryFPAddress, isolate))); - __ Ldr(x10, MemOperand(x11)); - - // TODO(all): Pushing the marker twice seems unnecessary. - // In this case perhaps we could push xzr in the slot for the context - // (see MAsm::EnterFrame). - __ Push(x13, x12, x12, x10); - // Set up fp. - __ Sub(fp, jssp, EntryFrameConstants::kCallerFPOffset); - - // Push the JS entry frame marker. Also set js_entry_sp if this is the - // outermost JS call. - Label non_outermost_js, done; - ExternalReference js_entry_sp(Isolate::kJSEntrySPAddress, isolate); - __ Mov(x10, Operand(ExternalReference(js_entry_sp))); - __ Ldr(x11, MemOperand(x10)); - __ Cbnz(x11, &non_outermost_js); - __ Str(fp, MemOperand(x10)); - __ Mov(x12, Operand(Smi::FromInt(StackFrame::OUTERMOST_JSENTRY_FRAME))); - __ Push(x12); - __ B(&done); - __ Bind(&non_outermost_js); - // We spare one instruction by pushing xzr since the marker is 0. - ASSERT(Smi::FromInt(StackFrame::INNER_JSENTRY_FRAME) == NULL); - __ Push(xzr); - __ Bind(&done); - - // The frame set up looks like this: - // jssp[0] : JS entry frame marker. - // jssp[1] : C entry FP. - // jssp[2] : stack frame marker. - // jssp[3] : stack frmae marker. - // jssp[4] : bad frame pointer 0xfff...ff <- fp points here. - - - // Jump to a faked try block that does the invoke, with a faked catch - // block that sets the pending exception. - __ B(&invoke); - - // Prevent the constant pool from being emitted between the record of the - // handler_entry position and the first instruction of the sequence here. - // There is no risk because Assembler::Emit() emits the instruction before - // checking for constant pool emission, but we do not want to depend on - // that. - { - Assembler::BlockConstPoolScope block_const_pool(masm); - __ 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. Coming in here the - // fp will be invalid because the PushTryHandler below sets it to 0 to - // signal the existence of the JSEntry frame. - // TODO(jbramley): Do this in the Assembler. - __ Mov(x10, Operand(ExternalReference(Isolate::kPendingExceptionAddress, - isolate))); - } - __ Str(code_entry, MemOperand(x10)); - __ Mov(x0, Operand(reinterpret_cast(Failure::Exception()))); - __ B(&exit); - - // Invoke: Link this frame into the handler chain. There's only one - // handler block in this code object, so its index is 0. - __ Bind(&invoke); - __ PushTryHandler(StackHandler::JS_ENTRY, 0); - // If an exception not caught by another handler occurs, this handler - // returns control to the code after the B(&invoke) above, which - // restores all callee-saved registers (including cp and fp) to their - // saved values before returning a failure to C. - - // Clear any pending exceptions. - __ Mov(x10, Operand(isolate->factory()->the_hole_value())); - __ Mov(x11, Operand(ExternalReference(Isolate::kPendingExceptionAddress, - isolate))); - __ Str(x10, MemOperand(x11)); - - // Invoke the function by calling through the JS entry trampoline builtin. - // Notice that we cannot store a reference to the trampoline code directly in - // this stub, because runtime stubs are not traversed when doing GC. - - // Expected registers by Builtins::JSEntryTrampoline - // x0: code entry. - // x1: function. - // x2: receiver. - // x3: argc. - // x4: argv. - // TODO(jbramley): The latest ARM code checks is_construct and conditionally - // uses construct_entry. We probably need to do the same here. - ExternalReference entry(is_construct ? Builtins::kJSConstructEntryTrampoline - : Builtins::kJSEntryTrampoline, - isolate); - __ Mov(x10, Operand(entry)); - - // Call the JSEntryTrampoline. - __ Ldr(x11, MemOperand(x10)); // Dereference the address. - __ Add(x12, x11, Code::kHeaderSize - kHeapObjectTag); - __ Blr(x12); - - // Unlink this frame from the handler chain. - __ PopTryHandler(); - - - __ Bind(&exit); - // x0 holds the result. - // The stack pointer points to the top of the entry frame pushed on entry from - // C++ (at the beginning of this stub): - // jssp[0] : JS entry frame marker. - // jssp[1] : C entry FP. - // jssp[2] : stack frame marker. - // jssp[3] : stack frmae marker. - // jssp[4] : bad frame pointer 0xfff...ff <- fp points here. - - // Check if the current stack frame is marked as the outermost JS frame. - Label non_outermost_js_2; - __ Pop(x10); - __ Cmp(x10, Operand(Smi::FromInt(StackFrame::OUTERMOST_JSENTRY_FRAME))); - __ B(ne, &non_outermost_js_2); - __ Mov(x11, Operand(ExternalReference(js_entry_sp))); - __ Str(xzr, MemOperand(x11)); - __ Bind(&non_outermost_js_2); - - // Restore the top frame descriptors from the stack. - __ Pop(x10); - __ Mov(x11, Operand(ExternalReference(Isolate::kCEntryFPAddress, isolate))); - __ Str(x10, MemOperand(x11)); - - // Reset the stack to the callee saved registers. - __ Drop(-EntryFrameConstants::kCallerFPOffset, kByteSizeInBytes); - // Restore the callee-saved registers and return. - ASSERT(jssp.Is(__ StackPointer())); - __ Mov(csp, jssp); - __ SetStackPointer(csp); - __ PopCalleeSavedRegisters(); - // After this point, we must not modify jssp because it is a callee-saved - // register which we have just restored. - __ Ret(); -} - - -void FunctionPrototypeStub::Generate(MacroAssembler* masm) { - Label miss; - Register receiver; - if (kind() == Code::KEYED_LOAD_IC) { - // ----------- S t a t e ------------- - // -- lr : return address - // -- x1 : receiver - // -- x0 : key - // ----------------------------------- - Register key = x0; - receiver = x1; - __ Cmp(key, Operand(masm->isolate()->factory()->prototype_string())); - __ B(ne, &miss); - } else { - ASSERT(kind() == Code::LOAD_IC); - // ----------- S t a t e ------------- - // -- lr : return address - // -- x2 : name - // -- x0 : receiver - // -- sp[0] : receiver - // ----------------------------------- - receiver = x0; - } - - StubCompiler::GenerateLoadFunctionPrototype(masm, receiver, x10, x11, &miss); - - __ Bind(&miss); - StubCompiler::TailCallBuiltin(masm, - BaseLoadStoreStubCompiler::MissBuiltin(kind())); -} - - -void StringLengthStub::Generate(MacroAssembler* masm) { - Label miss; - Register receiver; - if (kind() == Code::KEYED_LOAD_IC) { - // ----------- S t a t e ------------- - // -- lr : return address - // -- x1 : receiver - // -- x0 : key - // ----------------------------------- - Register key = x0; - receiver = x1; - __ Cmp(key, Operand(masm->isolate()->factory()->length_string())); - __ B(ne, &miss); - } else { - ASSERT(kind() == Code::LOAD_IC); - // ----------- S t a t e ------------- - // -- lr : return address - // -- x2 : name - // -- x0 : receiver - // -- sp[0] : receiver - // ----------------------------------- - receiver = x0; - } - - StubCompiler::GenerateLoadStringLength(masm, receiver, x10, x11, &miss); - - __ Bind(&miss); - StubCompiler::TailCallBuiltin(masm, - BaseLoadStoreStubCompiler::MissBuiltin(kind())); -} - - -void StoreArrayLengthStub::Generate(MacroAssembler* masm) { - ASM_LOCATION("StoreArrayLengthStub::Generate"); - // This accepts as a receiver anything JSArray::SetElementsLength accepts - // (currently anything except for external arrays which means anything with - // elements of FixedArray type). Value must be a number, but only smis are - // accepted as the most common case. - Label miss; - - Register receiver; - Register value; - if (kind() == Code::KEYED_STORE_IC) { - // ----------- S t a t e ------------- - // -- lr : return address - // -- x2 : receiver - // -- x1 : key - // -- x0 : value - // ----------------------------------- - Register key = x1; - receiver = x2; - value = x0; - __ Cmp(key, Operand(masm->isolate()->factory()->length_string())); - __ B(ne, &miss); - } else { - ASSERT(kind() == Code::STORE_IC); - // ----------- S t a t e ------------- - // -- lr : return address - // -- x2 : key - // -- x1 : receiver - // -- x0 : value - // ----------------------------------- - receiver = x1; - value = x0; - } - - // Check that the receiver isn't a smi. - __ JumpIfSmi(receiver, &miss); - - // Check that the object is a JS array. - __ CompareObjectType(receiver, x10, x11, JS_ARRAY_TYPE); - __ B(ne, &miss); - - // Check that elements are FixedArray. - // We rely on StoreIC_ArrayLength below to deal with all types of - // fast elements (including COW). - __ Ldr(x10, FieldMemOperand(receiver, JSArray::kElementsOffset)); - __ CompareObjectType(x10, x11, x12, FIXED_ARRAY_TYPE); - __ B(ne, &miss); - - // Check that the array has fast properties, otherwise the length - // property might have been redefined. - __ Ldr(x10, FieldMemOperand(receiver, JSArray::kPropertiesOffset)); - __ Ldr(x10, FieldMemOperand(x10, FixedArray::kMapOffset)); - __ CompareRoot(x10, Heap::kHashTableMapRootIndex); - __ B(eq, &miss); - - // Check that value is a smi. - __ JumpIfNotSmi(value, &miss); - - // Prepare tail call to StoreIC_ArrayLength. - __ Push(receiver, value); - - ExternalReference ref = - ExternalReference(IC_Utility(IC::kStoreIC_ArrayLength), masm->isolate()); - __ TailCallExternalReference(ref, 2, 1); - - __ Bind(&miss); - StubCompiler::TailCallBuiltin(masm, - BaseLoadStoreStubCompiler::MissBuiltin(kind())); -} - - -void InstanceofStub::Generate(MacroAssembler* masm) { - // Stack on entry: - // jssp[0]: function. - // jssp[8]: object. - // - // Returns result in x0. Zero indicates instanceof, smi 1 indicates not - // instanceof. - - Register result = x0; - Register function = right(); - Register object = left(); - Register scratch1 = x6; - Register scratch2 = x7; - Register res_true = x8; - Register res_false = x9; - // Only used if there was an inline map check site. (See - // LCodeGen::DoInstanceOfKnownGlobal().) - Register map_check_site = x4; - // Delta for the instructions generated between the inline map check and the - // instruction setting the result. - const int32_t kDeltaToLoadBoolResult = 4 * kInstructionSize; - - Label not_js_object, slow; - - if (!HasArgsInRegisters()) { - __ Pop(function, object); - } - - if (ReturnTrueFalseObject()) { - __ LoadTrueFalseRoots(res_true, res_false); - } else { - // This is counter-intuitive, but correct. - __ Mov(res_true, Operand(Smi::FromInt(0))); - __ Mov(res_false, Operand(Smi::FromInt(1))); - } - - // Check that the left hand side is a JS object and load its map as a side - // effect. - Register map = x12; - __ JumpIfSmi(object, ¬_js_object); - __ IsObjectJSObjectType(object, map, scratch2, ¬_js_object); - - // If there is a call site cache, don't look in the global cache, but do the - // real lookup and update the call site cache. - if (!HasCallSiteInlineCheck()) { - Label miss; - __ JumpIfNotRoot(function, Heap::kInstanceofCacheFunctionRootIndex, &miss); - __ JumpIfNotRoot(map, Heap::kInstanceofCacheMapRootIndex, &miss); - __ LoadRoot(result, Heap::kInstanceofCacheAnswerRootIndex); - __ Ret(); - __ Bind(&miss); - } - - // Get the prototype of the function. - Register prototype = x13; - __ TryGetFunctionPrototype(function, prototype, scratch2, &slow, - MacroAssembler::kMissOnBoundFunction); - - // Check that the function prototype is a JS object. - __ JumpIfSmi(prototype, &slow); - __ IsObjectJSObjectType(prototype, scratch1, scratch2, &slow); - - // Update the global instanceof or call site inlined cache with the current - // map and function. The cached answer will be set when it is known below. - if (HasCallSiteInlineCheck()) { - // Patch the (relocated) inlined map check. - __ GetRelocatedValueLocation(map_check_site, scratch1); - // We have a cell, so need another level of dereferencing. - __ Ldr(scratch1, MemOperand(scratch1)); - __ Str(map, FieldMemOperand(scratch1, Cell::kValueOffset)); - } else { - __ StoreRoot(function, Heap::kInstanceofCacheFunctionRootIndex); - __ StoreRoot(map, Heap::kInstanceofCacheMapRootIndex); - } - - Label return_true, return_result; - { - // Loop through the prototype chain looking for the function prototype. - Register chain_map = x1; - Register chain_prototype = x14; - Register null_value = x15; - Label loop; - __ Ldr(chain_prototype, FieldMemOperand(map, Map::kPrototypeOffset)); - __ LoadRoot(null_value, Heap::kNullValueRootIndex); - // Speculatively set a result. - __ Mov(result, res_false); - - __ Bind(&loop); - - // If the chain prototype is the object prototype, return true. - __ Cmp(chain_prototype, prototype); - __ B(eq, &return_true); - - // If the chain prototype is null, we've reached the end of the chain, so - // return false. - __ Cmp(chain_prototype, null_value); - __ B(eq, &return_result); - - // Otherwise, load the next prototype in the chain, and loop. - __ Ldr(chain_map, FieldMemOperand(chain_prototype, HeapObject::kMapOffset)); - __ Ldr(chain_prototype, FieldMemOperand(chain_map, Map::kPrototypeOffset)); - __ B(&loop); - } - - // Return sequence when no arguments are on the stack. - // We cannot fall through to here. - __ Bind(&return_true); - __ Mov(result, res_true); - __ Bind(&return_result); - if (HasCallSiteInlineCheck()) { - ASSERT(ReturnTrueFalseObject()); - __ Add(map_check_site, map_check_site, kDeltaToLoadBoolResult); - __ GetRelocatedValueLocation(map_check_site, scratch2); - __ Str(result, MemOperand(scratch2)); - } else { - __ StoreRoot(result, Heap::kInstanceofCacheAnswerRootIndex); - } - __ Ret(); - - Label object_not_null, object_not_null_or_smi; - - __ Bind(¬_js_object); - Register object_type = x14; - // x0 result result return register (uninit) - // x10 function pointer to function - // x11 object pointer to object - // x14 object_type type of object (uninit) - - // Before null, smi and string checks, check that the rhs is a function. - // For a non-function rhs, an exception must be thrown. - __ JumpIfSmi(function, &slow); - __ JumpIfNotObjectType( - function, scratch1, object_type, JS_FUNCTION_TYPE, &slow); - - __ Mov(result, res_false); - - // Null is not instance of anything. - __ Cmp(object_type, Operand(masm->isolate()->factory()->null_value())); - __ B(ne, &object_not_null); - __ Ret(); - - __ Bind(&object_not_null); - // Smi values are not instances of anything. - __ JumpIfNotSmi(object, &object_not_null_or_smi); - __ Ret(); - - __ Bind(&object_not_null_or_smi); - // String values are not instances of anything. - __ IsObjectJSStringType(object, scratch2, &slow); - __ Ret(); - - // Slow-case. Tail call builtin. - __ Bind(&slow); - { - FrameScope scope(masm, StackFrame::INTERNAL); - // Arguments have either been passed into registers or have been previously - // popped. We need to push them before calling builtin. - __ Push(object, function); - __ InvokeBuiltin(Builtins::INSTANCE_OF, CALL_FUNCTION); - } - if (ReturnTrueFalseObject()) { - // Reload true/false because they were clobbered in the builtin call. - __ LoadTrueFalseRoots(res_true, res_false); - __ Cmp(result, 0); - __ Csel(result, res_true, res_false, eq); - } - __ Ret(); -} - - -Register InstanceofStub::left() { - // Object to check (instanceof lhs). - return x11; -} - - -Register InstanceofStub::right() { - // Constructor function (instanceof rhs). - return x10; -} - - -void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) { - Register arg_count = x0; - Register key = x1; - - // The displacement is the offset of the last parameter (if any) relative - // to the frame pointer. - static const int kDisplacement = - StandardFrameConstants::kCallerSPOffset - kPointerSize; - - // Check that the key is a smi. - Label slow; - __ JumpIfNotSmi(key, &slow); - - // Check if the calling frame is an arguments adaptor frame. - Register local_fp = x11; - Register caller_fp = x11; - Register caller_ctx = x12; - Label skip_adaptor; - __ Ldr(caller_fp, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); - __ Ldr(caller_ctx, MemOperand(caller_fp, - StandardFrameConstants::kContextOffset)); - __ Cmp(caller_ctx, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); - __ Csel(local_fp, fp, caller_fp, ne); - __ B(ne, &skip_adaptor); - - // Load the actual arguments limit found in the arguments adaptor frame. - __ Ldr(arg_count, MemOperand(caller_fp, - ArgumentsAdaptorFrameConstants::kLengthOffset)); - __ Bind(&skip_adaptor); - - // Check index against formal parameters count limit. Use unsigned comparison - // to get negative check for free: branch if key < 0 or key >= arg_count. - __ Cmp(key, arg_count); - __ B(hs, &slow); - - // Read the argument from the stack and return it. - __ Sub(x10, arg_count, key); - __ Add(x10, local_fp, Operand::UntagSmiAndScale(x10, kPointerSizeLog2)); - __ Ldr(x0, MemOperand(x10, kDisplacement)); - __ Ret(); - - // Slow case: handle non-smi or out-of-bounds access to arguments by calling - // the runtime system. - __ Bind(&slow); - __ Push(key); - __ TailCallRuntime(Runtime::kGetArgumentsProperty, 1, 1); -} - - -void ArgumentsAccessStub::GenerateNewNonStrictSlow(MacroAssembler* masm) { - // Stack layout on entry. - // jssp[0]: number of parameters (tagged) - // jssp[8]: address of receiver argument - // jssp[16]: function - - // Check if the calling frame is an arguments adaptor frame. - Label runtime; - Register caller_fp = x10; - __ Ldr(caller_fp, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); - // Load and untag the context. - STATIC_ASSERT((kSmiShift / kBitsPerByte) == 4); - __ Ldr(w11, MemOperand(caller_fp, StandardFrameConstants::kContextOffset + - (kSmiShift / kBitsPerByte))); - __ Cmp(w11, StackFrame::ARGUMENTS_ADAPTOR); - __ B(ne, &runtime); - - // Patch the arguments.length and parameters pointer in the current frame. - __ Ldr(x11, MemOperand(caller_fp, - ArgumentsAdaptorFrameConstants::kLengthOffset)); - __ Poke(x11, 0 * kXRegSizeInBytes); - __ Add(x10, caller_fp, Operand::UntagSmiAndScale(x11, kPointerSizeLog2)); - __ Add(x10, x10, Operand(StandardFrameConstants::kCallerSPOffset)); - __ Poke(x10, 1 * kXRegSizeInBytes); - - __ Bind(&runtime); - __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1); -} - - -void ArgumentsAccessStub::GenerateNewNonStrictFast(MacroAssembler* masm) { - // Stack layout on entry. - // jssp[0]: number of parameters (tagged) - // jssp[8]: address of receiver argument - // jssp[16]: function - // - // Returns pointer to result object in x0. - - // Note: arg_count_smi is an alias of param_count_smi. - Register arg_count_smi = x3; - Register param_count_smi = x3; - Register param_count = x7; - Register recv_arg = x14; - Register function = x4; - __ Pop(param_count_smi, recv_arg, function); - __ SmiUntag(param_count, param_count_smi); - - // Check if the calling frame is an arguments adaptor frame. - Register caller_fp = x11; - Register caller_ctx = x12; - Label runtime; - Label adaptor_frame, try_allocate; - __ Ldr(caller_fp, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); - __ Ldr(caller_ctx, MemOperand(caller_fp, - StandardFrameConstants::kContextOffset)); - __ Cmp(caller_ctx, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); - __ B(eq, &adaptor_frame); - - // No adaptor, parameter count = argument count. - - // x1 mapped_params number of mapped params, min(params, args) (uninit) - // x2 arg_count number of function arguments (uninit) - // x3 arg_count_smi number of function arguments (smi) - // x4 function function pointer - // x7 param_count number of function parameters - // x11 caller_fp caller's frame pointer - // x14 recv_arg pointer to receiver arguments - - Register arg_count = x2; - __ Mov(arg_count, param_count); - __ B(&try_allocate); - - // We have an adaptor frame. Patch the parameters pointer. - __ Bind(&adaptor_frame); - __ Ldr(arg_count_smi, - MemOperand(caller_fp, - ArgumentsAdaptorFrameConstants::kLengthOffset)); - __ SmiUntag(arg_count, arg_count_smi); - __ Add(x10, caller_fp, Operand(arg_count, LSL, kPointerSizeLog2)); - __ Add(recv_arg, x10, StandardFrameConstants::kCallerSPOffset); - - // Compute the mapped parameter count = min(param_count, arg_count) - Register mapped_params = x1; - __ Cmp(param_count, arg_count); - __ Csel(mapped_params, param_count, arg_count, lt); - - __ Bind(&try_allocate); - - // x0 alloc_obj pointer to allocated objects: param map, backing - // store, arguments (uninit) - // x1 mapped_params number of mapped parameters, min(params, args) - // x2 arg_count number of function arguments - // x3 arg_count_smi number of function arguments (smi) - // x4 function function pointer - // x7 param_count number of function parameters - // x10 size size of objects to allocate (uninit) - // x14 recv_arg pointer to receiver arguments - - // Compute the size of backing store, parameter map, and arguments object. - // 1. Parameter map, has two extra words containing context and backing - // store. - const int kParameterMapHeaderSize = - FixedArray::kHeaderSize + 2 * kPointerSize; - - // Calculate the parameter map size, assuming it exists. - Register size = x10; - __ Mov(size, Operand(mapped_params, LSL, kPointerSizeLog2)); - __ Add(size, size, kParameterMapHeaderSize); - - // If there are no mapped parameters, set the running size total to zero. - // Otherwise, use the parameter map size calculated earlier. - __ Cmp(mapped_params, 0); - __ CzeroX(size, eq); - - // 2. Add the size of the backing store and arguments object. - __ Add(size, size, Operand(arg_count, LSL, kPointerSizeLog2)); - __ Add(size, size, FixedArray::kHeaderSize + Heap::kArgumentsObjectSize); - - // Do the allocation of all three objects in one go. Assign this to x0, as it - // will be returned to the caller. - Register alloc_obj = x0; - __ Allocate(size, alloc_obj, x11, x12, &runtime, TAG_OBJECT); - - // Get the arguments boilerplate from the current (global) context. - - // x0 alloc_obj pointer to allocated objects (param map, backing - // store, arguments) - // x1 mapped_params number of mapped parameters, min(params, args) - // x2 arg_count number of function arguments - // x3 arg_count_smi number of function arguments (smi) - // x4 function function pointer - // x7 param_count number of function parameters - // x11 args_offset offset to args (or aliased args) boilerplate (uninit) - // x14 recv_arg pointer to receiver arguments - - Register global_object = x10; - Register global_ctx = x10; - Register args_offset = x11; - Register aliased_args_offset = x10; - __ Ldr(global_object, GlobalObjectMemOperand()); - __ Ldr(global_ctx, FieldMemOperand(global_object, - GlobalObject::kNativeContextOffset)); - - __ Ldr(args_offset, ContextMemOperand(global_ctx, - Context::ARGUMENTS_BOILERPLATE_INDEX)); - __ Ldr(aliased_args_offset, - ContextMemOperand(global_ctx, - Context::ALIASED_ARGUMENTS_BOILERPLATE_INDEX)); - __ Cmp(mapped_params, 0); - __ CmovX(args_offset, aliased_args_offset, ne); - - // Copy the JS object part. - __ CopyFields(alloc_obj, args_offset, CPURegList(x10, x12, x13), - JSObject::kHeaderSize / kPointerSize); - - // Set up the callee in-object property. - STATIC_ASSERT(Heap::kArgumentsCalleeIndex == 1); - const int kCalleeOffset = JSObject::kHeaderSize + - Heap::kArgumentsCalleeIndex * kPointerSize; - __ Str(function, FieldMemOperand(alloc_obj, kCalleeOffset)); - - // Use the length and set that as an in-object property. - STATIC_ASSERT(Heap::kArgumentsLengthIndex == 0); - const int kLengthOffset = JSObject::kHeaderSize + - Heap::kArgumentsLengthIndex * kPointerSize; - __ Str(arg_count_smi, FieldMemOperand(alloc_obj, kLengthOffset)); - - // Set up the elements pointer in the allocated arguments object. - // If we allocated a parameter map, "elements" will point there, otherwise - // it will point to the backing store. - - // x0 alloc_obj pointer to allocated objects (param map, backing - // store, arguments) - // x1 mapped_params number of mapped parameters, min(params, args) - // x2 arg_count number of function arguments - // x3 arg_count_smi number of function arguments (smi) - // x4 function function pointer - // x5 elements pointer to parameter map or backing store (uninit) - // x6 backing_store pointer to backing store (uninit) - // x7 param_count number of function parameters - // x14 recv_arg pointer to receiver arguments - - Register elements = x5; - __ Add(elements, alloc_obj, Heap::kArgumentsObjectSize); - __ Str(elements, FieldMemOperand(alloc_obj, JSObject::kElementsOffset)); - - // Initialize parameter map. If there are no mapped arguments, we're done. - Label skip_parameter_map; - __ Cmp(mapped_params, 0); - // Set up backing store address, because it is needed later for filling in - // the unmapped arguments. - Register backing_store = x6; - __ CmovX(backing_store, elements, eq); - __ B(eq, &skip_parameter_map); - - __ LoadRoot(x10, Heap::kNonStrictArgumentsElementsMapRootIndex); - __ Str(x10, FieldMemOperand(elements, FixedArray::kMapOffset)); - __ Add(x10, mapped_params, 2); - __ SmiTag(x10); - __ Str(x10, FieldMemOperand(elements, FixedArray::kLengthOffset)); - __ Str(cp, FieldMemOperand(elements, - FixedArray::kHeaderSize + 0 * kPointerSize)); - __ Add(x10, elements, Operand(mapped_params, LSL, kPointerSizeLog2)); - __ Add(x10, x10, kParameterMapHeaderSize); - __ Str(x10, FieldMemOperand(elements, - FixedArray::kHeaderSize + 1 * kPointerSize)); - - // Copy the parameter slots and the holes in the arguments. - // We need to fill in mapped_parameter_count slots. Then index the context, - // where parameters are stored in reverse order, at: - // - // MIN_CONTEXT_SLOTS .. MIN_CONTEXT_SLOTS + parameter_count - 1 - // - // The mapped parameter thus needs to get indices: - // - // MIN_CONTEXT_SLOTS + parameter_count - 1 .. - // MIN_CONTEXT_SLOTS + parameter_count - mapped_parameter_count - // - // We loop from right to left. - - // x0 alloc_obj pointer to allocated objects (param map, backing - // store, arguments) - // x1 mapped_params number of mapped parameters, min(params, args) - // x2 arg_count number of function arguments - // x3 arg_count_smi number of function arguments (smi) - // x4 function function pointer - // x5 elements pointer to parameter map or backing store (uninit) - // x6 backing_store pointer to backing store (uninit) - // x7 param_count number of function parameters - // x11 loop_count parameter loop counter (uninit) - // x12 index parameter index (smi, uninit) - // x13 the_hole hole value (uninit) - // x14 recv_arg pointer to receiver arguments - - Register loop_count = x11; - Register index = x12; - Register the_hole = x13; - Label parameters_loop, parameters_test; - __ Mov(loop_count, mapped_params); - __ Add(index, param_count, static_cast(Context::MIN_CONTEXT_SLOTS)); - __ Sub(index, index, mapped_params); - __ SmiTag(index); - __ LoadRoot(the_hole, Heap::kTheHoleValueRootIndex); - __ Add(backing_store, elements, Operand(loop_count, LSL, kPointerSizeLog2)); - __ Add(backing_store, backing_store, kParameterMapHeaderSize); - - __ B(¶meters_test); - - __ Bind(¶meters_loop); - __ Sub(loop_count, loop_count, 1); - __ Mov(x10, Operand(loop_count, LSL, kPointerSizeLog2)); - __ Add(x10, x10, kParameterMapHeaderSize - kHeapObjectTag); - __ Str(index, MemOperand(elements, x10)); - __ Sub(x10, x10, kParameterMapHeaderSize - FixedArray::kHeaderSize); - __ Str(the_hole, MemOperand(backing_store, x10)); - __ Add(index, index, Operand(Smi::FromInt(1))); - __ Bind(¶meters_test); - __ Cbnz(loop_count, ¶meters_loop); - - __ Bind(&skip_parameter_map); - // Copy arguments header and remaining slots (if there are any.) - __ LoadRoot(x10, Heap::kFixedArrayMapRootIndex); - __ Str(x10, FieldMemOperand(backing_store, FixedArray::kMapOffset)); - __ Str(arg_count_smi, FieldMemOperand(backing_store, - FixedArray::kLengthOffset)); - - // x0 alloc_obj pointer to allocated objects (param map, backing - // store, arguments) - // x1 mapped_params number of mapped parameters, min(params, args) - // x2 arg_count number of function arguments - // x4 function function pointer - // x3 arg_count_smi number of function arguments (smi) - // x6 backing_store pointer to backing store (uninit) - // x14 recv_arg pointer to receiver arguments - - Label arguments_loop, arguments_test; - __ Mov(x10, mapped_params); - __ Sub(recv_arg, recv_arg, Operand(x10, LSL, kPointerSizeLog2)); - __ B(&arguments_test); - - __ Bind(&arguments_loop); - __ Sub(recv_arg, recv_arg, kPointerSize); - __ Ldr(x11, MemOperand(recv_arg)); - __ Add(x12, backing_store, Operand(x10, LSL, kPointerSizeLog2)); - __ Str(x11, FieldMemOperand(x12, FixedArray::kHeaderSize)); - __ Add(x10, x10, 1); - - __ Bind(&arguments_test); - __ Cmp(x10, arg_count); - __ B(lt, &arguments_loop); - - __ Ret(); - - // Do the runtime call to allocate the arguments object. - __ Bind(&runtime); - __ Push(function, recv_arg, arg_count_smi); - __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1); -} - - -void ArgumentsAccessStub::GenerateNewStrict(MacroAssembler* masm) { - // Stack layout on entry. - // jssp[0]: number of parameters (tagged) - // jssp[8]: address of receiver argument - // jssp[16]: function - // - // Returns pointer to result object in x0. - - // Get the stub arguments from the frame, and make an untagged copy of the - // parameter count. - Register param_count_smi = x1; - Register params = x2; - Register function = x3; - Register param_count = x13; - __ Pop(param_count_smi, params, function); - __ SmiUntag(param_count, param_count_smi); - - // Test if arguments adaptor needed. - Register caller_fp = x11; - Register caller_ctx = x12; - Label try_allocate, runtime; - __ Ldr(caller_fp, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); - __ Ldr(caller_ctx, MemOperand(caller_fp, - StandardFrameConstants::kContextOffset)); - __ Cmp(caller_ctx, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); - __ B(ne, &try_allocate); - - // x1 param_count_smi number of parameters passed to function (smi) - // x2 params pointer to parameters - // x3 function function pointer - // x11 caller_fp caller's frame pointer - // x13 param_count number of parameters passed to function - - // Patch the argument length and parameters pointer. - __ Ldr(param_count_smi, - MemOperand(caller_fp, - ArgumentsAdaptorFrameConstants::kLengthOffset)); - __ SmiUntag(param_count, param_count_smi); - __ Add(x10, caller_fp, Operand(param_count, LSL, kPointerSizeLog2)); - __ Add(params, x10, StandardFrameConstants::kCallerSPOffset); - - // Try the new space allocation. Start out with computing the size of the - // arguments object and the elements array in words. - Register size = x10; - __ Bind(&try_allocate); - __ Add(size, param_count, FixedArray::kHeaderSize / kPointerSize); - __ Cmp(param_count, 0); - __ CzeroX(size, eq); - __ Add(size, size, Heap::kArgumentsObjectSizeStrict / kPointerSize); - - // Do the allocation of both objects in one go. Assign this to x0, as it will - // be returned to the caller. - Register alloc_obj = x0; - __ Allocate(size, alloc_obj, x11, x12, &runtime, - static_cast(TAG_OBJECT | SIZE_IN_WORDS)); - - // Get the arguments boilerplate from the current (native) context. - Register global_object = x10; - Register global_ctx = x10; - Register args_offset = x4; - __ Ldr(global_object, GlobalObjectMemOperand()); - __ Ldr(global_ctx, FieldMemOperand(global_object, - GlobalObject::kNativeContextOffset)); - __ Ldr(args_offset, - ContextMemOperand(global_ctx, - Context::STRICT_MODE_ARGUMENTS_BOILERPLATE_INDEX)); - - // x0 alloc_obj pointer to allocated objects: parameter array and - // arguments object - // x1 param_count_smi number of parameters passed to function (smi) - // x2 params pointer to parameters - // x3 function function pointer - // x4 args_offset offset to arguments boilerplate - // x13 param_count number of parameters passed to function - - // Copy the JS object part. - __ CopyFields(alloc_obj, args_offset, CPURegList(x5, x6, x7), - JSObject::kHeaderSize / kPointerSize); - - // Set the smi-tagged length as an in-object property. - STATIC_ASSERT(Heap::kArgumentsLengthIndex == 0); - const int kLengthOffset = JSObject::kHeaderSize + - Heap::kArgumentsLengthIndex * kPointerSize; - __ Str(param_count_smi, FieldMemOperand(alloc_obj, kLengthOffset)); - - // If there are no actual arguments, we're done. - Label done; - __ Cbz(param_count, &done); - - // Set up the elements pointer in the allocated arguments object and - // initialize the header in the elements fixed array. - Register elements = x5; - __ Add(elements, alloc_obj, Heap::kArgumentsObjectSizeStrict); - __ Str(elements, FieldMemOperand(alloc_obj, JSObject::kElementsOffset)); - __ LoadRoot(x10, Heap::kFixedArrayMapRootIndex); - __ Str(x10, FieldMemOperand(elements, FixedArray::kMapOffset)); - __ Str(param_count_smi, FieldMemOperand(elements, FixedArray::kLengthOffset)); - - // x0 alloc_obj pointer to allocated objects: parameter array and - // arguments object - // x1 param_count_smi number of parameters passed to function (smi) - // x2 params pointer to parameters - // x3 function function pointer - // x4 array pointer to array slot (uninit) - // x5 elements pointer to elements array of alloc_obj - // x13 param_count number of parameters passed to function - - // Copy the fixed array slots. - Label loop; - Register array = x4; - // Set up pointer to first array slot. - __ Add(array, elements, FixedArray::kHeaderSize - kHeapObjectTag); - - __ Bind(&loop); - // Pre-decrement the parameters pointer by kPointerSize on each iteration. - // Pre-decrement in order to skip receiver. - __ Ldr(x10, MemOperand(params, -kPointerSize, PreIndex)); - // Post-increment elements by kPointerSize on each iteration. - __ Str(x10, MemOperand(array, kPointerSize, PostIndex)); - __ Sub(param_count, param_count, 1); - __ Cbnz(param_count, &loop); - - // Return from stub. - __ Bind(&done); - __ Ret(); - - // Do the runtime call to allocate the arguments object. - __ Bind(&runtime); - __ Push(function, params, param_count_smi); - __ TailCallRuntime(Runtime::kNewStrictArgumentsFast, 3, 1); -} - - -void RegExpExecStub::Generate(MacroAssembler* masm) { -#ifdef V8_INTERPRETED_REGEXP - __ TailCallRuntime(Runtime::kRegExpExec, 4, 1); -#else // V8_INTERPRETED_REGEXP - - // Stack frame on entry. - // jssp[0]: last_match_info (expected JSArray) - // jssp[8]: previous index - // jssp[16]: subject string - // jssp[24]: JSRegExp object - Label runtime; - - // Use of registers for this function. - - // Variable registers: - // x10-x13 used as scratch registers - // w0 string_type type of subject string - // x2 jsstring_length subject string length - // x3 jsregexp_object JSRegExp object - // w4 string_encoding ASCII or UC16 - // w5 sliced_string_offset if the string is a SlicedString - // offset to the underlying string - // w6 string_representation groups attributes of the string: - // - is a string - // - type of the string - // - is a short external string - Register string_type = w0; - Register jsstring_length = x2; - Register jsregexp_object = x3; - Register string_encoding = w4; - Register sliced_string_offset = w5; - Register string_representation = w6; - - // These are in callee save registers and will be preserved by the call - // to the native RegExp code, as this code is called using the normal - // C calling convention. When calling directly from generated code the - // native RegExp code will not do a GC and therefore the content of - // these registers are safe to use after the call. - - // x19 subject subject string - // x20 regexp_data RegExp data (FixedArray) - // x21 last_match_info_elements info relative to the last match - // (FixedArray) - // x22 code_object generated regexp code - Register subject = x19; - Register regexp_data = x20; - Register last_match_info_elements = x21; - Register code_object = x22; - - // TODO(jbramley): Is it necessary to preserve these? I don't think ARM does. - CPURegList used_callee_saved_registers(subject, - regexp_data, - last_match_info_elements, - code_object); - __ PushCPURegList(used_callee_saved_registers); - - // Stack frame. - // jssp[0] : x19 - // jssp[8] : x20 - // jssp[16]: x21 - // jssp[24]: x22 - // jssp[32]: last_match_info (JSArray) - // jssp[40]: previous index - // jssp[48]: subject string - // jssp[56]: JSRegExp object - - const int kLastMatchInfoOffset = 4 * kPointerSize; - const int kPreviousIndexOffset = 5 * kPointerSize; - const int kSubjectOffset = 6 * kPointerSize; - const int kJSRegExpOffset = 7 * kPointerSize; - - // Ensure that a RegExp stack is allocated. - Isolate* isolate = masm->isolate(); - ExternalReference address_of_regexp_stack_memory_address = - ExternalReference::address_of_regexp_stack_memory_address(isolate); - ExternalReference address_of_regexp_stack_memory_size = - ExternalReference::address_of_regexp_stack_memory_size(isolate); - __ Mov(x10, Operand(address_of_regexp_stack_memory_size)); - __ Ldr(x10, MemOperand(x10)); - __ Cbz(x10, &runtime); - - // Check that the first argument is a JSRegExp object. - ASSERT(jssp.Is(__ StackPointer())); - __ Peek(jsregexp_object, kJSRegExpOffset); - __ JumpIfSmi(jsregexp_object, &runtime); - __ JumpIfNotObjectType(jsregexp_object, x10, x10, JS_REGEXP_TYPE, &runtime); - - // Check that the RegExp has been compiled (data contains a fixed array). - __ Ldr(regexp_data, FieldMemOperand(jsregexp_object, JSRegExp::kDataOffset)); - if (FLAG_debug_code) { - STATIC_ASSERT(kSmiTag == 0); - __ Tst(regexp_data, kSmiTagMask); - __ Check(ne, kUnexpectedTypeForRegExpDataFixedArrayExpected); - __ CompareObjectType(regexp_data, x10, x10, FIXED_ARRAY_TYPE); - __ Check(eq, kUnexpectedTypeForRegExpDataFixedArrayExpected); - } - - // Check the type of the RegExp. Only continue if type is JSRegExp::IRREGEXP. - __ Ldr(x10, FieldMemOperand(regexp_data, JSRegExp::kDataTagOffset)); - __ Cmp(x10, Operand(Smi::FromInt(JSRegExp::IRREGEXP))); - __ B(ne, &runtime); - - // Check that the number of captures fit in the static offsets vector buffer. - // We have always at least one capture for the whole match, plus additional - // ones due to capturing parentheses. A capture takes 2 registers. - // The number of capture registers then is (number_of_captures + 1) * 2. - __ Ldrsw(x10, - UntagSmiFieldMemOperand(regexp_data, - JSRegExp::kIrregexpCaptureCountOffset)); - // Check (number_of_captures + 1) * 2 <= offsets vector size - // number_of_captures * 2 <= offsets vector size - 2 - STATIC_ASSERT(Isolate::kJSRegexpStaticOffsetsVectorSize >= 2); - __ Add(x10, x10, x10); - __ Cmp(x10, Isolate::kJSRegexpStaticOffsetsVectorSize - 2); - __ B(hi, &runtime); - - // Initialize offset for possibly sliced string. - __ Mov(sliced_string_offset, 0); - - ASSERT(jssp.Is(__ StackPointer())); - __ Peek(subject, kSubjectOffset); - __ JumpIfSmi(subject, &runtime); - - __ Ldr(x10, FieldMemOperand(subject, HeapObject::kMapOffset)); - __ Ldrb(string_type, FieldMemOperand(x10, Map::kInstanceTypeOffset)); - - __ Ldr(jsstring_length, FieldMemOperand(subject, String::kLengthOffset)); - - // Handle subject string according to its encoding and representation: - // (1) Sequential string? If yes, go to (5). - // (2) Anything but sequential or cons? If yes, go to (6). - // (3) Cons string. If the string is flat, replace subject with first string. - // Otherwise bailout. - // (4) Is subject external? If yes, go to (7). - // (5) Sequential string. Load regexp code according to encoding. - // (E) Carry on. - /// [...] - - // Deferred code at the end of the stub: - // (6) Not a long external string? If yes, go to (8). - // (7) External string. Make it, offset-wise, look like a sequential string. - // Go to (5). - // (8) Short external string or not a string? If yes, bail out to runtime. - // (9) Sliced string. Replace subject with parent. Go to (4). - - Label check_underlying; // (4) - Label seq_string; // (5) - Label not_seq_nor_cons; // (6) - Label external_string; // (7) - Label not_long_external; // (8) - - // (1) Sequential string? If yes, go to (5). - __ And(string_representation, - string_type, - kIsNotStringMask | - kStringRepresentationMask | - kShortExternalStringMask); - // We depend on the fact that Strings of type - // SeqString and not ShortExternalString are defined - // by the following pattern: - // string_type: 0XX0 XX00 - // ^ ^ ^^ - // | | || - // | | is a SeqString - // | is not a short external String - // is a String - STATIC_ASSERT((kStringTag | kSeqStringTag) == 0); - STATIC_ASSERT(kShortExternalStringTag != 0); - __ Cbz(string_representation, &seq_string); // Go to (5). - - // (2) Anything but sequential or cons? If yes, go to (6). - STATIC_ASSERT(kConsStringTag < kExternalStringTag); - STATIC_ASSERT(kSlicedStringTag > kExternalStringTag); - STATIC_ASSERT(kIsNotStringMask > kExternalStringTag); - STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag); - __ Cmp(string_representation, kExternalStringTag); - __ B(ge, ¬_seq_nor_cons); // Go to (6). - - // (3) Cons string. Check that it's flat. - __ Ldr(x10, FieldMemOperand(subject, ConsString::kSecondOffset)); - __ JumpIfNotRoot(x10, Heap::kempty_stringRootIndex, &runtime); - // Replace subject with first string. - __ Ldr(subject, FieldMemOperand(subject, ConsString::kFirstOffset)); - - // (4) Is subject external? If yes, go to (7). - __ Bind(&check_underlying); - // Reload the string type. - __ Ldr(x10, FieldMemOperand(subject, HeapObject::kMapOffset)); - __ Ldrb(string_type, FieldMemOperand(x10, Map::kInstanceTypeOffset)); - STATIC_ASSERT(kSeqStringTag == 0); - // The underlying external string is never a short external string. - STATIC_CHECK(ExternalString::kMaxShortLength < ConsString::kMinLength); - STATIC_CHECK(ExternalString::kMaxShortLength < SlicedString::kMinLength); - __ TestAndBranchIfAnySet(string_type.X(), - kStringRepresentationMask, - &external_string); // Go to (7). - - // (5) Sequential string. Load regexp code according to encoding. - __ Bind(&seq_string); - - // Check that the third argument is a positive smi less than the subject - // string length. A negative value will be greater (unsigned comparison). - ASSERT(jssp.Is(__ StackPointer())); - __ Peek(x10, kPreviousIndexOffset); - __ JumpIfNotSmi(x10, &runtime); - __ Cmp(jsstring_length, x10); - __ B(ls, &runtime); - - // Argument 2 (x1): We need to load argument 2 (the previous index) into x1 - // before entering the exit frame. - __ SmiUntag(x1, x10); - - // The third bit determines the string encoding in string_type. - STATIC_ASSERT(kOneByteStringTag == 0x04); - STATIC_ASSERT(kTwoByteStringTag == 0x00); - STATIC_ASSERT(kStringEncodingMask == 0x04); - - // Find the code object based on the assumptions above. - // kDataAsciiCodeOffset and kDataUC16CodeOffset are adjacent, adds an offset - // of kPointerSize to reach the latter. - ASSERT_EQ(JSRegExp::kDataAsciiCodeOffset + kPointerSize, - JSRegExp::kDataUC16CodeOffset); - __ Mov(x10, kPointerSize); - // We will need the encoding later: ASCII = 0x04 - // UC16 = 0x00 - __ Ands(string_encoding, string_type, kStringEncodingMask); - __ CzeroX(x10, ne); - __ Add(x10, regexp_data, x10); - __ Ldr(code_object, FieldMemOperand(x10, JSRegExp::kDataAsciiCodeOffset)); - - // (E) Carry on. String handling is done. - - // Check that the irregexp code has been generated for the actual string - // encoding. If it has, the field contains a code object otherwise it contains - // a smi (code flushing support). - __ JumpIfSmi(code_object, &runtime); - - // All checks done. Now push arguments for native regexp code. - __ IncrementCounter(isolate->counters()->regexp_entry_native(), 1, - x10, - x11); - - // Isolates: note we add an additional parameter here (isolate pointer). - __ EnterExitFrame(false, x10, 1); - ASSERT(csp.Is(__ StackPointer())); - - // We have 9 arguments to pass to the regexp code, therefore we have to pass - // one on the stack and the rest as registers. - - // Note that the placement of the argument on the stack isn't standard - // AAPCS64: - // csp[0]: Space for the return address placed by DirectCEntryStub. - // csp[8]: Argument 9, the current isolate address. - - __ Mov(x10, Operand(ExternalReference::isolate_address(isolate))); - __ Poke(x10, kPointerSize); - - Register length = w11; - Register previous_index_in_bytes = w12; - Register start = x13; - - // Load start of the subject string. - __ Add(start, subject, SeqString::kHeaderSize - kHeapObjectTag); - // Load the length from the original subject string from the previous stack - // frame. Therefore we have to use fp, which points exactly to two pointer - // sizes below the previous sp. (Because creating a new stack frame pushes - // the previous fp onto the stack and decrements sp by 2 * kPointerSize.) - __ Ldr(subject, MemOperand(fp, kSubjectOffset + 2 * kPointerSize)); - __ Ldr(length, UntagSmiFieldMemOperand(subject, String::kLengthOffset)); - - // Handle UC16 encoding, two bytes make one character. - // string_encoding: if ASCII: 0x04 - // if UC16: 0x00 - STATIC_ASSERT(kStringEncodingMask == 0x04); - __ Ubfx(string_encoding, string_encoding, 2, 1); - __ Eor(string_encoding, string_encoding, 1); - // string_encoding: if ASCII: 0 - // if UC16: 1 - - // Convert string positions from characters to bytes. - // Previous index is in x1. - __ Lsl(previous_index_in_bytes, w1, string_encoding); - __ Lsl(length, length, string_encoding); - __ Lsl(sliced_string_offset, sliced_string_offset, string_encoding); - - // Argument 1 (x0): Subject string. - __ Mov(x0, subject); - - // Argument 2 (x1): Previous index, already there. - - // Argument 3 (x2): Get the start of input. - // Start of input = start of string + previous index + substring offset - // (0 if the string - // is not sliced). - __ Add(w10, previous_index_in_bytes, sliced_string_offset); - __ Add(x2, start, Operand(w10, UXTW)); - - // Argument 4 (x3): - // End of input = start of input + (length of input - previous index) - __ Sub(w10, length, previous_index_in_bytes); - __ Add(x3, x2, Operand(w10, UXTW)); - - // Argument 5 (x4): static offsets vector buffer. - __ Mov(x4, - Operand(ExternalReference::address_of_static_offsets_vector(isolate))); - - // Argument 6 (x5): Set the number of capture registers to zero to force - // global regexps to behave as non-global. This stub is not used for global - // regexps. - __ Mov(x5, 0); - - // Argument 7 (x6): Start (high end) of backtracking stack memory area. - __ Mov(x10, Operand(address_of_regexp_stack_memory_address)); - __ Ldr(x10, MemOperand(x10)); - __ Mov(x11, Operand(address_of_regexp_stack_memory_size)); - __ Ldr(x11, MemOperand(x11)); - __ Add(x6, x10, x11); - - // Argument 8 (x7): Indicate that this is a direct call from JavaScript. - __ Mov(x7, 1); - - // Locate the code entry and call it. - __ Add(code_object, code_object, Code::kHeaderSize - kHeapObjectTag); - DirectCEntryStub stub; - stub.GenerateCall(masm, code_object); - - __ LeaveExitFrame(false, x10, true); - - // The generated regexp code returns an int32 in w0. - Label failure, exception; - __ CompareAndBranch(w0, NativeRegExpMacroAssembler::FAILURE, eq, &failure); - __ CompareAndBranch(w0, - NativeRegExpMacroAssembler::EXCEPTION, - eq, - &exception); - __ CompareAndBranch(w0, NativeRegExpMacroAssembler::RETRY, eq, &runtime); - - // Success: process the result from the native regexp code. - Register number_of_capture_registers = x12; - - // Calculate number of capture registers (number_of_captures + 1) * 2 - // and store it in the last match info. - __ Ldrsw(x10, - UntagSmiFieldMemOperand(regexp_data, - JSRegExp::kIrregexpCaptureCountOffset)); - __ Add(x10, x10, x10); - __ Add(number_of_capture_registers, x10, 2); - - // Check that the fourth object is a JSArray object. - ASSERT(jssp.Is(__ StackPointer())); - __ Peek(x10, kLastMatchInfoOffset); - __ JumpIfSmi(x10, &runtime); - __ JumpIfNotObjectType(x10, x11, x11, JS_ARRAY_TYPE, &runtime); - - // Check that the JSArray is the fast case. - __ Ldr(last_match_info_elements, - FieldMemOperand(x10, JSArray::kElementsOffset)); - __ Ldr(x10, - FieldMemOperand(last_match_info_elements, HeapObject::kMapOffset)); - __ JumpIfNotRoot(x10, Heap::kFixedArrayMapRootIndex, &runtime); - - // Check that the last match info has space for the capture registers and the - // additional information (overhead). - // (number_of_captures + 1) * 2 + overhead <= last match info size - // (number_of_captures * 2) + 2 + overhead <= last match info size - // number_of_capture_registers + overhead <= last match info size - __ Ldrsw(x10, - UntagSmiFieldMemOperand(last_match_info_elements, - FixedArray::kLengthOffset)); - __ Add(x11, number_of_capture_registers, RegExpImpl::kLastMatchOverhead); - __ Cmp(x11, x10); - __ B(gt, &runtime); - - // Store the capture count. - __ SmiTag(x10, number_of_capture_registers); - __ Str(x10, - FieldMemOperand(last_match_info_elements, - RegExpImpl::kLastCaptureCountOffset)); - // Store last subject and last input. - __ Str(subject, - FieldMemOperand(last_match_info_elements, - RegExpImpl::kLastSubjectOffset)); - // Use x10 as the subject string in order to only need - // one RecordWriteStub. - __ Mov(x10, subject); - __ RecordWriteField(last_match_info_elements, - RegExpImpl::kLastSubjectOffset, - x10, - x11, - kLRHasNotBeenSaved, - kDontSaveFPRegs); - __ Str(subject, - FieldMemOperand(last_match_info_elements, - RegExpImpl::kLastInputOffset)); - __ Mov(x10, subject); - __ RecordWriteField(last_match_info_elements, - RegExpImpl::kLastInputOffset, - x10, - x11, - kLRHasNotBeenSaved, - kDontSaveFPRegs); - - Register last_match_offsets = x13; - Register offsets_vector_index = x14; - Register current_offset = x15; - - // Get the static offsets vector filled by the native regexp code - // and fill the last match info. - ExternalReference address_of_static_offsets_vector = - ExternalReference::address_of_static_offsets_vector(isolate); - __ Mov(offsets_vector_index, Operand(address_of_static_offsets_vector)); - - Label next_capture, done; - // Capture register counter starts from number of capture registers and - // iterates down to zero (inclusive). - __ Add(last_match_offsets, - last_match_info_elements, - RegExpImpl::kFirstCaptureOffset - kHeapObjectTag); - __ Bind(&next_capture); - __ Subs(number_of_capture_registers, number_of_capture_registers, 2); - __ B(mi, &done); - // Read two 32 bit values from the static offsets vector buffer into - // an X register - __ Ldr(current_offset, - MemOperand(offsets_vector_index, kWRegSizeInBytes * 2, PostIndex)); - // Store the smi values in the last match info. - __ SmiTag(x10, current_offset); - // Clearing the 32 bottom bits gives us a Smi. - STATIC_ASSERT(kSmiShift == 32); - __ And(x11, current_offset, ~kWRegMask); - __ Stp(x10, - x11, - MemOperand(last_match_offsets, kXRegSizeInBytes * 2, PostIndex)); - __ B(&next_capture); - __ Bind(&done); - - // Return last match info. - __ Peek(x0, kLastMatchInfoOffset); - __ PopCPURegList(used_callee_saved_registers); - // Drop the 4 arguments of the stub from the stack. - __ Drop(4); - __ Ret(); - - __ Bind(&exception); - Register exception_value = x0; - // A stack overflow (on the backtrack stack) may have occured - // in the RegExp code but no exception has been created yet. - // If there is no pending exception, handle that in the runtime system. - __ Mov(x10, Operand(isolate->factory()->the_hole_value())); - __ Mov(x11, - Operand(ExternalReference(Isolate::kPendingExceptionAddress, - isolate))); - __ Ldr(exception_value, MemOperand(x11)); - __ Cmp(x10, exception_value); - __ B(eq, &runtime); - - __ Str(x10, MemOperand(x11)); // Clear pending exception. - - // Check if the exception is a termination. If so, throw as uncatchable. - Label termination_exception; - __ JumpIfRoot(exception_value, - Heap::kTerminationExceptionRootIndex, - &termination_exception); - - __ Throw(exception_value, x10, x11, x12, x13); - - __ Bind(&termination_exception); - __ ThrowUncatchable(exception_value, x10, x11, x12, x13); - - __ Bind(&failure); - __ Mov(x0, Operand(masm->isolate()->factory()->null_value())); - __ PopCPURegList(used_callee_saved_registers); - // Drop the 4 arguments of the stub from the stack. - __ Drop(4); - __ Ret(); - - __ Bind(&runtime); - __ PopCPURegList(used_callee_saved_registers); - __ TailCallRuntime(Runtime::kRegExpExec, 4, 1); - - // Deferred code for string handling. - // (6) Not a long external string? If yes, go to (8). - __ Bind(¬_seq_nor_cons); - // Compare flags are still set. - __ B(ne, ¬_long_external); // Go to (8). - - // (7) External string. Make it, offset-wise, look like a sequential string. - __ Bind(&external_string); - if (masm->emit_debug_code()) { - // Assert that we do not have a cons or slice (indirect strings) here. - // Sequential strings have already been ruled out. - __ Ldr(x10, FieldMemOperand(subject, HeapObject::kMapOffset)); - __ Ldrb(x10, FieldMemOperand(x10, Map::kInstanceTypeOffset)); - __ Tst(x10, kIsIndirectStringMask); - __ Check(eq, kExternalStringExpectedButNotFound); - __ And(x10, x10, kStringRepresentationMask); - __ Cmp(x10, 0); - __ Check(ne, kExternalStringExpectedButNotFound); - } - __ Ldr(subject, - FieldMemOperand(subject, ExternalString::kResourceDataOffset)); - // Move the pointer so that offset-wise, it looks like a sequential string. - STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqOneByteString::kHeaderSize); - __ Sub(subject, subject, SeqTwoByteString::kHeaderSize - kHeapObjectTag); - __ B(&seq_string); // Go to (5). - - // (8) If this is a short external string or not a string, bail out to - // runtime. - __ Bind(¬_long_external); - STATIC_ASSERT(kShortExternalStringTag != 0); - __ TestAndBranchIfAnySet(string_representation, - kShortExternalStringMask | kIsNotStringMask, - &runtime); - - // (9) Sliced string. Replace subject with parent. - __ Ldr(sliced_string_offset, - UntagSmiFieldMemOperand(subject, SlicedString::kOffsetOffset)); - __ Ldr(subject, FieldMemOperand(subject, SlicedString::kParentOffset)); - __ B(&check_underlying); // Go to (4). -#endif -} - - -// TODO(jbramley): Don't use static registers here, but take them as arguments. -static void GenerateRecordCallTarget(MacroAssembler* masm) { - ASM_LOCATION("GenerateRecordCallTarget"); - // Cache the called function in a feedback vector slot. Cache states are - // uninitialized, monomorphic (indicated by a JSFunction), and megamorphic. - // x0 : number of arguments to the construct function - // x1 : the function to call - // x2 : feedback vector - // x3 : slot in feedback vector (smi) - Label check_array, initialize_array, initialize_non_array, megamorphic, done; - - ASSERT_EQ(*TypeFeedbackInfo::MegamorphicSentinel(masm->isolate()), - masm->isolate()->heap()->undefined_value()); - Heap::RootListIndex kMegamorphicRootIndex = Heap::kUndefinedValueRootIndex; - ASSERT_EQ(*TypeFeedbackInfo::UninitializedSentinel(masm->isolate()), - masm->isolate()->heap()->the_hole_value()); - Heap::RootListIndex kUninitializedRootIndex = Heap::kTheHoleValueRootIndex; - ASSERT_EQ(*TypeFeedbackInfo::PremonomorphicSentinel(masm->isolate()), - masm->isolate()->heap()->null_value()); - Heap::RootListIndex kPremonomorphicRootIndex = Heap::kNullValueRootIndex; - - // Load the cache state. - __ Add(x4, x2, Operand::UntagSmiAndScale(x3, kPointerSizeLog2)); - __ Ldr(x4, FieldMemOperand(x4, FixedArray::kHeaderSize)); - - // A monomorphic cache hit or an already megamorphic state: invoke the - // function without changing the state. - __ Cmp(x4, x1); - __ B(eq, &done); - __ JumpIfRoot(x4, kMegamorphicRootIndex, &done); - - // Check if we're dealing with the Array function or not. - __ LoadArrayFunction(x5); - __ Cmp(x1, x5); - __ B(eq, &check_array); - - // Non-array cache: Check the cache state. - __ JumpIfRoot(x4, kPremonomorphicRootIndex, &initialize_non_array); - __ JumpIfNotRoot(x4, kUninitializedRootIndex, &megamorphic); - - // Non-array cache: Uninitialized -> premonomorphic. The sentinel is an - // immortal immovable object (null) so no write-barrier is needed. - __ Add(x4, x2, Operand::UntagSmiAndScale(x3, kPointerSizeLog2)); - __ LoadRoot(x10, kPremonomorphicRootIndex); - __ Str(x10, FieldMemOperand(x4, FixedArray::kHeaderSize)); - __ B(&done); - - // Array cache: Check the cache state to see if we're in a monomorphic - // state where the state object is an AllocationSite object. - __ Bind(&check_array); - __ Ldr(x5, FieldMemOperand(x4, AllocationSite::kMapOffset)); - __ JumpIfRoot(x5, Heap::kAllocationSiteMapRootIndex, &done); - - // Array cache: Uninitialized or premonomorphic -> monomorphic. - __ JumpIfRoot(x4, kUninitializedRootIndex, &initialize_array); - __ JumpIfRoot(x4, kPremonomorphicRootIndex, &initialize_array); - - // Both caches: Monomorphic -> megamorphic. The sentinel is an - // immortal immovable object (undefined) so no write-barrier is needed. - __ Bind(&megamorphic); - __ Add(x4, x2, Operand::UntagSmiAndScale(x3, kPointerSizeLog2)); - __ LoadRoot(x10, kMegamorphicRootIndex); - __ Str(x10, FieldMemOperand(x4, FixedArray::kHeaderSize)); - __ B(&done); - - // Array cache: Uninitialized or premonomorphic -> monomorphic. - __ Bind(&initialize_array); - { - FrameScope scope(masm, StackFrame::INTERNAL); - CreateAllocationSiteStub create_stub; - - // Arguments register must be smi-tagged to call out. - __ SmiTag(x0); - __ Push(x0, x1, x2, x3); - - __ CallStub(&create_stub); - - __ Pop(x3, x2, x1, x0); - __ SmiUntag(x0); - } - __ B(&done); - - // Non-array cache: Premonomorphic -> monomorphic. - __ Bind(&initialize_non_array); - __ Add(x4, x2, Operand::UntagSmiAndScale(x3, kPointerSizeLog2)); - // TODO(all): Does the value need to be left in x4? If not, FieldMemOperand - // could be used to avoid this add. - __ Add(x4, x4, FixedArray::kHeaderSize - kHeapObjectTag); - __ Str(x1, MemOperand(x4, 0)); - - __ Push(x4, x2, x1); - __ RecordWrite(x2, x4, x1, kLRHasNotBeenSaved, kDontSaveFPRegs, - EMIT_REMEMBERED_SET, OMIT_SMI_CHECK); - __ Pop(x1, x2, x4); - - // TODO(all): Are x4, x2 and x1 outputs? This isn't clear. - __ Bind(&done); -} - - -void CallFunctionStub::Generate(MacroAssembler* masm) { - ASM_LOCATION("CallFunctionStub::Generate"); - // x1 function the function to call - // x2 : feedback vector - // x3 : slot in feedback vector (smi) (if x2 is not undefined) - Register function = x1; - Register cache_cell = x2; - Register slot = x3; - Register type = x4; - Label slow, non_function, wrap, cont; - - // TODO(jbramley): This function has a lot of unnamed registers. Name them, - // and tidy things up a bit. - - if (NeedsChecks()) { - // Check that the function is really a JavaScript function. - __ JumpIfSmi(function, &non_function); - - // Goto slow case if we do not have a function. - __ JumpIfNotObjectType(function, x10, type, JS_FUNCTION_TYPE, &slow); - - if (RecordCallTarget()) { - GenerateRecordCallTarget(masm); - } - } - - // Fast-case: Invoke the function now. - // x1 function pushed function - ParameterCount actual(argc_); - - if (CallAsMethod()) { - if (NeedsChecks()) { - // Do not transform the receiver for strict mode functions. - __ Ldr(x3, FieldMemOperand(x1, JSFunction::kSharedFunctionInfoOffset)); - __ Ldr(w4, FieldMemOperand(x3, SharedFunctionInfo::kCompilerHintsOffset)); - __ Tbnz(w4, SharedFunctionInfo::kStrictModeFunction, &cont); - - // Do not transform the receiver for native (Compilerhints already in x3). - __ Tbnz(w4, SharedFunctionInfo::kNative, &cont); - } - - // Compute the receiver in non-strict mode. - __ Peek(x3, argc_ * kPointerSize); - - if (NeedsChecks()) { - __ JumpIfSmi(x3, &wrap); - __ JumpIfObjectType(x3, x10, type, FIRST_SPEC_OBJECT_TYPE, &wrap, lt); - } else { - __ B(&wrap); - } - - __ Bind(&cont); - } - __ InvokeFunction(function, - actual, - JUMP_FUNCTION, - NullCallWrapper()); - - if (NeedsChecks()) { - // Slow-case: Non-function called. - __ Bind(&slow); - if (RecordCallTarget()) { - // If there is a call target cache, mark it megamorphic in the - // non-function case. MegamorphicSentinel is an immortal immovable object - // (undefined) so no write barrier is needed. - ASSERT_EQ(*TypeFeedbackInfo::MegamorphicSentinel(masm->isolate()), - masm->isolate()->heap()->undefined_value()); - __ Add(x12, cache_cell, Operand::UntagSmiAndScale(slot, - kPointerSizeLog2)); - __ LoadRoot(x11, Heap::kUndefinedValueRootIndex); - __ Str(x11, FieldMemOperand(x12, FixedArray::kHeaderSize)); - } - // Check for function proxy. - // x10 : function type. - __ CompareAndBranch(type, JS_FUNCTION_PROXY_TYPE, ne, &non_function); - __ Push(function); // put proxy as additional argument - __ Mov(x0, argc_ + 1); - __ Mov(x2, 0); - __ GetBuiltinFunction(x1, Builtins::CALL_FUNCTION_PROXY); - { - Handle adaptor = - masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(); - __ Jump(adaptor, RelocInfo::CODE_TARGET); - } - - // CALL_NON_FUNCTION expects the non-function callee as receiver (instead - // of the original receiver from the call site). - __ Bind(&non_function); - __ Poke(function, argc_ * kXRegSizeInBytes); - __ Mov(x0, argc_); // Set up the number of arguments. - __ Mov(x2, 0); - __ GetBuiltinFunction(function, Builtins::CALL_NON_FUNCTION); - __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(), - RelocInfo::CODE_TARGET); - } - - if (CallAsMethod()) { - __ Bind(&wrap); - // Wrap the receiver and patch it back onto the stack. - { FrameScope frame_scope(masm, StackFrame::INTERNAL); - __ Push(x1, x3); - __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); - __ Pop(x1); - } - __ Poke(x0, argc_ * kPointerSize); - __ B(&cont); - } -} - - -void CallConstructStub::Generate(MacroAssembler* masm) { - ASM_LOCATION("CallConstructStub::Generate"); - // x0 : number of arguments - // x1 : the function to call - // x2 : feedback vector - // x3 : slot in feedback vector (smi) (if r2 is not undefined) - Register function = x1; - Label slow, non_function_call; - - // Check that the function is not a smi. - __ JumpIfSmi(function, &non_function_call); - // Check that the function is a JSFunction. - Register object_type = x10; - __ JumpIfNotObjectType(function, object_type, object_type, JS_FUNCTION_TYPE, - &slow); - - if (RecordCallTarget()) { - GenerateRecordCallTarget(masm); - } - - // Jump to the function-specific construct stub. - Register jump_reg = x4; - Register shared_func_info = jump_reg; - Register cons_stub = jump_reg; - Register cons_stub_code = jump_reg; - __ Ldr(shared_func_info, - FieldMemOperand(function, JSFunction::kSharedFunctionInfoOffset)); - __ Ldr(cons_stub, - FieldMemOperand(shared_func_info, - SharedFunctionInfo::kConstructStubOffset)); - __ Add(cons_stub_code, cons_stub, Code::kHeaderSize - kHeapObjectTag); - __ Br(cons_stub_code); - - Label do_call; - __ Bind(&slow); - __ Cmp(object_type, JS_FUNCTION_PROXY_TYPE); - __ B(ne, &non_function_call); - __ GetBuiltinFunction(x1, Builtins::CALL_FUNCTION_PROXY_AS_CONSTRUCTOR); - __ B(&do_call); - - __ Bind(&non_function_call); - __ GetBuiltinFunction(x1, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR); - - __ Bind(&do_call); - // Set expected number of arguments to zero (not changing x0). - __ Mov(x2, 0); - __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(), - RelocInfo::CODE_TARGET); -} - - -void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) { - // If the receiver is a smi trigger the non-string case. - __ JumpIfSmi(object_, receiver_not_string_); - - // Fetch the instance type of the receiver into result register. - __ Ldr(result_, FieldMemOperand(object_, HeapObject::kMapOffset)); - __ Ldrb(result_, FieldMemOperand(result_, Map::kInstanceTypeOffset)); - - // If the receiver is not a string trigger the non-string case. - __ TestAndBranchIfAnySet(result_, kIsNotStringMask, 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. - __ Ldrsw(result_, UntagSmiFieldMemOperand(object_, String::kLengthOffset)); - __ Cmp(result_, Operand::UntagSmi(index_)); - __ B(ls, index_out_of_range_); - - __ SmiUntag(index_); - - StringCharLoadGenerator::Generate(masm, - object_, - index_, - result_, - &call_runtime_); - __ SmiTag(result_); - __ Bind(&exit_); -} - - -void StringCharCodeAtGenerator::GenerateSlow( - MacroAssembler* masm, - const RuntimeCallHelper& call_helper) { - __ Abort(kUnexpectedFallthroughToCharCodeAtSlowCase); - - __ Bind(&index_not_smi_); - // If index is a heap number, try converting it to an integer. - __ CheckMap(index_, - result_, - Heap::kHeapNumberMapRootIndex, - index_not_number_, - DONT_DO_SMI_CHECK); - call_helper.BeforeCall(masm); - // Save object_ on the stack and pass index_ as argument for runtime call. - __ Push(object_, index_); - if (index_flags_ == STRING_INDEX_IS_NUMBER) { - __ CallRuntime(Runtime::kNumberToIntegerMapMinusZero, 1); - } else { - ASSERT(index_flags_ == STRING_INDEX_IS_ARRAY_INDEX); - // NumberToSmi discards numbers that are not exact integers. - __ CallRuntime(Runtime::kNumberToSmi, 1); - } - // Save the conversion result before the pop instructions below - // have a chance to overwrite it. - __ Mov(index_, x0); - __ Pop(object_); - // Reload the instance type. - __ Ldr(result_, FieldMemOperand(object_, HeapObject::kMapOffset)); - __ Ldrb(result_, FieldMemOperand(result_, Map::kInstanceTypeOffset)); - call_helper.AfterCall(masm); - - // If index is still not a smi, it must be out of range. - __ JumpIfNotSmi(index_, index_out_of_range_); - // Otherwise, return to the fast path. - __ B(&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); - __ SmiTag(index_); - __ Push(object_, index_); - __ CallRuntime(Runtime::kStringCharCodeAt, 2); - __ Mov(result_, x0); - call_helper.AfterCall(masm); - __ B(&exit_); - - __ Abort(kUnexpectedFallthroughFromCharCodeAtSlowCase); -} - - -void StringCharFromCodeGenerator::GenerateFast(MacroAssembler* masm) { - __ JumpIfNotSmi(code_, &slow_case_); - __ Cmp(code_, Operand(Smi::FromInt(String::kMaxOneByteCharCode))); - __ B(hi, &slow_case_); - - __ LoadRoot(result_, Heap::kSingleCharacterStringCacheRootIndex); - // At this point code register contains smi tagged ASCII char code. - STATIC_ASSERT(kSmiShift > kPointerSizeLog2); - __ Add(result_, result_, Operand(code_, LSR, kSmiShift - kPointerSizeLog2)); - __ Ldr(result_, FieldMemOperand(result_, FixedArray::kHeaderSize)); - __ JumpIfRoot(result_, Heap::kUndefinedValueRootIndex, &slow_case_); - __ Bind(&exit_); -} - - -void StringCharFromCodeGenerator::GenerateSlow( - MacroAssembler* masm, - const RuntimeCallHelper& call_helper) { - __ Abort(kUnexpectedFallthroughToCharFromCodeSlowCase); - - __ Bind(&slow_case_); - call_helper.BeforeCall(masm); - __ Push(code_); - __ CallRuntime(Runtime::kCharFromCode, 1); - __ Mov(result_, x0); - call_helper.AfterCall(masm); - __ B(&exit_); - - __ Abort(kUnexpectedFallthroughFromCharFromCodeSlowCase); -} - - -void ICCompareStub::GenerateSmis(MacroAssembler* masm) { - // Inputs are in x0 (lhs) and x1 (rhs). - ASSERT(state_ == CompareIC::SMI); - ASM_LOCATION("ICCompareStub[Smis]"); - Label miss; - // Bail out (to 'miss') unless both x0 and x1 are smis. - __ JumpIfEitherNotSmi(x0, x1, &miss); - - // TODO(jbramley): Why do we only set the flags for EQ? - if (GetCondition() == eq) { - // For equality we do not care about the sign of the result. - __ Subs(x0, x0, x1); - } else { - // Untag before subtracting to avoid handling overflow. - __ SmiUntag(x1); - __ Sub(x0, x1, Operand::UntagSmi(x0)); - } - __ Ret(); - - __ Bind(&miss); - GenerateMiss(masm); -} - - -void ICCompareStub::GenerateNumbers(MacroAssembler* masm) { - ASSERT(state_ == CompareIC::NUMBER); - ASM_LOCATION("ICCompareStub[HeapNumbers]"); - - Label unordered, maybe_undefined1, maybe_undefined2; - Label miss, handle_lhs, values_in_d_regs; - Label untag_rhs, untag_lhs; - - Register result = x0; - Register rhs = x0; - Register lhs = x1; - FPRegister rhs_d = d0; - FPRegister lhs_d = d1; - - if (left_ == CompareIC::SMI) { - __ JumpIfNotSmi(lhs, &miss); - } - if (right_ == CompareIC::SMI) { - __ JumpIfNotSmi(rhs, &miss); - } - - __ SmiUntagToDouble(rhs_d, rhs, kSpeculativeUntag); - __ SmiUntagToDouble(lhs_d, lhs, kSpeculativeUntag); - - // Load rhs if it's a heap number. - __ JumpIfSmi(rhs, &handle_lhs); - __ CheckMap(rhs, x10, Heap::kHeapNumberMapRootIndex, &maybe_undefined1, - DONT_DO_SMI_CHECK); - __ Ldr(rhs_d, FieldMemOperand(rhs, HeapNumber::kValueOffset)); - - // Load lhs if it's a heap number. - __ Bind(&handle_lhs); - __ JumpIfSmi(lhs, &values_in_d_regs); - __ CheckMap(lhs, x10, Heap::kHeapNumberMapRootIndex, &maybe_undefined2, - DONT_DO_SMI_CHECK); - __ Ldr(lhs_d, FieldMemOperand(lhs, HeapNumber::kValueOffset)); - - __ Bind(&values_in_d_regs); - __ Fcmp(lhs_d, rhs_d); - __ B(vs, &unordered); // Overflow flag set if either is NaN. - STATIC_ASSERT((LESS == -1) && (EQUAL == 0) && (GREATER == 1)); - __ Cset(result, gt); // gt => 1, otherwise (lt, eq) => 0 (EQUAL). - __ Csinv(result, result, xzr, ge); // lt => -1, gt => 1, eq => 0. - __ Ret(); - - __ Bind(&unordered); - ICCompareStub stub(op_, CompareIC::GENERIC, CompareIC::GENERIC, - CompareIC::GENERIC); - __ Jump(stub.GetCode(masm->isolate()), RelocInfo::CODE_TARGET); - - __ Bind(&maybe_undefined1); - if (Token::IsOrderedRelationalCompareOp(op_)) { - __ JumpIfNotRoot(rhs, Heap::kUndefinedValueRootIndex, &miss); - __ JumpIfSmi(lhs, &unordered); - __ JumpIfNotObjectType(lhs, x10, x10, HEAP_NUMBER_TYPE, &maybe_undefined2); - __ B(&unordered); - } - - __ Bind(&maybe_undefined2); - if (Token::IsOrderedRelationalCompareOp(op_)) { - __ JumpIfRoot(lhs, Heap::kUndefinedValueRootIndex, &unordered); - } - - __ Bind(&miss); - GenerateMiss(masm); -} - - -void ICCompareStub::GenerateInternalizedStrings(MacroAssembler* masm) { - ASSERT(state_ == CompareIC::INTERNALIZED_STRING); - ASM_LOCATION("ICCompareStub[InternalizedStrings]"); - Label miss; - - Register result = x0; - Register rhs = x0; - Register lhs = x1; - - // Check that both operands are heap objects. - __ JumpIfEitherSmi(lhs, rhs, &miss); - - // Check that both operands are internalized strings. - Register rhs_map = x10; - Register lhs_map = x11; - Register rhs_type = x10; - Register lhs_type = x11; - __ Ldr(lhs_map, FieldMemOperand(lhs, HeapObject::kMapOffset)); - __ Ldr(rhs_map, FieldMemOperand(rhs, HeapObject::kMapOffset)); - __ Ldrb(lhs_type, FieldMemOperand(lhs_map, Map::kInstanceTypeOffset)); - __ Ldrb(rhs_type, FieldMemOperand(rhs_map, Map::kInstanceTypeOffset)); - - STATIC_ASSERT((kInternalizedTag == 0) && (kStringTag == 0)); - __ Orr(x12, lhs_type, rhs_type); - __ TestAndBranchIfAnySet( - x12, kIsNotStringMask | kIsNotInternalizedMask, &miss); - - // Internalized strings are compared by identity. - STATIC_ASSERT(EQUAL == 0); - __ Cmp(lhs, rhs); - __ Cset(result, ne); - __ Ret(); - - __ Bind(&miss); - GenerateMiss(masm); -} - - -void ICCompareStub::GenerateUniqueNames(MacroAssembler* masm) { - ASSERT(state_ == CompareIC::UNIQUE_NAME); - ASM_LOCATION("ICCompareStub[UniqueNames]"); - ASSERT(GetCondition() == eq); - Label miss; - - Register result = x0; - Register rhs = x0; - Register lhs = x1; - - Register lhs_instance_type = w2; - Register rhs_instance_type = w3; - - // Check that both operands are heap objects. - __ JumpIfEitherSmi(lhs, rhs, &miss); - - // Check that both operands are unique names. This leaves the instance - // types loaded in tmp1 and tmp2. - __ Ldr(x10, FieldMemOperand(lhs, HeapObject::kMapOffset)); - __ Ldr(x11, FieldMemOperand(rhs, HeapObject::kMapOffset)); - __ Ldrb(lhs_instance_type, FieldMemOperand(x10, Map::kInstanceTypeOffset)); - __ Ldrb(rhs_instance_type, FieldMemOperand(x11, Map::kInstanceTypeOffset)); - - // To avoid a miss, each instance type should be either SYMBOL_TYPE or it - // should have kInternalizedTag set. - __ JumpIfNotUniqueName(lhs_instance_type, &miss); - __ JumpIfNotUniqueName(rhs_instance_type, &miss); - - // Unique names are compared by identity. - STATIC_ASSERT(EQUAL == 0); - __ Cmp(lhs, rhs); - __ Cset(result, ne); - __ Ret(); - - __ Bind(&miss); - GenerateMiss(masm); -} - - -void ICCompareStub::GenerateStrings(MacroAssembler* masm) { - ASSERT(state_ == CompareIC::STRING); - ASM_LOCATION("ICCompareStub[Strings]"); - - Label miss; - - bool equality = Token::IsEqualityOp(op_); - - Register result = x0; - Register rhs = x0; - Register lhs = x1; - - // Check that both operands are heap objects. - __ JumpIfEitherSmi(rhs, lhs, &miss); - - // Check that both operands are strings. - Register rhs_map = x10; - Register lhs_map = x11; - Register rhs_type = x10; - Register lhs_type = x11; - __ Ldr(lhs_map, FieldMemOperand(lhs, HeapObject::kMapOffset)); - __ Ldr(rhs_map, FieldMemOperand(rhs, HeapObject::kMapOffset)); - __ Ldrb(lhs_type, FieldMemOperand(lhs_map, Map::kInstanceTypeOffset)); - __ Ldrb(rhs_type, FieldMemOperand(rhs_map, Map::kInstanceTypeOffset)); - STATIC_ASSERT(kNotStringTag != 0); - __ Orr(x12, lhs_type, rhs_type); - __ Tbnz(x12, MaskToBit(kIsNotStringMask), &miss); - - // Fast check for identical strings. - Label not_equal; - __ Cmp(lhs, rhs); - __ B(ne, ¬_equal); - __ Mov(result, EQUAL); - __ Ret(); - - __ Bind(¬_equal); - // Handle not identical strings - - // Check that both strings are internalized strings. If they are, we're done - // because we already know they are not identical. We know they are both - // strings. - if (equality) { - ASSERT(GetCondition() == eq); - STATIC_ASSERT(kInternalizedTag == 0); - Label not_internalized_strings; - __ Orr(x12, lhs_type, rhs_type); - __ TestAndBranchIfAnySet( - x12, kIsNotInternalizedMask, ¬_internalized_strings); - // Result is in rhs (x0), and not EQUAL, as rhs is not a smi. - __ Ret(); - __ Bind(¬_internalized_strings); - } - - // Check that both strings are sequential ASCII. - Label runtime; - __ JumpIfBothInstanceTypesAreNotSequentialAscii( - lhs_type, rhs_type, x12, x13, &runtime); - - // Compare flat ASCII strings. Returns when done. - if (equality) { - StringCompareStub::GenerateFlatAsciiStringEquals( - masm, lhs, rhs, x10, x11, x12); - } else { - StringCompareStub::GenerateCompareFlatAsciiStrings( - masm, lhs, rhs, x10, x11, x12, x13); - } - - // Handle more complex cases in runtime. - __ Bind(&runtime); - __ Push(lhs, rhs); - if (equality) { - __ TailCallRuntime(Runtime::kStringEquals, 2, 1); - } else { - __ TailCallRuntime(Runtime::kStringCompare, 2, 1); - } - - __ Bind(&miss); - GenerateMiss(masm); -} - - -void ICCompareStub::GenerateObjects(MacroAssembler* masm) { - ASSERT(state_ == CompareIC::OBJECT); - ASM_LOCATION("ICCompareStub[Objects]"); - - Label miss; - - Register result = x0; - Register rhs = x0; - Register lhs = x1; - - __ JumpIfEitherSmi(rhs, lhs, &miss); - - __ JumpIfNotObjectType(rhs, x10, x10, JS_OBJECT_TYPE, &miss); - __ JumpIfNotObjectType(lhs, x10, x10, JS_OBJECT_TYPE, &miss); - - ASSERT(GetCondition() == eq); - __ Sub(result, rhs, lhs); - __ Ret(); - - __ Bind(&miss); - GenerateMiss(masm); -} - - -void ICCompareStub::GenerateKnownObjects(MacroAssembler* masm) { - ASM_LOCATION("ICCompareStub[KnownObjects]"); - - Label miss; - - Register result = x0; - Register rhs = x0; - Register lhs = x1; - - __ JumpIfEitherSmi(rhs, lhs, &miss); - - Register rhs_map = x10; - Register lhs_map = x11; - __ Ldr(rhs_map, FieldMemOperand(rhs, HeapObject::kMapOffset)); - __ Ldr(lhs_map, FieldMemOperand(lhs, HeapObject::kMapOffset)); - __ Cmp(rhs_map, Operand(known_map_)); - __ B(ne, &miss); - __ Cmp(lhs_map, Operand(known_map_)); - __ B(ne, &miss); - - __ Sub(result, rhs, lhs); - __ Ret(); - - __ Bind(&miss); - GenerateMiss(masm); -} - - -// This method handles the case where a compare stub had the wrong -// implementation. It calls a miss handler, which re-writes the stub. All other -// ICCompareStub::Generate* methods should fall back into this one if their -// operands were not the expected types. -void ICCompareStub::GenerateMiss(MacroAssembler* masm) { - ASM_LOCATION("ICCompareStub[Miss]"); - - Register stub_entry = x11; - { - ExternalReference miss = - ExternalReference(IC_Utility(IC::kCompareIC_Miss), masm->isolate()); - - FrameScope scope(masm, StackFrame::INTERNAL); - Register op = x10; - Register left = x1; - Register right = x0; - // Preserve some caller-saved registers. - __ Push(x1, x0, lr); - // Push the arguments. - __ Mov(op, Operand(Smi::FromInt(op_))); - __ Push(left, right, op); - - // Call the miss handler. This also pops the arguments. - __ CallExternalReference(miss, 3); - - // Compute the entry point of the rewritten stub. - __ Add(stub_entry, x0, Code::kHeaderSize - kHeapObjectTag); - // Restore caller-saved registers. - __ Pop(lr, x0, x1); - } - - // Tail-call to the new stub. - __ Jump(stub_entry); -} - - -void StringHelper::GenerateHashInit(MacroAssembler* masm, - Register hash, - Register character) { - ASSERT(!AreAliased(hash, character)); - - // hash = character + (character << 10); - __ LoadRoot(hash, Heap::kHashSeedRootIndex); - // Untag smi seed and add the character. - __ Add(hash, character, Operand(hash, LSR, kSmiShift)); - - // Compute hashes modulo 2^32 using a 32-bit W register. - Register hash_w = hash.W(); - - // hash += hash << 10; - __ Add(hash_w, hash_w, Operand(hash_w, LSL, 10)); - // hash ^= hash >> 6; - __ Eor(hash_w, hash_w, Operand(hash_w, LSR, 6)); -} - - -void StringHelper::GenerateHashAddCharacter(MacroAssembler* masm, - Register hash, - Register character) { - ASSERT(!AreAliased(hash, character)); - - // hash += character; - __ Add(hash, hash, character); - - // Compute hashes modulo 2^32 using a 32-bit W register. - Register hash_w = hash.W(); - - // hash += hash << 10; - __ Add(hash_w, hash_w, Operand(hash_w, LSL, 10)); - // hash ^= hash >> 6; - __ Eor(hash_w, hash_w, Operand(hash_w, LSR, 6)); -} - - -void StringHelper::GenerateHashGetHash(MacroAssembler* masm, - Register hash, - Register scratch) { - // Compute hashes modulo 2^32 using a 32-bit W register. - Register hash_w = hash.W(); - Register scratch_w = scratch.W(); - ASSERT(!AreAliased(hash_w, scratch_w)); - - // hash += hash << 3; - __ Add(hash_w, hash_w, Operand(hash_w, LSL, 3)); - // hash ^= hash >> 11; - __ Eor(hash_w, hash_w, Operand(hash_w, LSR, 11)); - // hash += hash << 15; - __ Add(hash_w, hash_w, Operand(hash_w, LSL, 15)); - - __ Ands(hash_w, hash_w, String::kHashBitMask); - - // if (hash == 0) hash = 27; - __ Mov(scratch_w, StringHasher::kZeroHash); - __ Csel(hash_w, scratch_w, hash_w, eq); -} - - -void SubStringStub::Generate(MacroAssembler* masm) { - ASM_LOCATION("SubStringStub::Generate"); - Label runtime; - - // Stack frame on entry. - // lr: return address - // jssp[0]: substring "to" offset - // jssp[8]: substring "from" offset - // jssp[16]: pointer to string object - - // This stub is called from the native-call %_SubString(...), so - // nothing can be assumed about the arguments. It is tested that: - // "string" is a sequential string, - // both "from" and "to" are smis, and - // 0 <= from <= to <= string.length (in debug mode.) - // If any of these assumptions fail, we call the runtime system. - - static const int kToOffset = 0 * kPointerSize; - static const int kFromOffset = 1 * kPointerSize; - static const int kStringOffset = 2 * kPointerSize; - - Register to = x0; - Register from = x15; - Register input_string = x10; - Register input_length = x11; - Register input_type = x12; - Register result_string = x0; - Register result_length = x1; - Register temp = x3; - - __ Peek(to, kToOffset); - __ Peek(from, kFromOffset); - - // Check that both from and to are smis. If not, jump to runtime. - __ JumpIfEitherNotSmi(from, to, &runtime); - __ SmiUntag(from); - __ SmiUntag(to); - - // Calculate difference between from and to. If to < from, branch to runtime. - __ Subs(result_length, to, from); - __ B(mi, &runtime); - - // Check from is positive. - __ Tbnz(from, kWSignBit, &runtime); - - // Make sure first argument is a string. - __ Peek(input_string, kStringOffset); - __ JumpIfSmi(input_string, &runtime); - __ IsObjectJSStringType(input_string, input_type, &runtime); - - Label single_char; - __ Cmp(result_length, 1); - __ B(eq, &single_char); - - // Short-cut for the case of trivial substring. - Label return_x0; - __ Ldrsw(input_length, - UntagSmiFieldMemOperand(input_string, String::kLengthOffset)); - - __ Cmp(result_length, input_length); - __ CmovX(x0, input_string, eq); - // Return original string. - __ B(eq, &return_x0); - - // Longer than original string's length or negative: unsafe arguments. - __ B(hi, &runtime); - - // Shorter than original string's length: an actual substring. - - // x0 to substring end character offset - // x1 result_length length of substring result - // x10 input_string pointer to input string object - // x10 unpacked_string pointer to unpacked string object - // x11 input_length length of input string - // x12 input_type instance type of input string - // x15 from substring start character offset - - // Deal with different string types: update the index if necessary and put - // the underlying string into register unpacked_string. - Label underlying_unpacked, sliced_string, seq_or_external_string; - Label update_instance_type; - // If the string is not indirect, it can only be sequential or external. - STATIC_ASSERT(kIsIndirectStringMask == (kSlicedStringTag & kConsStringTag)); - STATIC_ASSERT(kIsIndirectStringMask != 0); - - // Test for string types, and branch/fall through to appropriate unpacking - // code. - __ Tst(input_type, kIsIndirectStringMask); - __ B(eq, &seq_or_external_string); - __ Tst(input_type, kSlicedNotConsMask); - __ B(ne, &sliced_string); - - Register unpacked_string = input_string; - - // Cons string. Check whether it is flat, then fetch first part. - __ Ldr(temp, FieldMemOperand(input_string, ConsString::kSecondOffset)); - __ JumpIfNotRoot(temp, Heap::kempty_stringRootIndex, &runtime); - __ Ldr(unpacked_string, - FieldMemOperand(input_string, ConsString::kFirstOffset)); - __ B(&update_instance_type); - - __ Bind(&sliced_string); - // Sliced string. Fetch parent and correct start index by offset. - __ Ldrsw(temp, - UntagSmiFieldMemOperand(input_string, SlicedString::kOffsetOffset)); - __ Add(from, from, temp); - __ Ldr(unpacked_string, - FieldMemOperand(input_string, SlicedString::kParentOffset)); - - __ Bind(&update_instance_type); - __ Ldr(temp, FieldMemOperand(unpacked_string, HeapObject::kMapOffset)); - __ Ldrb(input_type, FieldMemOperand(temp, Map::kInstanceTypeOffset)); - // TODO(all): This generates "b #+0x4". Can these be optimised out? - __ B(&underlying_unpacked); - - __ Bind(&seq_or_external_string); - // Sequential or external string. Registers unpacked_string and input_string - // alias, so there's nothing to do here. - - // x0 result_string pointer to result string object (uninit) - // x1 result_length length of substring result - // x10 unpacked_string pointer to unpacked string object - // x11 input_length length of input string - // x12 input_type instance type of input string - // x15 from substring start character offset - __ Bind(&underlying_unpacked); - - if (FLAG_string_slices) { - Label copy_routine; - __ Cmp(result_length, SlicedString::kMinLength); - // Short slice. Copy instead of slicing. - __ B(lt, ©_routine); - // Allocate new sliced string. At this point we do not reload the instance - // type including the string encoding because we simply rely on the info - // provided by the original string. It does not matter if the original - // string's encoding is wrong because we always have to recheck encoding of - // the newly created string's parent anyway due to externalized strings. - Label two_byte_slice, set_slice_header; - STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0); - STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); - __ Tbz(input_type, MaskToBit(kStringEncodingMask), &two_byte_slice); - __ AllocateAsciiSlicedString(result_string, result_length, x3, x4, - &runtime); - __ B(&set_slice_header); - - __ Bind(&two_byte_slice); - __ AllocateTwoByteSlicedString(result_string, result_length, x3, x4, - &runtime); - - __ Bind(&set_slice_header); - __ SmiTag(from); - __ Str(from, FieldMemOperand(result_string, SlicedString::kOffsetOffset)); - __ Str(unpacked_string, - FieldMemOperand(result_string, SlicedString::kParentOffset)); - __ B(&return_x0); - - __ Bind(©_routine); - } - - // x0 result_string pointer to result string object (uninit) - // x1 result_length length of substring result - // x10 unpacked_string pointer to unpacked string object - // x11 input_length length of input string - // x12 input_type instance type of input string - // x13 unpacked_char0 pointer to first char of unpacked string (uninit) - // x13 substring_char0 pointer to first char of substring (uninit) - // x14 result_char0 pointer to first char of result (uninit) - // x15 from substring start character offset - Register unpacked_char0 = x13; - Register substring_char0 = x13; - Register result_char0 = x14; - Label two_byte_sequential, sequential_string, allocate_result; - STATIC_ASSERT(kExternalStringTag != 0); - STATIC_ASSERT(kSeqStringTag == 0); - - __ Tst(input_type, kExternalStringTag); - __ B(eq, &sequential_string); - - __ Tst(input_type, kShortExternalStringTag); - __ B(ne, &runtime); - __ Ldr(unpacked_char0, - FieldMemOperand(unpacked_string, ExternalString::kResourceDataOffset)); - // unpacked_char0 points to the first character of the underlying string. - __ B(&allocate_result); - - __ Bind(&sequential_string); - // Locate first character of underlying subject string. - STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqOneByteString::kHeaderSize); - __ Add(unpacked_char0, unpacked_string, - SeqOneByteString::kHeaderSize - kHeapObjectTag); - - __ Bind(&allocate_result); - // Sequential ASCII string. Allocate the result. - STATIC_ASSERT((kOneByteStringTag & kStringEncodingMask) != 0); - __ Tbz(input_type, MaskToBit(kStringEncodingMask), &two_byte_sequential); - - // Allocate and copy the resulting ASCII string. - __ AllocateAsciiString(result_string, result_length, x3, x4, x5, &runtime); - - // Locate first character of substring to copy. - __ Add(substring_char0, unpacked_char0, from); - - // Locate first character of result. - __ Add(result_char0, result_string, - SeqOneByteString::kHeaderSize - kHeapObjectTag); - - STATIC_ASSERT((SeqOneByteString::kHeaderSize & kObjectAlignmentMask) == 0); - __ CopyBytes(result_char0, substring_char0, result_length, x3, kCopyLong); - __ B(&return_x0); - - // Allocate and copy the resulting two-byte string. - __ Bind(&two_byte_sequential); - __ AllocateTwoByteString(result_string, result_length, x3, x4, x5, &runtime); - - // Locate first character of substring to copy. - __ Add(substring_char0, unpacked_char0, Operand(from, LSL, 1)); - - // Locate first character of result. - __ Add(result_char0, result_string, - SeqTwoByteString::kHeaderSize - kHeapObjectTag); - - STATIC_ASSERT((SeqTwoByteString::kHeaderSize & kObjectAlignmentMask) == 0); - __ Add(result_length, result_length, result_length); - __ CopyBytes(result_char0, substring_char0, result_length, x3, kCopyLong); - - __ Bind(&return_x0); - Counters* counters = masm->isolate()->counters(); - __ IncrementCounter(counters->sub_string_native(), 1, x3, x4); - __ Drop(3); - __ Ret(); - - __ Bind(&runtime); - __ TailCallRuntime(Runtime::kSubString, 3, 1); - - __ bind(&single_char); - // x1: result_length - // x10: input_string - // x12: input_type - // x15: from (untagged) - __ SmiTag(from); - StringCharAtGenerator generator( - input_string, from, result_length, x0, - &runtime, &runtime, &runtime, STRING_INDEX_IS_NUMBER); - generator.GenerateFast(masm); - // TODO(jbramley): Why doesn't this jump to return_x0? - __ Drop(3); - __ Ret(); - generator.SkipSlow(masm, &runtime); -} - - -void StringCompareStub::GenerateFlatAsciiStringEquals(MacroAssembler* masm, - Register left, - Register right, - Register scratch1, - Register scratch2, - Register scratch3) { - ASSERT(!AreAliased(left, right, scratch1, scratch2, scratch3)); - Register result = x0; - Register left_length = scratch1; - Register right_length = scratch2; - - // Compare lengths. If lengths differ, strings can't be equal. Lengths are - // smis, and don't need to be untagged. - Label strings_not_equal, check_zero_length; - __ Ldr(left_length, FieldMemOperand(left, String::kLengthOffset)); - __ Ldr(right_length, FieldMemOperand(right, String::kLengthOffset)); - __ Cmp(left_length, right_length); - __ B(eq, &check_zero_length); - - __ Bind(&strings_not_equal); - __ Mov(result, Operand(Smi::FromInt(NOT_EQUAL))); - __ Ret(); - - // Check if the length is zero. If so, the strings must be equal (and empty.) - Label compare_chars; - __ Bind(&check_zero_length); - STATIC_ASSERT(kSmiTag == 0); - __ Cbnz(left_length, &compare_chars); - __ Mov(result, Operand(Smi::FromInt(EQUAL))); - __ Ret(); - - // Compare characters. Falls through if all characters are equal. - __ Bind(&compare_chars); - GenerateAsciiCharsCompareLoop(masm, left, right, left_length, scratch2, - scratch3, &strings_not_equal); - - // Characters in strings are equal. - __ Mov(result, Operand(Smi::FromInt(EQUAL))); - __ Ret(); -} - - -void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm, - Register left, - Register right, - Register scratch1, - Register scratch2, - Register scratch3, - Register scratch4) { - ASSERT(!AreAliased(left, right, scratch1, scratch2, scratch3, scratch4)); - Label result_not_equal, compare_lengths; - - // Find minimum length and length difference. - Register length_delta = scratch3; - __ Ldr(scratch1, FieldMemOperand(left, String::kLengthOffset)); - __ Ldr(scratch2, FieldMemOperand(right, String::kLengthOffset)); - __ Subs(length_delta, scratch1, scratch2); - - Register min_length = scratch1; - __ Csel(min_length, scratch2, scratch1, gt); - __ Cbz(min_length, &compare_lengths); - - // Compare loop. - GenerateAsciiCharsCompareLoop(masm, - left, right, min_length, scratch2, scratch4, - &result_not_equal); - - // Compare lengths - strings up to min-length are equal. - __ Bind(&compare_lengths); - - ASSERT(Smi::FromInt(EQUAL) == static_cast(0)); - - // Use length_delta as result if it's zero. - Register result = x0; - __ Subs(result, length_delta, 0); - - __ Bind(&result_not_equal); - Register greater = x10; - Register less = x11; - __ Mov(greater, Operand(Smi::FromInt(GREATER))); - __ Mov(less, Operand(Smi::FromInt(LESS))); - __ CmovX(result, greater, gt); - __ CmovX(result, less, lt); - __ Ret(); -} - - -void StringCompareStub::GenerateAsciiCharsCompareLoop( - MacroAssembler* masm, - Register left, - Register right, - Register length, - Register scratch1, - Register scratch2, - Label* chars_not_equal) { - ASSERT(!AreAliased(left, right, length, scratch1, scratch2)); - - // 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); - __ Add(scratch1, length, SeqOneByteString::kHeaderSize - kHeapObjectTag); - __ Add(left, left, scratch1); - __ Add(right, right, scratch1); - - Register index = length; - __ Neg(index, length); // index = -length; - - // Compare loop - Label loop; - __ Bind(&loop); - __ Ldrb(scratch1, MemOperand(left, index)); - __ Ldrb(scratch2, MemOperand(right, index)); - __ Cmp(scratch1, scratch2); - __ B(ne, chars_not_equal); - __ Add(index, index, 1); - __ Cbnz(index, &loop); -} - - -void StringCompareStub::Generate(MacroAssembler* masm) { - Label runtime; - - Counters* counters = masm->isolate()->counters(); - - // Stack frame on entry. - // sp[0]: right string - // sp[8]: left string - Register right = x10; - Register left = x11; - Register result = x0; - __ Pop(right, left); - - Label not_same; - __ Subs(result, right, left); - __ B(ne, ¬_same); - STATIC_ASSERT(EQUAL == 0); - __ IncrementCounter(counters->string_compare_native(), 1, x3, x4); - __ Ret(); - - __ Bind(¬_same); - - // Check that both objects are sequential ASCII strings. - __ JumpIfEitherIsNotSequentialAsciiStrings(left, right, x12, x13, &runtime); - - // Compare flat ASCII strings natively. Remove arguments from stack first, - // as this function will generate a return. - __ IncrementCounter(counters->string_compare_native(), 1, x3, x4); - GenerateCompareFlatAsciiStrings(masm, left, right, x12, x13, x14, x15); - - __ Bind(&runtime); - - // Push arguments back on to the stack. - // sp[0] = right string - // sp[8] = left string. - __ Push(left, right); - - // Call the runtime. - // Returns -1 (less), 0 (equal), or 1 (greater) tagged as a small integer. - __ TailCallRuntime(Runtime::kStringCompare, 2, 1); -} - - -void ArrayPushStub::Generate(MacroAssembler* masm) { - Register receiver = x0; - - int argc = arguments_count(); - - if (argc == 0) { - // Nothing to do, just return the length. - __ Ldr(x0, FieldMemOperand(receiver, JSArray::kLengthOffset)); - __ Drop(argc + 1); - __ Ret(); - return; - } - - Isolate* isolate = masm->isolate(); - - if (argc != 1) { - __ TailCallExternalReference( - ExternalReference(Builtins::c_ArrayPush, isolate), argc + 1, 1); - return; - } - - Label call_builtin, attempt_to_grow_elements, with_write_barrier; - - Register elements_length = x8; - Register length = x7; - Register elements = x6; - Register end_elements = x5; - Register value = x4; - // Get the elements array of the object. - __ Ldr(elements, FieldMemOperand(receiver, JSArray::kElementsOffset)); - - if (IsFastSmiOrObjectElementsKind(elements_kind())) { - // Check that the elements are in fast mode and writable. - __ CheckMap(elements, - x10, - Heap::kFixedArrayMapRootIndex, - &call_builtin, - DONT_DO_SMI_CHECK); - } - - // Get the array's length and calculate new length. - __ Ldr(length, FieldMemOperand(receiver, JSArray::kLengthOffset)); - STATIC_ASSERT(kSmiTag == 0); - __ Add(length, length, Operand(Smi::FromInt(argc))); - - // Check if we could survive without allocation. - __ Ldr(elements_length, - FieldMemOperand(elements, FixedArray::kLengthOffset)); - __ Cmp(length, elements_length); - - const int kEndElementsOffset = - FixedArray::kHeaderSize - kHeapObjectTag - argc * kPointerSize; - - if (IsFastSmiOrObjectElementsKind(elements_kind())) { - __ B(gt, &attempt_to_grow_elements); - - // Check if value is a smi. - __ Peek(value, (argc - 1) * kPointerSize); - __ JumpIfNotSmi(value, &with_write_barrier); - - // Store the value. - // We may need a register containing the address end_elements below, - // so write back the value in end_elements. - __ Add(end_elements, elements, - Operand::UntagSmiAndScale(length, kPointerSizeLog2)); - __ Str(value, MemOperand(end_elements, kEndElementsOffset, PreIndex)); - } else { - // TODO(all): ARM has a redundant cmp here. - __ B(gt, &call_builtin); - - __ Peek(value, (argc - 1) * kPointerSize); - __ StoreNumberToDoubleElements(value, length, elements, x10, d0, d1, - &call_builtin, argc * kDoubleSize); - } - - // Save new length. - __ Str(length, FieldMemOperand(receiver, JSArray::kLengthOffset)); - - // Return length. - __ Drop(argc + 1); - __ Mov(x0, length); - __ Ret(); - - if (IsFastDoubleElementsKind(elements_kind())) { - __ Bind(&call_builtin); - __ TailCallExternalReference( - ExternalReference(Builtins::c_ArrayPush, isolate), argc + 1, 1); - return; - } - - __ Bind(&with_write_barrier); - - if (IsFastSmiElementsKind(elements_kind())) { - if (FLAG_trace_elements_transitions) { - __ B(&call_builtin); - } - - __ Ldr(x10, FieldMemOperand(value, HeapObject::kMapOffset)); - __ JumpIfHeapNumber(x10, &call_builtin); - - ElementsKind target_kind = IsHoleyElementsKind(elements_kind()) - ? FAST_HOLEY_ELEMENTS : FAST_ELEMENTS; - __ Ldr(x10, GlobalObjectMemOperand()); - __ Ldr(x10, FieldMemOperand(x10, GlobalObject::kNativeContextOffset)); - __ Ldr(x10, ContextMemOperand(x10, Context::JS_ARRAY_MAPS_INDEX)); - const int header_size = FixedArrayBase::kHeaderSize; - // Verify that the object can be transitioned in place. - const int origin_offset = header_size + elements_kind() * kPointerSize; - __ ldr(x11, FieldMemOperand(receiver, origin_offset)); - __ ldr(x12, FieldMemOperand(x10, HeapObject::kMapOffset)); - __ cmp(x11, x12); - __ B(ne, &call_builtin); - - const int target_offset = header_size + target_kind * kPointerSize; - __ Ldr(x10, FieldMemOperand(x10, target_offset)); - __ Mov(x11, receiver); - ElementsTransitionGenerator::GenerateMapChangeElementsTransition( - masm, DONT_TRACK_ALLOCATION_SITE, NULL); - } - - // Save new length. - __ Str(length, FieldMemOperand(receiver, JSArray::kLengthOffset)); - - // Store the value. - // We may need a register containing the address end_elements below, - // so write back the value in end_elements. - __ Add(end_elements, elements, - Operand::UntagSmiAndScale(length, kPointerSizeLog2)); - __ Str(value, MemOperand(end_elements, kEndElementsOffset, PreIndex)); - - __ RecordWrite(elements, - end_elements, - value, - kLRHasNotBeenSaved, - kDontSaveFPRegs, - EMIT_REMEMBERED_SET, - OMIT_SMI_CHECK); - __ Drop(argc + 1); - __ Mov(x0, length); - __ Ret(); - - __ Bind(&attempt_to_grow_elements); - - if (!FLAG_inline_new) { - __ B(&call_builtin); - } - - Register argument = x2; - __ Peek(argument, (argc - 1) * kPointerSize); - // Growing elements that are SMI-only requires special handling in case - // the new element is non-Smi. For now, delegate to the builtin. - if (IsFastSmiElementsKind(elements_kind())) { - __ JumpIfNotSmi(argument, &call_builtin); - } - - // We could be lucky and the elements array could be at the top of new-space. - // In this case we can just grow it in place by moving the allocation pointer - // up. - ExternalReference new_space_allocation_top = - ExternalReference::new_space_allocation_top_address(isolate); - ExternalReference new_space_allocation_limit = - ExternalReference::new_space_allocation_limit_address(isolate); - - const int kAllocationDelta = 4; - ASSERT(kAllocationDelta >= argc); - Register allocation_top_addr = x5; - Register allocation_top = x9; - // Load top and check if it is the end of elements. - __ Add(end_elements, elements, - Operand::UntagSmiAndScale(length, kPointerSizeLog2)); - __ Add(end_elements, end_elements, kEndElementsOffset); - __ Mov(allocation_top_addr, Operand(new_space_allocation_top)); - __ Ldr(allocation_top, MemOperand(allocation_top_addr)); - __ Cmp(end_elements, allocation_top); - __ B(ne, &call_builtin); - - __ Mov(x10, Operand(new_space_allocation_limit)); - __ Ldr(x10, MemOperand(x10)); - __ Add(allocation_top, allocation_top, kAllocationDelta * kPointerSize); - __ Cmp(allocation_top, x10); - __ B(hi, &call_builtin); - - // We fit and could grow elements. - // Update new_space_allocation_top. - __ Str(allocation_top, MemOperand(allocation_top_addr)); - // Push the argument. - __ Str(argument, MemOperand(end_elements)); - // Fill the rest with holes. - __ LoadRoot(x10, Heap::kTheHoleValueRootIndex); - for (int i = 1; i < kAllocationDelta; i++) { - // TODO(all): Try to use stp here. - __ Str(x10, MemOperand(end_elements, i * kPointerSize)); - } - - // Update elements' and array's sizes. - __ Str(length, FieldMemOperand(receiver, JSArray::kLengthOffset)); - __ Add(elements_length, - elements_length, - Operand(Smi::FromInt(kAllocationDelta))); - __ Str(elements_length, - FieldMemOperand(elements, FixedArray::kLengthOffset)); - - // Elements are in new space, so write barrier is not required. - __ Drop(argc + 1); - __ Mov(x0, length); - __ Ret(); - - __ Bind(&call_builtin); - __ TailCallExternalReference( - ExternalReference(Builtins::c_ArrayPush, isolate), argc + 1, 1); -} - - -void BinaryOpICWithAllocationSiteStub::Generate(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- x1 : left - // -- x0 : right - // -- lr : return address - // ----------------------------------- - Isolate* isolate = masm->isolate(); - - // Load x2 with the allocation site. We stick an undefined dummy value here - // and replace it with the real allocation site later when we instantiate this - // stub in BinaryOpICWithAllocationSiteStub::GetCodeCopyFromTemplate(). - __ LoadObject(x2, handle(isolate->heap()->undefined_value())); - - // Make sure that we actually patched the allocation site. - if (FLAG_debug_code) { - __ AssertNotSmi(x2, kExpectedAllocationSite); - __ Ldr(x10, FieldMemOperand(x2, HeapObject::kMapOffset)); - __ AssertRegisterIsRoot(x10, Heap::kAllocationSiteMapRootIndex, - kExpectedAllocationSite); - } - - // Tail call into the stub that handles binary operations with allocation - // sites. - BinaryOpWithAllocationSiteStub stub(state_); - __ TailCallStub(&stub); -} - - -bool CodeStub::CanUseFPRegisters() { - // FP registers always available on A64. - return true; -} - - -void RecordWriteStub::GenerateIncremental(MacroAssembler* masm, Mode mode) { - // We need some extra registers for this stub, they have been allocated - // but we need to save them before using them. - regs_.Save(masm); - - if (remembered_set_action_ == EMIT_REMEMBERED_SET) { - Label dont_need_remembered_set; - - Register value = regs_.scratch0(); - __ Ldr(value, MemOperand(regs_.address())); - __ JumpIfNotInNewSpace(value, &dont_need_remembered_set); - - __ CheckPageFlagSet(regs_.object(), - value, - 1 << MemoryChunk::SCAN_ON_SCAVENGE, - &dont_need_remembered_set); - - // First notify the incremental marker if necessary, then update the - // remembered set. - CheckNeedsToInformIncrementalMarker( - masm, kUpdateRememberedSetOnNoNeedToInformIncrementalMarker, mode); - InformIncrementalMarker(masm, mode); - regs_.Restore(masm); // Restore the extra scratch registers we used. - __ RememberedSetHelper(object_, - address_, - value_, - save_fp_regs_mode_, - MacroAssembler::kReturnAtEnd); - - __ Bind(&dont_need_remembered_set); - } - - CheckNeedsToInformIncrementalMarker( - masm, kReturnOnNoNeedToInformIncrementalMarker, mode); - InformIncrementalMarker(masm, mode); - regs_.Restore(masm); // Restore the extra scratch registers we used. - __ Ret(); -} - - -void RecordWriteStub::InformIncrementalMarker(MacroAssembler* masm, Mode mode) { - regs_.SaveCallerSaveRegisters(masm, save_fp_regs_mode_); - Register address = - x0.Is(regs_.address()) ? regs_.scratch0() : regs_.address(); - ASSERT(!address.Is(regs_.object())); - ASSERT(!address.Is(x0)); - __ Mov(address, regs_.address()); - __ Mov(x0, regs_.object()); - __ Mov(x1, address); - __ Mov(x2, Operand(ExternalReference::isolate_address(masm->isolate()))); - - AllowExternalCallThatCantCauseGC scope(masm); - ExternalReference function = (mode == INCREMENTAL_COMPACTION) - ? ExternalReference::incremental_evacuation_record_write_function( - masm->isolate()) - : ExternalReference::incremental_marking_record_write_function( - masm->isolate()); - __ CallCFunction(function, 3, 0); - - regs_.RestoreCallerSaveRegisters(masm, save_fp_regs_mode_); -} - - -void RecordWriteStub::CheckNeedsToInformIncrementalMarker( - MacroAssembler* masm, - OnNoNeedToInformIncrementalMarker on_no_need, - Mode mode) { - Label on_black; - Label need_incremental; - Label need_incremental_pop_scratch; - - Register mem_chunk = regs_.scratch0(); - Register counter = regs_.scratch1(); - __ Bic(mem_chunk, regs_.object(), Page::kPageAlignmentMask); - __ Ldr(counter, - MemOperand(mem_chunk, MemoryChunk::kWriteBarrierCounterOffset)); - __ Subs(counter, counter, 1); - __ Str(counter, - MemOperand(mem_chunk, MemoryChunk::kWriteBarrierCounterOffset)); - __ B(mi, &need_incremental); - - // If the object is not black we don't have to inform the incremental marker. - __ JumpIfBlack(regs_.object(), regs_.scratch0(), regs_.scratch1(), &on_black); - - regs_.Restore(masm); // Restore the extra scratch registers we used. - if (on_no_need == kUpdateRememberedSetOnNoNeedToInformIncrementalMarker) { - __ RememberedSetHelper(object_, - address_, - value_, - save_fp_regs_mode_, - MacroAssembler::kReturnAtEnd); - } else { - __ Ret(); - } - - __ Bind(&on_black); - // Get the value from the slot. - Register value = regs_.scratch0(); - __ Ldr(value, MemOperand(regs_.address())); - - if (mode == INCREMENTAL_COMPACTION) { - Label ensure_not_white; - - __ CheckPageFlagClear(value, - regs_.scratch1(), - MemoryChunk::kEvacuationCandidateMask, - &ensure_not_white); - - __ CheckPageFlagClear(regs_.object(), - regs_.scratch1(), - MemoryChunk::kSkipEvacuationSlotsRecordingMask, - &need_incremental); - - __ Bind(&ensure_not_white); - } - - // We need extra registers for this, so we push the object and the address - // register temporarily. - __ Push(regs_.address(), regs_.object()); - __ EnsureNotWhite(value, - regs_.scratch1(), // Scratch. - regs_.object(), // Scratch. - regs_.address(), // Scratch. - regs_.scratch2(), // Scratch. - &need_incremental_pop_scratch); - __ Pop(regs_.object(), regs_.address()); - - regs_.Restore(masm); // Restore the extra scratch registers we used. - if (on_no_need == kUpdateRememberedSetOnNoNeedToInformIncrementalMarker) { - __ RememberedSetHelper(object_, - address_, - value_, - save_fp_regs_mode_, - MacroAssembler::kReturnAtEnd); - } else { - __ Ret(); - } - - __ Bind(&need_incremental_pop_scratch); - __ Pop(regs_.object(), regs_.address()); - - __ Bind(&need_incremental); - // Fall through when we need to inform the incremental marker. -} - - -void RecordWriteStub::Generate(MacroAssembler* masm) { - Label skip_to_incremental_noncompacting; - Label skip_to_incremental_compacting; - - // We patch these two first instructions back and forth between a nop and - // real branch when we start and stop incremental heap marking. - // Initially the stub is expected to be in STORE_BUFFER_ONLY mode, so 2 nops - // are generated. - // See RecordWriteStub::Patch for details. - { - InstructionAccurateScope scope(masm, 2); - __ adr(xzr, &skip_to_incremental_noncompacting); - __ adr(xzr, &skip_to_incremental_compacting); - } - - if (remembered_set_action_ == EMIT_REMEMBERED_SET) { - __ RememberedSetHelper(object_, - address_, - value_, - save_fp_regs_mode_, - MacroAssembler::kReturnAtEnd); - } - __ Ret(); - - __ Bind(&skip_to_incremental_noncompacting); - GenerateIncremental(masm, INCREMENTAL); - - __ Bind(&skip_to_incremental_compacting); - GenerateIncremental(masm, INCREMENTAL_COMPACTION); -} - - -void StoreArrayLiteralElementStub::Generate(MacroAssembler* masm) { - // TODO(all): Possible optimisations in this function: - // 1. Merge CheckFastElements and CheckFastSmiElements, so that the map - // bitfield is loaded only once. - // 2. Refactor the Ldr/Add sequence at the start of fast_elements and - // smi_element. - - // x0 value element value to store - // x3 index_smi element index as smi - // sp[0] array_index_smi array literal index in function as smi - // sp[1] array array literal - - Register value = x0; - Register index_smi = x3; - - Register array = x1; - Register array_map = x2; - Register array_index_smi = x4; - __ PeekPair(array_index_smi, array, 0); - __ Ldr(array_map, FieldMemOperand(array, JSObject::kMapOffset)); - - Label double_elements, smi_element, fast_elements, slow_elements; - __ CheckFastElements(array_map, x10, &double_elements); - __ JumpIfSmi(value, &smi_element); - __ CheckFastSmiElements(array_map, x10, &fast_elements); - - // Store into the array literal requires an elements transition. Call into - // the runtime. - __ Bind(&slow_elements); - __ Push(array, index_smi, value); - __ Ldr(x10, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); - __ Ldr(x11, FieldMemOperand(x10, JSFunction::kLiteralsOffset)); - __ Push(x11, array_index_smi); - __ TailCallRuntime(Runtime::kStoreArrayLiteralElement, 5, 1); - - // Array literal has ElementsKind of FAST_*_ELEMENTS and value is an object. - __ Bind(&fast_elements); - __ Ldr(x10, FieldMemOperand(array, JSObject::kElementsOffset)); - __ Add(x11, x10, Operand::UntagSmiAndScale(index_smi, kPointerSizeLog2)); - __ Add(x11, x11, FixedArray::kHeaderSize - kHeapObjectTag); - __ Str(value, MemOperand(x11)); - // Update the write barrier for the array store. - __ RecordWrite(x10, x11, value, kLRHasNotBeenSaved, kDontSaveFPRegs, - EMIT_REMEMBERED_SET, OMIT_SMI_CHECK); - __ Ret(); - - // Array literal has ElementsKind of FAST_*_SMI_ELEMENTS or FAST_*_ELEMENTS, - // and value is Smi. - __ Bind(&smi_element); - __ Ldr(x10, FieldMemOperand(array, JSObject::kElementsOffset)); - __ Add(x11, x10, Operand::UntagSmiAndScale(index_smi, kPointerSizeLog2)); - __ Str(value, FieldMemOperand(x11, FixedArray::kHeaderSize)); - __ Ret(); - - __ Bind(&double_elements); - __ Ldr(x10, FieldMemOperand(array, JSObject::kElementsOffset)); - __ StoreNumberToDoubleElements(value, index_smi, x10, x11, d0, d1, - &slow_elements); - __ Ret(); -} - - -void StubFailureTrampolineStub::Generate(MacroAssembler* masm) { - // TODO(jbramley): The ARM code leaves the (shifted) offset in r1. Why? - CEntryStub ces(1, kSaveFPRegs); - __ Call(ces.GetCode(masm->isolate()), RelocInfo::CODE_TARGET); - int parameter_count_offset = - StubFailureTrampolineFrame::kCallerStackParameterCountFrameOffset; - __ Ldr(x1, MemOperand(fp, parameter_count_offset)); - if (function_mode_ == JS_FUNCTION_STUB_MODE) { - __ Add(x1, x1, 1); - } - masm->LeaveFrame(StackFrame::STUB_FAILURE_TRAMPOLINE); - __ Drop(x1); - // Return to IC Miss stub, continuation still on stack. - __ Ret(); -} - - -void ProfileEntryHookStub::MaybeCallEntryHook(MacroAssembler* masm) { - if (masm->isolate()->function_entry_hook() != NULL) { - // TODO(all): This needs to be reliably consistent with - // kReturnAddressDistanceFromFunctionStart in ::Generate. - Assembler::BlockConstPoolScope no_const_pools(masm); - ProfileEntryHookStub stub; - __ Push(lr); - __ CallStub(&stub); - __ Pop(lr); - } -} - - -void ProfileEntryHookStub::Generate(MacroAssembler* masm) { - MacroAssembler::NoUseRealAbortsScope no_use_real_aborts(masm); - // The entry hook is a "BumpSystemStackPointer" instruction (sub), followed by - // a "Push lr" instruction, followed by a call. - // TODO(jbramley): Verify that this call is always made with relocation. - static const int kReturnAddressDistanceFromFunctionStart = - Assembler::kCallSizeWithRelocation + (2 * kInstructionSize); - - // Save all kCallerSaved registers (including lr), since this can be called - // from anywhere. - // TODO(jbramley): What about FP registers? - __ PushCPURegList(kCallerSaved); - ASSERT(kCallerSaved.IncludesAliasOf(lr)); - const int kNumSavedRegs = kCallerSaved.Count(); - - // Compute the function's address as the first argument. - __ Sub(x0, lr, kReturnAddressDistanceFromFunctionStart); - -#if V8_HOST_ARCH_A64 - uintptr_t entry_hook = - reinterpret_cast(masm->isolate()->function_entry_hook()); - __ Mov(x10, entry_hook); -#else - // Under the simulator we need to indirect the entry hook through a trampoline - // function at a known address. - ApiFunction dispatcher(FUNCTION_ADDR(EntryHookTrampoline)); - __ Mov(x10, Operand(ExternalReference(&dispatcher, - ExternalReference::BUILTIN_CALL, - masm->isolate()))); - // It additionally takes an isolate as a third parameter - __ Mov(x2, Operand(ExternalReference::isolate_address(masm->isolate()))); -#endif - - // The caller's return address is above the saved temporaries. - // Grab its location for the second argument to the hook. - __ Add(x1, __ StackPointer(), kNumSavedRegs * kPointerSize); - - { - // Create a dummy frame, as CallCFunction requires this. - FrameScope frame(masm, StackFrame::MANUAL); - __ CallCFunction(x10, 2, 0); - } - - __ PopCPURegList(kCallerSaved); - __ Ret(); -} - - -void DirectCEntryStub::Generate(MacroAssembler* masm) { - // When calling into C++ code the stack pointer must be csp. - // Therefore this code must use csp for peek/poke operations when the - // stub is generated. When the stub is called - // (via DirectCEntryStub::GenerateCall), the caller must setup an ExitFrame - // and configure the stack pointer *before* doing the call. - const Register old_stack_pointer = __ StackPointer(); - __ SetStackPointer(csp); - - // Put return address on the stack (accessible to GC through exit frame pc). - __ Poke(lr, 0); - // Call the C++ function. - __ Blr(x10); - // Return to calling code. - __ Peek(lr, 0); - __ Ret(); - - __ SetStackPointer(old_stack_pointer); -} - -void DirectCEntryStub::GenerateCall(MacroAssembler* masm, - Register target) { - // Make sure the caller configured the stack pointer (see comment in - // DirectCEntryStub::Generate). - ASSERT(csp.Is(__ StackPointer())); - - intptr_t code = - reinterpret_cast(GetCode(masm->isolate()).location()); - __ Mov(lr, Operand(code, RelocInfo::CODE_TARGET)); - __ Mov(x10, target); - // Branch to the stub. - __ Blr(lr); -} - - -// Probe the name dictionary in the 'elements' register. -// Jump to the 'done' label if a property with the given name is found. -// Jump to the 'miss' label otherwise. -// -// If lookup was successful 'scratch2' will be equal to elements + 4 * index. -// 'elements' and 'name' registers are preserved on miss. -void NameDictionaryLookupStub::GeneratePositiveLookup( - MacroAssembler* masm, - Label* miss, - Label* done, - Register elements, - Register name, - Register scratch1, - Register scratch2) { - ASSERT(!AreAliased(elements, name, scratch1, scratch2)); - - // Assert that name contains a string. - __ AssertName(name); - - // Compute the capacity mask. - __ Ldrsw(scratch1, UntagSmiFieldMemOperand(elements, kCapacityOffset)); - __ Sub(scratch1, scratch1, 1); - - // Generate an unrolled loop that performs a few probes before giving up. - for (int i = 0; i < kInlinedProbes; i++) { - // Compute the masked index: (hash + i + i * i) & mask. - __ Ldr(scratch2, FieldMemOperand(name, Name::kHashFieldOffset)); - if (i > 0) { - // Add the probe offset (i + i * i) left shifted to avoid right shifting - // the hash in a separate instruction. The value hash + i + i * i is right - // shifted in the following and instruction. - ASSERT(NameDictionary::GetProbeOffset(i) < - 1 << (32 - Name::kHashFieldOffset)); - __ Add(scratch2, scratch2, Operand( - NameDictionary::GetProbeOffset(i) << Name::kHashShift)); - } - __ And(scratch2, scratch1, Operand(scratch2, LSR, Name::kHashShift)); - - // Scale the index by multiplying by the element size. - ASSERT(NameDictionary::kEntrySize == 3); - __ Add(scratch2, scratch2, Operand(scratch2, LSL, 1)); - - // Check if the key is identical to the name. - __ Add(scratch2, elements, Operand(scratch2, LSL, kPointerSizeLog2)); - // TODO(jbramley): We need another scratch here, but some callers can't - // provide a scratch3 so we have to use Tmp1(). We should find a clean way - // to make it unavailable to the MacroAssembler for a short time. - __ Ldr(__ Tmp1(), FieldMemOperand(scratch2, kElementsStartOffset)); - __ Cmp(name, __ Tmp1()); - __ B(eq, done); - } - - // The inlined probes didn't find the entry. - // Call the complete stub to scan the whole dictionary. - - CPURegList spill_list(CPURegister::kRegister, kXRegSize, 0, 6); - spill_list.Combine(lr); - spill_list.Remove(scratch1); - spill_list.Remove(scratch2); - - __ PushCPURegList(spill_list); - - if (name.is(x0)) { - ASSERT(!elements.is(x1)); - __ Mov(x1, name); - __ Mov(x0, elements); - } else { - __ Mov(x0, elements); - __ Mov(x1, name); - } - - Label not_found; - NameDictionaryLookupStub stub(POSITIVE_LOOKUP); - __ CallStub(&stub); - __ Cbz(x0, ¬_found); - __ Mov(scratch2, x2); // Move entry index into scratch2. - __ PopCPURegList(spill_list); - __ B(done); - - __ Bind(¬_found); - __ PopCPURegList(spill_list); - __ B(miss); -} - - -void NameDictionaryLookupStub::GenerateNegativeLookup(MacroAssembler* masm, - Label* miss, - Label* done, - Register receiver, - Register properties, - Handle name, - Register scratch0) { - ASSERT(!AreAliased(receiver, properties, scratch0)); - ASSERT(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++) { - // scratch0 points to properties hash. - // Compute the masked index: (hash + i + i * i) & mask. - Register index = scratch0; - // Capacity is smi 2^n. - __ Ldrsw(index, UntagSmiFieldMemOperand(properties, kCapacityOffset)); - __ Sub(index, index, 1); - __ And(index, index, name->Hash() + NameDictionary::GetProbeOffset(i)); - - // Scale the index by multiplying by the entry size. - ASSERT(NameDictionary::kEntrySize == 3); - __ Add(index, index, Operand(index, LSL, 1)); // index *= 3. - - Register entity_name = scratch0; - // Having undefined at this place means the name is not contained. - Register tmp = index; - __ Add(tmp, properties, Operand(index, LSL, kPointerSizeLog2)); - __ Ldr(entity_name, FieldMemOperand(tmp, kElementsStartOffset)); - - __ JumpIfRoot(entity_name, Heap::kUndefinedValueRootIndex, done); - - // Stop if found the property. - __ Cmp(entity_name, Operand(name)); - __ B(eq, miss); - - Label good; - __ JumpIfRoot(entity_name, Heap::kTheHoleValueRootIndex, &good); - - // Check if the entry name is not a unique name. - __ Ldr(entity_name, FieldMemOperand(entity_name, HeapObject::kMapOffset)); - __ Ldrb(entity_name, - FieldMemOperand(entity_name, Map::kInstanceTypeOffset)); - __ JumpIfNotUniqueName(entity_name, miss); - __ Bind(&good); - } - - CPURegList spill_list(CPURegister::kRegister, kXRegSize, 0, 6); - spill_list.Combine(lr); - spill_list.Remove(scratch0); // Scratch registers don't need to be preserved. - - __ PushCPURegList(spill_list); - - __ Ldr(x0, FieldMemOperand(receiver, JSObject::kPropertiesOffset)); - __ Mov(x1, Operand(name)); - NameDictionaryLookupStub stub(NEGATIVE_LOOKUP); - __ CallStub(&stub); - // Move stub return value to scratch0. Note that scratch0 is not included in - // spill_list and won't be clobbered by PopCPURegList. - __ Mov(scratch0, x0); - __ PopCPURegList(spill_list); - - __ Cbz(scratch0, done); - __ B(miss); -} - - -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. - // - // Arguments are in x0 and x1: - // x0: property dictionary. - // x1: the name of the property we are looking for. - // - // Return value is in x0 and is zero if lookup failed, non zero otherwise. - // If the lookup is successful, x2 will contains the index of the entry. - - Register result = x0; - Register dictionary = x0; - Register key = x1; - Register index = x2; - Register mask = x3; - Register hash = x4; - Register undefined = x5; - Register entry_key = x6; - - Label in_dictionary, maybe_in_dictionary, not_in_dictionary; - - __ Ldrsw(mask, UntagSmiFieldMemOperand(dictionary, kCapacityOffset)); - __ Sub(mask, mask, 1); - - __ Ldr(hash, FieldMemOperand(key, Name::kHashFieldOffset)); - __ LoadRoot(undefined, Heap::kUndefinedValueRootIndex); - - for (int i = kInlinedProbes; i < kTotalProbes; i++) { - // Compute the masked index: (hash + i + i * i) & mask. - // Capacity is smi 2^n. - if (i > 0) { - // Add the probe offset (i + i * i) left shifted to avoid right shifting - // the hash in a separate instruction. The value hash + i + i * i is right - // shifted in the following and instruction. - ASSERT(NameDictionary::GetProbeOffset(i) < - 1 << (32 - Name::kHashFieldOffset)); - __ Add(index, hash, - NameDictionary::GetProbeOffset(i) << Name::kHashShift); - } else { - __ Mov(index, hash); - } - __ And(index, mask, Operand(index, LSR, Name::kHashShift)); - - // Scale the index by multiplying by the entry size. - ASSERT(NameDictionary::kEntrySize == 3); - __ Add(index, index, Operand(index, LSL, 1)); // index *= 3. - - __ Add(index, dictionary, Operand(index, LSL, kPointerSizeLog2)); - __ Ldr(entry_key, FieldMemOperand(index, kElementsStartOffset)); - - // Having undefined at this place means the name is not contained. - __ Cmp(entry_key, undefined); - __ B(eq, ¬_in_dictionary); - - // Stop if found the property. - __ Cmp(entry_key, key); - __ B(eq, &in_dictionary); - - if (i != kTotalProbes - 1 && mode_ == NEGATIVE_LOOKUP) { - // Check if the entry name is not a unique name. - __ Ldr(entry_key, FieldMemOperand(entry_key, HeapObject::kMapOffset)); - __ Ldrb(entry_key, FieldMemOperand(entry_key, Map::kInstanceTypeOffset)); - __ JumpIfNotUniqueName(entry_key, &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, 0); - __ Ret(); - } - - __ Bind(&in_dictionary); - __ Mov(result, 1); - __ Ret(); - - __ Bind(¬_in_dictionary); - __ Mov(result, 0); - __ Ret(); -} - - -template -static void CreateArrayDispatch(MacroAssembler* masm, - AllocationSiteOverrideMode mode) { - ASM_LOCATION("CreateArrayDispatch"); - if (mode == DISABLE_ALLOCATION_SITES) { - T stub(GetInitialFastElementsKind(), mode); - __ TailCallStub(&stub); - - } else if (mode == DONT_OVERRIDE) { - Register kind = x3; - int last_index = - GetSequenceIndexFromFastElementsKind(TERMINAL_FAST_ELEMENTS_KIND); - for (int i = 0; i <= last_index; ++i) { - Label next; - ElementsKind candidate_kind = GetFastElementsKindFromSequenceIndex(i); - // TODO(jbramley): Is this the best way to handle this? Can we make the - // tail calls conditional, rather than hopping over each one? - __ CompareAndBranch(kind, candidate_kind, ne, &next); - T stub(candidate_kind); - __ TailCallStub(&stub); - __ Bind(&next); - } - - // If we reached this point there is a problem. - __ Abort(kUnexpectedElementsKindInArrayConstructor); - - } else { - UNREACHABLE(); - } -} - - -// TODO(jbramley): If this needs to be a special case, make it a proper template -// specialization, and not a separate function. -static void CreateArrayDispatchOneArgument(MacroAssembler* masm, - AllocationSiteOverrideMode mode) { - ASM_LOCATION("CreateArrayDispatchOneArgument"); - // x0 - argc - // x1 - constructor? - // x2 - allocation site (if mode != DISABLE_ALLOCATION_SITES) - // x3 - kind (if mode != DISABLE_ALLOCATION_SITES) - // sp[0] - last argument - - Register allocation_site = x2; - Register kind = x3; - - Label normal_sequence; - if (mode == DONT_OVERRIDE) { - STATIC_ASSERT(FAST_SMI_ELEMENTS == 0); - STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1); - STATIC_ASSERT(FAST_ELEMENTS == 2); - STATIC_ASSERT(FAST_HOLEY_ELEMENTS == 3); - STATIC_ASSERT(FAST_DOUBLE_ELEMENTS == 4); - STATIC_ASSERT(FAST_HOLEY_DOUBLE_ELEMENTS == 5); - - // Is the low bit set? If so, the array is holey. - __ Tbnz(kind, 0, &normal_sequence); - } - - // Look at the last argument. - // TODO(jbramley): What does a 0 argument represent? - __ Peek(x10, 0); - __ Cbz(x10, &normal_sequence); - - if (mode == DISABLE_ALLOCATION_SITES) { - ElementsKind initial = GetInitialFastElementsKind(); - ElementsKind holey_initial = GetHoleyElementsKind(initial); - - ArraySingleArgumentConstructorStub stub_holey(holey_initial, - DISABLE_ALLOCATION_SITES); - __ TailCallStub(&stub_holey); - - __ Bind(&normal_sequence); - ArraySingleArgumentConstructorStub stub(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 (only if we have an allocation site in the slot). - __ Orr(kind, kind, 1); - - if (FLAG_debug_code) { - __ Ldr(x10, FieldMemOperand(allocation_site, 0)); - __ JumpIfNotRoot(x10, Heap::kAllocationSiteMapRootIndex, - &normal_sequence); - __ Assert(eq, kExpectedAllocationSite); - } - - // Save the resulting elements kind in type info. We can't just store 'kind' - // 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); - __ Ldr(x11, FieldMemOperand(allocation_site, - AllocationSite::kTransitionInfoOffset)); - __ Add(x11, x11, Operand(Smi::FromInt(kFastElementsKindPackedToHoley))); - __ Str(x11, FieldMemOperand(allocation_site, - AllocationSite::kTransitionInfoOffset)); - - __ Bind(&normal_sequence); - int last_index = - GetSequenceIndexFromFastElementsKind(TERMINAL_FAST_ELEMENTS_KIND); - for (int i = 0; i <= last_index; ++i) { - Label next; - ElementsKind candidate_kind = GetFastElementsKindFromSequenceIndex(i); - // TODO(jbramley): Is this the best way to handle this? Can we make the - // tail calls conditional, rather than hopping over each one? - __ CompareAndBranch(kind, candidate_kind, ne, &next); - ArraySingleArgumentConstructorStub stub(candidate_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(kind); - stub.GetCode(isolate); - if (AllocationSite::GetMode(kind) != DONT_TRACK_ALLOCATION_SITE) { - T stub1(kind, DISABLE_ALLOCATION_SITES); - stub1.GetCode(isolate); - } - } -} - - -void ArrayConstructorStubBase::GenerateStubsAheadOfTime(Isolate* isolate) { - ArrayConstructorStubAheadOfTimeHelper( - isolate); - ArrayConstructorStubAheadOfTimeHelper( - isolate); - ArrayConstructorStubAheadOfTimeHelper( - isolate); -} - - -void InternalArrayConstructorStubBase::GenerateStubsAheadOfTime( - Isolate* isolate) { - ElementsKind kinds[2] = { FAST_ELEMENTS, FAST_HOLEY_ELEMENTS }; - for (int i = 0; i < 2; i++) { - // For internal arrays we only need a few things - InternalArrayNoArgumentConstructorStub stubh1(kinds[i]); - stubh1.GetCode(isolate); - InternalArraySingleArgumentConstructorStub stubh2(kinds[i]); - stubh2.GetCode(isolate); - InternalArrayNArgumentsConstructorStub stubh3(kinds[i]); - stubh3.GetCode(isolate); - } -} - - -void ArrayConstructorStub::GenerateDispatchToArrayStub( - MacroAssembler* masm, - AllocationSiteOverrideMode mode) { - Register argc = x0; - if (argument_count_ == ANY) { - Label zero_case, n_case; - __ Cbz(argc, &zero_case); - __ Cmp(argc, 1); - __ B(ne, &n_case); - - // One argument. - CreateArrayDispatchOneArgument(masm, mode); - - __ Bind(&zero_case); - // No arguments. - CreateArrayDispatch(masm, mode); - - __ Bind(&n_case); - // N arguments. - CreateArrayDispatch(masm, mode); - - } else if (argument_count_ == NONE) { - CreateArrayDispatch(masm, mode); - } else if (argument_count_ == ONE) { - CreateArrayDispatchOneArgument(masm, mode); - } else if (argument_count_ == MORE_THAN_ONE) { - CreateArrayDispatch(masm, mode); - } else { - UNREACHABLE(); - } -} - - -void ArrayConstructorStub::Generate(MacroAssembler* masm) { - ASM_LOCATION("ArrayConstructorStub::Generate"); - // ----------- S t a t e ------------- - // -- x0 : argc (only if argument_count_ == ANY) - // -- x1 : constructor - // -- x2 : feedback vector (fixed array or undefined) - // -- x3 : slot index (if x2 is fixed array) - // -- sp[0] : return address - // -- sp[4] : last argument - // ----------------------------------- - Register constructor = x1; - Register feedback_vector = x2; - Register slot_index = x3; - - if (FLAG_debug_code) { - // The array construct code is only set for the global and natives - // builtin Array functions which always have maps. - - Label unexpected_map, map_ok; - // Initial map for the builtin Array function should be a map. - __ Ldr(x10, FieldMemOperand(constructor, - JSFunction::kPrototypeOrInitialMapOffset)); - // Will both indicate a NULL and a Smi. - __ JumpIfSmi(x10, &unexpected_map); - __ JumpIfObjectType(x10, x10, x11, MAP_TYPE, &map_ok); - __ Bind(&unexpected_map); - __ Abort(kUnexpectedInitialMapForArrayFunction); - __ Bind(&map_ok); - - // In feedback_vector, we expect either undefined or a valid fixed array. - Label okay_here; - Handle fixed_array_map = masm->isolate()->factory()->fixed_array_map(); - __ JumpIfRoot(feedback_vector, Heap::kUndefinedValueRootIndex, &okay_here); - __ Ldr(x10, FieldMemOperand(feedback_vector, FixedArray::kMapOffset)); - __ Cmp(x10, Operand(fixed_array_map)); - __ Assert(eq, kExpectedFixedArrayInFeedbackVector); - - // slot_index should be a smi if we don't have undefined in feedback_vector. - __ AssertSmi(slot_index); - - __ Bind(&okay_here); - } - - Register allocation_site = x2; // Overwrites feedback_vector. - Register kind = x3; - Label no_info; - // Get the elements kind and case on that. - __ JumpIfRoot(feedback_vector, Heap::kUndefinedValueRootIndex, &no_info); - __ Add(feedback_vector, feedback_vector, - Operand::UntagSmiAndScale(slot_index, kPointerSizeLog2)); - __ Ldr(allocation_site, FieldMemOperand(feedback_vector, - FixedArray::kHeaderSize)); - - // If the feedback vector is undefined, or contains anything other than an - // AllocationSite, call an array constructor that doesn't use AllocationSites. - __ Ldr(x10, FieldMemOperand(allocation_site, AllocationSite::kMapOffset)); - __ JumpIfNotRoot(x10, Heap::kAllocationSiteMapRootIndex, &no_info); - - __ Ldrsw(kind, - UntagSmiFieldMemOperand(allocation_site, - AllocationSite::kTransitionInfoOffset)); - __ And(kind, kind, AllocationSite::ElementsKindBits::kMask); - GenerateDispatchToArrayStub(masm, DONT_OVERRIDE); - - __ Bind(&no_info); - GenerateDispatchToArrayStub(masm, DISABLE_ALLOCATION_SITES); -} - - -void InternalArrayConstructorStub::GenerateCase( - MacroAssembler* masm, ElementsKind kind) { - Label zero_case, n_case; - Register argc = x0; - - __ Cbz(argc, &zero_case); - __ CompareAndBranch(argc, 1, ne, &n_case); - - // One argument. - if (IsFastPackedElementsKind(kind)) { - Label packed_case; - - // We might need to create a holey array; look at the first argument. - __ Peek(x10, 0); - __ Cbz(x10, &packed_case); - - InternalArraySingleArgumentConstructorStub - stub1_holey(GetHoleyElementsKind(kind)); - __ TailCallStub(&stub1_holey); - - __ Bind(&packed_case); - } - InternalArraySingleArgumentConstructorStub stub1(kind); - __ TailCallStub(&stub1); - - __ Bind(&zero_case); - // No arguments. - InternalArrayNoArgumentConstructorStub stub0(kind); - __ TailCallStub(&stub0); - - __ Bind(&n_case); - // N arguments. - InternalArrayNArgumentsConstructorStub stubN(kind); - __ TailCallStub(&stubN); -} - - -void InternalArrayConstructorStub::Generate(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- x0 : argc - // -- x1 : constructor - // -- sp[0] : return address - // -- sp[4] : last argument - // ----------------------------------- - Handle undefined_sentinel( - masm->isolate()->heap()->undefined_value(), masm->isolate()); - - Register constructor = x1; - - if (FLAG_debug_code) { - // The array construct code is only set for the global and natives - // builtin Array functions which always have maps. - - Label unexpected_map, map_ok; - // Initial map for the builtin Array function should be a map. - __ Ldr(x10, FieldMemOperand(constructor, - JSFunction::kPrototypeOrInitialMapOffset)); - // Will both indicate a NULL and a Smi. - __ JumpIfSmi(x10, &unexpected_map); - __ JumpIfObjectType(x10, x10, x11, MAP_TYPE, &map_ok); - __ Bind(&unexpected_map); - __ Abort(kUnexpectedInitialMapForArrayFunction); - __ Bind(&map_ok); - } - - Register kind = w3; - // Figure out the right elements kind - __ Ldr(x10, FieldMemOperand(constructor, - JSFunction::kPrototypeOrInitialMapOffset)); - - // TODO(jbramley): Add a helper function to read elements kind from an - // existing map. - // Load the map's "bit field 2" into result. - __ Ldr(kind, FieldMemOperand(x10, Map::kBitField2Offset)); - // Retrieve elements_kind from bit field 2. - __ Ubfx(kind, kind, Map::kElementsKindShift, Map::kElementsKindBitCount); - - if (FLAG_debug_code) { - Label done; - __ Cmp(x3, FAST_ELEMENTS); - __ Ccmp(x3, FAST_HOLEY_ELEMENTS, ZFlag, ne); - __ Assert(eq, kInvalidElementsKindForInternalArrayOrInternalPackedArray); - } - - Label fast_elements_case; - __ CompareAndBranch(kind, FAST_ELEMENTS, eq, &fast_elements_case); - GenerateCase(masm, FAST_HOLEY_ELEMENTS); - - __ Bind(&fast_elements_case); - GenerateCase(masm, FAST_ELEMENTS); -} - - -void CallApiFunctionStub::Generate(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- x0 : callee - // -- x4 : call_data - // -- x2 : holder - // -- x1 : api_function_address - // -- cp : context - // -- - // -- sp[0] : last argument - // -- ... - // -- sp[(argc - 1) * 8] : first argument - // -- sp[argc * 8] : receiver - // ----------------------------------- - - Register callee = x0; - Register call_data = x4; - Register holder = x2; - Register api_function_address = x1; - Register context = cp; - - int argc = ArgumentBits::decode(bit_field_); - bool is_store = IsStoreBits::decode(bit_field_); - bool call_data_undefined = CallDataUndefinedBits::decode(bit_field_); - - 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::kArgsLength == 7); - - Isolate* isolate = masm->isolate(); - - // FunctionCallbackArguments: context, callee and call data. - __ Push(context, callee, call_data); - - // Load context from callee - __ Ldr(context, FieldMemOperand(callee, JSFunction::kContextOffset)); - - if (!call_data_undefined) { - __ LoadRoot(call_data, Heap::kUndefinedValueRootIndex); - } - Register isolate_reg = x5; - __ Mov(isolate_reg, Operand(ExternalReference::isolate_address(isolate))); - - // FunctionCallbackArguments: - // return value, return value default, isolate, holder. - __ Push(call_data, call_data, isolate_reg, holder); - - // Prepare arguments. - Register args = x6; - __ Mov(args, masm->StackPointer()); - - // Allocate the v8::Arguments structure in the arguments' space, since it's - // not controlled by GC. - const int kApiStackSpace = 4; - - // Allocate space for CallApiFunctionAndReturn can store some scratch - // registeres on the stack. - const int kCallApiFunctionSpillSpace = 4; - - FrameScope frame_scope(masm, StackFrame::MANUAL); - __ EnterExitFrame(false, x10, kApiStackSpace + kCallApiFunctionSpillSpace); - - // TODO(all): Optimize this with stp and suchlike. - ASSERT(!AreAliased(x0, api_function_address)); - // x0 = FunctionCallbackInfo& - // Arguments is after the return address. - __ Add(x0, masm->StackPointer(), 1 * kPointerSize); - // FunctionCallbackInfo::implicit_args_ - __ Str(args, MemOperand(x0, 0 * kPointerSize)); - // FunctionCallbackInfo::values_ - __ Add(x10, args, Operand((FCA::kArgsLength - 1 + argc) * kPointerSize)); - __ Str(x10, MemOperand(x0, 1 * kPointerSize)); - // FunctionCallbackInfo::length_ = argc - __ Mov(x10, argc); - __ Str(x10, MemOperand(x0, 2 * kPointerSize)); - // FunctionCallbackInfo::is_construct_call = 0 - __ Str(xzr, MemOperand(x0, 3 * kPointerSize)); - - const int kStackUnwindSpace = argc + FCA::kArgsLength + 1; - Address thunk_address = FUNCTION_ADDR(&InvokeFunctionCallback); - ExternalReference::Type thunk_type = ExternalReference::PROFILING_API_CALL; - ApiFunction thunk_fun(thunk_address); - ExternalReference thunk_ref = ExternalReference(&thunk_fun, thunk_type, - masm->isolate()); - - AllowExternalCallThatCantCauseGC scope(masm); - MemOperand context_restore_operand( - fp, (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; - } - MemOperand return_value_operand(fp, return_value_offset * kPointerSize); - - const int spill_offset = 1 + kApiStackSpace; - __ CallApiFunctionAndReturn(api_function_address, - thunk_ref, - kStackUnwindSpace, - spill_offset, - return_value_operand, - &context_restore_operand); -} - - -void CallApiGetterStub::Generate(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- sp[0] : name - // -- sp[8 - kArgsLength*8] : PropertyCallbackArguments object - // -- ... - // -- x2 : api_function_address - // ----------------------------------- - - Register api_function_address = x2; - - __ Mov(x0, masm->StackPointer()); // x0 = Handle - __ Add(x1, x0, 1 * kPointerSize); // x1 = PCA - - const int kApiStackSpace = 1; - - // Allocate space for CallApiFunctionAndReturn can store some scratch - // registeres on the stack. - const int kCallApiFunctionSpillSpace = 4; - - FrameScope frame_scope(masm, StackFrame::MANUAL); - __ EnterExitFrame(false, x10, kApiStackSpace + kCallApiFunctionSpillSpace); - - // Create PropertyAccessorInfo instance on the stack above the exit frame with - // x1 (internal::Object** args_) as the data. - __ Poke(x1, 1 * kPointerSize); - __ Add(x1, masm->StackPointer(), 1 * kPointerSize); // x1 = AccessorInfo& - - const int kStackUnwindSpace = PropertyCallbackArguments::kArgsLength + 1; - - Address thunk_address = FUNCTION_ADDR(&InvokeAccessorGetterCallback); - ExternalReference::Type thunk_type = - ExternalReference::PROFILING_GETTER_CALL; - ApiFunction thunk_fun(thunk_address); - ExternalReference thunk_ref = ExternalReference(&thunk_fun, thunk_type, - masm->isolate()); - - const int spill_offset = 1 + kApiStackSpace; - __ CallApiFunctionAndReturn(api_function_address, - thunk_ref, - kStackUnwindSpace, - spill_offset, - MemOperand(fp, 6 * kPointerSize), - NULL); -} - - -#undef __ - -} } // namespace v8::internal - -#endif // V8_TARGET_ARCH_A64 diff --git a/deps/v8/src/a64/code-stubs-a64.h b/deps/v8/src/a64/code-stubs-a64.h deleted file mode 100644 index 0709bfc511..0000000000 --- a/deps/v8/src/a64/code-stubs-a64.h +++ /dev/null @@ -1,469 +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. - -#ifndef V8_A64_CODE_STUBS_A64_H_ -#define V8_A64_CODE_STUBS_A64_H_ - -#include "ic-inl.h" - -namespace v8 { -namespace internal { - - -void ArrayNativeCode(MacroAssembler* masm, Label* call_generic_code); - - -class StoreBufferOverflowStub: public PlatformCodeStub { - public: - explicit StoreBufferOverflowStub(SaveFPRegsMode save_fp) - : save_doubles_(save_fp) { } - - void Generate(MacroAssembler* masm); - - static void GenerateFixedRegStubsAheadOfTime(Isolate* isolate); - virtual bool SometimesSetsUpAFrame() { return false; } - - private: - SaveFPRegsMode save_doubles_; - - Major MajorKey() { return StoreBufferOverflow; } - int MinorKey() { return (save_doubles_ == kSaveFPRegs) ? 1 : 0; } -}; - - -class StringHelper : public AllStatic { - public: - // TODO(all): These don't seem to be used any more. Delete them. - - // Generate string hash. - static void GenerateHashInit(MacroAssembler* masm, - Register hash, - Register character); - - static void GenerateHashAddCharacter(MacroAssembler* masm, - Register hash, - Register character); - - static void GenerateHashGetHash(MacroAssembler* masm, - Register hash, - Register scratch); - - private: - DISALLOW_IMPLICIT_CONSTRUCTORS(StringHelper); -}; - - -class RecordWriteStub: public PlatformCodeStub { - public: - // Stub to record the write of 'value' at 'address' in 'object'. - // Typically 'address' = 'object' + . - // See MacroAssembler::RecordWriteField() for example. - RecordWriteStub(Register object, - Register value, - Register address, - RememberedSetAction remembered_set_action, - SaveFPRegsMode fp_mode) - : object_(object), - value_(value), - address_(address), - remembered_set_action_(remembered_set_action), - save_fp_regs_mode_(fp_mode), - regs_(object, // An input reg. - address, // An input reg. - value) { // One scratch reg. - } - - enum Mode { - STORE_BUFFER_ONLY, - INCREMENTAL, - INCREMENTAL_COMPACTION - }; - - virtual bool SometimesSetsUpAFrame() { return false; } - - static Mode GetMode(Code* stub) { - // Find the mode depending on the first two instructions. - Instruction* instr1 = - reinterpret_cast(stub->instruction_start()); - Instruction* instr2 = instr1->following(); - - if (instr1->IsUncondBranchImm()) { - ASSERT(instr2->IsPCRelAddressing() && (instr2->Rd() == xzr.code())); - return INCREMENTAL; - } - - ASSERT(instr1->IsPCRelAddressing() && (instr1->Rd() == xzr.code())); - - if (instr2->IsUncondBranchImm()) { - return INCREMENTAL_COMPACTION; - } - - ASSERT(instr2->IsPCRelAddressing()); - - return STORE_BUFFER_ONLY; - } - - // We patch the two first instructions of the stub back and forth between an - // adr and branch when we start and stop incremental heap marking. - // The branch is - // b label - // The adr is - // adr xzr label - // so effectively a nop. - static void Patch(Code* stub, Mode mode) { - // We are going to patch the two first instructions of the stub. - PatchingAssembler patcher( - reinterpret_cast(stub->instruction_start()), 2); - Instruction* instr1 = patcher.InstructionAt(0); - Instruction* instr2 = patcher.InstructionAt(kInstructionSize); - // Instructions must be either 'adr' or 'b'. - ASSERT(instr1->IsPCRelAddressing() || instr1->IsUncondBranchImm()); - ASSERT(instr2->IsPCRelAddressing() || instr2->IsUncondBranchImm()); - // Retrieve the offsets to the labels. - int32_t offset_to_incremental_noncompacting = instr1->ImmPCOffset(); - int32_t offset_to_incremental_compacting = instr2->ImmPCOffset(); - - switch (mode) { - case STORE_BUFFER_ONLY: - ASSERT(GetMode(stub) == INCREMENTAL || - GetMode(stub) == INCREMENTAL_COMPACTION); - patcher.adr(xzr, offset_to_incremental_noncompacting); - patcher.adr(xzr, offset_to_incremental_compacting); - break; - case INCREMENTAL: - ASSERT(GetMode(stub) == STORE_BUFFER_ONLY); - patcher.b(offset_to_incremental_noncompacting >> kInstructionSizeLog2); - patcher.adr(xzr, offset_to_incremental_compacting); - break; - case INCREMENTAL_COMPACTION: - ASSERT(GetMode(stub) == STORE_BUFFER_ONLY); - patcher.adr(xzr, offset_to_incremental_noncompacting); - patcher.b(offset_to_incremental_compacting >> kInstructionSizeLog2); - break; - } - ASSERT(GetMode(stub) == mode); - } - - private: - // This is a helper class to manage the registers associated with the stub. - // The 'object' and 'address' registers must be preserved. - class RegisterAllocation { - public: - RegisterAllocation(Register object, - Register address, - Register scratch) - : object_(object), - address_(address), - scratch0_(scratch), - saved_regs_(kCallerSaved) { - ASSERT(!AreAliased(scratch, object, address)); - - // We would like to require more scratch registers for this stub, - // but the number of registers comes down to the ones used in - // FullCodeGen::SetVar(), which is architecture independent. - // We allocate 2 extra scratch registers that we'll save on the stack. - CPURegList pool_available = GetValidRegistersForAllocation(); - CPURegList used_regs(object, address, scratch); - pool_available.Remove(used_regs); - scratch1_ = Register(pool_available.PopLowestIndex()); - scratch2_ = Register(pool_available.PopLowestIndex()); - - // SaveCallerRegisters method needs to save caller saved register, however - // we don't bother saving ip0 and ip1 because they are used as scratch - // registers by the MacroAssembler. - saved_regs_.Remove(ip0); - saved_regs_.Remove(ip1); - - // The scratch registers will be restored by other means so we don't need - // to save them with the other caller saved registers. - saved_regs_.Remove(scratch0_); - saved_regs_.Remove(scratch1_); - saved_regs_.Remove(scratch2_); - } - - void Save(MacroAssembler* masm) { - // We don't have to save scratch0_ because it was given to us as - // a scratch register. - masm->Push(scratch1_, scratch2_); - } - - void Restore(MacroAssembler* masm) { - masm->Pop(scratch2_, scratch1_); - } - - // If we have to call into C then we need to save and restore all caller- - // saved registers that were not already preserved. - void SaveCallerSaveRegisters(MacroAssembler* masm, SaveFPRegsMode mode) { - // TODO(all): This can be very expensive, and it is likely that not every - // register will need to be preserved. Can we improve this? - masm->PushCPURegList(saved_regs_); - if (mode == kSaveFPRegs) { - masm->PushCPURegList(kCallerSavedFP); - } - } - - void RestoreCallerSaveRegisters(MacroAssembler*masm, SaveFPRegsMode mode) { - // TODO(all): This can be very expensive, and it is likely that not every - // register will need to be preserved. Can we improve this? - if (mode == kSaveFPRegs) { - masm->PopCPURegList(kCallerSavedFP); - } - masm->PopCPURegList(saved_regs_); - } - - Register object() { return object_; } - Register address() { return address_; } - Register scratch0() { return scratch0_; } - Register scratch1() { return scratch1_; } - Register scratch2() { return scratch2_; } - - private: - Register object_; - Register address_; - Register scratch0_; - Register scratch1_; - Register scratch2_; - CPURegList saved_regs_; - - // TODO(all): We should consider moving this somewhere else. - static CPURegList GetValidRegistersForAllocation() { - // The list of valid registers for allocation is defined as all the - // registers without those with a special meaning. - // - // The default list excludes registers x26 to x31 because they are - // reserved for the following purpose: - // - x26 root register - // - x27 context pointer register - // - x28 jssp - // - x29 frame pointer - // - x30 link register(lr) - // - x31 xzr/stack pointer - CPURegList list(CPURegister::kRegister, kXRegSize, 0, 25); - - // We also remove MacroAssembler's scratch registers. - list.Remove(ip0); - list.Remove(ip1); - list.Remove(x8); - list.Remove(x9); - - return list; - } - - friend class RecordWriteStub; - }; - - // A list of stub variants which are pregenerated. - // The variants are stored in the same format as the minor key, so - // MinorKeyFor() can be used to populate and check this list. - static const int kAheadOfTime[]; - - void Generate(MacroAssembler* masm); - void GenerateIncremental(MacroAssembler* masm, Mode mode); - - enum OnNoNeedToInformIncrementalMarker { - kReturnOnNoNeedToInformIncrementalMarker, - kUpdateRememberedSetOnNoNeedToInformIncrementalMarker - }; - - void CheckNeedsToInformIncrementalMarker( - MacroAssembler* masm, - OnNoNeedToInformIncrementalMarker on_no_need, - Mode mode); - void InformIncrementalMarker(MacroAssembler* masm, Mode mode); - - Major MajorKey() { return RecordWrite; } - - int MinorKey() { - return MinorKeyFor(object_, value_, address_, remembered_set_action_, - save_fp_regs_mode_); - } - - static int MinorKeyFor(Register object, - Register value, - Register address, - RememberedSetAction action, - SaveFPRegsMode fp_mode) { - ASSERT(object.Is64Bits()); - ASSERT(value.Is64Bits()); - ASSERT(address.Is64Bits()); - return ObjectBits::encode(object.code()) | - ValueBits::encode(value.code()) | - AddressBits::encode(address.code()) | - RememberedSetActionBits::encode(action) | - SaveFPRegsModeBits::encode(fp_mode); - } - - void Activate(Code* code) { - code->GetHeap()->incremental_marking()->ActivateGeneratedStub(code); - } - - class ObjectBits: public BitField {}; - class ValueBits: public BitField {}; - class AddressBits: public BitField {}; - class RememberedSetActionBits: public BitField {}; - class SaveFPRegsModeBits: public BitField {}; - - Register object_; - Register value_; - Register address_; - RememberedSetAction remembered_set_action_; - SaveFPRegsMode save_fp_regs_mode_; - Label slow_; - RegisterAllocation regs_; -}; - - -// Helper to call C++ functions from generated code. The caller must prepare -// the exit frame before doing the call with GenerateCall. -class DirectCEntryStub: public PlatformCodeStub { - public: - DirectCEntryStub() {} - void Generate(MacroAssembler* masm); - void GenerateCall(MacroAssembler* masm, Register target); - - private: - Major MajorKey() { return DirectCEntry; } - int MinorKey() { return 0; } - - bool NeedsImmovableCode() { return true; } -}; - - -class NameDictionaryLookupStub: public PlatformCodeStub { - public: - enum LookupMode { POSITIVE_LOOKUP, NEGATIVE_LOOKUP }; - - explicit NameDictionaryLookupStub(LookupMode mode) : mode_(mode) { } - - void Generate(MacroAssembler* masm); - - static void GenerateNegativeLookup(MacroAssembler* masm, - Label* miss, - Label* done, - Register receiver, - Register properties, - Handle name, - Register scratch0); - - static void GeneratePositiveLookup(MacroAssembler* masm, - Label* miss, - Label* done, - Register elements, - Register name, - Register scratch1, - Register scratch2); - - virtual bool SometimesSetsUpAFrame() { 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; - - Major MajorKey() { return NameDictionaryLookup; } - - int MinorKey() { - return LookupModeBits::encode(mode_); - } - - class LookupModeBits: public BitField {}; - - LookupMode mode_; -}; - - -class SubStringStub: public PlatformCodeStub { - public: - SubStringStub() {} - - private: - Major MajorKey() { return SubString; } - int MinorKey() { return 0; } - - void Generate(MacroAssembler* masm); -}; - - -class StringCompareStub: public PlatformCodeStub { - public: - StringCompareStub() { } - - // Compares two flat ASCII strings and returns result in x0. - static void GenerateCompareFlatAsciiStrings(MacroAssembler* masm, - Register left, - Register right, - Register scratch1, - Register scratch2, - Register scratch3, - Register scratch4); - - // Compare two flat ASCII strings for equality and returns result - // in x0. - static void GenerateFlatAsciiStringEquals(MacroAssembler* masm, - Register left, - Register right, - Register scratch1, - Register scratch2, - Register scratch3); - - private: - virtual Major MajorKey() { return StringCompare; } - virtual int MinorKey() { return 0; } - virtual void Generate(MacroAssembler* masm); - - static void GenerateAsciiCharsCompareLoop(MacroAssembler* masm, - Register left, - Register right, - Register length, - Register scratch1, - Register scratch2, - Label* chars_not_equal); -}; - - -struct PlatformCallInterfaceDescriptor { - explicit PlatformCallInterfaceDescriptor( - TargetAddressStorageMode storage_mode) - : storage_mode_(storage_mode) { } - - TargetAddressStorageMode storage_mode() { return storage_mode_; } - - private: - TargetAddressStorageMode storage_mode_; -}; - - -} } // namespace v8::internal - -#endif // V8_A64_CODE_STUBS_A64_H_ diff --git a/deps/v8/src/a64/codegen-a64.cc b/deps/v8/src/a64/codegen-a64.cc deleted file mode 100644 index 3f0e2295df..0000000000 --- a/deps/v8/src/a64/codegen-a64.cc +++ /dev/null @@ -1,616 +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 "v8.h" - -#if V8_TARGET_ARCH_A64 - -#include "codegen.h" -#include "macro-assembler.h" -#include "simulator-a64.h" - -namespace v8 { -namespace internal { - -#define __ ACCESS_MASM(masm) - -#if defined(USE_SIMULATOR) -byte* fast_exp_a64_machine_code = NULL; -double fast_exp_simulator(double x) { - Simulator * simulator = Simulator::current(Isolate::Current()); - Simulator::CallArgument args[] = { - Simulator::CallArgument(x), - Simulator::CallArgument::End() - }; - return simulator->CallDouble(fast_exp_a64_machine_code, args); -} -#endif - - -UnaryMathFunction CreateExpFunction() { - if (!FLAG_fast_math) return &std::exp; - - // Use the Math.exp implemetation in MathExpGenerator::EmitMathExp() to create - // an AAPCS64-compliant exp() function. This will be faster than the C - // library's exp() function, but probably less accurate. - size_t actual_size; - byte* buffer = static_cast(OS::Allocate(1 * KB, &actual_size, true)); - if (buffer == NULL) return &std::exp; - - ExternalReference::InitializeMathExpData(); - MacroAssembler masm(NULL, buffer, static_cast(actual_size)); - masm.SetStackPointer(csp); - - // The argument will be in d0 on entry. - DoubleRegister input = d0; - // Use other caller-saved registers for all other values. - DoubleRegister result = d1; - DoubleRegister double_temp1 = d2; - DoubleRegister double_temp2 = d3; - Register temp1 = x10; - Register temp2 = x11; - Register temp3 = x12; - - MathExpGenerator::EmitMathExp(&masm, input, result, - double_temp1, double_temp2, - temp1, temp2, temp3); - // Move the result to the return register. - masm.Fmov(d0, result); - masm.Ret(); - - CodeDesc desc; - masm.GetCode(&desc); - ASSERT(!RelocInfo::RequiresRelocation(desc)); - - CPU::FlushICache(buffer, actual_size); - OS::ProtectCode(buffer, actual_size); - -#if !defined(USE_SIMULATOR) - return FUNCTION_CAST(buffer); -#else - fast_exp_a64_machine_code = buffer; - return &fast_exp_simulator; -#endif -} - - -UnaryMathFunction CreateSqrtFunction() { - return &std::sqrt; -} - - -// ------------------------------------------------------------------------- -// Platform-specific RuntimeCallHelper functions. - -void StubRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const { - masm->EnterFrame(StackFrame::INTERNAL); - ASSERT(!masm->has_frame()); - masm->set_has_frame(true); -} - - -void StubRuntimeCallHelper::AfterCall(MacroAssembler* masm) const { - masm->LeaveFrame(StackFrame::INTERNAL); - ASSERT(masm->has_frame()); - masm->set_has_frame(false); -} - - -// ------------------------------------------------------------------------- -// Code generators - -void ElementsTransitionGenerator::GenerateMapChangeElementsTransition( - MacroAssembler* masm, AllocationSiteMode mode, - Label* allocation_memento_found) { - // ----------- S t a t e ------------- - // -- x2 : receiver - // -- x3 : target map - // ----------------------------------- - Register receiver = x2; - Register map = x3; - - if (mode == TRACK_ALLOCATION_SITE) { - ASSERT(allocation_memento_found != NULL); - __ JumpIfJSArrayHasAllocationMemento(receiver, x10, x11, - allocation_memento_found); - } - - // Set transitioned map. - __ Str(map, FieldMemOperand(receiver, HeapObject::kMapOffset)); - __ RecordWriteField(receiver, - HeapObject::kMapOffset, - map, - x10, - kLRHasNotBeenSaved, - kDontSaveFPRegs, - EMIT_REMEMBERED_SET, - OMIT_SMI_CHECK); -} - - -void ElementsTransitionGenerator::GenerateSmiToDouble( - MacroAssembler* masm, AllocationSiteMode mode, Label* fail) { - ASM_LOCATION("ElementsTransitionGenerator::GenerateSmiToDouble"); - // ----------- S t a t e ------------- - // -- lr : return address - // -- x0 : value - // -- x1 : key - // -- x2 : receiver - // -- x3 : target map, scratch for subsequent call - // ----------------------------------- - Register receiver = x2; - Register target_map = x3; - - Label gc_required, only_change_map; - - if (mode == TRACK_ALLOCATION_SITE) { - __ JumpIfJSArrayHasAllocationMemento(receiver, x10, x11, fail); - } - - // Check for empty arrays, which only require a map transition and no changes - // to the backing store. - Register elements = x4; - __ Ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset)); - __ JumpIfRoot(elements, Heap::kEmptyFixedArrayRootIndex, &only_change_map); - - __ Push(lr); - Register length = x5; - __ Ldrsw(length, UntagSmiFieldMemOperand(elements, - FixedArray::kLengthOffset)); - - // Allocate new FixedDoubleArray. - Register array_size = x6; - Register array = x7; - __ Lsl(array_size, length, kDoubleSizeLog2); - __ Add(array_size, array_size, FixedDoubleArray::kHeaderSize); - __ Allocate(array_size, array, x10, x11, &gc_required, DOUBLE_ALIGNMENT); - // Register array is non-tagged heap object. - - // Set the destination FixedDoubleArray's length and map. - Register map_root = x6; - __ LoadRoot(map_root, Heap::kFixedDoubleArrayMapRootIndex); - __ SmiTag(x11, length); - __ Str(x11, MemOperand(array, FixedDoubleArray::kLengthOffset)); - __ Str(map_root, MemOperand(array, HeapObject::kMapOffset)); - - __ Str(target_map, FieldMemOperand(receiver, HeapObject::kMapOffset)); - __ RecordWriteField(receiver, HeapObject::kMapOffset, target_map, x6, - kLRHasBeenSaved, kDontSaveFPRegs, OMIT_REMEMBERED_SET, - OMIT_SMI_CHECK); - - // Replace receiver's backing store with newly created FixedDoubleArray. - __ Add(x10, array, kHeapObjectTag); - __ Str(x10, FieldMemOperand(receiver, JSObject::kElementsOffset)); - __ RecordWriteField(receiver, JSObject::kElementsOffset, x10, - x6, kLRHasBeenSaved, kDontSaveFPRegs, - EMIT_REMEMBERED_SET, OMIT_SMI_CHECK); - - // Prepare for conversion loop. - Register src_elements = x10; - Register dst_elements = x11; - Register dst_end = x12; - __ Add(src_elements, elements, FixedArray::kHeaderSize - kHeapObjectTag); - __ Add(dst_elements, array, FixedDoubleArray::kHeaderSize); - __ Add(dst_end, dst_elements, Operand(length, LSL, kDoubleSizeLog2)); - - FPRegister nan_d = d1; - __ Fmov(nan_d, rawbits_to_double(kHoleNanInt64)); - - Label entry, done; - __ B(&entry); - - __ Bind(&only_change_map); - __ Str(target_map, FieldMemOperand(receiver, HeapObject::kMapOffset)); - __ RecordWriteField(receiver, HeapObject::kMapOffset, target_map, x6, - kLRHasNotBeenSaved, kDontSaveFPRegs, OMIT_REMEMBERED_SET, - OMIT_SMI_CHECK); - __ B(&done); - - // Call into runtime if GC is required. - __ Bind(&gc_required); - __ Pop(lr); - __ B(fail); - - // Iterate over the array, copying and coverting smis to doubles. If an - // element is non-smi, write a hole to the destination. - { - Label loop; - __ Bind(&loop); - __ Ldr(x13, MemOperand(src_elements, kPointerSize, PostIndex)); - __ SmiUntagToDouble(d0, x13, kSpeculativeUntag); - __ Tst(x13, kSmiTagMask); - __ Fcsel(d0, d0, nan_d, eq); - __ Str(d0, MemOperand(dst_elements, kDoubleSize, PostIndex)); - - __ Bind(&entry); - __ Cmp(dst_elements, dst_end); - __ B(lt, &loop); - } - - __ Pop(lr); - __ Bind(&done); -} - - -void ElementsTransitionGenerator::GenerateDoubleToObject( - MacroAssembler* masm, AllocationSiteMode mode, Label* fail) { - ASM_LOCATION("ElementsTransitionGenerator::GenerateDoubleToObject"); - // ----------- S t a t e ------------- - // -- x0 : value - // -- x1 : key - // -- x2 : receiver - // -- lr : return address - // -- x3 : target map, scratch for subsequent call - // -- x4 : scratch (elements) - // ----------------------------------- - Register value = x0; - Register key = x1; - Register receiver = x2; - Register target_map = x3; - - if (mode == TRACK_ALLOCATION_SITE) { - __ JumpIfJSArrayHasAllocationMemento(receiver, x10, x11, fail); - } - - // Check for empty arrays, which only require a map transition and no changes - // to the backing store. - Label only_change_map; - Register elements = x4; - __ Ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset)); - __ JumpIfRoot(elements, Heap::kEmptyFixedArrayRootIndex, &only_change_map); - - __ Push(lr); - // TODO(all): These registers may not need to be pushed. Examine - // RecordWriteStub and check whether it's needed. - __ Push(target_map, receiver, key, value); - Register length = x5; - __ Ldrsw(length, UntagSmiFieldMemOperand(elements, - FixedArray::kLengthOffset)); - - // Allocate new FixedArray. - Register array_size = x6; - Register array = x7; - Label gc_required; - __ Mov(array_size, FixedDoubleArray::kHeaderSize); - __ Add(array_size, array_size, Operand(length, LSL, kPointerSizeLog2)); - __ Allocate(array_size, array, x10, x11, &gc_required, NO_ALLOCATION_FLAGS); - - // Set destination FixedDoubleArray's length and map. - Register map_root = x6; - __ LoadRoot(map_root, Heap::kFixedArrayMapRootIndex); - __ SmiTag(x11, length); - __ Str(x11, MemOperand(array, FixedDoubleArray::kLengthOffset)); - __ Str(map_root, MemOperand(array, HeapObject::kMapOffset)); - - // Prepare for conversion loop. - Register src_elements = x10; - Register dst_elements = x11; - Register dst_end = x12; - __ Add(src_elements, elements, - FixedDoubleArray::kHeaderSize - kHeapObjectTag); - __ Add(dst_elements, array, FixedArray::kHeaderSize); - __ Add(array, array, kHeapObjectTag); - __ Add(dst_end, dst_elements, Operand(length, LSL, kPointerSizeLog2)); - - Register the_hole = x14; - Register heap_num_map = x15; - __ LoadRoot(the_hole, Heap::kTheHoleValueRootIndex); - __ LoadRoot(heap_num_map, Heap::kHeapNumberMapRootIndex); - - Label entry; - __ B(&entry); - - // Call into runtime if GC is required. - __ Bind(&gc_required); - __ Pop(value, key, receiver, target_map); - __ Pop(lr); - __ B(fail); - - { - Label loop, convert_hole; - __ Bind(&loop); - __ Ldr(x13, MemOperand(src_elements, kPointerSize, PostIndex)); - __ Cmp(x13, kHoleNanInt64); - __ B(eq, &convert_hole); - - // Non-hole double, copy value into a heap number. - Register heap_num = x5; - __ AllocateHeapNumber(heap_num, &gc_required, x6, x4, heap_num_map); - __ Str(x13, FieldMemOperand(heap_num, HeapNumber::kValueOffset)); - __ Mov(x13, dst_elements); - __ Str(heap_num, MemOperand(dst_elements, kPointerSize, PostIndex)); - __ RecordWrite(array, x13, heap_num, kLRHasBeenSaved, kDontSaveFPRegs, - EMIT_REMEMBERED_SET, OMIT_SMI_CHECK); - - __ B(&entry); - - // Replace the-hole NaN with the-hole pointer. - __ Bind(&convert_hole); - __ Str(the_hole, MemOperand(dst_elements, kPointerSize, PostIndex)); - - __ Bind(&entry); - __ Cmp(dst_elements, dst_end); - __ B(lt, &loop); - } - - __ Pop(value, key, receiver, target_map); - // Replace receiver's backing store with newly created and filled FixedArray. - __ Str(array, FieldMemOperand(receiver, JSObject::kElementsOffset)); - __ RecordWriteField(receiver, JSObject::kElementsOffset, array, x13, - kLRHasBeenSaved, kDontSaveFPRegs, EMIT_REMEMBERED_SET, - OMIT_SMI_CHECK); - __ Pop(lr); - - __ Bind(&only_change_map); - __ Str(target_map, FieldMemOperand(receiver, HeapObject::kMapOffset)); - __ RecordWriteField(receiver, HeapObject::kMapOffset, target_map, x13, - kLRHasNotBeenSaved, kDontSaveFPRegs, OMIT_REMEMBERED_SET, - OMIT_SMI_CHECK); -} - - -bool Code::IsYoungSequence(byte* sequence) { - return MacroAssembler::IsYoungSequence(sequence); -} - - -void Code::GetCodeAgeAndParity(byte* sequence, Age* age, - MarkingParity* parity) { - if (IsYoungSequence(sequence)) { - *age = kNoAgeCodeAge; - *parity = NO_MARKING_PARITY; - } else { - byte* target = sequence + kCodeAgeStubEntryOffset; - Code* stub = GetCodeFromTargetAddress(Memory::Address_at(target)); - GetCodeAgeAndParity(stub, age, parity); - } -} - - -void Code::PatchPlatformCodeAge(Isolate* isolate, - byte* sequence, - Code::Age age, - MarkingParity parity) { - PatchingAssembler patcher(sequence, kCodeAgeSequenceSize / kInstructionSize); - if (age == kNoAgeCodeAge) { - MacroAssembler::EmitFrameSetupForCodeAgePatching(&patcher); - } else { - Code * stub = GetCodeAgeStub(isolate, age, parity); - MacroAssembler::EmitCodeAgeSequence(&patcher, stub); - } -} - - -void StringCharLoadGenerator::Generate(MacroAssembler* masm, - Register string, - Register index, - Register result, - Label* call_runtime) { - // Fetch the instance type of the receiver into result register. - __ Ldr(result, FieldMemOperand(string, HeapObject::kMapOffset)); - __ Ldrb(result, FieldMemOperand(result, Map::kInstanceTypeOffset)); - - // We need special handling for indirect strings. - Label check_sequential; - __ TestAndBranchIfAllClear(result, kIsIndirectStringMask, &check_sequential); - - // Dispatch on the indirect string shape: slice or cons. - Label cons_string; - __ TestAndBranchIfAllClear(result, kSlicedNotConsMask, &cons_string); - - // Handle slices. - Label indirect_string_loaded; - __ Ldrsw(result, - UntagSmiFieldMemOperand(string, SlicedString::kOffsetOffset)); - __ Ldr(string, FieldMemOperand(string, SlicedString::kParentOffset)); - __ Add(index, index, result); - __ B(&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); - __ Ldr(result, FieldMemOperand(string, ConsString::kSecondOffset)); - __ JumpIfNotRoot(result, Heap::kempty_stringRootIndex, call_runtime); - // Get the first of the two strings and load its instance type. - __ Ldr(string, FieldMemOperand(string, ConsString::kFirstOffset)); - - __ Bind(&indirect_string_loaded); - __ Ldr(result, FieldMemOperand(string, HeapObject::kMapOffset)); - __ Ldrb(result, FieldMemOperand(result, Map::kInstanceTypeOffset)); - - // 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 external_string, check_encoding; - __ Bind(&check_sequential); - STATIC_ASSERT(kSeqStringTag == 0); - __ TestAndBranchIfAnySet(result, kStringRepresentationMask, &external_string); - - // Prepare sequential strings - STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqOneByteString::kHeaderSize); - __ Add(string, string, SeqTwoByteString::kHeaderSize - kHeapObjectTag); - __ B(&check_encoding); - - // Handle external strings. - __ Bind(&external_string); - if (FLAG_debug_code) { - // Assert that we do not have a cons or slice (indirect strings) here. - // Sequential strings have already been ruled out. - __ Tst(result, kIsIndirectStringMask); - __ Assert(eq, kExternalStringExpectedButNotFound); - } - // Rule out short external strings. - STATIC_CHECK(kShortExternalStringTag != 0); - // TestAndBranchIfAnySet can emit Tbnz. Do not use it because call_runtime - // can be bound far away in deferred code. - __ Tst(result, kShortExternalStringMask); - __ B(ne, call_runtime); - __ Ldr(string, FieldMemOperand(string, ExternalString::kResourceDataOffset)); - - Label ascii, done; - __ Bind(&check_encoding); - STATIC_ASSERT(kTwoByteStringTag == 0); - __ TestAndBranchIfAnySet(result, kStringEncodingMask, &ascii); - // Two-byte string. - __ Ldrh(result, MemOperand(string, index, LSL, 1)); - __ B(&done); - __ Bind(&ascii); - // Ascii string. - __ Ldrb(result, MemOperand(string, index)); - __ Bind(&done); -} - - -static MemOperand ExpConstant(Register base, int index) { - return MemOperand(base, index * kDoubleSize); -} - - -void MathExpGenerator::EmitMathExp(MacroAssembler* masm, - DoubleRegister input, - DoubleRegister result, - DoubleRegister double_temp1, - DoubleRegister double_temp2, - Register temp1, - Register temp2, - Register temp3) { - // TODO(jbramley): There are several instances where fnmsub could be used - // instead of fmul and fsub. Doing this changes the result, but since this is - // an estimation anyway, does it matter? - - ASSERT(!AreAliased(input, result, - double_temp1, double_temp2, - temp1, temp2, temp3)); - ASSERT(ExternalReference::math_exp_constants(0).address() != NULL); - - Label done; - DoubleRegister double_temp3 = result; - Register constants = temp3; - - // The algorithm used relies on some magic constants which are initialized in - // ExternalReference::InitializeMathExpData(). - - // Load the address of the start of the array. - __ Mov(constants, Operand(ExternalReference::math_exp_constants(0))); - - // We have to do a four-way split here: - // - If input <= about -708.4, the output always rounds to zero. - // - If input >= about 709.8, the output always rounds to +infinity. - // - If the input is NaN, the output is NaN. - // - Otherwise, the result needs to be calculated. - Label result_is_finite_non_zero; - // Assert that we can load offset 0 (the small input threshold) and offset 1 - // (the large input threshold) with a single ldp. - ASSERT(kDRegSizeInBytes == (ExpConstant(constants, 1).offset() - - ExpConstant(constants, 0).offset())); - __ Ldp(double_temp1, double_temp2, ExpConstant(constants, 0)); - - __ Fcmp(input, double_temp1); - __ Fccmp(input, double_temp2, NoFlag, hi); - // At this point, the condition flags can be in one of five states: - // NZCV - // 1000 -708.4 < input < 709.8 result = exp(input) - // 0110 input == 709.8 result = +infinity - // 0010 input > 709.8 result = +infinity - // 0011 input is NaN result = input - // 0000 input <= -708.4 result = +0.0 - - // Continue the common case first. 'mi' tests N == 1. - __ B(&result_is_finite_non_zero, mi); - - // TODO(jbramley): Add (and use) a zero D register for A64. - // TODO(jbramley): Consider adding a +infinity register for A64. - __ Ldr(double_temp2, ExpConstant(constants, 2)); // Synthesize +infinity. - __ Fsub(double_temp1, double_temp1, double_temp1); // Synthesize +0.0. - - // Select between +0.0 and +infinity. 'lo' tests C == 0. - __ Fcsel(result, double_temp1, double_temp2, lo); - // Select between {+0.0 or +infinity} and input. 'vc' tests V == 0. - __ Fcsel(result, result, input, vc); - __ B(&done); - - // The rest is magic, as described in InitializeMathExpData(). - __ Bind(&result_is_finite_non_zero); - - // Assert that we can load offset 3 and offset 4 with a single ldp. - ASSERT(kDRegSizeInBytes == (ExpConstant(constants, 4).offset() - - ExpConstant(constants, 3).offset())); - __ Ldp(double_temp1, double_temp3, ExpConstant(constants, 3)); - __ Fmadd(double_temp1, double_temp1, input, double_temp3); - __ Fmov(temp2.W(), double_temp1.S()); - __ Fsub(double_temp1, double_temp1, double_temp3); - - // Assert that we can load offset 5 and offset 6 with a single ldp. - ASSERT(kDRegSizeInBytes == (ExpConstant(constants, 6).offset() - - ExpConstant(constants, 5).offset())); - __ Ldp(double_temp2, double_temp3, ExpConstant(constants, 5)); - // TODO(jbramley): Consider using Fnmsub here. - __ Fmul(double_temp1, double_temp1, double_temp2); - __ Fsub(double_temp1, double_temp1, input); - - __ Fmul(double_temp2, double_temp1, double_temp1); - __ Fsub(double_temp3, double_temp3, double_temp1); - __ Fmul(double_temp3, double_temp3, double_temp2); - - __ Mov(temp1.W(), Operand(temp2.W(), LSR, 11)); - - __ Ldr(double_temp2, ExpConstant(constants, 7)); - // TODO(jbramley): Consider using Fnmsub here. - __ Fmul(double_temp3, double_temp3, double_temp2); - __ Fsub(double_temp3, double_temp3, double_temp1); - - // The 8th constant is 1.0, so use an immediate move rather than a load. - // We can't generate a runtime assertion here as we would need to call Abort - // in the runtime and we don't have an Isolate when we generate this code. - __ Fmov(double_temp2, 1.0); - __ Fadd(double_temp3, double_temp3, double_temp2); - - __ And(temp2, temp2, 0x7ff); - __ Add(temp1, temp1, 0x3ff); - - // Do the final table lookup. - __ Mov(temp3, Operand(ExternalReference::math_exp_log_table())); - - __ Add(temp3, temp3, Operand(temp2, LSL, kDRegSizeInBytesLog2)); - __ Ldp(temp2.W(), temp3.W(), MemOperand(temp3)); - __ Orr(temp1.W(), temp3.W(), Operand(temp1.W(), LSL, 20)); - __ Bfi(temp2, temp1, 32, 32); - __ Fmov(double_temp1, temp2); - - __ Fmul(result, double_temp3, double_temp1); - - __ Bind(&done); -} - -#undef __ - -} } // namespace v8::internal - -#endif // V8_TARGET_ARCH_A64 diff --git a/deps/v8/src/a64/codegen-a64.h b/deps/v8/src/a64/codegen-a64.h deleted file mode 100644 index d66bd34a93..0000000000 --- a/deps/v8/src/a64/codegen-a64.h +++ /dev/null @@ -1,70 +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. - -#ifndef V8_A64_CODEGEN_A64_H_ -#define V8_A64_CODEGEN_A64_H_ - -#include "ast.h" -#include "ic-inl.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, - Register string, - Register index, - Register result, - Label* call_runtime); - - private: - DISALLOW_COPY_AND_ASSIGN(StringCharLoadGenerator); -}; - - -class MathExpGenerator : public AllStatic { - public: - static void EmitMathExp(MacroAssembler* masm, - DoubleRegister input, - DoubleRegister result, - DoubleRegister double_scratch1, - DoubleRegister double_scratch2, - Register temp1, - Register temp2, - Register temp3); - - private: - DISALLOW_COPY_AND_ASSIGN(MathExpGenerator); -}; - -} } // namespace v8::internal - -#endif // V8_A64_CODEGEN_A64_H_ diff --git a/deps/v8/src/a64/constants-a64.h b/deps/v8/src/a64/constants-a64.h deleted file mode 100644 index 4f43f13537..0000000000 --- a/deps/v8/src/a64/constants-a64.h +++ /dev/null @@ -1,1262 +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. - -#ifndef V8_A64_CONSTANTS_A64_H_ -#define V8_A64_CONSTANTS_A64_H_ - - -// Assert that this is an LP64 system. -STATIC_ASSERT(sizeof(int) == sizeof(int32_t)); // NOLINT(runtime/sizeof) -STATIC_ASSERT(sizeof(long) == sizeof(int64_t)); // NOLINT(runtime/int) -STATIC_ASSERT(sizeof(void *) == sizeof(int64_t)); // NOLINT(runtime/sizeof) -STATIC_ASSERT(sizeof(1) == sizeof(int32_t)); // NOLINT(runtime/sizeof) -STATIC_ASSERT(sizeof(1L) == sizeof(int64_t)); // NOLINT(runtime/sizeof) - - -// Get the standard printf format macros for C99 stdint types. -#define __STDC_FORMAT_MACROS -#include - - -namespace v8 { -namespace internal { - - -const unsigned kInstructionSize = 4; -const unsigned kInstructionSizeLog2 = 2; -const unsigned kLiteralEntrySize = 4; -const unsigned kLiteralEntrySizeLog2 = 2; -const unsigned kMaxLoadLiteralRange = 1 * MB; - -const unsigned kNumberOfRegisters = 32; -const unsigned kNumberOfFPRegisters = 32; -// Callee saved registers are x19-x30(lr). -const int kNumberOfCalleeSavedRegisters = 11; -const int kFirstCalleeSavedRegisterIndex = 19; -// Callee saved FP registers are d8-d15. -const int kNumberOfCalleeSavedFPRegisters = 8; -const int kFirstCalleeSavedFPRegisterIndex = 8; -// Callee saved registers with no specific purpose in JS are x19-x25. -const unsigned kJSCalleeSavedRegList = 0x03f80000; -// TODO(all): kRegSize should probably be kRegSizeInBits. -const unsigned kWRegSize = 32; -const unsigned kWRegSizeLog2 = 5; -const unsigned kWRegSizeInBytes = kWRegSize >> 3; -const unsigned kWRegSizeInBytesLog2 = kWRegSizeLog2 - 3; -const unsigned kXRegSize = 64; -const unsigned kXRegSizeLog2 = 6; -const unsigned kXRegSizeInBytes = kXRegSize >> 3; -const unsigned kXRegSizeInBytesLog2 = kXRegSizeLog2 - 3; -const unsigned kSRegSize = 32; -const unsigned kSRegSizeLog2 = 5; -const unsigned kSRegSizeInBytes = kSRegSize >> 3; -const unsigned kSRegSizeInBytesLog2 = kSRegSizeLog2 - 3; -const unsigned kDRegSize = 64; -const unsigned kDRegSizeLog2 = 6; -const unsigned kDRegSizeInBytes = kDRegSize >> 3; -const unsigned kDRegSizeInBytesLog2 = kDRegSizeLog2 - 3; -const int64_t kWRegMask = 0x00000000ffffffffL; -const int64_t kXRegMask = 0xffffffffffffffffL; -const int64_t kSRegMask = 0x00000000ffffffffL; -const int64_t kDRegMask = 0xffffffffffffffffL; -// TODO(all) check if the expression below works on all compilers or if it -// triggers an overflow error. -const int64_t kDSignMask = 0x1L << 63; -const int64_t kDSignBit = 63; -const int64_t kXSignMask = 0x1L << 63; -const int64_t kXSignBit = 63; -const int64_t kWSignMask = 0x1L << 31; -const int64_t kWSignBit = 31; -const int64_t kByteMask = 0xffL; -const int64_t kHalfWordMask = 0xffffL; -const int64_t kWordMask = 0xffffffffL; -const uint64_t kXMaxUInt = 0xffffffffffffffffUL; -const uint64_t kWMaxUInt = 0xffffffffUL; -const int64_t kXMaxInt = 0x7fffffffffffffffL; -const int64_t kXMinInt = 0x8000000000000000L; -const int32_t kWMaxInt = 0x7fffffff; -const int32_t kWMinInt = 0x80000000; -const unsigned kFramePointerRegCode = 29; -const unsigned kLinkRegCode = 30; -const unsigned kZeroRegCode = 31; -const unsigned kJSSPCode = 28; -const unsigned kSPRegInternalCode = 63; -const unsigned kRegCodeMask = 0x1f; -// Standard machine types defined by AAPCS64. -const unsigned kByteSize = 8; -const unsigned kByteSizeInBytes = kByteSize >> 3; -const unsigned kHalfWordSize = 16; -const unsigned kHalfWordSizeLog2 = 4; -const unsigned kHalfWordSizeInBytes = kHalfWordSize >> 3; -const unsigned kHalfWordSizeInBytesLog2 = kHalfWordSizeLog2 - 3; -const unsigned kWordSize = 32; -const unsigned kWordSizeLog2 = 5; -const unsigned kWordSizeInBytes = kWordSize >> 3; -const unsigned kWordSizeInBytesLog2 = kWordSizeLog2 - 3; -const unsigned kDoubleWordSize = 64; -const unsigned kDoubleWordSizeInBytes = kDoubleWordSize >> 3; -const unsigned kQuadWordSize = 128; -const unsigned kQuadWordSizeInBytes = kQuadWordSize >> 3; -// AArch64 floating-point specifics. These match IEEE-754. -const unsigned kDoubleMantissaBits = 52; -const unsigned kDoubleExponentBits = 11; -const unsigned kFloatMantissaBits = 23; -const unsigned kFloatExponentBits = 8; - -#define REGISTER_CODE_LIST(R) \ -R(0) R(1) R(2) R(3) R(4) R(5) R(6) R(7) \ -R(8) R(9) R(10) R(11) R(12) R(13) R(14) R(15) \ -R(16) R(17) R(18) R(19) R(20) R(21) R(22) R(23) \ -R(24) R(25) R(26) R(27) R(28) R(29) R(30) R(31) - -#define INSTRUCTION_FIELDS_LIST(V_) \ -/* Register fields */ \ -V_(Rd, 4, 0, Bits) /* Destination register. */ \ -V_(Rn, 9, 5, Bits) /* First source register. */ \ -V_(Rm, 20, 16, Bits) /* Second source register. */ \ -V_(Ra, 14, 10, Bits) /* Third source register. */ \ -V_(Rt, 4, 0, Bits) /* Load dest / store source. */ \ -V_(Rt2, 14, 10, Bits) /* Load second dest / */ \ - /* store second source. */ \ -V_(PrefetchMode, 4, 0, Bits) \ - \ -/* Common bits */ \ -V_(SixtyFourBits, 31, 31, Bits) \ -V_(FlagsUpdate, 29, 29, Bits) \ - \ -/* PC relative addressing */ \ -V_(ImmPCRelHi, 23, 5, SignedBits) \ -V_(ImmPCRelLo, 30, 29, Bits) \ - \ -/* Add/subtract/logical shift register */ \ -V_(ShiftDP, 23, 22, Bits) \ -V_(ImmDPShift, 15, 10, Bits) \ - \ -/* Add/subtract immediate */ \ -V_(ImmAddSub, 21, 10, Bits) \ -V_(ShiftAddSub, 23, 22, Bits) \ - \ -/* Add/substract extend */ \ -V_(ImmExtendShift, 12, 10, Bits) \ -V_(ExtendMode, 15, 13, Bits) \ - \ -/* Move wide */ \ -V_(ImmMoveWide, 20, 5, Bits) \ -V_(ShiftMoveWide, 22, 21, Bits) \ - \ -/* Logical immediate, bitfield and extract */ \ -V_(BitN, 22, 22, Bits) \ -V_(ImmRotate, 21, 16, Bits) \ -V_(ImmSetBits, 15, 10, Bits) \ -V_(ImmR, 21, 16, Bits) \ -V_(ImmS, 15, 10, Bits) \ - \ -/* Test and branch immediate */ \ -V_(ImmTestBranch, 18, 5, SignedBits) \ -V_(ImmTestBranchBit40, 23, 19, Bits) \ -V_(ImmTestBranchBit5, 31, 31, Bits) \ - \ -/* Conditionals */ \ -V_(Condition, 15, 12, Bits) \ -V_(ConditionBranch, 3, 0, Bits) \ -V_(Nzcv, 3, 0, Bits) \ -V_(ImmCondCmp, 20, 16, Bits) \ -V_(ImmCondBranch, 23, 5, SignedBits) \ - \ -/* Floating point */ \ -V_(FPType, 23, 22, Bits) \ -V_(ImmFP, 20, 13, Bits) \ -V_(FPScale, 15, 10, Bits) \ - \ -/* Load Store */ \ -V_(ImmLS, 20, 12, SignedBits) \ -V_(ImmLSUnsigned, 21, 10, Bits) \ -V_(ImmLSPair, 21, 15, SignedBits) \ -V_(SizeLS, 31, 30, Bits) \ -V_(ImmShiftLS, 12, 12, Bits) \ - \ -/* Other immediates */ \ -V_(ImmUncondBranch, 25, 0, SignedBits) \ -V_(ImmCmpBranch, 23, 5, SignedBits) \ -V_(ImmLLiteral, 23, 5, SignedBits) \ -V_(ImmException, 20, 5, Bits) \ -V_(ImmHint, 11, 5, Bits) \ -V_(ImmBarrierDomain, 11, 10, Bits) \ -V_(ImmBarrierType, 9, 8, Bits) \ - \ -/* System (MRS, MSR) */ \ -V_(ImmSystemRegister, 19, 5, Bits) \ -V_(SysO0, 19, 19, Bits) \ -V_(SysOp1, 18, 16, Bits) \ -V_(SysOp2, 7, 5, Bits) \ -V_(CRn, 15, 12, Bits) \ -V_(CRm, 11, 8, Bits) \ - - -#define SYSTEM_REGISTER_FIELDS_LIST(V_, M_) \ -/* NZCV */ \ -V_(Flags, 31, 28, Bits) \ -V_(N, 31, 31, Bits) \ -V_(Z, 30, 30, Bits) \ -V_(C, 29, 29, Bits) \ -V_(V, 28, 28, Bits) \ -M_(NZCV, Flags_mask) \ - \ -/* FPCR */ \ -V_(AHP, 26, 26, Bits) \ -V_(DN, 25, 25, Bits) \ -V_(FZ, 24, 24, Bits) \ -V_(RMode, 23, 22, Bits) \ -M_(FPCR, AHP_mask | DN_mask | FZ_mask | RMode_mask) - - -// Fields offsets. -#define DECLARE_FIELDS_OFFSETS(Name, HighBit, LowBit, X) \ -const int Name##_offset = LowBit; \ -const int Name##_width = HighBit - LowBit + 1; \ -const uint32_t Name##_mask = ((1 << Name##_width) - 1) << LowBit; -#define NOTHING(A, B) -INSTRUCTION_FIELDS_LIST(DECLARE_FIELDS_OFFSETS) -SYSTEM_REGISTER_FIELDS_LIST(DECLARE_FIELDS_OFFSETS, NOTHING) -#undef NOTHING -#undef DECLARE_FIELDS_BITS - -// ImmPCRel is a compound field (not present in INSTRUCTION_FIELDS_LIST), formed -// from ImmPCRelLo and ImmPCRelHi. -const int ImmPCRel_mask = ImmPCRelLo_mask | ImmPCRelHi_mask; - -// Condition codes. -enum Condition { - eq = 0, - ne = 1, - hs = 2, - lo = 3, - mi = 4, - pl = 5, - vs = 6, - vc = 7, - hi = 8, - ls = 9, - ge = 10, - lt = 11, - gt = 12, - le = 13, - al = 14, - nv = 15 // Behaves as always/al. -}; - -inline Condition InvertCondition(Condition cond) { - // Conditions al and nv behave identically, as "always true". They can't be - // inverted, because there is no never condition. - ASSERT((cond != al) && (cond != nv)); - return static_cast(cond ^ 1); -} - -// Corresponds to transposing the operands of a comparison. -inline Condition ReverseConditionForCmp(Condition cond) { - switch (cond) { - case lo: - return hi; - case hi: - return lo; - case hs: - return ls; - case ls: - return hs; - case lt: - return gt; - case gt: - return lt; - case ge: - return le; - case le: - return ge; - case eq: - return eq; - default: - // In practice this function is only used with a condition coming from - // TokenToCondition in lithium-codegen-a64.cc. Any other condition is - // invalid as it doesn't necessary make sense to reverse it (consider - // 'mi' for instance). - UNREACHABLE(); - return nv; - }; -} - -enum FlagsUpdate { - SetFlags = 1, - LeaveFlags = 0 -}; - -enum StatusFlags { - NoFlag = 0, - - // Derive the flag combinations from the system register bit descriptions. - NFlag = N_mask, - ZFlag = Z_mask, - CFlag = C_mask, - VFlag = V_mask, - NZFlag = NFlag | ZFlag, - NCFlag = NFlag | CFlag, - NVFlag = NFlag | VFlag, - ZCFlag = ZFlag | CFlag, - ZVFlag = ZFlag | VFlag, - CVFlag = CFlag | VFlag, - NZCFlag = NFlag | ZFlag | CFlag, - NZVFlag = NFlag | ZFlag | VFlag, - NCVFlag = NFlag | CFlag | VFlag, - ZCVFlag = ZFlag | CFlag | VFlag, - NZCVFlag = NFlag | ZFlag | CFlag | VFlag, - - // Floating-point comparison results. - FPEqualFlag = ZCFlag, - FPLessThanFlag = NFlag, - FPGreaterThanFlag = CFlag, - FPUnorderedFlag = CVFlag -}; - -enum Shift { - NO_SHIFT = -1, - LSL = 0x0, - LSR = 0x1, - ASR = 0x2, - ROR = 0x3 -}; - -enum Extend { - NO_EXTEND = -1, - UXTB = 0, - UXTH = 1, - UXTW = 2, - UXTX = 3, - SXTB = 4, - SXTH = 5, - SXTW = 6, - SXTX = 7 -}; - -enum SystemHint { - NOP = 0, - YIELD = 1, - WFE = 2, - WFI = 3, - SEV = 4, - SEVL = 5 -}; - -enum BarrierDomain { - OuterShareable = 0, - NonShareable = 1, - InnerShareable = 2, - FullSystem = 3 -}; - -enum BarrierType { - BarrierOther = 0, - BarrierReads = 1, - BarrierWrites = 2, - BarrierAll = 3 -}; - -// System/special register names. -// This information is not encoded as one field but as the concatenation of -// multiple fields (Op0<0>, Op1, Crn, Crm, Op2). -enum SystemRegister { - NZCV = ((0x1 << SysO0_offset) | - (0x3 << SysOp1_offset) | - (0x4 << CRn_offset) | - (0x2 << CRm_offset) | - (0x0 << SysOp2_offset)) >> ImmSystemRegister_offset, - FPCR = ((0x1 << SysO0_offset) | - (0x3 << SysOp1_offset) | - (0x4 << CRn_offset) | - (0x4 << CRm_offset) | - (0x0 << SysOp2_offset)) >> ImmSystemRegister_offset -}; - -// Instruction enumerations. -// -// These are the masks that define a class of instructions, and the list of -// instructions within each class. Each enumeration has a Fixed, FMask and -// Mask value. -// -// Fixed: The fixed bits in this instruction class. -// FMask: The mask used to extract the fixed bits in the class. -// Mask: The mask used to identify the instructions within a class. -// -// The enumerations can be used like this: -// -// ASSERT(instr->Mask(PCRelAddressingFMask) == PCRelAddressingFixed); -// switch(instr->Mask(PCRelAddressingMask)) { -// case ADR: Format("adr 'Xd, 'AddrPCRelByte"); break; -// case ADRP: Format("adrp 'Xd, 'AddrPCRelPage"); break; -// default: printf("Unknown instruction\n"); -// } - - -// Generic fields. -enum GenericInstrField { - SixtyFourBits = 0x80000000, - ThirtyTwoBits = 0x00000000, - FP32 = 0x00000000, - FP64 = 0x00400000 -}; - -// PC relative addressing. -enum PCRelAddressingOp { - PCRelAddressingFixed = 0x10000000, - PCRelAddressingFMask = 0x1F000000, - PCRelAddressingMask = 0x9F000000, - ADR = PCRelAddressingFixed | 0x00000000, - ADRP = PCRelAddressingFixed | 0x80000000 -}; - -// Add/sub (immediate, shifted and extended.) -const int kSFOffset = 31; -enum AddSubOp { - AddSubOpMask = 0x60000000, - AddSubSetFlagsBit = 0x20000000, - ADD = 0x00000000, - ADDS = ADD | AddSubSetFlagsBit, - SUB = 0x40000000, - SUBS = SUB | AddSubSetFlagsBit -}; - -#define ADD_SUB_OP_LIST(V) \ - V(ADD), \ - V(ADDS), \ - V(SUB), \ - V(SUBS) - -enum AddSubImmediateOp { - AddSubImmediateFixed = 0x11000000, - AddSubImmediateFMask = 0x1F000000, - AddSubImmediateMask = 0xFF000000, - #define ADD_SUB_IMMEDIATE(A) \ - A##_w_imm = AddSubImmediateFixed | A, \ - A##_x_imm = AddSubImmediateFixed | A | SixtyFourBits - ADD_SUB_OP_LIST(ADD_SUB_IMMEDIATE) - #undef ADD_SUB_IMMEDIATE -}; - -enum AddSubShiftedOp { - AddSubShiftedFixed = 0x0B000000, - AddSubShiftedFMask = 0x1F200000, - AddSubShiftedMask = 0xFF200000, - #define ADD_SUB_SHIFTED(A) \ - A##_w_shift = AddSubShiftedFixed | A, \ - A##_x_shift = AddSubShiftedFixed | A | SixtyFourBits - ADD_SUB_OP_LIST(ADD_SUB_SHIFTED) - #undef ADD_SUB_SHIFTED -}; - -enum AddSubExtendedOp { - AddSubExtendedFixed = 0x0B200000, - AddSubExtendedFMask = 0x1F200000, - AddSubExtendedMask = 0xFFE00000, - #define ADD_SUB_EXTENDED(A) \ - A##_w_ext = AddSubExtendedFixed | A, \ - A##_x_ext = AddSubExtendedFixed | A | SixtyFourBits - ADD_SUB_OP_LIST(ADD_SUB_EXTENDED) - #undef ADD_SUB_EXTENDED -}; - -// Add/sub with carry. -enum AddSubWithCarryOp { - AddSubWithCarryFixed = 0x1A000000, - AddSubWithCarryFMask = 0x1FE00000, - AddSubWithCarryMask = 0xFFE0FC00, - ADC_w = AddSubWithCarryFixed | ADD, - ADC_x = AddSubWithCarryFixed | ADD | SixtyFourBits, - ADC = ADC_w, - ADCS_w = AddSubWithCarryFixed | ADDS, - ADCS_x = AddSubWithCarryFixed | ADDS | SixtyFourBits, - SBC_w = AddSubWithCarryFixed | SUB, - SBC_x = AddSubWithCarryFixed | SUB | SixtyFourBits, - SBC = SBC_w, - SBCS_w = AddSubWithCarryFixed | SUBS, - SBCS_x = AddSubWithCarryFixed | SUBS | SixtyFourBits -}; - - -// Logical (immediate and shifted register). -enum LogicalOp { - LogicalOpMask = 0x60200000, - NOT = 0x00200000, - AND = 0x00000000, - BIC = AND | NOT, - ORR = 0x20000000, - ORN = ORR | NOT, - EOR = 0x40000000, - EON = EOR | NOT, - ANDS = 0x60000000, - BICS = ANDS | NOT -}; - -// Logical immediate. -enum LogicalImmediateOp { - LogicalImmediateFixed = 0x12000000, - LogicalImmediateFMask = 0x1F800000, - LogicalImmediateMask = 0xFF800000, - AND_w_imm = LogicalImmediateFixed | AND, - AND_x_imm = LogicalImmediateFixed | AND | SixtyFourBits, - ORR_w_imm = LogicalImmediateFixed | ORR, - ORR_x_imm = LogicalImmediateFixed | ORR | SixtyFourBits, - EOR_w_imm = LogicalImmediateFixed | EOR, - EOR_x_imm = LogicalImmediateFixed | EOR | SixtyFourBits, - ANDS_w_imm = LogicalImmediateFixed | ANDS, - ANDS_x_imm = LogicalImmediateFixed | ANDS | SixtyFourBits -}; - -// Logical shifted register. -enum LogicalShiftedOp { - LogicalShiftedFixed = 0x0A000000, - LogicalShiftedFMask = 0x1F000000, - LogicalShiftedMask = 0xFF200000, - AND_w = LogicalShiftedFixed | AND, - AND_x = LogicalShiftedFixed | AND | SixtyFourBits, - AND_shift = AND_w, - BIC_w = LogicalShiftedFixed | BIC, - BIC_x = LogicalShiftedFixed | BIC | SixtyFourBits, - BIC_shift = BIC_w, - ORR_w = LogicalShiftedFixed | ORR, - ORR_x = LogicalShiftedFixed | ORR | SixtyFourBits, - ORR_shift = ORR_w, - ORN_w = LogicalShiftedFixed | ORN, - ORN_x = LogicalShiftedFixed | ORN | SixtyFourBits, - ORN_shift = ORN_w, - EOR_w = LogicalShiftedFixed | EOR, - EOR_x = LogicalShiftedFixed | EOR | SixtyFourBits, - EOR_shift = EOR_w, - EON_w = LogicalShiftedFixed | EON, - EON_x = LogicalShiftedFixed | EON | SixtyFourBits, - EON_shift = EON_w, - ANDS_w = LogicalShiftedFixed | ANDS, - ANDS_x = LogicalShiftedFixed | ANDS | SixtyFourBits, - ANDS_shift = ANDS_w, - BICS_w = LogicalShiftedFixed | BICS, - BICS_x = LogicalShiftedFixed | BICS | SixtyFourBits, - BICS_shift = BICS_w -}; - -// Move wide immediate. -enum MoveWideImmediateOp { - MoveWideImmediateFixed = 0x12800000, - MoveWideImmediateFMask = 0x1F800000, - MoveWideImmediateMask = 0xFF800000, - MOVN = 0x00000000, - MOVZ = 0x40000000, - MOVK = 0x60000000, - MOVN_w = MoveWideImmediateFixed | MOVN, - MOVN_x = MoveWideImmediateFixed | MOVN | SixtyFourBits, - MOVZ_w = MoveWideImmediateFixed | MOVZ, - MOVZ_x = MoveWideImmediateFixed | MOVZ | SixtyFourBits, - MOVK_w = MoveWideImmediateFixed | MOVK, - MOVK_x = MoveWideImmediateFixed | MOVK | SixtyFourBits -}; - -// Bitfield. -const int kBitfieldNOffset = 22; -enum BitfieldOp { - BitfieldFixed = 0x13000000, - BitfieldFMask = 0x1F800000, - BitfieldMask = 0xFF800000, - SBFM_w = BitfieldFixed | 0x00000000, - SBFM_x = BitfieldFixed | 0x80000000, - SBFM = SBFM_w, - BFM_w = BitfieldFixed | 0x20000000, - BFM_x = BitfieldFixed | 0xA0000000, - BFM = BFM_w, - UBFM_w = BitfieldFixed | 0x40000000, - UBFM_x = BitfieldFixed | 0xC0000000, - UBFM = UBFM_w - // Bitfield N field. -}; - -// Extract. -enum ExtractOp { - ExtractFixed = 0x13800000, - ExtractFMask = 0x1F800000, - ExtractMask = 0xFFA00000, - EXTR_w = ExtractFixed | 0x00000000, - EXTR_x = ExtractFixed | 0x80000000, - EXTR = EXTR_w -}; - -// Unconditional branch. -enum UnconditionalBranchOp { - UnconditionalBranchFixed = 0x14000000, - UnconditionalBranchFMask = 0x7C000000, - UnconditionalBranchMask = 0xFC000000, - B = UnconditionalBranchFixed | 0x00000000, - BL = UnconditionalBranchFixed | 0x80000000 -}; - -// Unconditional branch to register. -enum UnconditionalBranchToRegisterOp { - UnconditionalBranchToRegisterFixed = 0xD6000000, - UnconditionalBranchToRegisterFMask = 0xFE000000, - UnconditionalBranchToRegisterMask = 0xFFFFFC1F, - BR = UnconditionalBranchToRegisterFixed | 0x001F0000, - BLR = UnconditionalBranchToRegisterFixed | 0x003F0000, - RET = UnconditionalBranchToRegisterFixed | 0x005F0000 -}; - -// Compare and branch. -enum CompareBranchOp { - CompareBranchFixed = 0x34000000, - CompareBranchFMask = 0x7E000000, - CompareBranchMask = 0xFF000000, - CBZ_w = CompareBranchFixed | 0x00000000, - CBZ_x = CompareBranchFixed | 0x80000000, - CBZ = CBZ_w, - CBNZ_w = CompareBranchFixed | 0x01000000, - CBNZ_x = CompareBranchFixed | 0x81000000, - CBNZ = CBNZ_w -}; - -// Test and branch. -enum TestBranchOp { - TestBranchFixed = 0x36000000, - TestBranchFMask = 0x7E000000, - TestBranchMask = 0x7F000000, - TBZ = TestBranchFixed | 0x00000000, - TBNZ = TestBranchFixed | 0x01000000 -}; - -// Conditional branch. -enum ConditionalBranchOp { - ConditionalBranchFixed = 0x54000000, - ConditionalBranchFMask = 0xFE000000, - ConditionalBranchMask = 0xFF000010, - B_cond = ConditionalBranchFixed | 0x00000000 -}; - -// System. -// System instruction encoding is complicated because some instructions use op -// and CR fields to encode parameters. To handle this cleanly, the system -// instructions are split into more than one enum. - -enum SystemOp { - SystemFixed = 0xD5000000, - SystemFMask = 0xFFC00000 -}; - -enum SystemSysRegOp { - SystemSysRegFixed = 0xD5100000, - SystemSysRegFMask = 0xFFD00000, - SystemSysRegMask = 0xFFF00000, - MRS = SystemSysRegFixed | 0x00200000, - MSR = SystemSysRegFixed | 0x00000000 -}; - -enum SystemHintOp { - SystemHintFixed = 0xD503201F, - SystemHintFMask = 0xFFFFF01F, - SystemHintMask = 0xFFFFF01F, - HINT = SystemHintFixed | 0x00000000 -}; - -// Exception. -enum ExceptionOp { - ExceptionFixed = 0xD4000000, - ExceptionFMask = 0xFF000000, - ExceptionMask = 0xFFE0001F, - HLT = ExceptionFixed | 0x00400000, - BRK = ExceptionFixed | 0x00200000, - SVC = ExceptionFixed | 0x00000001, - HVC = ExceptionFixed | 0x00000002, - SMC = ExceptionFixed | 0x00000003, - DCPS1 = ExceptionFixed | 0x00A00001, - DCPS2 = ExceptionFixed | 0x00A00002, - DCPS3 = ExceptionFixed | 0x00A00003 -}; -// Code used to spot hlt instructions that should not be hit. -const int kHltBadCode = 0xbad; - -enum MemBarrierOp { - MemBarrierFixed = 0xD503309F, - MemBarrierFMask = 0xFFFFF09F, - MemBarrierMask = 0xFFFFF0FF, - DSB = MemBarrierFixed | 0x00000000, - DMB = MemBarrierFixed | 0x00000020, - ISB = MemBarrierFixed | 0x00000040 -}; - -// Any load or store (including pair). -enum LoadStoreAnyOp { - LoadStoreAnyFMask = 0x0a000000, - LoadStoreAnyFixed = 0x08000000 -}; - -// Any load pair or store pair. -enum LoadStorePairAnyOp { - LoadStorePairAnyFMask = 0x3a000000, - LoadStorePairAnyFixed = 0x28000000 -}; - -#define LOAD_STORE_PAIR_OP_LIST(V) \ - V(STP, w, 0x00000000), \ - V(LDP, w, 0x00400000), \ - V(LDPSW, x, 0x40400000), \ - V(STP, x, 0x80000000), \ - V(LDP, x, 0x80400000), \ - V(STP, s, 0x04000000), \ - V(LDP, s, 0x04400000), \ - V(STP, d, 0x44000000), \ - V(LDP, d, 0x44400000) - -// Load/store pair (post, pre and offset.) -enum LoadStorePairOp { - LoadStorePairMask = 0xC4400000, - LoadStorePairLBit = 1 << 22, - #define LOAD_STORE_PAIR(A, B, C) \ - A##_##B = C - LOAD_STORE_PAIR_OP_LIST(LOAD_STORE_PAIR) - #undef LOAD_STORE_PAIR -}; - -enum LoadStorePairPostIndexOp { - LoadStorePairPostIndexFixed = 0x28800000, - LoadStorePairPostIndexFMask = 0x3B800000, - LoadStorePairPostIndexMask = 0xFFC00000, - #define LOAD_STORE_PAIR_POST_INDEX(A, B, C) \ - A##_##B##_post = LoadStorePairPostIndexFixed | A##_##B - LOAD_STORE_PAIR_OP_LIST(LOAD_STORE_PAIR_POST_INDEX) - #undef LOAD_STORE_PAIR_POST_INDEX -}; - -enum LoadStorePairPreIndexOp { - LoadStorePairPreIndexFixed = 0x29800000, - LoadStorePairPreIndexFMask = 0x3B800000, - LoadStorePairPreIndexMask = 0xFFC00000, - #define LOAD_STORE_PAIR_PRE_INDEX(A, B, C) \ - A##_##B##_pre = LoadStorePairPreIndexFixed | A##_##B - LOAD_STORE_PAIR_OP_LIST(LOAD_STORE_PAIR_PRE_INDEX) - #undef LOAD_STORE_PAIR_PRE_INDEX -}; - -enum LoadStorePairOffsetOp { - LoadStorePairOffsetFixed = 0x29000000, - LoadStorePairOffsetFMask = 0x3B800000, - LoadStorePairOffsetMask = 0xFFC00000, - #define LOAD_STORE_PAIR_OFFSET(A, B, C) \ - A##_##B##_off = LoadStorePairOffsetFixed | A##_##B - LOAD_STORE_PAIR_OP_LIST(LOAD_STORE_PAIR_OFFSET) - #undef LOAD_STORE_PAIR_OFFSET -}; - -enum LoadStorePairNonTemporalOp { - LoadStorePairNonTemporalFixed = 0x28000000, - LoadStorePairNonTemporalFMask = 0x3B800000, - LoadStorePairNonTemporalMask = 0xFFC00000, - STNP_w = LoadStorePairNonTemporalFixed | STP_w, - LDNP_w = LoadStorePairNonTemporalFixed | LDP_w, - STNP_x = LoadStorePairNonTemporalFixed | STP_x, - LDNP_x = LoadStorePairNonTemporalFixed | LDP_x, - STNP_s = LoadStorePairNonTemporalFixed | STP_s, - LDNP_s = LoadStorePairNonTemporalFixed | LDP_s, - STNP_d = LoadStorePairNonTemporalFixed | STP_d, - LDNP_d = LoadStorePairNonTemporalFixed | LDP_d -}; - -// Load literal. -enum LoadLiteralOp { - LoadLiteralFixed = 0x18000000, - LoadLiteralFMask = 0x3B000000, - LoadLiteralMask = 0xFF000000, - LDR_w_lit = LoadLiteralFixed | 0x00000000, - LDR_x_lit = LoadLiteralFixed | 0x40000000, - LDRSW_x_lit = LoadLiteralFixed | 0x80000000, - PRFM_lit = LoadLiteralFixed | 0xC0000000, - LDR_s_lit = LoadLiteralFixed | 0x04000000, - LDR_d_lit = LoadLiteralFixed | 0x44000000 -}; - -#define LOAD_STORE_OP_LIST(V) \ - V(ST, RB, w, 0x00000000), \ - V(ST, RH, w, 0x40000000), \ - V(ST, R, w, 0x80000000), \ - V(ST, R, x, 0xC0000000), \ - V(LD, RB, w, 0x00400000), \ - V(LD, RH, w, 0x40400000), \ - V(LD, R, w, 0x80400000), \ - V(LD, R, x, 0xC0400000), \ - V(LD, RSB, x, 0x00800000), \ - V(LD, RSH, x, 0x40800000), \ - V(LD, RSW, x, 0x80800000), \ - V(LD, RSB, w, 0x00C00000), \ - V(LD, RSH, w, 0x40C00000), \ - V(ST, R, s, 0x84000000), \ - V(ST, R, d, 0xC4000000), \ - V(LD, R, s, 0x84400000), \ - V(LD, R, d, 0xC4400000) - - -// Load/store unscaled offset. -enum LoadStoreUnscaledOffsetOp { - LoadStoreUnscaledOffsetFixed = 0x38000000, - LoadStoreUnscaledOffsetFMask = 0x3B200C00, - LoadStoreUnscaledOffsetMask = 0xFFE00C00, - #define LOAD_STORE_UNSCALED(A, B, C, D) \ - A##U##B##_##C = LoadStoreUnscaledOffsetFixed | D - LOAD_STORE_OP_LIST(LOAD_STORE_UNSCALED) - #undef LOAD_STORE_UNSCALED -}; - -// Load/store (post, pre, offset and unsigned.) -enum LoadStoreOp { - LoadStoreOpMask = 0xC4C00000, - #define LOAD_STORE(A, B, C, D) \ - A##B##_##C = D - LOAD_STORE_OP_LIST(LOAD_STORE), - #undef LOAD_STORE - PRFM = 0xC0800000 -}; - -// Load/store post index. -enum LoadStorePostIndex { - LoadStorePostIndexFixed = 0x38000400, - LoadStorePostIndexFMask = 0x3B200C00, - LoadStorePostIndexMask = 0xFFE00C00, - #define LOAD_STORE_POST_INDEX(A, B, C, D) \ - A##B##_##C##_post = LoadStorePostIndexFixed | D - LOAD_STORE_OP_LIST(LOAD_STORE_POST_INDEX) - #undef LOAD_STORE_POST_INDEX -}; - -// Load/store pre index. -enum LoadStorePreIndex { - LoadStorePreIndexFixed = 0x38000C00, - LoadStorePreIndexFMask = 0x3B200C00, - LoadStorePreIndexMask = 0xFFE00C00, - #define LOAD_STORE_PRE_INDEX(A, B, C, D) \ - A##B##_##C##_pre = LoadStorePreIndexFixed | D - LOAD_STORE_OP_LIST(LOAD_STORE_PRE_INDEX) - #undef LOAD_STORE_PRE_INDEX -}; - -// Load/store unsigned offset. -enum LoadStoreUnsignedOffset { - LoadStoreUnsignedOffsetFixed = 0x39000000, - LoadStoreUnsignedOffsetFMask = 0x3B000000, - LoadStoreUnsignedOffsetMask = 0xFFC00000, - PRFM_unsigned = LoadStoreUnsignedOffsetFixed | PRFM, - #define LOAD_STORE_UNSIGNED_OFFSET(A, B, C, D) \ - A##B##_##C##_unsigned = LoadStoreUnsignedOffsetFixed | D - LOAD_STORE_OP_LIST(LOAD_STORE_UNSIGNED_OFFSET) - #undef LOAD_STORE_UNSIGNED_OFFSET -}; - -// Load/store register offset. -enum LoadStoreRegisterOffset { - LoadStoreRegisterOffsetFixed = 0x38200800, - LoadStoreRegisterOffsetFMask = 0x3B200C00, - LoadStoreRegisterOffsetMask = 0xFFE00C00, - PRFM_reg = LoadStoreRegisterOffsetFixed | PRFM, - #define LOAD_STORE_REGISTER_OFFSET(A, B, C, D) \ - A##B##_##C##_reg = LoadStoreRegisterOffsetFixed | D - LOAD_STORE_OP_LIST(LOAD_STORE_REGISTER_OFFSET) - #undef LOAD_STORE_REGISTER_OFFSET -}; - -// Conditional compare. -enum ConditionalCompareOp { - ConditionalCompareMask = 0x60000000, - CCMN = 0x20000000, - CCMP = 0x60000000 -}; - -// Conditional compare register. -enum ConditionalCompareRegisterOp { - ConditionalCompareRegisterFixed = 0x1A400000, - ConditionalCompareRegisterFMask = 0x1FE00800, - ConditionalCompareRegisterMask = 0xFFE00C10, - CCMN_w = ConditionalCompareRegisterFixed | CCMN, - CCMN_x = ConditionalCompareRegisterFixed | SixtyFourBits | CCMN, - CCMP_w = ConditionalCompareRegisterFixed | CCMP, - CCMP_x = ConditionalCompareRegisterFixed | SixtyFourBits | CCMP -}; - -// Conditional compare immediate. -enum ConditionalCompareImmediateOp { - ConditionalCompareImmediateFixed = 0x1A400800, - ConditionalCompareImmediateFMask = 0x1FE00800, - ConditionalCompareImmediateMask = 0xFFE00C10, - CCMN_w_imm = ConditionalCompareImmediateFixed | CCMN, - CCMN_x_imm = ConditionalCompareImmediateFixed | SixtyFourBits | CCMN, - CCMP_w_imm = ConditionalCompareImmediateFixed | CCMP, - CCMP_x_imm = ConditionalCompareImmediateFixed | SixtyFourBits | CCMP -}; - -// Conditional select. -enum ConditionalSelectOp { - ConditionalSelectFixed = 0x1A800000, - ConditionalSelectFMask = 0x1FE00000, - ConditionalSelectMask = 0xFFE00C00, - CSEL_w = ConditionalSelectFixed | 0x00000000, - CSEL_x = ConditionalSelectFixed | 0x80000000, - CSEL = CSEL_w, - CSINC_w = ConditionalSelectFixed | 0x00000400, - CSINC_x = ConditionalSelectFixed | 0x80000400, - CSINC = CSINC_w, - CSINV_w = ConditionalSelectFixed | 0x40000000, - CSINV_x = ConditionalSelectFixed | 0xC0000000, - CSINV = CSINV_w, - CSNEG_w = ConditionalSelectFixed | 0x40000400, - CSNEG_x = ConditionalSelectFixed | 0xC0000400, - CSNEG = CSNEG_w -}; - -// Data processing 1 source. -enum DataProcessing1SourceOp { - DataProcessing1SourceFixed = 0x5AC00000, - DataProcessing1SourceFMask = 0x5FE00000, - DataProcessing1SourceMask = 0xFFFFFC00, - RBIT = DataProcessing1SourceFixed | 0x00000000, - RBIT_w = RBIT, - RBIT_x = RBIT | SixtyFourBits, - REV16 = DataProcessing1SourceFixed | 0x00000400, - REV16_w = REV16, - REV16_x = REV16 | SixtyFourBits, - REV = DataProcessing1SourceFixed | 0x00000800, - REV_w = REV, - REV32_x = REV | SixtyFourBits, - REV_x = DataProcessing1SourceFixed | SixtyFourBits | 0x00000C00, - CLZ = DataProcessing1SourceFixed | 0x00001000, - CLZ_w = CLZ, - CLZ_x = CLZ | SixtyFourBits, - CLS = DataProcessing1SourceFixed | 0x00001400, - CLS_w = CLS, - CLS_x = CLS | SixtyFourBits -}; - -// Data processing 2 source. -enum DataProcessing2SourceOp { - DataProcessing2SourceFixed = 0x1AC00000, - DataProcessing2SourceFMask = 0x5FE00000, - DataProcessing2SourceMask = 0xFFE0FC00, - UDIV_w = DataProcessing2SourceFixed | 0x00000800, - UDIV_x = DataProcessing2SourceFixed | 0x80000800, - UDIV = UDIV_w, - SDIV_w = DataProcessing2SourceFixed | 0x00000C00, - SDIV_x = DataProcessing2SourceFixed | 0x80000C00, - SDIV = SDIV_w, - LSLV_w = DataProcessing2SourceFixed | 0x00002000, - LSLV_x = DataProcessing2SourceFixed | 0x80002000, - LSLV = LSLV_w, - LSRV_w = DataProcessing2SourceFixed | 0x00002400, - LSRV_x = DataProcessing2SourceFixed | 0x80002400, - LSRV = LSRV_w, - ASRV_w = DataProcessing2SourceFixed | 0x00002800, - ASRV_x = DataProcessing2SourceFixed | 0x80002800, - ASRV = ASRV_w, - RORV_w = DataProcessing2SourceFixed | 0x00002C00, - RORV_x = DataProcessing2SourceFixed | 0x80002C00, - RORV = RORV_w, - CRC32B = DataProcessing2SourceFixed | 0x00004000, - CRC32H = DataProcessing2SourceFixed | 0x00004400, - CRC32W = DataProcessing2SourceFixed | 0x00004800, - CRC32X = DataProcessing2SourceFixed | SixtyFourBits | 0x00004C00, - CRC32CB = DataProcessing2SourceFixed | 0x00005000, - CRC32CH = DataProcessing2SourceFixed | 0x00005400, - CRC32CW = DataProcessing2SourceFixed | 0x00005800, - CRC32CX = DataProcessing2SourceFixed | SixtyFourBits | 0x00005C00 -}; - -// Data processing 3 source. -enum DataProcessing3SourceOp { - DataProcessing3SourceFixed = 0x1B000000, - DataProcessing3SourceFMask = 0x1F000000, - DataProcessing3SourceMask = 0xFFE08000, - MADD_w = DataProcessing3SourceFixed | 0x00000000, - MADD_x = DataProcessing3SourceFixed | 0x80000000, - MADD = MADD_w, - MSUB_w = DataProcessing3SourceFixed | 0x00008000, - MSUB_x = DataProcessing3SourceFixed | 0x80008000, - MSUB = MSUB_w, - SMADDL_x = DataProcessing3SourceFixed | 0x80200000, - SMSUBL_x = DataProcessing3SourceFixed | 0x80208000, - SMULH_x = DataProcessing3SourceFixed | 0x80400000, - UMADDL_x = DataProcessing3SourceFixed | 0x80A00000, - UMSUBL_x = DataProcessing3SourceFixed | 0x80A08000, - UMULH_x = DataProcessing3SourceFixed | 0x80C00000 -}; - -// Floating point compare. -enum FPCompareOp { - FPCompareFixed = 0x1E202000, - FPCompareFMask = 0x5F203C00, - FPCompareMask = 0xFFE0FC1F, - FCMP_s = FPCompareFixed | 0x00000000, - FCMP_d = FPCompareFixed | FP64 | 0x00000000, - FCMP = FCMP_s, - FCMP_s_zero = FPCompareFixed | 0x00000008, - FCMP_d_zero = FPCompareFixed | FP64 | 0x00000008, - FCMP_zero = FCMP_s_zero, - FCMPE_s = FPCompareFixed | 0x00000010, - FCMPE_d = FPCompareFixed | FP64 | 0x00000010, - FCMPE_s_zero = FPCompareFixed | 0x00000018, - FCMPE_d_zero = FPCompareFixed | FP64 | 0x00000018 -}; - -// Floating point conditional compare. -enum FPConditionalCompareOp { - FPConditionalCompareFixed = 0x1E200400, - FPConditionalCompareFMask = 0x5F200C00, - FPConditionalCompareMask = 0xFFE00C10, - FCCMP_s = FPConditionalCompareFixed | 0x00000000, - FCCMP_d = FPConditionalCompareFixed | FP64 | 0x00000000, - FCCMP = FCCMP_s, - FCCMPE_s = FPConditionalCompareFixed | 0x00000010, - FCCMPE_d = FPConditionalCompareFixed | FP64 | 0x00000010, - FCCMPE = FCCMPE_s -}; - -// Floating point conditional select. -enum FPConditionalSelectOp { - FPConditionalSelectFixed = 0x1E200C00, - FPConditionalSelectFMask = 0x5F200C00, - FPConditionalSelectMask = 0xFFE00C00, - FCSEL_s = FPConditionalSelectFixed | 0x00000000, - FCSEL_d = FPConditionalSelectFixed | FP64 | 0x00000000, - FCSEL = FCSEL_s -}; - -// Floating point immediate. -enum FPImmediateOp { - FPImmediateFixed = 0x1E201000, - FPImmediateFMask = 0x5F201C00, - FPImmediateMask = 0xFFE01C00, - FMOV_s_imm = FPImmediateFixed | 0x00000000, - FMOV_d_imm = FPImmediateFixed | FP64 | 0x00000000 -}; - -// Floating point data processing 1 source. -enum FPDataProcessing1SourceOp { - FPDataProcessing1SourceFixed = 0x1E204000, - FPDataProcessing1SourceFMask = 0x5F207C00, - FPDataProcessing1SourceMask = 0xFFFFFC00, - FMOV_s = FPDataProcessing1SourceFixed | 0x00000000, - FMOV_d = FPDataProcessing1SourceFixed | FP64 | 0x00000000, - FMOV = FMOV_s, - FABS_s = FPDataProcessing1SourceFixed | 0x00008000, - FABS_d = FPDataProcessing1SourceFixed | FP64 | 0x00008000, - FABS = FABS_s, - FNEG_s = FPDataProcessing1SourceFixed | 0x00010000, - FNEG_d = FPDataProcessing1SourceFixed | FP64 | 0x00010000, - FNEG = FNEG_s, - FSQRT_s = FPDataProcessing1SourceFixed | 0x00018000, - FSQRT_d = FPDataProcessing1SourceFixed | FP64 | 0x00018000, - FSQRT = FSQRT_s, - FCVT_ds = FPDataProcessing1SourceFixed | 0x00028000, - FCVT_sd = FPDataProcessing1SourceFixed | FP64 | 0x00020000, - FRINTN_s = FPDataProcessing1SourceFixed | 0x00040000, - FRINTN_d = FPDataProcessing1SourceFixed | FP64 | 0x00040000, - FRINTN = FRINTN_s, - FRINTP_s = FPDataProcessing1SourceFixed | 0x00048000, - FRINTP_d = FPDataProcessing1SourceFixed | FP64 | 0x00048000, - FRINTP = FRINTP_s, - FRINTM_s = FPDataProcessing1SourceFixed | 0x00050000, - FRINTM_d = FPDataProcessing1SourceFixed | FP64 | 0x00050000, - FRINTM = FRINTM_s, - FRINTZ_s = FPDataProcessing1SourceFixed | 0x00058000, - FRINTZ_d = FPDataProcessing1SourceFixed | FP64 | 0x00058000, - FRINTZ = FRINTZ_s, - FRINTA_s = FPDataProcessing1SourceFixed | 0x00060000, - FRINTA_d = FPDataProcessing1SourceFixed | FP64 | 0x00060000, - FRINTA = FRINTA_s, - FRINTX_s = FPDataProcessing1SourceFixed | 0x00070000, - FRINTX_d = FPDataProcessing1SourceFixed | FP64 | 0x00070000, - FRINTX = FRINTX_s, - FRINTI_s = FPDataProcessing1SourceFixed | 0x00078000, - FRINTI_d = FPDataProcessing1SourceFixed | FP64 | 0x00078000, - FRINTI = FRINTI_s -}; - -// Floating point data processing 2 source. -enum FPDataProcessing2SourceOp { - FPDataProcessing2SourceFixed = 0x1E200800, - FPDataProcessing2SourceFMask = 0x5F200C00, - FPDataProcessing2SourceMask = 0xFFE0FC00, - FMUL = FPDataProcessing2SourceFixed | 0x00000000, - FMUL_s = FMUL, - FMUL_d = FMUL | FP64, - FDIV = FPDataProcessing2SourceFixed | 0x00001000, - FDIV_s = FDIV, - FDIV_d = FDIV | FP64, - FADD = FPDataProcessing2SourceFixed | 0x00002000, - FADD_s = FADD, - FADD_d = FADD | FP64, - FSUB = FPDataProcessing2SourceFixed | 0x00003000, - FSUB_s = FSUB, - FSUB_d = FSUB | FP64, - FMAX = FPDataProcessing2SourceFixed | 0x00004000, - FMAX_s = FMAX, - FMAX_d = FMAX | FP64, - FMIN = FPDataProcessing2SourceFixed | 0x00005000, - FMIN_s = FMIN, - FMIN_d = FMIN | FP64, - FMAXNM = FPDataProcessing2SourceFixed | 0x00006000, - FMAXNM_s = FMAXNM, - FMAXNM_d = FMAXNM | FP64, - FMINNM = FPDataProcessing2SourceFixed | 0x00007000, - FMINNM_s = FMINNM, - FMINNM_d = FMINNM | FP64, - FNMUL = FPDataProcessing2SourceFixed | 0x00008000, - FNMUL_s = FNMUL, - FNMUL_d = FNMUL | FP64 -}; - -// Floating point data processing 3 source. -enum FPDataProcessing3SourceOp { - FPDataProcessing3SourceFixed = 0x1F000000, - FPDataProcessing3SourceFMask = 0x5F000000, - FPDataProcessing3SourceMask = 0xFFE08000, - FMADD_s = FPDataProcessing3SourceFixed | 0x00000000, - FMSUB_s = FPDataProcessing3SourceFixed | 0x00008000, - FNMADD_s = FPDataProcessing3SourceFixed | 0x00200000, - FNMSUB_s = FPDataProcessing3SourceFixed | 0x00208000, - FMADD_d = FPDataProcessing3SourceFixed | 0x00400000, - FMSUB_d = FPDataProcessing3SourceFixed | 0x00408000, - FNMADD_d = FPDataProcessing3SourceFixed | 0x00600000, - FNMSUB_d = FPDataProcessing3SourceFixed | 0x00608000 -}; - -// Conversion between floating point and integer. -enum FPIntegerConvertOp { - FPIntegerConvertFixed = 0x1E200000, - FPIntegerConvertFMask = 0x5F20FC00, - FPIntegerConvertMask = 0xFFFFFC00, - FCVTNS = FPIntegerConvertFixed | 0x00000000, - FCVTNS_ws = FCVTNS, - FCVTNS_xs = FCVTNS | SixtyFourBits, - FCVTNS_wd = FCVTNS | FP64, - FCVTNS_xd = FCVTNS | SixtyFourBits | FP64, - FCVTNU = FPIntegerConvertFixed | 0x00010000, - FCVTNU_ws = FCVTNU, - FCVTNU_xs = FCVTNU | SixtyFourBits, - FCVTNU_wd = FCVTNU | FP64, - FCVTNU_xd = FCVTNU | SixtyFourBits | FP64, - FCVTPS = FPIntegerConvertFixed | 0x00080000, - FCVTPS_ws = FCVTPS, - FCVTPS_xs = FCVTPS | SixtyFourBits, - FCVTPS_wd = FCVTPS | FP64, - FCVTPS_xd = FCVTPS | SixtyFourBits | FP64, - FCVTPU = FPIntegerConvertFixed | 0x00090000, - FCVTPU_ws = FCVTPU, - FCVTPU_xs = FCVTPU | SixtyFourBits, - FCVTPU_wd = FCVTPU | FP64, - FCVTPU_xd = FCVTPU | SixtyFourBits | FP64, - FCVTMS = FPIntegerConvertFixed | 0x00100000, - FCVTMS_ws = FCVTMS, - FCVTMS_xs = FCVTMS | SixtyFourBits, - FCVTMS_wd = FCVTMS | FP64, - FCVTMS_xd = FCVTMS | SixtyFourBits | FP64, - FCVTMU = FPIntegerConvertFixed | 0x00110000, - FCVTMU_ws = FCVTMU, - FCVTMU_xs = FCVTMU | SixtyFourBits, - FCVTMU_wd = FCVTMU | FP64, - FCVTMU_xd = FCVTMU | SixtyFourBits | FP64, - FCVTZS = FPIntegerConvertFixed | 0x00180000, - FCVTZS_ws = FCVTZS, - FCVTZS_xs = FCVTZS | SixtyFourBits, - FCVTZS_wd = FCVTZS | FP64, - FCVTZS_xd = FCVTZS | SixtyFourBits | FP64, - FCVTZU = FPIntegerConvertFixed | 0x00190000, - FCVTZU_ws = FCVTZU, - FCVTZU_xs = FCVTZU | SixtyFourBits, - FCVTZU_wd = FCVTZU | FP64, - FCVTZU_xd = FCVTZU | SixtyFourBits | FP64, - SCVTF = FPIntegerConvertFixed | 0x00020000, - SCVTF_sw = SCVTF, - SCVTF_sx = SCVTF | SixtyFourBits, - SCVTF_dw = SCVTF | FP64, - SCVTF_dx = SCVTF | SixtyFourBits | FP64, - UCVTF = FPIntegerConvertFixed | 0x00030000, - UCVTF_sw = UCVTF, - UCVTF_sx = UCVTF | SixtyFourBits, - UCVTF_dw = UCVTF | FP64, - UCVTF_dx = UCVTF | SixtyFourBits | FP64, - FCVTAS = FPIntegerConvertFixed | 0x00040000, - FCVTAS_ws = FCVTAS, - FCVTAS_xs = FCVTAS | SixtyFourBits, - FCVTAS_wd = FCVTAS | FP64, - FCVTAS_xd = FCVTAS | SixtyFourBits | FP64, - FCVTAU = FPIntegerConvertFixed | 0x00050000, - FCVTAU_ws = FCVTAU, - FCVTAU_xs = FCVTAU | SixtyFourBits, - FCVTAU_wd = FCVTAU | FP64, - FCVTAU_xd = FCVTAU | SixtyFourBits | FP64, - FMOV_ws = FPIntegerConvertFixed | 0x00060000, - FMOV_sw = FPIntegerConvertFixed | 0x00070000, - FMOV_xd = FMOV_ws | SixtyFourBits | FP64, - FMOV_dx = FMOV_sw | SixtyFourBits | FP64 -}; - -// Conversion between fixed point and floating point. -enum FPFixedPointConvertOp { - FPFixedPointConvertFixed = 0x1E000000, - FPFixedPointConvertFMask = 0x5F200000, - FPFixedPointConvertMask = 0xFFFF0000, - FCVTZS_fixed = FPFixedPointConvertFixed | 0x00180000, - FCVTZS_ws_fixed = FCVTZS_fixed, - FCVTZS_xs_fixed = FCVTZS_fixed | SixtyFourBits, - FCVTZS_wd_fixed = FCVTZS_fixed | FP64, - FCVTZS_xd_fixed = FCVTZS_fixed | SixtyFourBits | FP64, - FCVTZU_fixed = FPFixedPointConvertFixed | 0x00190000, - FCVTZU_ws_fixed = FCVTZU_fixed, - FCVTZU_xs_fixed = FCVTZU_fixed | SixtyFourBits, - FCVTZU_wd_fixed = FCVTZU_fixed | FP64, - FCVTZU_xd_fixed = FCVTZU_fixed | SixtyFourBits | FP64, - SCVTF_fixed = FPFixedPointConvertFixed | 0x00020000, - SCVTF_sw_fixed = SCVTF_fixed, - SCVTF_sx_fixed = SCVTF_fixed | SixtyFourBits, - SCVTF_dw_fixed = SCVTF_fixed | FP64, - SCVTF_dx_fixed = SCVTF_fixed | SixtyFourBits | FP64, - UCVTF_fixed = FPFixedPointConvertFixed | 0x00030000, - UCVTF_sw_fixed = UCVTF_fixed, - UCVTF_sx_fixed = UCVTF_fixed | SixtyFourBits, - UCVTF_dw_fixed = UCVTF_fixed | FP64, - UCVTF_dx_fixed = UCVTF_fixed | SixtyFourBits | FP64 -}; - -// Unimplemented and unallocated instructions. These are defined to make fixed -// bit assertion easier. -enum UnimplementedOp { - UnimplementedFixed = 0x00000000, - UnimplementedFMask = 0x00000000 -}; - -enum UnallocatedOp { - UnallocatedFixed = 0x00000000, - UnallocatedFMask = 0x00000000 -}; - -} } // namespace v8::internal - -#endif // V8_A64_CONSTANTS_A64_H_ diff --git a/deps/v8/src/a64/cpu-a64.cc b/deps/v8/src/a64/cpu-a64.cc deleted file mode 100644 index 6dd5e52ae2..0000000000 --- a/deps/v8/src/a64/cpu-a64.cc +++ /dev/null @@ -1,199 +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. - -// CPU specific code for arm independent of OS goes here. - -#include "v8.h" - -#if V8_TARGET_ARCH_A64 - -#include "a64/cpu-a64.h" -#include "a64/utils-a64.h" - -namespace v8 { -namespace internal { - -#ifdef DEBUG -bool CpuFeatures::initialized_ = false; -#endif -unsigned CpuFeatures::supported_ = 0; -unsigned CpuFeatures::found_by_runtime_probing_only_ = 0; -unsigned CpuFeatures::cross_compile_ = 0; - -// Initialise to smallest possible cache size. -unsigned CpuFeatures::dcache_line_size_ = 1; -unsigned CpuFeatures::icache_line_size_ = 1; - - -void CPU::SetUp() { - CpuFeatures::Probe(); -} - - -bool CPU::SupportsCrankshaft() { - return true; -} - - -void CPU::FlushICache(void* address, size_t length) { - if (length == 0) { - return; - } - -#ifdef USE_SIMULATOR - // TODO(all): consider doing some cache simulation to ensure every address - // run has been synced. - USE(address); - USE(length); -#else - // The code below assumes user space cache operations are allowed. The goal - // of this routine is to make sure the code generated is visible to the I - // side of the CPU. - - uintptr_t start = reinterpret_cast(address); - // Sizes will be used to generate a mask big enough to cover a pointer. - uintptr_t dsize = static_cast(CpuFeatures::dcache_line_size()); - uintptr_t isize = static_cast(CpuFeatures::icache_line_size()); - // Cache line sizes are always a power of 2. - ASSERT(CountSetBits(dsize, 64) == 1); - ASSERT(CountSetBits(isize, 64) == 1); - uintptr_t dstart = start & ~(dsize - 1); - uintptr_t istart = start & ~(isize - 1); - uintptr_t end = start + length; - - __asm__ __volatile__ ( // NOLINT - // Clean every line of the D cache containing the target data. - "0: \n\t" - // dc : Data Cache maintenance - // c : Clean - // va : by (Virtual) Address - // u : to the point of Unification - // The point of unification for a processor is the point by which the - // instruction and data caches are guaranteed to see the same copy of a - // memory location. See ARM DDI 0406B page B2-12 for more information. - "dc cvau, %[dline] \n\t" - "add %[dline], %[dline], %[dsize] \n\t" - "cmp %[dline], %[end] \n\t" - "b.lt 0b \n\t" - // Barrier to make sure the effect of the code above is visible to the rest - // of the world. - // dsb : Data Synchronisation Barrier - // ish : Inner SHareable domain - // The point of unification for an Inner Shareable shareability domain is - // the point by which the instruction and data caches of all the processors - // in that Inner Shareable shareability domain are guaranteed to see the - // same copy of a memory location. See ARM DDI 0406B page B2-12 for more - // information. - "dsb ish \n\t" - // Invalidate every line of the I cache containing the target data. - "1: \n\t" - // ic : instruction cache maintenance - // i : invalidate - // va : by address - // u : to the point of unification - "ic ivau, %[iline] \n\t" - "add %[iline], %[iline], %[isize] \n\t" - "cmp %[iline], %[end] \n\t" - "b.lt 1b \n\t" - // Barrier to make sure the effect of the code above is visible to the rest - // of the world. - "dsb ish \n\t" - // Barrier to ensure any prefetching which happened before this code is - // discarded. - // isb : Instruction Synchronisation Barrier - "isb \n\t" - : [dline] "+r" (dstart), - [iline] "+r" (istart) - : [dsize] "r" (dsize), - [isize] "r" (isize), - [end] "r" (end) - // This code does not write to memory but without the dependency gcc might - // move this code before the code is generated. - : "cc", "memory" - ); // NOLINT -#endif -} - - -void CpuFeatures::Probe() { - // Compute I and D cache line size. The cache type register holds - // information about the caches. - uint32_t cache_type_register = GetCacheType(); - - static const int kDCacheLineSizeShift = 16; - static const int kICacheLineSizeShift = 0; - static const uint32_t kDCacheLineSizeMask = 0xf << kDCacheLineSizeShift; - static const uint32_t kICacheLineSizeMask = 0xf << kICacheLineSizeShift; - - // The cache type register holds the size of the I and D caches as a power of - // two. - uint32_t dcache_line_size_power_of_two = - (cache_type_register & kDCacheLineSizeMask) >> kDCacheLineSizeShift; - uint32_t icache_line_size_power_of_two = - (cache_type_register & kICacheLineSizeMask) >> kICacheLineSizeShift; - - dcache_line_size_ = 1 << dcache_line_size_power_of_two; - icache_line_size_ = 1 << icache_line_size_power_of_two; - - // AArch64 has no configuration options, no further probing is required. - supported_ = 0; - -#ifdef DEBUG - initialized_ = true; -#endif -} - - -unsigned CpuFeatures::dcache_line_size() { - ASSERT(initialized_); - return dcache_line_size_; -} - - -unsigned CpuFeatures::icache_line_size() { - ASSERT(initialized_); - return icache_line_size_; -} - - -uint32_t CpuFeatures::GetCacheType() { -#ifdef USE_SIMULATOR - // This will lead to a cache with 1 byte long lines, which is fine since the - // simulator will not need this information. - return 0; -#else - uint32_t cache_type_register; - // Copy the content of the cache type register to a core register. - __asm__ __volatile__ ("mrs %[ctr], ctr_el0" // NOLINT - : [ctr] "=r" (cache_type_register)); - return cache_type_register; -#endif -} - -} } // namespace v8::internal - -#endif // V8_TARGET_ARCH_A64 diff --git a/deps/v8/src/a64/cpu-a64.h b/deps/v8/src/a64/cpu-a64.h deleted file mode 100644 index 969312b3c4..0000000000 --- a/deps/v8/src/a64/cpu-a64.h +++ /dev/null @@ -1,107 +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. - -#ifndef V8_A64_CPU_A64_H_ -#define V8_A64_CPU_A64_H_ - -#include -#include "serialize.h" -#include "cpu.h" - -namespace v8 { -namespace internal { - - -// CpuFeatures keeps track of which features are supported by the target CPU. -// Supported features must be enabled by a CpuFeatureScope before use. -class CpuFeatures : public AllStatic { - public: - // Detect features of the target CPU. Set safe defaults if the serializer - // is enabled (snapshots must be portable). - static void Probe(); - - // Check whether a feature is supported by the target CPU. - static bool IsSupported(CpuFeature f) { - ASSERT(initialized_); - // There are no optional features for A64. - return false; - }; - - static bool IsFoundByRuntimeProbingOnly(CpuFeature f) { - ASSERT(initialized_); - // There are no optional features for A64. - return false; - } - - static bool IsSafeForSnapshot(CpuFeature f) { - return (IsSupported(f) && - (!Serializer::enabled() || !IsFoundByRuntimeProbingOnly(f))); - } - - // I and D cache line size in bytes. - static unsigned dcache_line_size(); - static unsigned icache_line_size(); - - static unsigned supported_; - - static bool VerifyCrossCompiling() { - // There are no optional features for A64. - ASSERT(cross_compile_ == 0); - return true; - } - - static bool VerifyCrossCompiling(CpuFeature f) { - // There are no optional features for A64. - USE(f); - ASSERT(cross_compile_ == 0); - return true; - } - - private: - // Return the content of the cache type register. - static uint32_t GetCacheType(); - - // I and D cache line size in bytes. - static unsigned icache_line_size_; - static unsigned dcache_line_size_; - -#ifdef DEBUG - static bool initialized_; -#endif - - // This isn't used (and is always 0), but it is required by V8. - static unsigned found_by_runtime_probing_only_; - - static unsigned cross_compile_; - - friend class PlatformFeatureScope; - DISALLOW_COPY_AND_ASSIGN(CpuFeatures); -}; - -} } // namespace v8::internal - -#endif // V8_A64_CPU_A64_H_ diff --git a/deps/v8/src/a64/debug-a64.cc b/deps/v8/src/a64/debug-a64.cc deleted file mode 100644 index d8711650c1..0000000000 --- a/deps/v8/src/a64/debug-a64.cc +++ /dev/null @@ -1,394 +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 "v8.h" - -#if V8_TARGET_ARCH_A64 - -#include "codegen.h" -#include "debug.h" - -namespace v8 { -namespace internal { - - -#define __ ACCESS_MASM(masm) - - -#ifdef ENABLE_DEBUGGER_SUPPORT -bool BreakLocationIterator::IsDebugBreakAtReturn() { - return Debug::IsDebugBreakAtReturn(rinfo()); -} - - -void BreakLocationIterator::SetDebugBreakAtReturn() { - // Patch the code emitted by FullCodeGenerator::EmitReturnSequence, changing - // the return from JS function sequence from - // mov sp, fp - // ldp fp, lr, [sp] #16 - // lrd ip0, [pc, #(3 * kInstructionSize)] - // add sp, sp, ip0 - // ret - // - // to a call to the debug break return code. - // ldr ip0, [pc, #(3 * kInstructionSize)] - // blr ip0 - // hlt kHltBadCode @ code should not return, catch if it does. - // - - // The patching code must not overflow the space occupied by the return - // sequence. - STATIC_ASSERT(Assembler::kJSRetSequenceInstructions >= 5); - PatchingAssembler patcher(reinterpret_cast(rinfo()->pc()), 5); - byte* entry = - debug_info_->GetIsolate()->debug()->debug_break_return()->entry(); - - // The first instruction of a patched return sequence must be a load literal - // loading the address of the debug break return code. - patcher.LoadLiteral(ip0, 3 * kInstructionSize); - // TODO(all): check the following is correct. - // The debug break return code will push a frame and call statically compiled - // code. By using blr, even though control will not return after the branch, - // this call site will be registered in the frame (lr being saved as the pc - // of the next instruction to execute for this frame). The debugger can now - // iterate on the frames to find call to debug break return code. - patcher.blr(ip0); - patcher.hlt(kHltBadCode); - patcher.dc64(reinterpret_cast(entry)); -} - - -void BreakLocationIterator::ClearDebugBreakAtReturn() { - // Reset the code emitted by EmitReturnSequence to its original state. - rinfo()->PatchCode(original_rinfo()->pc(), - Assembler::kJSRetSequenceInstructions); -} - - -bool Debug::IsDebugBreakAtReturn(RelocInfo* rinfo) { - ASSERT(RelocInfo::IsJSReturn(rinfo->rmode())); - return rinfo->IsPatchedReturnSequence(); -} - - -bool BreakLocationIterator::IsDebugBreakAtSlot() { - ASSERT(IsDebugBreakSlot()); - // Check whether the debug break slot instructions have been patched. - return rinfo()->IsPatchedDebugBreakSlotSequence(); -} - - -void BreakLocationIterator::SetDebugBreakAtSlot() { - // Patch the code emitted by Debug::GenerateSlots, changing the debug break - // slot code from - // mov x0, x0 @ nop DEBUG_BREAK_NOP - // mov x0, x0 @ nop DEBUG_BREAK_NOP - // mov x0, x0 @ nop DEBUG_BREAK_NOP - // mov x0, x0 @ nop DEBUG_BREAK_NOP - // to a call to the debug slot code. - // ldr ip0, [pc, #(2 * kInstructionSize)] - // blr ip0 - // - - // TODO(all): consider adding a hlt instruction after the blr as we don't - // expect control to return here. This implies increasing - // kDebugBreakSlotInstructions to 5 instructions. - - // The patching code must not overflow the space occupied by the return - // sequence. - STATIC_ASSERT(Assembler::kDebugBreakSlotInstructions >= 4); - PatchingAssembler patcher(reinterpret_cast(rinfo()->pc()), 4); - byte* entry = - debug_info_->GetIsolate()->debug()->debug_break_slot()->entry(); - - // The first instruction of a patched debug break slot must be a load literal - // loading the address of the debug break slot code. - patcher.LoadLiteral(ip0, 2 * kInstructionSize); - // TODO(all): check the following is correct. - // The debug break slot code will push a frame and call statically compiled - // code. By using blr, event hough control will not return after the branch, - // this call site will be registered in the frame (lr being saved as the pc - // of the next instruction to execute for this frame). The debugger can now - // iterate on the frames to find call to debug break slot code. - patcher.blr(ip0); - patcher.dc64(reinterpret_cast(entry)); -} - - -void BreakLocationIterator::ClearDebugBreakAtSlot() { - ASSERT(IsDebugBreakSlot()); - rinfo()->PatchCode(original_rinfo()->pc(), - Assembler::kDebugBreakSlotInstructions); -} - -const bool Debug::FramePaddingLayout::kIsSupported = false; - -static void Generate_DebugBreakCallHelper(MacroAssembler* masm, - RegList object_regs, - RegList non_object_regs, - Register scratch) { - { - FrameScope scope(masm, StackFrame::INTERNAL); - - // Any live values (object_regs and non_object_regs) in caller-saved - // registers (or lr) need to be stored on the stack so that their values are - // safely preserved for a call into C code. - // - // Also: - // * object_regs may be modified during the C code by the garbage - // collector. Every object register must be a valid tagged pointer or - // SMI. - // - // * non_object_regs will be converted to SMIs so that the garbage - // collector doesn't try to interpret them as pointers. - // - // TODO(jbramley): Why can't this handle callee-saved registers? - ASSERT((~kCallerSaved.list() & object_regs) == 0); - ASSERT((~kCallerSaved.list() & non_object_regs) == 0); - ASSERT((object_regs & non_object_regs) == 0); - ASSERT((scratch.Bit() & object_regs) == 0); - ASSERT((scratch.Bit() & non_object_regs) == 0); - ASSERT((ip0.Bit() & (object_regs | non_object_regs)) == 0); - ASSERT((ip1.Bit() & (object_regs | non_object_regs)) == 0); - STATIC_ASSERT(kSmiValueSize == 32); - - CPURegList non_object_list = - CPURegList(CPURegister::kRegister, kXRegSize, non_object_regs); - while (!non_object_list.IsEmpty()) { - // Store each non-object register as two SMIs. - Register reg = Register(non_object_list.PopLowestIndex()); - __ Push(reg); - __ Poke(wzr, 0); - __ Push(reg.W(), wzr); - // Stack: - // jssp[12]: reg[63:32] - // jssp[8]: 0x00000000 (SMI tag & padding) - // jssp[4]: reg[31:0] - // jssp[0]: 0x00000000 (SMI tag & padding) - STATIC_ASSERT((kSmiTag == 0) && (kSmiShift == 32)); - } - - if (object_regs != 0) { - __ PushXRegList(object_regs); - } - -#ifdef DEBUG - __ RecordComment("// Calling from debug break to runtime - come in - over"); -#endif - __ Mov(x0, 0); // No arguments. - __ Mov(x1, Operand(ExternalReference::debug_break(masm->isolate()))); - - CEntryStub stub(1); - __ CallStub(&stub); - - // Restore the register values from the expression stack. - if (object_regs != 0) { - __ PopXRegList(object_regs); - } - - non_object_list = - CPURegList(CPURegister::kRegister, kXRegSize, non_object_regs); - while (!non_object_list.IsEmpty()) { - // Load each non-object register from two SMIs. - // Stack: - // jssp[12]: reg[63:32] - // jssp[8]: 0x00000000 (SMI tag & padding) - // jssp[4]: reg[31:0] - // jssp[0]: 0x00000000 (SMI tag & padding) - Register reg = Register(non_object_list.PopHighestIndex()); - __ Pop(scratch, reg); - __ Bfxil(reg, scratch, 32, 32); - } - - // Leave the internal frame. - } - - // 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(Debug_Address::AfterBreakTarget(), - masm->isolate()); - __ Mov(scratch, Operand(after_break_target)); - __ Ldr(scratch, MemOperand(scratch)); - __ Br(scratch); -} - - -void Debug::GenerateLoadICDebugBreak(MacroAssembler* masm) { - // Calling convention for IC load (from ic-arm.cc). - // ----------- S t a t e ------------- - // -- x2 : name - // -- lr : return address - // -- x0 : receiver - // -- [sp] : receiver - // ----------------------------------- - // Registers x0 and x2 contain objects that need to be pushed on the - // expression stack of the fake JS frame. - Generate_DebugBreakCallHelper(masm, x0.Bit() | x2.Bit(), 0, x10); -} - - -void Debug::GenerateStoreICDebugBreak(MacroAssembler* masm) { - // Calling convention for IC store (from ic-arm.cc). - // ----------- S t a t e ------------- - // -- x0 : value - // -- x1 : receiver - // -- x2 : name - // -- lr : return address - // ----------------------------------- - // Registers x0, x1, and x2 contain objects that need to be pushed on the - // expression stack of the fake JS frame. - Generate_DebugBreakCallHelper(masm, x0.Bit() | x1.Bit() | x2.Bit(), 0, x10); -} - - -void Debug::GenerateKeyedLoadICDebugBreak(MacroAssembler* masm) { - // ---------- S t a t e -------------- - // -- lr : return address - // -- x0 : key - // -- x1 : receiver - Generate_DebugBreakCallHelper(masm, x0.Bit() | x1.Bit(), 0, x10); -} - - -void Debug::GenerateKeyedStoreICDebugBreak(MacroAssembler* masm) { - // ---------- S t a t e -------------- - // -- x0 : value - // -- x1 : key - // -- x2 : receiver - // -- lr : return address - Generate_DebugBreakCallHelper(masm, x0.Bit() | x1.Bit() | x2.Bit(), 0, x10); -} - - -void Debug::GenerateCompareNilICDebugBreak(MacroAssembler* masm) { - // Register state for CompareNil IC - // ----------- S t a t e ------------- - // -- r0 : value - // ----------------------------------- - Generate_DebugBreakCallHelper(masm, x0.Bit(), 0, x10); -} - - -void Debug::GenerateCallICDebugBreak(MacroAssembler* masm) { - // Calling convention for IC call (from ic-arm.cc) - // ----------- S t a t e ------------- - // -- x2 : name - // ----------------------------------- - Generate_DebugBreakCallHelper(masm, x2.Bit(), 0, x10); -} - - -void Debug::GenerateReturnDebugBreak(MacroAssembler* masm) { - // In places other than IC call sites it is expected that r0 is TOS which - // is an object - this is not generally the case so this should be used with - // care. - Generate_DebugBreakCallHelper(masm, x0.Bit(), 0, x10); -} - - -void Debug::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) { - // Register state for CallFunctionStub (from code-stubs-a64.cc). - // ----------- S t a t e ------------- - // -- x1 : function - // ----------------------------------- - Generate_DebugBreakCallHelper(masm, x1.Bit(), 0, x10); -} - - -void Debug::GenerateCallFunctionStubRecordDebugBreak(MacroAssembler* masm) { - // Register state for CallFunctionStub (from code-stubs-a64.cc). - // ----------- S t a t e ------------- - // -- x1 : function - // -- x2 : feedback array - // -- x3 : slot in feedback array - // ----------------------------------- - Generate_DebugBreakCallHelper(masm, x1.Bit() | x2.Bit() | x3.Bit(), 0, x10); -} - - -void Debug::GenerateCallConstructStubDebugBreak(MacroAssembler* masm) { - // Calling convention for CallConstructStub (from code-stubs-a64.cc). - // ----------- S t a t e ------------- - // -- x0 : number of arguments (not smi) - // -- x1 : constructor function - // ----------------------------------- - Generate_DebugBreakCallHelper(masm, x1.Bit(), x0.Bit(), x10); -} - - -void Debug::GenerateCallConstructStubRecordDebugBreak(MacroAssembler* masm) { - // Calling convention for CallConstructStub (from code-stubs-a64.cc). - // ----------- S t a t e ------------- - // -- x0 : number of arguments (not smi) - // -- x1 : constructor function - // -- x2 : feedback array - // -- x3 : feedback slot (smi) - // ----------------------------------- - Generate_DebugBreakCallHelper( - masm, x1.Bit() | x2.Bit() | x3.Bit(), x0.Bit(), x10); -} - - -void Debug::GenerateSlot(MacroAssembler* masm) { - // Generate enough nop's to make space for a call instruction. Avoid emitting - // the constant pool in the debug break slot code. - InstructionAccurateScope scope(masm, Assembler::kDebugBreakSlotInstructions); - - __ RecordDebugBreakSlot(); - for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) { - __ nop(Assembler::DEBUG_BREAK_NOP); - } -} - - -void Debug::GenerateSlotDebugBreak(MacroAssembler* masm) { - // In the places where a debug break slot is inserted no registers can contain - // object pointers. - Generate_DebugBreakCallHelper(masm, 0, 0, x10); -} - - -void Debug::GeneratePlainReturnLiveEdit(MacroAssembler* masm) { - masm->Abort(kLiveEditFrameDroppingIsNotSupportedOnA64); -} - - -void Debug::GenerateFrameDropperLiveEdit(MacroAssembler* masm) { - masm->Abort(kLiveEditFrameDroppingIsNotSupportedOnA64); -} - -const bool Debug::kFrameDropperSupported = false; - -#endif // ENABLE_DEBUGGER_SUPPORT - -} } // namespace v8::internal - -#endif // V8_TARGET_ARCH_A64 diff --git a/deps/v8/src/a64/debugger-a64.cc b/deps/v8/src/a64/debugger-a64.cc deleted file mode 100644 index 5bccc39776..0000000000 --- a/deps/v8/src/a64/debugger-a64.cc +++ /dev/null @@ -1,111 +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. - -#if V8_TARGET_ARCH_A64 - -#if defined(USE_SIMULATOR) - -#include "a64/debugger-a64.h" - -namespace v8 { -namespace internal { - - -void Debugger::VisitException(Instruction* instr) { - switch (instr->Mask(ExceptionMask)) { - case HLT: { - if (instr->ImmException() == kImmExceptionIsDebug) { - // Read the arguments encoded inline in the instruction stream. - uint32_t code; - uint32_t parameters; - char const * message; - - ASSERT(sizeof(*pc_) == 1); - memcpy(&code, pc_ + kDebugCodeOffset, sizeof(code)); - memcpy(¶meters, pc_ + kDebugParamsOffset, sizeof(parameters)); - message = reinterpret_cast(pc_ + kDebugMessageOffset); - - if (message[0] == '\0') { - fprintf(stream_, "Debugger hit %" PRIu32 ".\n", code); - } else { - fprintf(stream_, "Debugger hit %" PRIu32 ": %s\n", code, message); - } - - // Other options. - switch (parameters & kDebuggerTracingDirectivesMask) { - case TRACE_ENABLE: - set_log_parameters(log_parameters() | parameters); - break; - case TRACE_DISABLE: - set_log_parameters(log_parameters() & ~parameters); - break; - case TRACE_OVERRIDE: - set_log_parameters(parameters); - break; - default: - // We don't support a one-shot LOG_DISASM. - ASSERT((parameters & LOG_DISASM) == 0); - // Don't print information that is already being traced. - parameters &= ~log_parameters(); - // Print the requested information. - if (parameters & LOG_SYS_REGS) PrintSystemRegisters(true); - if (parameters & LOG_REGS) PrintRegisters(true); - if (parameters & LOG_FP_REGS) PrintFPRegisters(true); - } - - // Check if the debugger should break. - if (parameters & BREAK) OS::DebugBreak(); - - // The stop parameters are inlined in the code. Skip them: - // - Skip to the end of the message string. - pc_ += kDebugMessageOffset + strlen(message) + 1; - // - Advance to the next aligned location. - pc_ = AlignUp(pc_, kInstructionSize); - // - Verify that the unreachable marker is present. - ASSERT(reinterpret_cast(pc_)->Mask(ExceptionMask) == HLT); - ASSERT(reinterpret_cast(pc_)->ImmException() == - kImmExceptionIsUnreachable); - // - Skip past the unreachable marker. - pc_ += kInstructionSize; - pc_modified_ = true; - } else { - Simulator::VisitException(instr); - } - break; - } - - default: - UNIMPLEMENTED(); - } -} - - -} } // namespace v8::internal - -#endif // USE_SIMULATOR - -#endif // V8_TARGET_ARCH_A64 diff --git a/deps/v8/src/a64/debugger-a64.h b/deps/v8/src/a64/debugger-a64.h deleted file mode 100644 index 1317b5f37d..0000000000 --- a/deps/v8/src/a64/debugger-a64.h +++ /dev/null @@ -1,56 +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. - -#ifndef V8_A64_DEBUGGER_A64_H_ -#define V8_A64_DEBUGGER_A64_H_ - -#if defined(USE_SIMULATOR) - -#include "globals.h" -#include "utils.h" -#include "a64/constants-a64.h" -#include "a64/simulator-a64.h" - -namespace v8 { -namespace internal { - - -class Debugger : public Simulator { - public: - Debugger(Decoder* decoder, FILE* stream = stderr) - : Simulator(decoder, NULL, stream) {} - - // Functions overloading. - void VisitException(Instruction* instr); -}; - - -} } // namespace v8::internal - -#endif // USE_SIMULATOR - -#endif // V8_A64_DEBUGGER_A64_H_ diff --git a/deps/v8/src/a64/decoder-a64.cc b/deps/v8/src/a64/decoder-a64.cc deleted file mode 100644 index e7383d446a..0000000000 --- a/deps/v8/src/a64/decoder-a64.cc +++ /dev/null @@ -1,726 +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 "v8.h" - -#if V8_TARGET_ARCH_A64 - -#include "globals.h" -#include "utils.h" -#include "a64/decoder-a64.h" - - -namespace v8 { -namespace internal { - -// Top-level instruction decode function. -void Decoder::Decode(Instruction *instr) { - if (instr->Bits(28, 27) == 0) { - VisitUnallocated(instr); - } else { - switch (instr->Bits(27, 24)) { - // 0: PC relative addressing. - case 0x0: DecodePCRelAddressing(instr); break; - - // 1: Add/sub immediate. - case 0x1: DecodeAddSubImmediate(instr); break; - - // A: Logical shifted register. - // Add/sub with carry. - // Conditional compare register. - // Conditional compare immediate. - // Conditional select. - // Data processing 1 source. - // Data processing 2 source. - // B: Add/sub shifted register. - // Add/sub extended register. - // Data processing 3 source. - case 0xA: - case 0xB: DecodeDataProcessing(instr); break; - - // 2: Logical immediate. - // Move wide immediate. - case 0x2: DecodeLogical(instr); break; - - // 3: Bitfield. - // Extract. - case 0x3: DecodeBitfieldExtract(instr); break; - - // 4: Unconditional branch immediate. - // Exception generation. - // Compare and branch immediate. - // 5: Compare and branch immediate. - // Conditional branch. - // System. - // 6,7: Unconditional branch. - // Test and branch immediate. - case 0x4: - case 0x5: - case 0x6: - case 0x7: DecodeBranchSystemException(instr); break; - - // 8,9: Load/store register pair post-index. - // Load register literal. - // Load/store register unscaled immediate. - // Load/store register immediate post-index. - // Load/store register immediate pre-index. - // Load/store register offset. - // C,D: Load/store register pair offset. - // Load/store register pair pre-index. - // Load/store register unsigned immediate. - // Advanced SIMD. - case 0x8: - case 0x9: - case 0xC: - case 0xD: DecodeLoadStore(instr); break; - - // E: FP fixed point conversion. - // FP integer conversion. - // FP data processing 1 source. - // FP compare. - // FP immediate. - // FP data processing 2 source. - // FP conditional compare. - // FP conditional select. - // Advanced SIMD. - // F: FP data processing 3 source. - // Advanced SIMD. - case 0xE: - case 0xF: DecodeFP(instr); break; - } - } -} - - -void Decoder::AppendVisitor(DecoderVisitor* new_visitor) { - visitors_.remove(new_visitor); - visitors_.push_front(new_visitor); -} - - -void Decoder::PrependVisitor(DecoderVisitor* new_visitor) { - visitors_.remove(new_visitor); - visitors_.push_back(new_visitor); -} - - -void Decoder::InsertVisitorBefore(DecoderVisitor* new_visitor, - DecoderVisitor* registered_visitor) { - visitors_.remove(new_visitor); - std::list::iterator it; - for (it = visitors_.begin(); it != visitors_.end(); it++) { - if (*it == registered_visitor) { - visitors_.insert(it, new_visitor); - return; - } - } - // We reached the end of the list. The last element must be - // registered_visitor. - ASSERT(*it == registered_visitor); - visitors_.insert(it, new_visitor); -} - - -void Decoder::InsertVisitorAfter(DecoderVisitor* new_visitor, - DecoderVisitor* registered_visitor) { - visitors_.remove(new_visitor); - std::list::iterator it; - for (it = visitors_.begin(); it != visitors_.end(); it++) { - if (*it == registered_visitor) { - it++; - visitors_.insert(it, new_visitor); - return; - } - } - // We reached the end of the list. The last element must be - // registered_visitor. - ASSERT(*it == registered_visitor); - visitors_.push_back(new_visitor); -} - - -void Decoder::RemoveVisitor(DecoderVisitor* visitor) { - visitors_.remove(visitor); -} - - -void Decoder::DecodePCRelAddressing(Instruction* instr) { - ASSERT(instr->Bits(27, 24) == 0x0); - // We know bit 28 is set, as = 0 is filtered out at the top level - // decode. - ASSERT(instr->Bit(28) == 0x1); - VisitPCRelAddressing(instr); -} - - -void Decoder::DecodeBranchSystemException(Instruction* instr) { - ASSERT((instr->Bits(27, 24) == 0x4) || - (instr->Bits(27, 24) == 0x5) || - (instr->Bits(27, 24) == 0x6) || - (instr->Bits(27, 24) == 0x7) ); - - switch (instr->Bits(31, 29)) { - case 0: - case 4: { - VisitUnconditionalBranch(instr); - break; - } - case 1: - case 5: { - if (instr->Bit(25) == 0) { - VisitCompareBranch(instr); - } else { - VisitTestBranch(instr); - } - break; - } - case 2: { - if (instr->Bit(25) == 0) { - if ((instr->Bit(24) == 0x1) || - (instr->Mask(0x01000010) == 0x00000010)) { - VisitUnallocated(instr); - } else { - VisitConditionalBranch(instr); - } - } else { - VisitUnallocated(instr); - } - break; - } - case 6: { - if (instr->Bit(25) == 0) { - if (instr->Bit(24) == 0) { - if ((instr->Bits(4, 2) != 0) || - (instr->Mask(0x00E0001D) == 0x00200001) || - (instr->Mask(0x00E0001D) == 0x00400001) || - (instr->Mask(0x00E0001E) == 0x00200002) || - (instr->Mask(0x00E0001E) == 0x00400002) || - (instr->Mask(0x00E0001C) == 0x00600000) || - (instr->Mask(0x00E0001C) == 0x00800000) || - (instr->Mask(0x00E0001F) == 0x00A00000) || - (instr->Mask(0x00C0001C) == 0x00C00000)) { - VisitUnallocated(instr); - } else { - VisitException(instr); - } - } else { - if (instr->Bits(23, 22) == 0) { - const Instr masked_003FF0E0 = instr->Mask(0x003FF0E0); - if ((instr->Bits(21, 19) == 0x4) || - (masked_003FF0E0 == 0x00033000) || - (masked_003FF0E0 == 0x003FF020) || - (masked_003FF0E0 == 0x003FF060) || - (masked_003FF0E0 == 0x003FF0E0) || - (instr->Mask(0x00388000) == 0x00008000) || - (instr->Mask(0x0038E000) == 0x00000000) || - (instr->Mask(0x0039E000) == 0x00002000) || - (instr->Mask(0x003AE000) == 0x00002000) || - (instr->Mask(0x003CE000) == 0x00042000) || - (instr->Mask(0x003FFFC0) == 0x000320C0) || - (instr->Mask(0x003FF100) == 0x00032100) || - (instr->Mask(0x003FF200) == 0x00032200) || - (instr->Mask(0x003FF400) == 0x00032400) || - (instr->Mask(0x003FF800) == 0x00032800) || - (instr->Mask(0x0038F000) == 0x00005000) || - (instr->Mask(0x0038E000) == 0x00006000)) { - VisitUnallocated(instr); - } else { - VisitSystem(instr); - } - } else { - VisitUnallocated(instr); - } - } - } else { - if ((instr->Bit(24) == 0x1) || - (instr->Bits(20, 16) != 0x1F) || - (instr->Bits(15, 10) != 0) || - (instr->Bits(4, 0) != 0) || - (instr->Bits(24, 21) == 0x3) || - (instr->Bits(24, 22) == 0x3)) { - VisitUnallocated(instr); - } else { - VisitUnconditionalBranchToRegister(instr); - } - } - break; - } - case 3: - case 7: { - VisitUnallocated(instr); - break; - } - } -} - - -void Decoder::DecodeLoadStore(Instruction* instr) { - ASSERT((instr->Bits(27, 24) == 0x8) || - (instr->Bits(27, 24) == 0x9) || - (instr->Bits(27, 24) == 0xC) || - (instr->Bits(27, 24) == 0xD) ); - - if (instr->Bit(24) == 0) { - if (instr->Bit(28) == 0) { - if (instr->Bit(29) == 0) { - if (instr->Bit(26) == 0) { - // TODO(all): VisitLoadStoreExclusive. - VisitUnimplemented(instr); - } else { - DecodeAdvSIMDLoadStore(instr); - } - } else { - if ((instr->Bits(31, 30) == 0x3) || - (instr->Mask(0xC4400000) == 0x40000000)) { - VisitUnallocated(instr); - } else { - if (instr->Bit(23) == 0) { - if (instr->Mask(0xC4400000) == 0xC0400000) { - VisitUnallocated(instr); - } else { - VisitLoadStorePairNonTemporal(instr); - } - } else { - VisitLoadStorePairPostIndex(instr); - } - } - } - } else { - if (instr->Bit(29) == 0) { - if (instr->Mask(0xC4000000) == 0xC4000000) { - VisitUnallocated(instr); - } else { - VisitLoadLiteral(instr); - } - } else { - if ((instr->Mask(0x84C00000) == 0x80C00000) || - (instr->Mask(0x44800000) == 0x44800000) || - (instr->Mask(0x84800000) == 0x84800000)) { - VisitUnallocated(instr); - } else { - if (instr->Bit(21) == 0) { - switch (instr->Bits(11, 10)) { - case 0: { - VisitLoadStoreUnscaledOffset(instr); - break; - } - case 1: { - if (instr->Mask(0xC4C00000) == 0xC0800000) { - VisitUnallocated(instr); - } else { - VisitLoadStorePostIndex(instr); - } - break; - } - case 2: { - // TODO(all): VisitLoadStoreRegisterOffsetUnpriv. - VisitUnimplemented(instr); - break; - } - case 3: { - if (instr->Mask(0xC4C00000) == 0xC0800000) { - VisitUnallocated(instr); - } else { - VisitLoadStorePreIndex(instr); - } - break; - } - } - } else { - if (instr->Bits(11, 10) == 0x2) { - if (instr->Bit(14) == 0) { - VisitUnallocated(instr); - } else { - VisitLoadStoreRegisterOffset(instr); - } - } else { - VisitUnallocated(instr); - } - } - } - } - } - } else { - if (instr->Bit(28) == 0) { - if (instr->Bit(29) == 0) { - VisitUnallocated(instr); - } else { - if ((instr->Bits(31, 30) == 0x3) || - (instr->Mask(0xC4400000) == 0x40000000)) { - VisitUnallocated(instr); - } else { - if (instr->Bit(23) == 0) { - VisitLoadStorePairOffset(instr); - } else { - VisitLoadStorePairPreIndex(instr); - } - } - } - } else { - if (instr->Bit(29) == 0) { - VisitUnallocated(instr); - } else { - if ((instr->Mask(0x84C00000) == 0x80C00000) || - (instr->Mask(0x44800000) == 0x44800000) || - (instr->Mask(0x84800000) == 0x84800000)) { - VisitUnallocated(instr); - } else { - VisitLoadStoreUnsignedOffset(instr); - } - } - } - } -} - - -void Decoder::DecodeLogical(Instruction* instr) { - ASSERT(instr->Bits(27, 24) == 0x2); - - if (instr->Mask(0x80400000) == 0x00400000) { - VisitUnallocated(instr); - } else { - if (instr->Bit(23) == 0) { - VisitLogicalImmediate(instr); - } else { - if (instr->Bits(30, 29) == 0x1) { - VisitUnallocated(instr); - } else { - VisitMoveWideImmediate(instr); - } - } - } -} - - -void Decoder::DecodeBitfieldExtract(Instruction* instr) { - ASSERT(instr->Bits(27, 24) == 0x3); - - if ((instr->Mask(0x80400000) == 0x80000000) || - (instr->Mask(0x80400000) == 0x00400000) || - (instr->Mask(0x80008000) == 0x00008000)) { - VisitUnallocated(instr); - } else if (instr->Bit(23) == 0) { - if ((instr->Mask(0x80200000) == 0x00200000) || - (instr->Mask(0x60000000) == 0x60000000)) { - VisitUnallocated(instr); - } else { - VisitBitfield(instr); - } - } else { - if ((instr->Mask(0x60200000) == 0x00200000) || - (instr->Mask(0x60000000) != 0x00000000)) { - VisitUnallocated(instr); - } else { - VisitExtract(instr); - } - } -} - - -void Decoder::DecodeAddSubImmediate(Instruction* instr) { - ASSERT(instr->Bits(27, 24) == 0x1); - if (instr->Bit(23) == 1) { - VisitUnallocated(instr); - } else { - VisitAddSubImmediate(instr); - } -} - - -void Decoder::DecodeDataProcessing(Instruction* instr) { - ASSERT((instr->Bits(27, 24) == 0xA) || - (instr->Bits(27, 24) == 0xB) ); - - if (instr->Bit(24) == 0) { - if (instr->Bit(28) == 0) { - if (instr->Mask(0x80008000) == 0x00008000) { - VisitUnallocated(instr); - } else { - VisitLogicalShifted(instr); - } - } else { - switch (instr->Bits(23, 21)) { - case 0: { - if (instr->Mask(0x0000FC00) != 0) { - VisitUnallocated(instr); - } else { - VisitAddSubWithCarry(instr); - } - break; - } - case 2: { - if ((instr->Bit(29) == 0) || - (instr->Mask(0x00000410) != 0)) { - VisitUnallocated(instr); - } else { - if (instr->Bit(11) == 0) { - VisitConditionalCompareRegister(instr); - } else { - VisitConditionalCompareImmediate(instr); - } - } - break; - } - case 4: { - if (instr->Mask(0x20000800) != 0x00000000) { - VisitUnallocated(instr); - } else { - VisitConditionalSelect(instr); - } - break; - } - case 6: { - if (instr->Bit(29) == 0x1) { - VisitUnallocated(instr); - } else { - if (instr->Bit(30) == 0) { - if ((instr->Bit(15) == 0x1) || - (instr->Bits(15, 11) == 0) || - (instr->Bits(15, 12) == 0x1) || - (instr->Bits(15, 12) == 0x3) || - (instr->Bits(15, 13) == 0x3) || - (instr->Mask(0x8000EC00) == 0x00004C00) || - (instr->Mask(0x8000E800) == 0x80004000) || - (instr->Mask(0x8000E400) == 0x80004000)) { - VisitUnallocated(instr); - } else { - VisitDataProcessing2Source(instr); - } - } else { - if ((instr->Bit(13) == 1) || - (instr->Bits(20, 16) != 0) || - (instr->Bits(15, 14) != 0) || - (instr->Mask(0xA01FFC00) == 0x00000C00) || - (instr->Mask(0x201FF800) == 0x00001800)) { - VisitUnallocated(instr); - } else { - VisitDataProcessing1Source(instr); - } - } - break; - } - } - case 1: - case 3: - case 5: - case 7: VisitUnallocated(instr); break; - } - } - } else { - if (instr->Bit(28) == 0) { - if (instr->Bit(21) == 0) { - if ((instr->Bits(23, 22) == 0x3) || - (instr->Mask(0x80008000) == 0x00008000)) { - VisitUnallocated(instr); - } else { - VisitAddSubShifted(instr); - } - } else { - if ((instr->Mask(0x00C00000) != 0x00000000) || - (instr->Mask(0x00001400) == 0x00001400) || - (instr->Mask(0x00001800) == 0x00001800)) { - VisitUnallocated(instr); - } else { - VisitAddSubExtended(instr); - } - } - } else { - if ((instr->Bit(30) == 0x1) || - (instr->Bits(30, 29) == 0x1) || - (instr->Mask(0xE0600000) == 0x00200000) || - (instr->Mask(0xE0608000) == 0x00400000) || - (instr->Mask(0x60608000) == 0x00408000) || - (instr->Mask(0x60E00000) == 0x00E00000) || - (instr->Mask(0x60E00000) == 0x00800000) || - (instr->Mask(0x60E00000) == 0x00600000)) { - VisitUnallocated(instr); - } else { - VisitDataProcessing3Source(instr); - } - } - } -} - - -void Decoder::DecodeFP(Instruction* instr) { - ASSERT((instr->Bits(27, 24) == 0xE) || - (instr->Bits(27, 24) == 0xF) ); - - if (instr->Bit(28) == 0) { - DecodeAdvSIMDDataProcessing(instr); - } else { - if (instr->Bit(29) == 1) { - VisitUnallocated(instr); - } else { - if (instr->Bits(31, 30) == 0x3) { - VisitUnallocated(instr); - } else if (instr->Bits(31, 30) == 0x1) { - DecodeAdvSIMDDataProcessing(instr); - } else { - if (instr->Bit(24) == 0) { - if (instr->Bit(21) == 0) { - if ((instr->Bit(23) == 1) || - (instr->Bit(18) == 1) || - (instr->Mask(0x80008000) == 0x00000000) || - (instr->Mask(0x000E0000) == 0x00000000) || - (instr->Mask(0x000E0000) == 0x000A0000) || - (instr->Mask(0x00160000) == 0x00000000) || - (instr->Mask(0x00160000) == 0x00120000)) { - VisitUnallocated(instr); - } else { - VisitFPFixedPointConvert(instr); - } - } else { - if (instr->Bits(15, 10) == 32) { - VisitUnallocated(instr); - } else if (instr->Bits(15, 10) == 0) { - if ((instr->Bits(23, 22) == 0x3) || - (instr->Mask(0x000E0000) == 0x000A0000) || - (instr->Mask(0x000E0000) == 0x000C0000) || - (instr->Mask(0x00160000) == 0x00120000) || - (instr->Mask(0x00160000) == 0x00140000) || - (instr->Mask(0x20C40000) == 0x00800000) || - (instr->Mask(0x20C60000) == 0x00840000) || - (instr->Mask(0xA0C60000) == 0x80060000) || - (instr->Mask(0xA0C60000) == 0x00860000) || - (instr->Mask(0xA0C60000) == 0x00460000) || - (instr->Mask(0xA0CE0000) == 0x80860000) || - (instr->Mask(0xA0CE0000) == 0x804E0000) || - (instr->Mask(0xA0CE0000) == 0x000E0000) || - (instr->Mask(0xA0D60000) == 0x00160000) || - (instr->Mask(0xA0D60000) == 0x80560000) || - (instr->Mask(0xA0D60000) == 0x80960000)) { - VisitUnallocated(instr); - } else { - VisitFPIntegerConvert(instr); - } - } else if (instr->Bits(14, 10) == 16) { - const Instr masked_A0DF8000 = instr->Mask(0xA0DF8000); - if ((instr->Mask(0x80180000) != 0) || - (masked_A0DF8000 == 0x00020000) || - (masked_A0DF8000 == 0x00030000) || - (masked_A0DF8000 == 0x00068000) || - (masked_A0DF8000 == 0x00428000) || - (masked_A0DF8000 == 0x00430000) || - (masked_A0DF8000 == 0x00468000) || - (instr->Mask(0xA0D80000) == 0x00800000) || - (instr->Mask(0xA0DE0000) == 0x00C00000) || - (instr->Mask(0xA0DF0000) == 0x00C30000) || - (instr->Mask(0xA0DC0000) == 0x00C40000)) { - VisitUnallocated(instr); - } else { - VisitFPDataProcessing1Source(instr); - } - } else if (instr->Bits(13, 10) == 8) { - if ((instr->Bits(15, 14) != 0) || - (instr->Bits(2, 0) != 0) || - (instr->Mask(0x80800000) != 0x00000000)) { - VisitUnallocated(instr); - } else { - VisitFPCompare(instr); - } - } else if (instr->Bits(12, 10) == 4) { - if ((instr->Bits(9, 5) != 0) || - (instr->Mask(0x80800000) != 0x00000000)) { - VisitUnallocated(instr); - } else { - VisitFPImmediate(instr); - } - } else { - if (instr->Mask(0x80800000) != 0x00000000) { - VisitUnallocated(instr); - } else { - switch (instr->Bits(11, 10)) { - case 1: { - VisitFPConditionalCompare(instr); - break; - } - case 2: { - if ((instr->Bits(15, 14) == 0x3) || - (instr->Mask(0x00009000) == 0x00009000) || - (instr->Mask(0x0000A000) == 0x0000A000)) { - VisitUnallocated(instr); - } else { - VisitFPDataProcessing2Source(instr); - } - break; - } - case 3: { - VisitFPConditionalSelect(instr); - break; - } - default: UNREACHABLE(); - } - } - } - } - } else { - // Bit 30 == 1 has been handled earlier. - ASSERT(instr->Bit(30) == 0); - if (instr->Mask(0xA0800000) != 0) { - VisitUnallocated(instr); - } else { - VisitFPDataProcessing3Source(instr); - } - } - } - } - } -} - - -void Decoder::DecodeAdvSIMDLoadStore(Instruction* instr) { - // TODO(all): Implement Advanced SIMD load/store instruction decode. - ASSERT(instr->Bits(29, 25) == 0x6); - VisitUnimplemented(instr); -} - - -void Decoder::DecodeAdvSIMDDataProcessing(Instruction* instr) { - // TODO(all): Implement Advanced SIMD data processing instruction decode. - ASSERT(instr->Bits(27, 25) == 0x7); - VisitUnimplemented(instr); -} - - -#define DEFINE_VISITOR_CALLERS(A) \ - void Decoder::Visit##A(Instruction *instr) { \ - if (!(instr->Mask(A##FMask) == A##Fixed)) { \ - ASSERT(instr->Mask(A##FMask) == A##Fixed); \ - } \ - std::list::iterator it; \ - for (it = visitors_.begin(); it != visitors_.end(); it++) { \ - (*it)->Visit##A(instr); \ - } \ - } -VISITOR_LIST(DEFINE_VISITOR_CALLERS) -#undef DEFINE_VISITOR_CALLERS - - -} } // namespace v8::internal - -#endif // V8_TARGET_ARCH_A64 diff --git a/deps/v8/src/a64/decoder-a64.h b/deps/v8/src/a64/decoder-a64.h deleted file mode 100644 index 0f53c34e88..0000000000 --- a/deps/v8/src/a64/decoder-a64.h +++ /dev/null @@ -1,202 +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. - -#ifndef V8_A64_DECODER_A64_H_ -#define V8_A64_DECODER_A64_H_ - -#include - -#include "globals.h" -#include "a64/instructions-a64.h" - -namespace v8 { -namespace internal { - - -// List macro containing all visitors needed by the decoder class. - -#define VISITOR_LIST(V) \ - V(PCRelAddressing) \ - V(AddSubImmediate) \ - V(LogicalImmediate) \ - V(MoveWideImmediate) \ - V(Bitfield) \ - V(Extract) \ - V(UnconditionalBranch) \ - V(UnconditionalBranchToRegister) \ - V(CompareBranch) \ - V(TestBranch) \ - V(ConditionalBranch) \ - V(System) \ - V(Exception) \ - V(LoadStorePairPostIndex) \ - V(LoadStorePairOffset) \ - V(LoadStorePairPreIndex) \ - V(LoadStorePairNonTemporal) \ - V(LoadLiteral) \ - V(LoadStoreUnscaledOffset) \ - V(LoadStorePostIndex) \ - V(LoadStorePreIndex) \ - V(LoadStoreRegisterOffset) \ - V(LoadStoreUnsignedOffset) \ - V(LogicalShifted) \ - V(AddSubShifted) \ - V(AddSubExtended) \ - V(AddSubWithCarry) \ - V(ConditionalCompareRegister) \ - V(ConditionalCompareImmediate) \ - V(ConditionalSelect) \ - V(DataProcessing1Source) \ - V(DataProcessing2Source) \ - V(DataProcessing3Source) \ - V(FPCompare) \ - V(FPConditionalCompare) \ - V(FPConditionalSelect) \ - V(FPImmediate) \ - V(FPDataProcessing1Source) \ - V(FPDataProcessing2Source) \ - V(FPDataProcessing3Source) \ - V(FPIntegerConvert) \ - V(FPFixedPointConvert) \ - V(Unallocated) \ - V(Unimplemented) - -// The Visitor interface. Disassembler and simulator (and other tools) -// must provide implementations for all of these functions. -class DecoderVisitor { - public: - #define DECLARE(A) virtual void Visit##A(Instruction* instr) = 0; - VISITOR_LIST(DECLARE) - #undef DECLARE - - virtual ~DecoderVisitor() {} - - private: - // Visitors are registered in a list. - std::list visitors_; - - friend class Decoder; -}; - - -class Decoder: public DecoderVisitor { - public: - explicit Decoder() {} - - // Top-level instruction decoder function. Decodes an instruction and calls - // the visitor functions registered with the Decoder class. - void Decode(Instruction *instr); - - // Register a new visitor class with the decoder. - // Decode() will call the corresponding visitor method from all registered - // visitor classes when decoding reaches the leaf node of the instruction - // decode tree. - // Visitors are called in the order. - // A visitor can only be registered once. - // Registering an already registered visitor will update its position. - // - // d.AppendVisitor(V1); - // d.AppendVisitor(V2); - // d.PrependVisitor(V2); // Move V2 at the start of the list. - // d.InsertVisitorBefore(V3, V2); - // d.AppendVisitor(V4); - // d.AppendVisitor(V4); // No effect. - // - // d.Decode(i); - // - // will call in order visitor methods in V3, V2, V1, V4. - void AppendVisitor(DecoderVisitor* visitor); - void PrependVisitor(DecoderVisitor* visitor); - void InsertVisitorBefore(DecoderVisitor* new_visitor, - DecoderVisitor* registered_visitor); - void InsertVisitorAfter(DecoderVisitor* new_visitor, - DecoderVisitor* registered_visitor); - - // Remove a previously registered visitor class from the list of visitors - // stored by the decoder. - void RemoveVisitor(DecoderVisitor* visitor); - - #define DECLARE(A) void Visit##A(Instruction* instr); - VISITOR_LIST(DECLARE) - #undef DECLARE - - private: - // Decode the PC relative addressing instruction, and call the corresponding - // visitors. - // On entry, instruction bits 27:24 = 0x0. - void DecodePCRelAddressing(Instruction* instr); - - // Decode the add/subtract immediate instruction, and call the corresponding - // visitors. - // On entry, instruction bits 27:24 = 0x1. - void DecodeAddSubImmediate(Instruction* instr); - - // Decode the branch, system command, and exception generation parts of - // the instruction tree, and call the corresponding visitors. - // On entry, instruction bits 27:24 = {0x4, 0x5, 0x6, 0x7}. - void DecodeBranchSystemException(Instruction* instr); - - // Decode the load and store parts of the instruction tree, and call - // the corresponding visitors. - // On entry, instruction bits 27:24 = {0x8, 0x9, 0xC, 0xD}. - void DecodeLoadStore(Instruction* instr); - - // Decode the logical immediate and move wide immediate parts of the - // instruction tree, and call the corresponding visitors. - // On entry, instruction bits 27:24 = 0x2. - void DecodeLogical(Instruction* instr); - - // Decode the bitfield and extraction parts of the instruction tree, - // and call the corresponding visitors. - // On entry, instruction bits 27:24 = 0x3. - void DecodeBitfieldExtract(Instruction* instr); - - // Decode the data processing parts of the instruction tree, and call the - // corresponding visitors. - // On entry, instruction bits 27:24 = {0x1, 0xA, 0xB}. - void DecodeDataProcessing(Instruction* instr); - - // Decode the floating point parts of the instruction tree, and call the - // corresponding visitors. - // On entry, instruction bits 27:24 = {0xE, 0xF}. - void DecodeFP(Instruction* instr); - - // Decode the Advanced SIMD (NEON) load/store part of the instruction tree, - // and call the corresponding visitors. - // On entry, instruction bits 29:25 = 0x6. - void DecodeAdvSIMDLoadStore(Instruction* instr); - - // Decode the Advanced SIMD (NEON) data processing part of the instruction - // tree, and call the corresponding visitors. - // On entry, instruction bits 27:25 = 0x7. - void DecodeAdvSIMDDataProcessing(Instruction* instr); -}; - - -} } // namespace v8::internal - -#endif // V8_A64_DECODER_A64_H_ diff --git a/deps/v8/src/a64/deoptimizer-a64.cc b/deps/v8/src/a64/deoptimizer-a64.cc deleted file mode 100644 index 660feb2394..0000000000 --- a/deps/v8/src/a64/deoptimizer-a64.cc +++ /dev/null @@ -1,376 +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 "v8.h" - -#include "codegen.h" -#include "deoptimizer.h" -#include "full-codegen.h" -#include "safepoint-table.h" - - -namespace v8 { -namespace internal { - - -int Deoptimizer::patch_size() { - // Size of the code used to patch lazy bailout points. - // Patching is done by Deoptimizer::DeoptimizeFunction. - return 4 * kInstructionSize; -} - - - -void Deoptimizer::PatchCodeForDeoptimization(Isolate* isolate, Code* code) { - // Invalidate the relocation information, as it will become invalid by the - // code patching below, and is not needed any more. - code->InvalidateRelocation(); - - // For each LLazyBailout instruction insert a call to the corresponding - // deoptimization entry. - DeoptimizationInputData* deopt_data = - DeoptimizationInputData::cast(code->deoptimization_data()); - Address code_start_address = code->instruction_start(); -#ifdef DEBUG - Address prev_call_address = NULL; -#endif - - for (int i = 0; i < deopt_data->DeoptCount(); i++) { - if (deopt_data->Pc(i)->value() == -1) continue; - - Address call_address = code_start_address + deopt_data->Pc(i)->value(); - Address deopt_entry = GetDeoptimizationEntry(isolate, i, LAZY); - - PatchingAssembler patcher(call_address, patch_size() / kInstructionSize); - patcher.LoadLiteral(ip0, 2 * kInstructionSize); - patcher.blr(ip0); - patcher.dc64(reinterpret_cast(deopt_entry)); - - ASSERT((prev_call_address == NULL) || - (call_address >= prev_call_address + patch_size())); - ASSERT(call_address + patch_size() <= code->instruction_end()); -#ifdef DEBUG - prev_call_address = call_address; -#endif - } -} - - -void Deoptimizer::FillInputFrame(Address tos, JavaScriptFrame* frame) { - // Set the register values. The values are not important as there are no - // callee saved registers in JavaScript frames, so all registers are - // spilled. Registers fp and sp are set to the correct values though. - for (int i = 0; i < Register::NumRegisters(); i++) { - input_->SetRegister(i, 0); - } - - // TODO(all): Do we also need to set a value to csp? - input_->SetRegister(jssp.code(), reinterpret_cast(frame->sp())); - input_->SetRegister(fp.code(), reinterpret_cast(frame->fp())); - - for (int i = 0; i < DoubleRegister::NumAllocatableRegisters(); i++) { - input_->SetDoubleRegister(i, 0.0); - } - - // Fill the frame content from the actual data on the frame. - for (unsigned i = 0; i < input_->GetFrameSize(); i += kPointerSize) { - input_->SetFrameSlot(i, Memory::uint64_at(tos + i)); - } -} - - -bool Deoptimizer::HasAlignmentPadding(JSFunction* function) { - // There is no dynamic alignment padding on A64 in the input frame. - return false; -} - - -void Deoptimizer::SetPlatformCompiledStubRegisters( - FrameDescription* output_frame, CodeStubInterfaceDescriptor* descriptor) { - ApiFunction function(descriptor->deoptimization_handler_); - ExternalReference xref(&function, ExternalReference::BUILTIN_CALL, isolate_); - intptr_t handler = reinterpret_cast(xref.address()); - int params = descriptor->GetHandlerParameterCount(); - output_frame->SetRegister(x0.code(), params); - output_frame->SetRegister(x1.code(), handler); -} - - -void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) { - for (int i = 0; i < DoubleRegister::kMaxNumRegisters; ++i) { - double double_value = input_->GetDoubleRegister(i); - output_frame->SetDoubleRegister(i, double_value); - } -} - - -Code* Deoptimizer::NotifyStubFailureBuiltin() { - return isolate_->builtins()->builtin(Builtins::kNotifyStubFailureSaveDoubles); -} - - -#define __ masm()-> - -void Deoptimizer::EntryGenerator::Generate() { - GeneratePrologue(); - - // TODO(all): This code needs to be revisited. We probably only need to save - // caller-saved registers here. Callee-saved registers can be stored directly - // in the input frame. - - // Save all allocatable floating point registers. - CPURegList saved_fp_registers(CPURegister::kFPRegister, kDRegSize, - 0, FPRegister::NumAllocatableRegisters() - 1); - __ PushCPURegList(saved_fp_registers); - - // We save all the registers expcept jssp, sp and lr. - CPURegList saved_registers(CPURegister::kRegister, kXRegSize, 0, 27); - saved_registers.Combine(fp); - __ PushCPURegList(saved_registers); - - const int kSavedRegistersAreaSize = - (saved_registers.Count() * kXRegSizeInBytes) + - (saved_fp_registers.Count() * kDRegSizeInBytes); - - // Floating point registers are saved on the stack above core registers. - const int kFPRegistersOffset = saved_registers.Count() * kXRegSizeInBytes; - - // Get the bailout id from the stack. - Register bailout_id = x2; - __ Peek(bailout_id, kSavedRegistersAreaSize); - - Register code_object = x3; - Register fp_to_sp = x4; - // Get the address of the location in the code object. This is the return - // address for lazy deoptimization. - __ Mov(code_object, lr); - // Compute the fp-to-sp delta, and correct one word for bailout id. - __ Add(fp_to_sp, masm()->StackPointer(), - kSavedRegistersAreaSize + (1 * kPointerSize)); - __ Sub(fp_to_sp, fp, fp_to_sp); - - // Allocate a new deoptimizer object. - __ Ldr(x0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); - __ Mov(x1, type()); - // Following arguments are already loaded: - // - x2: bailout id - // - x3: code object address - // - x4: fp-to-sp delta - __ Mov(x5, Operand(ExternalReference::isolate_address(isolate()))); - - { - // Call Deoptimizer::New(). - AllowExternalCallThatCantCauseGC scope(masm()); - __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate()), 6); - } - - // Preserve "deoptimizer" object in register x0. - Register deoptimizer = x0; - - // Get the input frame descriptor pointer. - __ Ldr(x1, MemOperand(deoptimizer, Deoptimizer::input_offset())); - - // Copy core registers into the input frame. - CPURegList copy_to_input = saved_registers; - for (int i = 0; i < saved_registers.Count(); i++) { - // TODO(all): Look for opportunities to optimize this by using ldp/stp. - __ Peek(x2, i * kPointerSize); - CPURegister current_reg = copy_to_input.PopLowestIndex(); - int offset = (current_reg.code() * kPointerSize) + - FrameDescription::registers_offset(); - __ Str(x2, MemOperand(x1, offset)); - } - - // Copy FP registers to the input frame. - for (int i = 0; i < saved_fp_registers.Count(); i++) { - // TODO(all): Look for opportunities to optimize this by using ldp/stp. - int dst_offset = FrameDescription::double_registers_offset() + - (i * kDoubleSize); - int src_offset = kFPRegistersOffset + (i * kDoubleSize); - __ Peek(x2, src_offset); - __ Str(x2, MemOperand(x1, dst_offset)); - } - - // Remove the bailout id and the saved registers from the stack. - __ Drop(1 + (kSavedRegistersAreaSize / kXRegSizeInBytes)); - - // Compute a pointer to the unwinding limit in register x2; that is - // the first stack slot not part of the input frame. - Register unwind_limit = x2; - __ Ldr(unwind_limit, MemOperand(x1, FrameDescription::frame_size_offset())); - __ Add(unwind_limit, unwind_limit, __ StackPointer()); - - // Unwind the stack down to - but not including - the unwinding - // limit and copy the contents of the activation frame to the input - // frame description. - __ Add(x3, x1, FrameDescription::frame_content_offset()); - Label pop_loop; - Label pop_loop_header; - __ B(&pop_loop_header); - __ Bind(&pop_loop); - __ Pop(x4); - __ Str(x4, MemOperand(x3, kPointerSize, PostIndex)); - __ Bind(&pop_loop_header); - __ Cmp(unwind_limit, __ StackPointer()); - __ B(ne, &pop_loop); - - // Compute the output frame in the deoptimizer. - __ Push(x0); // Preserve deoptimizer object across call. - - { - // Call Deoptimizer::ComputeOutputFrames(). - AllowExternalCallThatCantCauseGC scope(masm()); - __ CallCFunction( - ExternalReference::compute_output_frames_function(isolate()), 1); - } - __ Pop(x4); // Restore deoptimizer object (class Deoptimizer). - - // Replace the current (input) frame with the output frames. - Label outer_push_loop, inner_push_loop, - outer_loop_header, inner_loop_header; - __ Ldrsw(x1, MemOperand(x4, Deoptimizer::output_count_offset())); - __ Ldr(x0, MemOperand(x4, Deoptimizer::output_offset())); - __ Add(x1, x0, Operand(x1, LSL, kPointerSizeLog2)); - __ B(&outer_loop_header); - - __ Bind(&outer_push_loop); - Register current_frame = x2; - __ Ldr(current_frame, MemOperand(x0, 0)); - __ Ldr(x3, MemOperand(current_frame, FrameDescription::frame_size_offset())); - __ B(&inner_loop_header); - - __ Bind(&inner_push_loop); - __ Sub(x3, x3, kPointerSize); - __ Add(x6, current_frame, x3); - __ Ldr(x7, MemOperand(x6, FrameDescription::frame_content_offset())); - __ Push(x7); - __ Bind(&inner_loop_header); - __ Cbnz(x3, &inner_push_loop); - - __ Add(x0, x0, kPointerSize); - __ Bind(&outer_loop_header); - __ Cmp(x0, x1); - __ B(lt, &outer_push_loop); - - __ Ldr(x1, MemOperand(x4, Deoptimizer::input_offset())); - ASSERT(!saved_fp_registers.IncludesAliasOf(crankshaft_fp_scratch) && - !saved_fp_registers.IncludesAliasOf(fp_zero) && - !saved_fp_registers.IncludesAliasOf(fp_scratch)); - int src_offset = FrameDescription::double_registers_offset(); - while (!saved_fp_registers.IsEmpty()) { - const CPURegister reg = saved_fp_registers.PopLowestIndex(); - __ Ldr(reg, MemOperand(x1, src_offset)); - src_offset += kDoubleSize; - } - - // Push state from the last output frame. - __ Ldr(x6, MemOperand(current_frame, FrameDescription::state_offset())); - __ Push(x6); - - // TODO(all): ARM copies a lot (if not all) of the last output frame onto the - // stack, then pops it all into registers. Here, we try to load it directly - // into the relevant registers. Is this correct? If so, we should improve the - // ARM code. - - // TODO(all): This code needs to be revisited, We probably don't need to - // restore all the registers as fullcodegen does not keep live values in - // registers (note that at least fp must be restored though). - - // Restore registers from the last output frame. - // Note that lr is not in the list of saved_registers and will be restored - // later. We can use it to hold the address of last output frame while - // reloading the other registers. - ASSERT(!saved_registers.IncludesAliasOf(lr)); - Register last_output_frame = lr; - __ Mov(last_output_frame, current_frame); - - // We don't need to restore x7 as it will be clobbered later to hold the - // continuation address. - Register continuation = x7; - saved_registers.Remove(continuation); - - while (!saved_registers.IsEmpty()) { - // TODO(all): Look for opportunities to optimize this by using ldp. - CPURegister current_reg = saved_registers.PopLowestIndex(); - int offset = (current_reg.code() * kPointerSize) + - FrameDescription::registers_offset(); - __ Ldr(current_reg, MemOperand(last_output_frame, offset)); - } - - __ Ldr(continuation, MemOperand(last_output_frame, - FrameDescription::continuation_offset())); - __ Ldr(lr, MemOperand(last_output_frame, FrameDescription::pc_offset())); - __ InitializeRootRegister(); - __ Br(continuation); -} - - -// Size of an entry of the second level deopt table. -// This is the code size generated by GeneratePrologue for one entry. -const int Deoptimizer::table_entry_size_ = 2 * kInstructionSize; - - -void Deoptimizer::TableEntryGenerator::GeneratePrologue() { - // Create a sequence of deoptimization entries. - // Note that registers are still live when jumping to an entry. - Label done; - { - InstructionAccurateScope scope(masm()); - - // The number of entry will never exceed kMaxNumberOfEntries. - // As long as kMaxNumberOfEntries is a valid 16 bits immediate you can use - // a movz instruction to load the entry id. - ASSERT(is_uint16(Deoptimizer::kMaxNumberOfEntries)); - - for (int i = 0; i < count(); i++) { - int start = masm()->pc_offset(); - USE(start); - __ movz(masm()->Tmp0(), i); - __ b(&done); - ASSERT(masm()->pc_offset() - start == table_entry_size_); - } - } - __ Bind(&done); - // TODO(all): We need to add some kind of assertion to verify that Tmp0() - // is not clobbered by Push. - __ Push(masm()->Tmp0()); -} - - -void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) { - SetFrameSlot(offset, value); -} - - -void FrameDescription::SetCallerFp(unsigned offset, intptr_t value) { - SetFrameSlot(offset, value); -} - - -#undef __ - -} } // namespace v8::internal diff --git a/deps/v8/src/a64/disasm-a64.cc b/deps/v8/src/a64/disasm-a64.cc deleted file mode 100644 index 5ef75d55e2..0000000000 --- a/deps/v8/src/a64/disasm-a64.cc +++ /dev/null @@ -1,1854 +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 -#include - -#include "v8.h" - -#if V8_TARGET_ARCH_A64 - -#include "disasm.h" -#include "a64/disasm-a64.h" -#include "macro-assembler.h" -#include "platform.h" - -namespace v8 { -namespace internal { - - -Disassembler::Disassembler() { - buffer_size_ = 256; - buffer_ = reinterpret_cast(malloc(buffer_size_)); - buffer_pos_ = 0; - own_buffer_ = true; -} - - -Disassembler::Disassembler(char* text_buffer, int buffer_size) { - buffer_size_ = buffer_size; - buffer_ = text_buffer; - buffer_pos_ = 0; - own_buffer_ = false; -} - - -Disassembler::~Disassembler() { - if (own_buffer_) { - free(buffer_); - } -} - - -char* Disassembler::GetOutput() { - return buffer_; -} - - -void Disassembler::VisitAddSubImmediate(Instruction* instr) { - bool rd_is_zr = RdIsZROrSP(instr); - bool stack_op = (rd_is_zr || RnIsZROrSP(instr)) && - (instr->ImmAddSub() == 0) ? true : false; - const char *mnemonic = ""; - const char *form = "'Rds, 'Rns, 'IAddSub"; - const char *form_cmp = "'Rns, 'IAddSub"; - const char *form_mov = "'Rds, 'Rns"; - - switch (instr->Mask(AddSubImmediateMask)) { - case ADD_w_imm: - case ADD_x_imm: { - mnemonic = "add"; - if (stack_op) { - mnemonic = "mov"; - form = form_mov; - } - break; - } - case ADDS_w_imm: - case ADDS_x_imm: { - mnemonic = "adds"; - if (rd_is_zr) { - mnemonic = "cmn"; - form = form_cmp; - } - break; - } - case SUB_w_imm: - case SUB_x_imm: mnemonic = "sub"; break; - case SUBS_w_imm: - case SUBS_x_imm: { - mnemonic = "subs"; - if (rd_is_zr) { - mnemonic = "cmp"; - form = form_cmp; - } - break; - } - default: UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitAddSubShifted(Instruction* instr) { - bool rd_is_zr = RdIsZROrSP(instr); - bool rn_is_zr = RnIsZROrSP(instr); - const char *mnemonic = ""; - const char *form = "'Rd, 'Rn, 'Rm'HDP"; - const char *form_cmp = "'Rn, 'Rm'HDP"; - const char *form_neg = "'Rd, 'Rm'HDP"; - - switch (instr->Mask(AddSubShiftedMask)) { - case ADD_w_shift: - case ADD_x_shift: mnemonic = "add"; break; - case ADDS_w_shift: - case ADDS_x_shift: { - mnemonic = "adds"; - if (rd_is_zr) { - mnemonic = "cmn"; - form = form_cmp; - } - break; - } - case SUB_w_shift: - case SUB_x_shift: { - mnemonic = "sub"; - if (rn_is_zr) { - mnemonic = "neg"; - form = form_neg; - } - break; - } - case SUBS_w_shift: - case SUBS_x_shift: { - mnemonic = "subs"; - if (rd_is_zr) { - mnemonic = "cmp"; - form = form_cmp; - } else if (rn_is_zr) { - mnemonic = "negs"; - form = form_neg; - } - break; - } - default: UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitAddSubExtended(Instruction* instr) { - bool rd_is_zr = RdIsZROrSP(instr); - const char *mnemonic = ""; - Extend mode = static_cast(instr->ExtendMode()); - const char *form = ((mode == UXTX) || (mode == SXTX)) ? - "'Rds, 'Rns, 'Xm'Ext" : "'Rds, 'Rns, 'Wm'Ext"; - const char *form_cmp = ((mode == UXTX) || (mode == SXTX)) ? - "'Rns, 'Xm'Ext" : "'Rns, 'Wm'Ext"; - - switch (instr->Mask(AddSubExtendedMask)) { - case ADD_w_ext: - case ADD_x_ext: mnemonic = "add"; break; - case ADDS_w_ext: - case ADDS_x_ext: { - mnemonic = "adds"; - if (rd_is_zr) { - mnemonic = "cmn"; - form = form_cmp; - } - break; - } - case SUB_w_ext: - case SUB_x_ext: mnemonic = "sub"; break; - case SUBS_w_ext: - case SUBS_x_ext: { - mnemonic = "subs"; - if (rd_is_zr) { - mnemonic = "cmp"; - form = form_cmp; - } - break; - } - default: UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitAddSubWithCarry(Instruction* instr) { - bool rn_is_zr = RnIsZROrSP(instr); - const char *mnemonic = ""; - const char *form = "'Rd, 'Rn, 'Rm"; - const char *form_neg = "'Rd, 'Rm"; - - switch (instr->Mask(AddSubWithCarryMask)) { - case ADC_w: - case ADC_x: mnemonic = "adc"; break; - case ADCS_w: - case ADCS_x: mnemonic = "adcs"; break; - case SBC_w: - case SBC_x: { - mnemonic = "sbc"; - if (rn_is_zr) { - mnemonic = "ngc"; - form = form_neg; - } - break; - } - case SBCS_w: - case SBCS_x: { - mnemonic = "sbcs"; - if (rn_is_zr) { - mnemonic = "ngcs"; - form = form_neg; - } - break; - } - default: UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLogicalImmediate(Instruction* instr) { - bool rd_is_zr = RdIsZROrSP(instr); - bool rn_is_zr = RnIsZROrSP(instr); - const char *mnemonic = ""; - const char *form = "'Rds, 'Rn, 'ITri"; - - if (instr->ImmLogical() == 0) { - // The immediate encoded in the instruction is not in the expected format. - Format(instr, "unallocated", "(LogicalImmediate)"); - return; - } - - switch (instr->Mask(LogicalImmediateMask)) { - case AND_w_imm: - case AND_x_imm: mnemonic = "and"; break; - case ORR_w_imm: - case ORR_x_imm: { - mnemonic = "orr"; - unsigned reg_size = (instr->SixtyFourBits() == 1) ? kXRegSize - : kWRegSize; - if (rn_is_zr && !IsMovzMovnImm(reg_size, instr->ImmLogical())) { - mnemonic = "mov"; - form = "'Rds, 'ITri"; - } - break; - } - case EOR_w_imm: - case EOR_x_imm: mnemonic = "eor"; break; - case ANDS_w_imm: - case ANDS_x_imm: { - mnemonic = "ands"; - if (rd_is_zr) { - mnemonic = "tst"; - form = "'Rn, 'ITri"; - } - break; - } - default: UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -bool Disassembler::IsMovzMovnImm(unsigned reg_size, uint64_t value) { - ASSERT((reg_size == kXRegSize) || - ((reg_size == kWRegSize) && (value <= 0xffffffff))); - - // Test for movz: 16-bits set at positions 0, 16, 32 or 48. - if (((value & 0xffffffffffff0000UL) == 0UL) || - ((value & 0xffffffff0000ffffUL) == 0UL) || - ((value & 0xffff0000ffffffffUL) == 0UL) || - ((value & 0x0000ffffffffffffUL) == 0UL)) { - return true; - } - - // Test for movn: NOT(16-bits set at positions 0, 16, 32 or 48). - if ((reg_size == kXRegSize) && - (((value & 0xffffffffffff0000UL) == 0xffffffffffff0000UL) || - ((value & 0xffffffff0000ffffUL) == 0xffffffff0000ffffUL) || - ((value & 0xffff0000ffffffffUL) == 0xffff0000ffffffffUL) || - ((value & 0x0000ffffffffffffUL) == 0x0000ffffffffffffUL))) { - return true; - } - if ((reg_size == kWRegSize) && - (((value & 0xffff0000) == 0xffff0000) || - ((value & 0x0000ffff) == 0x0000ffff))) { - return true; - } - return false; -} - - -void Disassembler::VisitLogicalShifted(Instruction* instr) { - bool rd_is_zr = RdIsZROrSP(instr); - bool rn_is_zr = RnIsZROrSP(instr); - const char *mnemonic = ""; - const char *form = "'Rd, 'Rn, 'Rm'HLo"; - - switch (instr->Mask(LogicalShiftedMask)) { - case AND_w: - case AND_x: mnemonic = "and"; break; - case BIC_w: - case BIC_x: mnemonic = "bic"; break; - case EOR_w: - case EOR_x: mnemonic = "eor"; break; - case EON_w: - case EON_x: mnemonic = "eon"; break; - case BICS_w: - case BICS_x: mnemonic = "bics"; break; - case ANDS_w: - case ANDS_x: { - mnemonic = "ands"; - if (rd_is_zr) { - mnemonic = "tst"; - form = "'Rn, 'Rm'HLo"; - } - break; - } - case ORR_w: - case ORR_x: { - mnemonic = "orr"; - if (rn_is_zr && (instr->ImmDPShift() == 0) && (instr->ShiftDP() == LSL)) { - mnemonic = "mov"; - form = "'Rd, 'Rm"; - } - break; - } - case ORN_w: - case ORN_x: { - mnemonic = "orn"; - if (rn_is_zr) { - mnemonic = "mvn"; - form = "'Rd, 'Rm'HLo"; - } - break; - } - default: UNREACHABLE(); - } - - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitConditionalCompareRegister(Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Rn, 'Rm, 'INzcv, 'Cond"; - - switch (instr->Mask(ConditionalCompareRegisterMask)) { - case CCMN_w: - case CCMN_x: mnemonic = "ccmn"; break; - case CCMP_w: - case CCMP_x: mnemonic = "ccmp"; break; - default: UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitConditionalCompareImmediate(Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Rn, 'IP, 'INzcv, 'Cond"; - - switch (instr->Mask(ConditionalCompareImmediateMask)) { - case CCMN_w_imm: - case CCMN_x_imm: mnemonic = "ccmn"; break; - case CCMP_w_imm: - case CCMP_x_imm: mnemonic = "ccmp"; break; - default: UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitConditionalSelect(Instruction* instr) { - bool rnm_is_zr = (RnIsZROrSP(instr) && RmIsZROrSP(instr)); - bool rn_is_rm = (instr->Rn() == instr->Rm()); - const char *mnemonic = ""; - const char *form = "'Rd, 'Rn, 'Rm, 'Cond"; - const char *form_test = "'Rd, 'CInv"; - const char *form_update = "'Rd, 'Rn, 'CInv"; - - Condition cond = static_cast(instr->Condition()); - bool invertible_cond = (cond != al) && (cond != nv); - - switch (instr->Mask(ConditionalSelectMask)) { - case CSEL_w: - case CSEL_x: mnemonic = "csel"; break; - case CSINC_w: - case CSINC_x: { - mnemonic = "csinc"; - if (rnm_is_zr && invertible_cond) { - mnemonic = "cset"; - form = form_test; - } else if (rn_is_rm && invertible_cond) { - mnemonic = "cinc"; - form = form_update; - } - break; - } - case CSINV_w: - case CSINV_x: { - mnemonic = "csinv"; - if (rnm_is_zr && invertible_cond) { - mnemonic = "csetm"; - form = form_test; - } else if (rn_is_rm && invertible_cond) { - mnemonic = "cinv"; - form = form_update; - } - break; - } - case CSNEG_w: - case CSNEG_x: { - mnemonic = "csneg"; - if (rn_is_rm && invertible_cond) { - mnemonic = "cneg"; - form = form_update; - } - break; - } - default: UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitBitfield(Instruction* instr) { - unsigned s = instr->ImmS(); - unsigned r = instr->ImmR(); - unsigned rd_size_minus_1 = - ((instr->SixtyFourBits() == 1) ? kXRegSize : kWRegSize) - 1; - const char *mnemonic = ""; - const char *form = ""; - const char *form_shift_right = "'Rd, 'Rn, 'IBr"; - const char *form_extend = "'Rd, 'Wn"; - const char *form_bfiz = "'Rd, 'Rn, 'IBZ-r, 'IBs+1"; - const char *form_bfx = "'Rd, 'Rn, 'IBr, 'IBs-r+1"; - const char *form_lsl = "'Rd, 'Rn, 'IBZ-r"; - - switch (instr->Mask(BitfieldMask)) { - case SBFM_w: - case SBFM_x: { - mnemonic = "sbfx"; - form = form_bfx; - if (r == 0) { - form = form_extend; - if (s == 7) { - mnemonic = "sxtb"; - } else if (s == 15) { - mnemonic = "sxth"; - } else if ((s == 31) && (instr->SixtyFourBits() == 1)) { - mnemonic = "sxtw"; - } else { - form = form_bfx; - } - } else if (s == rd_size_minus_1) { - mnemonic = "asr"; - form = form_shift_right; - } else if (s < r) { - mnemonic = "sbfiz"; - form = form_bfiz; - } - break; - } - case UBFM_w: - case UBFM_x: { - mnemonic = "ubfx"; - form = form_bfx; - if (r == 0) { - form = form_extend; - if (s == 7) { - mnemonic = "uxtb"; - } else if (s == 15) { - mnemonic = "uxth"; - } else { - form = form_bfx; - } - } - if (s == rd_size_minus_1) { - mnemonic = "lsr"; - form = form_shift_right; - } else if (r == s + 1) { - mnemonic = "lsl"; - form = form_lsl; - } else if (s < r) { - mnemonic = "ubfiz"; - form = form_bfiz; - } - break; - } - case BFM_w: - case BFM_x: { - mnemonic = "bfxil"; - form = form_bfx; - if (s < r) { - mnemonic = "bfi"; - form = form_bfiz; - } - } - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitExtract(Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Rd, 'Rn, 'Rm, 'IExtract"; - - switch (instr->Mask(ExtractMask)) { - case EXTR_w: - case EXTR_x: { - if (instr->Rn() == instr->Rm()) { - mnemonic = "ror"; - form = "'Rd, 'Rn, 'IExtract"; - } else { - mnemonic = "extr"; - } - break; - } - default: UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitPCRelAddressing(Instruction* instr) { - switch (instr->Mask(PCRelAddressingMask)) { - case ADR: Format(instr, "adr", "'Xd, 'AddrPCRelByte"); break; - // ADRP is not implemented. - default: Format(instr, "unimplemented", "(PCRelAddressing)"); - } -} - - -void Disassembler::VisitConditionalBranch(Instruction* instr) { - switch (instr->Mask(ConditionalBranchMask)) { - case B_cond: Format(instr, "b.'CBrn", "'BImmCond"); break; - default: UNREACHABLE(); - } -} - - -void Disassembler::VisitUnconditionalBranchToRegister(Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "'Xn"; - - switch (instr->Mask(UnconditionalBranchToRegisterMask)) { - case BR: mnemonic = "br"; break; - case BLR: mnemonic = "blr"; break; - case RET: { - mnemonic = "ret"; - if (instr->Rn() == kLinkRegCode) { - form = NULL; - } - break; - } - default: form = "(UnconditionalBranchToRegister)"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitUnconditionalBranch(Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'BImmUncn"; - - switch (instr->Mask(UnconditionalBranchMask)) { - case B: mnemonic = "b"; break; - case BL: mnemonic = "bl"; break; - default: UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitDataProcessing1Source(Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Rd, 'Rn"; - - switch (instr->Mask(DataProcessing1SourceMask)) { - #define FORMAT(A, B) \ - case A##_w: \ - case A##_x: mnemonic = B; break; - FORMAT(RBIT, "rbit"); - FORMAT(REV16, "rev16"); - FORMAT(REV, "rev"); - FORMAT(CLZ, "clz"); - FORMAT(CLS, "cls"); - #undef FORMAT - case REV32_x: mnemonic = "rev32"; break; - default: UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitDataProcessing2Source(Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "'Rd, 'Rn, 'Rm"; - - switch (instr->Mask(DataProcessing2SourceMask)) { - #define FORMAT(A, B) \ - case A##_w: \ - case A##_x: mnemonic = B; break; - FORMAT(UDIV, "udiv"); - FORMAT(SDIV, "sdiv"); - FORMAT(LSLV, "lsl"); - FORMAT(LSRV, "lsr"); - FORMAT(ASRV, "asr"); - FORMAT(RORV, "ror"); - #undef FORMAT - default: form = "(DataProcessing2Source)"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitDataProcessing3Source(Instruction* instr) { - bool ra_is_zr = RaIsZROrSP(instr); - const char *mnemonic = ""; - const char *form = "'Xd, 'Wn, 'Wm, 'Xa"; - const char *form_rrr = "'Rd, 'Rn, 'Rm"; - const char *form_rrrr = "'Rd, 'Rn, 'Rm, 'Ra"; - const char *form_xww = "'Xd, 'Wn, 'Wm"; - const char *form_xxx = "'Xd, 'Xn, 'Xm"; - - switch (instr->Mask(DataProcessing3SourceMask)) { - case MADD_w: - case MADD_x: { - mnemonic = "madd"; - form = form_rrrr; - if (ra_is_zr) { - mnemonic = "mul"; - form = form_rrr; - } - break; - } - case MSUB_w: - case MSUB_x: { - mnemonic = "msub"; - form = form_rrrr; - if (ra_is_zr) { - mnemonic = "mneg"; - form = form_rrr; - } - break; - } - case SMADDL_x: { - mnemonic = "smaddl"; - if (ra_is_zr) { - mnemonic = "smull"; - form = form_xww; - } - break; - } - case SMSUBL_x: { - mnemonic = "smsubl"; - if (ra_is_zr) { - mnemonic = "smnegl"; - form = form_xww; - } - break; - } - case UMADDL_x: { - mnemonic = "umaddl"; - if (ra_is_zr) { - mnemonic = "umull"; - form = form_xww; - } - break; - } - case UMSUBL_x: { - mnemonic = "umsubl"; - if (ra_is_zr) { - mnemonic = "umnegl"; - form = form_xww; - } - break; - } - case SMULH_x: { - mnemonic = "smulh"; - form = form_xxx; - break; - } - case UMULH_x: { - mnemonic = "umulh"; - form = form_xxx; - break; - } - default: UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitCompareBranch(Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Rt, 'BImmCmpa"; - - switch (instr->Mask(CompareBranchMask)) { - case CBZ_w: - case CBZ_x: mnemonic = "cbz"; break; - case CBNZ_w: - case CBNZ_x: mnemonic = "cbnz"; break; - default: UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitTestBranch(Instruction* instr) { - const char *mnemonic = ""; - // If the top bit of the immediate is clear, the tested register is - // disassembled as Wt, otherwise Xt. As the top bit of the immediate is - // encoded in bit 31 of the instruction, we can reuse the Rt form, which - // uses bit 31 (normally "sf") to choose the register size. - const char *form = "'Rt, 'IS, 'BImmTest"; - - switch (instr->Mask(TestBranchMask)) { - case TBZ: mnemonic = "tbz"; break; - case TBNZ: mnemonic = "tbnz"; break; - default: UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitMoveWideImmediate(Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Rd, 'IMoveImm"; - - // Print the shift separately for movk, to make it clear which half word will - // be overwritten. Movn and movz print the computed immediate, which includes - // shift calculation. - switch (instr->Mask(MoveWideImmediateMask)) { - case MOVN_w: - case MOVN_x: mnemonic = "movn"; break; - case MOVZ_w: - case MOVZ_x: mnemonic = "movz"; break; - case MOVK_w: - case MOVK_x: mnemonic = "movk"; form = "'Rd, 'IMoveLSL"; break; - default: UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -#define LOAD_STORE_LIST(V) \ - V(STRB_w, "strb", "'Wt") \ - V(STRH_w, "strh", "'Wt") \ - V(STR_w, "str", "'Wt") \ - V(STR_x, "str", "'Xt") \ - V(LDRB_w, "ldrb", "'Wt") \ - V(LDRH_w, "ldrh", "'Wt") \ - V(LDR_w, "ldr", "'Wt") \ - V(LDR_x, "ldr", "'Xt") \ - V(LDRSB_x, "ldrsb", "'Xt") \ - V(LDRSH_x, "ldrsh", "'Xt") \ - V(LDRSW_x, "ldrsw", "'Xt") \ - V(LDRSB_w, "ldrsb", "'Wt") \ - V(LDRSH_w, "ldrsh", "'Wt") \ - V(STR_s, "str", "'St") \ - V(STR_d, "str", "'Dt") \ - V(LDR_s, "ldr", "'St") \ - V(LDR_d, "ldr", "'Dt") - -void Disassembler::VisitLoadStorePreIndex(Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(LoadStorePreIndex)"; - - switch (instr->Mask(LoadStorePreIndexMask)) { - #define LS_PREINDEX(A, B, C) \ - case A##_pre: mnemonic = B; form = C ", ['Xns'ILS]!"; break; - LOAD_STORE_LIST(LS_PREINDEX) - #undef LS_PREINDEX - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLoadStorePostIndex(Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(LoadStorePostIndex)"; - - switch (instr->Mask(LoadStorePostIndexMask)) { - #define LS_POSTINDEX(A, B, C) \ - case A##_post: mnemonic = B; form = C ", ['Xns]'ILS"; break; - LOAD_STORE_LIST(LS_POSTINDEX) - #undef LS_POSTINDEX - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLoadStoreUnsignedOffset(Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(LoadStoreUnsignedOffset)"; - - switch (instr->Mask(LoadStoreUnsignedOffsetMask)) { - #define LS_UNSIGNEDOFFSET(A, B, C) \ - case A##_unsigned: mnemonic = B; form = C ", ['Xns'ILU]"; break; - LOAD_STORE_LIST(LS_UNSIGNEDOFFSET) - #undef LS_UNSIGNEDOFFSET - case PRFM_unsigned: mnemonic = "prfm"; form = "'PrefOp, ['Xn'ILU]"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLoadStoreRegisterOffset(Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(LoadStoreRegisterOffset)"; - - switch (instr->Mask(LoadStoreRegisterOffsetMask)) { - #define LS_REGISTEROFFSET(A, B, C) \ - case A##_reg: mnemonic = B; form = C ", ['Xns, 'Offsetreg]"; break; - LOAD_STORE_LIST(LS_REGISTEROFFSET) - #undef LS_REGISTEROFFSET - case PRFM_reg: mnemonic = "prfm"; form = "'PrefOp, ['Xns, 'Offsetreg]"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLoadStoreUnscaledOffset(Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "'Wt, ['Xns'ILS]"; - const char *form_x = "'Xt, ['Xns'ILS]"; - const char *form_s = "'St, ['Xns'ILS]"; - const char *form_d = "'Dt, ['Xns'ILS]"; - - switch (instr->Mask(LoadStoreUnscaledOffsetMask)) { - case STURB_w: mnemonic = "sturb"; break; - case STURH_w: mnemonic = "sturh"; break; - case STUR_w: mnemonic = "stur"; break; - case STUR_x: mnemonic = "stur"; form = form_x; break; - case STUR_s: mnemonic = "stur"; form = form_s; break; - case STUR_d: mnemonic = "stur"; form = form_d; break; - case LDURB_w: mnemonic = "ldurb"; break; - case LDURH_w: mnemonic = "ldurh"; break; - case LDUR_w: mnemonic = "ldur"; break; - case LDUR_x: mnemonic = "ldur"; form = form_x; break; - case LDUR_s: mnemonic = "ldur"; form = form_s; break; - case LDUR_d: mnemonic = "ldur"; form = form_d; break; - case LDURSB_x: form = form_x; // Fall through. - case LDURSB_w: mnemonic = "ldursb"; break; - case LDURSH_x: form = form_x; // Fall through. - case LDURSH_w: mnemonic = "ldursh"; break; - case LDURSW_x: mnemonic = "ldursw"; form = form_x; break; - default: form = "(LoadStoreUnscaledOffset)"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLoadLiteral(Instruction* instr) { - const char *mnemonic = "ldr"; - const char *form = "(LoadLiteral)"; - - switch (instr->Mask(LoadLiteralMask)) { - case LDR_w_lit: form = "'Wt, 'ILLiteral 'LValue"; break; - case LDR_x_lit: form = "'Xt, 'ILLiteral 'LValue"; break; - case LDR_s_lit: form = "'St, 'ILLiteral 'LValue"; break; - case LDR_d_lit: form = "'Dt, 'ILLiteral 'LValue"; break; - default: mnemonic = "unimplemented"; - } - Format(instr, mnemonic, form); -} - - -#define LOAD_STORE_PAIR_LIST(V) \ - V(STP_w, "stp", "'Wt, 'Wt2", "4") \ - V(LDP_w, "ldp", "'Wt, 'Wt2", "4") \ - V(LDPSW_x, "ldpsw", "'Xt, 'Xt2", "4") \ - V(STP_x, "stp", "'Xt, 'Xt2", "8") \ - V(LDP_x, "ldp", "'Xt, 'Xt2", "8") \ - V(STP_s, "stp", "'St, 'St2", "4") \ - V(LDP_s, "ldp", "'St, 'St2", "4") \ - V(STP_d, "stp", "'Dt, 'Dt2", "8") \ - V(LDP_d, "ldp", "'Dt, 'Dt2", "8") - -void Disassembler::VisitLoadStorePairPostIndex(Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(LoadStorePairPostIndex)"; - - switch (instr->Mask(LoadStorePairPostIndexMask)) { - #define LSP_POSTINDEX(A, B, C, D) \ - case A##_post: mnemonic = B; form = C ", ['Xns]'ILP" D; break; - LOAD_STORE_PAIR_LIST(LSP_POSTINDEX) - #undef LSP_POSTINDEX - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLoadStorePairPreIndex(Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(LoadStorePairPreIndex)"; - - switch (instr->Mask(LoadStorePairPreIndexMask)) { - #define LSP_PREINDEX(A, B, C, D) \ - case A##_pre: mnemonic = B; form = C ", ['Xns'ILP" D "]!"; break; - LOAD_STORE_PAIR_LIST(LSP_PREINDEX) - #undef LSP_PREINDEX - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLoadStorePairOffset(Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(LoadStorePairOffset)"; - - switch (instr->Mask(LoadStorePairOffsetMask)) { - #define LSP_OFFSET(A, B, C, D) \ - case A##_off: mnemonic = B; form = C ", ['Xns'ILP" D "]"; break; - LOAD_STORE_PAIR_LIST(LSP_OFFSET) - #undef LSP_OFFSET - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitLoadStorePairNonTemporal(Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form; - - switch (instr->Mask(LoadStorePairNonTemporalMask)) { - case STNP_w: mnemonic = "stnp"; form = "'Wt, 'Wt2, ['Xns'ILP4]"; break; - case LDNP_w: mnemonic = "ldnp"; form = "'Wt, 'Wt2, ['Xns'ILP4]"; break; - case STNP_x: mnemonic = "stnp"; form = "'Xt, 'Xt2, ['Xns'ILP8]"; break; - case LDNP_x: mnemonic = "ldnp"; form = "'Xt, 'Xt2, ['Xns'ILP8]"; break; - case STNP_s: mnemonic = "stnp"; form = "'St, 'St2, ['Xns'ILP4]"; break; - case LDNP_s: mnemonic = "ldnp"; form = "'St, 'St2, ['Xns'ILP4]"; break; - case STNP_d: mnemonic = "stnp"; form = "'Dt, 'Dt2, ['Xns'ILP8]"; break; - case LDNP_d: mnemonic = "ldnp"; form = "'Dt, 'Dt2, ['Xns'ILP8]"; break; - default: form = "(LoadStorePairNonTemporal)"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitFPCompare(Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "'Fn, 'Fm"; - const char *form_zero = "'Fn, #0.0"; - - switch (instr->Mask(FPCompareMask)) { - case FCMP_s_zero: - case FCMP_d_zero: form = form_zero; // Fall through. - case FCMP_s: - case FCMP_d: mnemonic = "fcmp"; break; - default: form = "(FPCompare)"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitFPConditionalCompare(Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "'Fn, 'Fm, 'INzcv, 'Cond"; - - switch (instr->Mask(FPConditionalCompareMask)) { - case FCCMP_s: - case FCCMP_d: mnemonic = "fccmp"; break; - case FCCMPE_s: - case FCCMPE_d: mnemonic = "fccmpe"; break; - default: form = "(FPConditionalCompare)"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitFPConditionalSelect(Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Fd, 'Fn, 'Fm, 'Cond"; - - switch (instr->Mask(FPConditionalSelectMask)) { - case FCSEL_s: - case FCSEL_d: mnemonic = "fcsel"; break; - default: UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitFPDataProcessing1Source(Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "'Fd, 'Fn"; - - switch (instr->Mask(FPDataProcessing1SourceMask)) { - #define FORMAT(A, B) \ - case A##_s: \ - case A##_d: mnemonic = B; break; - FORMAT(FMOV, "fmov"); - FORMAT(FABS, "fabs"); - FORMAT(FNEG, "fneg"); - FORMAT(FSQRT, "fsqrt"); - FORMAT(FRINTN, "frintn"); - FORMAT(FRINTP, "frintp"); - FORMAT(FRINTM, "frintm"); - FORMAT(FRINTZ, "frintz"); - FORMAT(FRINTA, "frinta"); - FORMAT(FRINTX, "frintx"); - FORMAT(FRINTI, "frinti"); - #undef FORMAT - case FCVT_ds: mnemonic = "fcvt"; form = "'Dd, 'Sn"; break; - case FCVT_sd: mnemonic = "fcvt"; form = "'Sd, 'Dn"; break; - default: form = "(FPDataProcessing1Source)"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitFPDataProcessing2Source(Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Fd, 'Fn, 'Fm"; - - switch (instr->Mask(FPDataProcessing2SourceMask)) { - #define FORMAT(A, B) \ - case A##_s: \ - case A##_d: mnemonic = B; break; - FORMAT(FMUL, "fmul"); - FORMAT(FDIV, "fdiv"); - FORMAT(FADD, "fadd"); - FORMAT(FSUB, "fsub"); - FORMAT(FMAX, "fmax"); - FORMAT(FMIN, "fmin"); - FORMAT(FMAXNM, "fmaxnm"); - FORMAT(FMINNM, "fminnm"); - FORMAT(FNMUL, "fnmul"); - #undef FORMAT - default: UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitFPDataProcessing3Source(Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Fd, 'Fn, 'Fm, 'Fa"; - - switch (instr->Mask(FPDataProcessing3SourceMask)) { - #define FORMAT(A, B) \ - case A##_s: \ - case A##_d: mnemonic = B; break; - FORMAT(FMADD, "fmadd"); - FORMAT(FMSUB, "fmsub"); - FORMAT(FNMADD, "fnmadd"); - FORMAT(FNMSUB, "fnmsub"); - #undef FORMAT - default: UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitFPImmediate(Instruction* instr) { - const char *mnemonic = ""; - const char *form = "(FPImmediate)"; - - switch (instr->Mask(FPImmediateMask)) { - case FMOV_s_imm: mnemonic = "fmov"; form = "'Sd, 'IFPSingle"; break; - case FMOV_d_imm: mnemonic = "fmov"; form = "'Dd, 'IFPDouble"; break; - default: UNREACHABLE(); - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitFPIntegerConvert(Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "(FPIntegerConvert)"; - const char *form_rf = "'Rd, 'Fn"; - const char *form_fr = "'Fd, 'Rn"; - - switch (instr->Mask(FPIntegerConvertMask)) { - case FMOV_ws: - case FMOV_xd: mnemonic = "fmov"; form = form_rf; break; - case FMOV_sw: - case FMOV_dx: mnemonic = "fmov"; form = form_fr; break; - case FCVTAS_ws: - case FCVTAS_xs: - case FCVTAS_wd: - case FCVTAS_xd: mnemonic = "fcvtas"; form = form_rf; break; - case FCVTAU_ws: - case FCVTAU_xs: - case FCVTAU_wd: - case FCVTAU_xd: mnemonic = "fcvtau"; form = form_rf; break; - case FCVTMS_ws: - case FCVTMS_xs: - case FCVTMS_wd: - case FCVTMS_xd: mnemonic = "fcvtms"; form = form_rf; break; - case FCVTMU_ws: - case FCVTMU_xs: - case FCVTMU_wd: - case FCVTMU_xd: mnemonic = "fcvtmu"; form = form_rf; break; - case FCVTNS_ws: - case FCVTNS_xs: - case FCVTNS_wd: - case FCVTNS_xd: mnemonic = "fcvtns"; form = form_rf; break; - case FCVTNU_ws: - case FCVTNU_xs: - case FCVTNU_wd: - case FCVTNU_xd: mnemonic = "fcvtnu"; form = form_rf; break; - case FCVTZU_xd: - case FCVTZU_ws: - case FCVTZU_wd: - case FCVTZU_xs: mnemonic = "fcvtzu"; form = form_rf; break; - case FCVTZS_xd: - case FCVTZS_wd: - case FCVTZS_xs: - case FCVTZS_ws: mnemonic = "fcvtzs"; form = form_rf; break; - case SCVTF_sw: - case SCVTF_sx: - case SCVTF_dw: - case SCVTF_dx: mnemonic = "scvtf"; form = form_fr; break; - case UCVTF_sw: - case UCVTF_sx: - case UCVTF_dw: - case UCVTF_dx: mnemonic = "ucvtf"; form = form_fr; break; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitFPFixedPointConvert(Instruction* instr) { - const char *mnemonic = ""; - const char *form = "'Rd, 'Fn, 'IFPFBits"; - const char *form_fr = "'Fd, 'Rn, 'IFPFBits"; - - switch (instr->Mask(FPFixedPointConvertMask)) { - case FCVTZS_ws_fixed: - case FCVTZS_xs_fixed: - case FCVTZS_wd_fixed: - case FCVTZS_xd_fixed: mnemonic = "fcvtzs"; break; - case FCVTZU_ws_fixed: - case FCVTZU_xs_fixed: - case FCVTZU_wd_fixed: - case FCVTZU_xd_fixed: mnemonic = "fcvtzu"; break; - case SCVTF_sw_fixed: - case SCVTF_sx_fixed: - case SCVTF_dw_fixed: - case SCVTF_dx_fixed: mnemonic = "scvtf"; form = form_fr; break; - case UCVTF_sw_fixed: - case UCVTF_sx_fixed: - case UCVTF_dw_fixed: - case UCVTF_dx_fixed: mnemonic = "ucvtf"; form = form_fr; break; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitSystem(Instruction* instr) { - // Some system instructions hijack their Op and Cp fields to represent a - // range of immediates instead of indicating a different instruction. This - // makes the decoding tricky. - const char *mnemonic = "unimplemented"; - const char *form = "(System)"; - - if (instr->Mask(SystemSysRegFMask) == SystemSysRegFixed) { - switch (instr->Mask(SystemSysRegMask)) { - case MRS: { - mnemonic = "mrs"; - switch (instr->ImmSystemRegister()) { - case NZCV: form = "'Xt, nzcv"; break; - case FPCR: form = "'Xt, fpcr"; break; - default: form = "'Xt, (unknown)"; break; - } - break; - } - case MSR: { - mnemonic = "msr"; - switch (instr->ImmSystemRegister()) { - case NZCV: form = "nzcv, 'Xt"; break; - case FPCR: form = "fpcr, 'Xt"; break; - default: form = "(unknown), 'Xt"; break; - } - break; - } - } - } else if (instr->Mask(SystemHintFMask) == SystemHintFixed) { - ASSERT(instr->Mask(SystemHintMask) == HINT); - switch (instr->ImmHint()) { - case NOP: { - mnemonic = "nop"; - form = NULL; - break; - } - } - } else if (instr->Mask(MemBarrierFMask) == MemBarrierFixed) { - switch (instr->Mask(MemBarrierMask)) { - case DMB: { - mnemonic = "dmb"; - form = "'M"; - break; - } - case DSB: { - mnemonic = "dsb"; - form = "'M"; - break; - } - case ISB: { - mnemonic = "isb"; - form = NULL; - break; - } - } - } - - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitException(Instruction* instr) { - const char *mnemonic = "unimplemented"; - const char *form = "'IDebug"; - - switch (instr->Mask(ExceptionMask)) { - case HLT: mnemonic = "hlt"; break; - case BRK: mnemonic = "brk"; break; - case SVC: mnemonic = "svc"; break; - case HVC: mnemonic = "hvc"; break; - case SMC: mnemonic = "smc"; break; - case DCPS1: mnemonic = "dcps1"; form = "{'IDebug}"; break; - case DCPS2: mnemonic = "dcps2"; form = "{'IDebug}"; break; - case DCPS3: mnemonic = "dcps3"; form = "{'IDebug}"; break; - default: form = "(Exception)"; - } - Format(instr, mnemonic, form); -} - - -void Disassembler::VisitUnimplemented(Instruction* instr) { - Format(instr, "unimplemented", "(Unimplemented)"); -} - - -void Disassembler::VisitUnallocated(Instruction* instr) { - Format(instr, "unallocated", "(Unallocated)"); -} - - -void Disassembler::ProcessOutput(Instruction* /*instr*/) { - // The base disasm does nothing more than disassembling into a buffer. -} - - -void Disassembler::Format(Instruction* instr, const char* mnemonic, - const char* format) { - // TODO(mcapewel) don't think I can use the instr address here - there needs - // to be a base address too - ASSERT(mnemonic != NULL); - ResetOutput(); - Substitute(instr, mnemonic); - if (format != NULL) { - buffer_[buffer_pos_++] = ' '; - Substitute(instr, format); - } - buffer_[buffer_pos_] = 0; - ProcessOutput(instr); -} - - -void Disassembler::Substitute(Instruction* instr, const char* string) { - char chr = *string++; - while (chr != '\0') { - if (chr == '\'') { - string += SubstituteField(instr, string); - } else { - buffer_[buffer_pos_++] = chr; - } - chr = *string++; - } -} - - -int Disassembler::SubstituteField(Instruction* instr, const char* format) { - switch (format[0]) { - case 'R': // Register. X or W, selected by sf bit. - case 'F': // FP Register. S or D, selected by type field. - case 'W': - case 'X': - case 'S': - case 'D': return SubstituteRegisterField(instr, format); - case 'I': return SubstituteImmediateField(instr, format); - case 'L': return SubstituteLiteralField(instr, format); - case 'H': return SubstituteShiftField(instr, format); - case 'P': return SubstitutePrefetchField(instr, format); - case 'C': return SubstituteConditionField(instr, format); - case 'E': return SubstituteExtendField(instr, format); - case 'A': return SubstitutePCRelAddressField(instr, format); - case 'B': return SubstituteBranchTargetField(instr, format); - case 'O': return SubstituteLSRegOffsetField(instr, format); - case 'M': return SubstituteBarrierField(instr, format); - default: { - UNREACHABLE(); - return 1; - } - } -} - - -int Disassembler::SubstituteRegisterField(Instruction* instr, - const char* format) { - unsigned reg_num = 0; - unsigned field_len = 2; - switch (format[1]) { - case 'd': reg_num = instr->Rd(); break; - case 'n': reg_num = instr->Rn(); break; - case 'm': reg_num = instr->Rm(); break; - case 'a': reg_num = instr->Ra(); break; - case 't': { - if (format[2] == '2') { - reg_num = instr->Rt2(); - field_len = 3; - } else { - reg_num = instr->Rt(); - } - break; - } - default: UNREACHABLE(); - } - - // Increase field length for registers tagged as stack. - if (format[2] == 's') { - field_len = 3; - } - - char reg_type; - if (format[0] == 'R') { - // Register type is R: use sf bit to choose X and W. - reg_type = instr->SixtyFourBits() ? 'x' : 'w'; - } else if (format[0] == 'F') { - // Floating-point register: use type field to choose S or D. - reg_type = ((instr->FPType() & 1) == 0) ? 's' : 'd'; - } else { - // Register type is specified. Make it lower case. - reg_type = format[0] + 0x20; - } - - if ((reg_num != kZeroRegCode) || (reg_type == 's') || (reg_type == 'd')) { - // A normal register: w0 - w30, x0 - x30, s0 - s31, d0 - d31. - - // Filter special registers - if ((reg_type == 'x') && (reg_num == 27)) { - AppendToOutput("cp"); - } else if ((reg_type == 'x') && (reg_num == 28)) { - AppendToOutput("jssp"); - } else if ((reg_type == 'x') && (reg_num == 29)) { - AppendToOutput("fp"); - } else if ((reg_type == 'x') && (reg_num == 30)) { - AppendToOutput("lr"); - } else { - AppendToOutput("%c%d", reg_type, reg_num); - } - } else if (format[2] == 's') { - // Disassemble w31/x31 as stack pointer wcsp/csp. - AppendToOutput("%s", (reg_type == 'w') ? "wcsp" : "csp"); - } else { - // Disassemble w31/x31 as zero register wzr/xzr. - AppendToOutput("%czr", reg_type); - } - - return field_len; -} - - -int Disassembler::SubstituteImmediateField(Instruction* instr, - const char* format) { - ASSERT(format[0] == 'I'); - - switch (format[1]) { - case 'M': { // IMoveImm or IMoveLSL. - if (format[5] == 'I') { - uint64_t imm = instr->ImmMoveWide() << (16 * instr->ShiftMoveWide()); - AppendToOutput("#0x%" PRIx64, imm); - } else { - ASSERT(format[5] == 'L'); - AppendToOutput("#0x%" PRIx64, instr->ImmMoveWide()); - if (instr->ShiftMoveWide() > 0) { - AppendToOutput(", lsl #%d", 16 * instr->ShiftMoveWide()); - } - } - return 8; - } - case 'L': { - switch (format[2]) { - case 'L': { // ILLiteral - Immediate Load Literal. - AppendToOutput("pc%+" PRId64, - instr->ImmLLiteral() << kLiteralEntrySizeLog2); - return 9; - } - case 'S': { // ILS - Immediate Load/Store. - if (instr->ImmLS() != 0) { - AppendToOutput(", #%" PRId64, instr->ImmLS()); - } - return 3; - } - case 'P': { // ILPx - Immediate Load/Store Pair, x = access size. - if (instr->ImmLSPair() != 0) { - // format[3] is the scale value. Convert to a number. - int scale = format[3] - 0x30; - AppendToOutput(", #%" PRId64, instr->ImmLSPair() * scale); - } - return 4; - } - case 'U': { // ILU - Immediate Load/Store Unsigned. - if (instr->ImmLSUnsigned() != 0) { - AppendToOutput(", #%" PRIu64, - instr->ImmLSUnsigned() << instr->SizeLS()); - } - return 3; - } - } - } - case 'C': { // ICondB - Immediate Conditional Branch. - int64_t offset = instr->ImmCondBranch() << 2; - char sign = (offset >= 0) ? '+' : '-'; - AppendToOutput("#%c0x%" PRIx64, sign, offset); - return 6; - } - case 'A': { // IAddSub. - ASSERT(instr->ShiftAddSub() <= 1); - int64_t imm = instr->ImmAddSub() << (12 * instr->ShiftAddSub()); - AppendToOutput("#0x%" PRIx64 " (%" PRId64 ")", imm, imm); - return 7; - } - case 'F': { // IFPSingle, IFPDouble or IFPFBits. - if (format[3] == 'F') { // IFPFBits. - AppendToOutput("#%d", 64 - instr->FPScale()); - return 8; - } else { - AppendToOutput("#0x%" PRIx64 " (%.4f)", instr->ImmFP(), - format[3] == 'S' ? instr->ImmFP32() : instr->ImmFP64()); - return 9; - } - } - case 'T': { // ITri - Immediate Triangular Encoded. - AppendToOutput("#0x%" PRIx64, instr->ImmLogical()); - return 4; - } - case 'N': { // INzcv. - int nzcv = (instr->Nzcv() << Flags_offset); - AppendToOutput("#%c%c%c%c", ((nzcv & NFlag) == 0) ? 'n' : 'N', - ((nzcv & ZFlag) == 0) ? 'z' : 'Z', - ((nzcv & CFlag) == 0) ? 'c' : 'C', - ((nzcv & VFlag) == 0) ? 'v' : 'V'); - return 5; - } - case 'P': { // IP - Conditional compare. - AppendToOutput("#%d", instr->ImmCondCmp()); - return 2; - } - case 'B': { // Bitfields. - return SubstituteBitfieldImmediateField(instr, format); - } - case 'E': { // IExtract. - AppendToOutput("#%d", instr->ImmS()); - return 8; - } - case 'S': { // IS - Test and branch bit. - AppendToOutput("#%d", (instr->ImmTestBranchBit5() << 5) | - instr->ImmTestBranchBit40()); - return 2; - } - case 'D': { // IDebug - HLT and BRK instructions. - AppendToOutput("#0x%x", instr->ImmException()); - return 6; - } - default: { - UNIMPLEMENTED(); - return 0; - } - } -} - - -int Disassembler::SubstituteBitfieldImmediateField(Instruction* instr, - const char* format) { - ASSERT((format[0] == 'I') && (format[1] == 'B')); - unsigned r = instr->ImmR(); - unsigned s = instr->ImmS(); - - switch (format[2]) { - case 'r': { // IBr. - AppendToOutput("#%d", r); - return 3; - } - case 's': { // IBs+1 or IBs-r+1. - if (format[3] == '+') { - AppendToOutput("#%d", s + 1); - return 5; - } else { - ASSERT(format[3] == '-'); - AppendToOutput("#%d", s - r + 1); - return 7; - } - } - case 'Z': { // IBZ-r. - ASSERT((format[3] == '-') && (format[4] == 'r')); - unsigned reg_size = (instr->SixtyFourBits() == 1) ? kXRegSize : kWRegSize; - AppendToOutput("#%d", reg_size - r); - return 5; - } - default: { - UNREACHABLE(); - return 0; - } - } -} - - -int Disassembler::SubstituteLiteralField(Instruction* instr, - const char* format) { - ASSERT(strncmp(format, "LValue", 6) == 0); - USE(format); - - switch (instr->Mask(LoadLiteralMask)) { - case LDR_w_lit: - case LDR_x_lit: - case LDR_s_lit: - case LDR_d_lit: AppendToOutput("(addr %p)", instr->LiteralAddress()); break; - default: UNREACHABLE(); - } - - return 6; -} - - -int Disassembler::SubstituteShiftField(Instruction* instr, const char* format) { - ASSERT(format[0] == 'H'); - ASSERT(instr->ShiftDP() <= 0x3); - - switch (format[1]) { - case 'D': { // HDP. - ASSERT(instr->ShiftDP() != ROR); - } // Fall through. - case 'L': { // HLo. - if (instr->ImmDPShift() != 0) { - const char* shift_type[] = {"lsl", "lsr", "asr", "ror"}; - AppendToOutput(", %s #%" PRId64, shift_type[instr->ShiftDP()], - instr->ImmDPShift()); - } - return 3; - } - default: - UNIMPLEMENTED(); - return 0; - } -} - - -int Disassembler::SubstituteConditionField(Instruction* instr, - const char* format) { - ASSERT(format[0] == 'C'); - const char* condition_code[] = { "eq", "ne", "hs", "lo", - "mi", "pl", "vs", "vc", - "hi", "ls", "ge", "lt", - "gt", "le", "al", "nv" }; - int cond; - switch (format[1]) { - case 'B': cond = instr->ConditionBranch(); break; - case 'I': { - cond = InvertCondition(static_cast(instr->Condition())); - break; - } - default: cond = instr->Condition(); - } - AppendToOutput("%s", condition_code[cond]); - return 4; -} - - -int Disassembler::SubstitutePCRelAddressField(Instruction* instr, - const char* format) { - USE(format); - ASSERT(strncmp(format, "AddrPCRel", 9) == 0); - - int offset = instr->ImmPCRel(); - - // Only ADR (AddrPCRelByte) is supported. - ASSERT(strcmp(format, "AddrPCRelByte") == 0); - - char sign = '+'; - if (offset < 0) { - offset = -offset; - sign = '-'; - } - // TODO(jbramley): Can we print the target address here? - AppendToOutput("#%c0x%x", sign, offset); - return 13; -} - - -int Disassembler::SubstituteBranchTargetField(Instruction* instr, - const char* format) { - ASSERT(strncmp(format, "BImm", 4) == 0); - - int64_t offset = 0; - switch (format[5]) { - // BImmUncn - unconditional branch immediate. - case 'n': offset = instr->ImmUncondBranch(); break; - // BImmCond - conditional branch immediate. - case 'o': offset = instr->ImmCondBranch(); break; - // BImmCmpa - compare and branch immediate. - case 'm': offset = instr->ImmCmpBranch(); break; - // BImmTest - test and branch immediate. - case 'e': offset = instr->ImmTestBranch(); break; - default: UNIMPLEMENTED(); - } - offset <<= kInstructionSizeLog2; - char sign = '+'; - if (offset < 0) { - offset = -offset; - sign = '-'; - } - // TODO(mcapewel): look up pc + offset in label table. - AppendToOutput("#%c0x%" PRIx64, sign, offset); - return 8; -} - - -int Disassembler::SubstituteExtendField(Instruction* instr, - const char* format) { - ASSERT(strncmp(format, "Ext", 3) == 0); - ASSERT(instr->ExtendMode() <= 7); - USE(format); - - const char* extend_mode[] = { "uxtb", "uxth", "uxtw", "uxtx", - "sxtb", "sxth", "sxtw", "sxtx" }; - - // If rd or rn is SP, uxtw on 32-bit registers and uxtx on 64-bit - // registers becomes lsl. - if (((instr->Rd() == kZeroRegCode) || (instr->Rn() == kZeroRegCode)) && - (((instr->ExtendMode() == UXTW) && (instr->SixtyFourBits() == 0)) || - (instr->ExtendMode() == UXTX))) { - if (instr->ImmExtendShift() > 0) { - AppendToOutput(", lsl #%d", instr->ImmExtendShift()); - } - } else { - AppendToOutput(", %s", extend_mode[instr->ExtendMode()]); - if (instr->ImmExtendShift() > 0) { - AppendToOutput(" #%d", instr->ImmExtendShift()); - } - } - return 3; -} - - -int Disassembler::SubstituteLSRegOffsetField(Instruction* instr, - const char* format) { - ASSERT(strncmp(format, "Offsetreg", 9) == 0); - const char* extend_mode[] = { "undefined", "undefined", "uxtw", "lsl", - "undefined", "undefined", "sxtw", "sxtx" }; - USE(format); - - unsigned shift = instr->ImmShiftLS(); - Extend ext = static_cast(instr->ExtendMode()); - char reg_type = ((ext == UXTW) || (ext == SXTW)) ? 'w' : 'x'; - - unsigned rm = instr->Rm(); - if (rm == kZeroRegCode) { - AppendToOutput("%czr", reg_type); - } else { - AppendToOutput("%c%d", reg_type, rm); - } - - // Extend mode UXTX is an alias for shift mode LSL here. - if (!((ext == UXTX) && (shift == 0))) { - AppendToOutput(", %s", extend_mode[ext]); - if (shift != 0) { - AppendToOutput(" #%d", instr->SizeLS()); - } - } - return 9; -} - - -int Disassembler::SubstitutePrefetchField(Instruction* instr, - const char* format) { - ASSERT(format[0] == 'P'); - USE(format); - - int prefetch_mode = instr->PrefetchMode(); - - const char* ls = (prefetch_mode & 0x10) ? "st" : "ld"; - int level = (prefetch_mode >> 1) + 1; - const char* ks = (prefetch_mode & 1) ? "strm" : "keep"; - - AppendToOutput("p%sl%d%s", ls, level, ks); - return 6; -} - -int Disassembler::SubstituteBarrierField(Instruction* instr, - const char* format) { - ASSERT(format[0] == 'M'); - USE(format); - - static const char* options[4][4] = { - { "sy (0b0000)", "oshld", "oshst", "osh" }, - { "sy (0b0100)", "nshld", "nshst", "nsh" }, - { "sy (0b1000)", "ishld", "ishst", "ish" }, - { "sy (0b1100)", "ld", "st", "sy" } - }; - int domain = instr->ImmBarrierDomain(); - int type = instr->ImmBarrierType(); - - AppendToOutput("%s", options[domain][type]); - return 1; -} - - -void Disassembler::ResetOutput() { - buffer_pos_ = 0; - buffer_[buffer_pos_] = 0; -} - - -void Disassembler::AppendToOutput(const char* format, ...) { - va_list args; - va_start(args, format); - buffer_pos_ += vsnprintf(&buffer_[buffer_pos_], buffer_size_, format, args); - va_end(args); -} - - -void PrintDisassembler::ProcessOutput(Instruction* instr) { - fprintf(stream_, "0x%016" PRIx64 " %08" PRIx32 "\t\t%s\n", - reinterpret_cast(instr), instr->InstructionBits(), - GetOutput()); -} - -} } // namespace v8::internal - - -namespace disasm { - - -const char* NameConverter::NameOfAddress(byte* addr) const { - v8::internal::OS::SNPrintF(tmp_buffer_, "%p", addr); - return tmp_buffer_.start(); -} - - -const char* NameConverter::NameOfConstant(byte* addr) const { - return NameOfAddress(addr); -} - - -const char* NameConverter::NameOfCPURegister(int reg) const { - unsigned ureg = reg; // Avoid warnings about signed/unsigned comparisons. - if (ureg >= v8::internal::kNumberOfRegisters) { - return "noreg"; - } - if (ureg == v8::internal::kZeroRegCode) { - return "xzr"; - } - v8::internal::OS::SNPrintF(tmp_buffer_, "x%u", ureg); - return tmp_buffer_.start(); -} - - -const char* NameConverter::NameOfByteCPURegister(int reg) const { - UNREACHABLE(); // A64 does not have the concept of a byte register - return "nobytereg"; -} - - -const char* NameConverter::NameOfXMMRegister(int reg) const { - UNREACHABLE(); // A64 does not have any XMM registers - return "noxmmreg"; -} - - -const char* NameConverter::NameInCode(byte* addr) const { - // The default name converter is called for unknown code, so we will not try - // to access any memory. - return ""; -} - - -//------------------------------------------------------------------------------ - -class BufferDisassembler : public v8::internal::Disassembler { - public: - explicit BufferDisassembler(v8::internal::Vector out_buffer) - : out_buffer_(out_buffer) { } - - ~BufferDisassembler() { } - - virtual void ProcessOutput(v8::internal::Instruction* instr) { - v8::internal::OS::SNPrintF(out_buffer_, "%s", GetOutput()); - } - - private: - v8::internal::Vector out_buffer_; -}; - -Disassembler::Disassembler(const NameConverter& converter) - : converter_(converter) {} - - -Disassembler::~Disassembler() {} - - -int Disassembler::InstructionDecode(v8::internal::Vector buffer, - byte* instr) { - v8::internal::Decoder decoder; - BufferDisassembler disasm(buffer); - decoder.AppendVisitor(&disasm); - - decoder.Decode(reinterpret_cast(instr)); - return v8::internal::kInstructionSize; -} - - -int Disassembler::ConstantPoolSizeAt(byte* instr) { - return v8::internal::Assembler::ConstantPoolSizeAt( - reinterpret_cast(instr)); -} - - -void Disassembler::Disassemble(FILE* file, byte* start, byte* end) { - v8::internal::Decoder decoder; - v8::internal::PrintDisassembler disasm(file); - decoder.AppendVisitor(&disasm); - - for (byte* pc = start; pc < end; pc += v8::internal::kInstructionSize) { - decoder.Decode(reinterpret_cast(pc)); - } -} - -} // namespace disasm - -#endif // V8_TARGET_ARCH_A64 diff --git a/deps/v8/src/a64/disasm-a64.h b/deps/v8/src/a64/disasm-a64.h deleted file mode 100644 index 35b8fe1f63..0000000000 --- a/deps/v8/src/a64/disasm-a64.h +++ /dev/null @@ -1,115 +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. - -#ifndef V8_A64_DISASM_A64_H -#define V8_A64_DISASM_A64_H - -#include "v8.h" - -#include "globals.h" -#include "utils.h" -#include "instructions-a64.h" -#include "decoder-a64.h" - -namespace v8 { -namespace internal { - - -class Disassembler: public DecoderVisitor { - public: - Disassembler(); - Disassembler(char* text_buffer, int buffer_size); - virtual ~Disassembler(); - char* GetOutput(); - - // Declare all Visitor functions. - #define DECLARE(A) void Visit##A(Instruction* instr); - VISITOR_LIST(DECLARE) - #undef DECLARE - - protected: - virtual void ProcessOutput(Instruction* instr); - - void Format(Instruction* instr, const char* mnemonic, const char* format); - void Substitute(Instruction* instr, const char* string); - int SubstituteField(Instruction* instr, const char* format); - int SubstituteRegisterField(Instruction* instr, const char* format); - int SubstituteImmediateField(Instruction* instr, const char* format); - int SubstituteLiteralField(Instruction* instr, const char* format); - int SubstituteBitfieldImmediateField(Instruction* instr, const char* format); - int SubstituteShiftField(Instruction* instr, const char* format); - int SubstituteExtendField(Instruction* instr, const char* format); - int SubstituteConditionField(Instruction* instr, const char* format); - int SubstitutePCRelAddressField(Instruction* instr, const char* format); - int SubstituteBranchTargetField(Instruction* instr, const char* format); - int SubstituteLSRegOffsetField(Instruction* instr, const char* format); - int SubstitutePrefetchField(Instruction* instr, const char* format); - int SubstituteBarrierField(Instruction* instr, const char* format); - - bool RdIsZROrSP(Instruction* instr) const { - return (instr->Rd() == kZeroRegCode); - } - - bool RnIsZROrSP(Instruction* instr) const { - return (instr->Rn() == kZeroRegCode); - } - - bool RmIsZROrSP(Instruction* instr) const { - return (instr->Rm() == kZeroRegCode); - } - - bool RaIsZROrSP(Instruction* instr) const { - return (instr->Ra() == kZeroRegCode); - } - - bool IsMovzMovnImm(unsigned reg_size, uint64_t value); - - void ResetOutput(); - void AppendToOutput(const char* string, ...); - - char* buffer_; - uint32_t buffer_pos_; - uint32_t buffer_size_; - bool own_buffer_; -}; - - -class PrintDisassembler: public Disassembler { - public: - explicit PrintDisassembler(FILE* stream) : stream_(stream) { } - ~PrintDisassembler() { } - - virtual void ProcessOutput(Instruction* instr); - - private: - FILE *stream_; -}; - - -} } // namespace v8::internal - -#endif // V8_A64_DISASM_A64_H diff --git a/deps/v8/src/a64/frames-a64.cc b/deps/v8/src/a64/frames-a64.cc deleted file mode 100644 index 56d2e26b72..0000000000 --- a/deps/v8/src/a64/frames-a64.cc +++ /dev/null @@ -1,57 +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 "v8.h" - -#if V8_TARGET_ARCH_A64 - -#include "assembler.h" -#include "assembler-a64.h" -#include "assembler-a64-inl.h" -#include "frames.h" - -namespace v8 { -namespace internal { - - -Register JavaScriptFrame::fp_register() { return v8::internal::fp; } -Register JavaScriptFrame::context_register() { return cp; } - - -Register StubFailureTrampolineFrame::fp_register() { return v8::internal::fp; } -Register StubFailureTrampolineFrame::context_register() { return cp; } - - -Object*& ExitFrame::constant_pool_slot() const { - UNREACHABLE(); - return Memory::Object_at(NULL); -} - - -} } // namespace v8::internal - -#endif // V8_TARGET_ARCH_A64 diff --git a/deps/v8/src/a64/frames-a64.h b/deps/v8/src/a64/frames-a64.h deleted file mode 100644 index 5ef7681645..0000000000 --- a/deps/v8/src/a64/frames-a64.h +++ /dev/null @@ -1,131 +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 "a64/constants-a64.h" -#include "a64/assembler-a64.h" - -#ifndef V8_A64_FRAMES_A64_H_ -#define V8_A64_FRAMES_A64_H_ - -namespace v8 { -namespace internal { - -const int kNumRegs = kNumberOfRegisters; -// Registers x0-x17 are caller-saved. -const int kNumJSCallerSaved = 18; -const RegList kJSCallerSaved = 0x3ffff; -typedef Object* JSCallerSavedBuffer[kNumJSCallerSaved]; - -// Number of registers for which space is reserved in safepoints. Must be a -// multiple of eight. -// TODO(all): Refine this number. -const int kNumSafepointRegisters = 32; - -// Define the list of registers actually saved at safepoints. -// Note that the number of saved registers may be smaller than the reserved -// space, i.e. kNumSafepointSavedRegisters <= kNumSafepointRegisters. -#define kSafepointSavedRegisters CPURegList::GetSafepointSavedRegisters().list() -#define kNumSafepointSavedRegisters \ - CPURegList::GetSafepointSavedRegisters().Count(); - -class EntryFrameConstants : public AllStatic { - public: - static const int kCallerFPOffset = - -(StandardFrameConstants::kFixedFrameSizeFromFp + kPointerSize); -}; - - -class ExitFrameConstants : public AllStatic { - public: - static const int kFrameSize = 2 * kPointerSize; - - static const int kCallerSPDisplacement = 2 * kPointerSize; - static const int kCallerPCOffset = 1 * kPointerSize; - static const int kCallerFPOffset = 0 * kPointerSize; // <- fp - static const int kSPOffset = -1 * kPointerSize; - static const int kCodeOffset = -2 * kPointerSize; - static const int kLastExitFrameField = kCodeOffset; -}; - - -class JavaScriptFrameConstants : public AllStatic { - public: - // FP-relative. - static const int kLocal0Offset = StandardFrameConstants::kExpressionsOffset; - - // There are two words on the stack (saved fp and saved lr) between fp and - // the arguments. - static const int kLastParameterOffset = 2 * kPointerSize; - - static const int kFunctionOffset = StandardFrameConstants::kMarkerOffset; -}; - - -class ArgumentsAdaptorFrameConstants : public AllStatic { - public: - // FP-relative. - static const int kLengthOffset = StandardFrameConstants::kExpressionsOffset; - - static const int kFrameSize = - StandardFrameConstants::kFixedFrameSize + kPointerSize; -}; - - -class ConstructFrameConstants : public AllStatic { - public: - // FP-relative. - static const int kCodeOffset = StandardFrameConstants::kExpressionsOffset; - static const int kLengthOffset = -4 * kPointerSize; - static const int kConstructorOffset = -5 * kPointerSize; - static const int kImplicitReceiverOffset = -6 * kPointerSize; - - static const int kFrameSize = - StandardFrameConstants::kFixedFrameSize + 4 * kPointerSize; -}; - - -class InternalFrameConstants : public AllStatic { - public: - // FP-relative. - static const int kCodeOffset = StandardFrameConstants::kExpressionsOffset; -}; - - -inline Object* JavaScriptFrame::function_slot_object() const { - const int offset = JavaScriptFrameConstants::kFunctionOffset; - return Memory::Object_at(fp() + offset); -} - - -inline void StackHandler::SetFp(Address slot, Address fp) { - Memory::Address_at(slot) = fp; -} - - -} } // namespace v8::internal - -#endif // V8_A64_FRAMES_A64_H_ diff --git a/deps/v8/src/a64/full-codegen-a64.cc b/deps/v8/src/a64/full-codegen-a64.cc deleted file mode 100644 index ec5d339781..0000000000 --- a/deps/v8/src/a64/full-codegen-a64.cc +++ /dev/null @@ -1,5010 +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 "v8.h" - -#if V8_TARGET_ARCH_A64 - -#include "code-stubs.h" -#include "codegen.h" -#include "compiler.h" -#include "debug.h" -#include "full-codegen.h" -#include "isolate-inl.h" -#include "parser.h" -#include "scopes.h" -#include "stub-cache.h" - -#include "a64/code-stubs-a64.h" -#include "a64/macro-assembler-a64.h" - -namespace v8 { -namespace internal { - -#define __ ACCESS_MASM(masm_) - -class JumpPatchSite BASE_EMBEDDED { - public: - explicit JumpPatchSite(MacroAssembler* masm) : masm_(masm), reg_(NoReg) { -#ifdef DEBUG - info_emitted_ = false; -#endif - } - - ~JumpPatchSite() { - if (patch_site_.is_bound()) { - ASSERT(info_emitted_); - } else { - ASSERT(reg_.IsNone()); - } - } - - void EmitJumpIfNotSmi(Register reg, Label* target) { - // This code will be patched by PatchInlinedSmiCode, in ic-a64.cc. - InstructionAccurateScope scope(masm_, 1); - ASSERT(!info_emitted_); - ASSERT(reg.Is64Bits()); - ASSERT(!reg.Is(csp)); - reg_ = reg; - __ bind(&patch_site_); - __ tbz(xzr, 0, target); // Always taken before patched. - } - - void EmitJumpIfSmi(Register reg, Label* target) { - // This code will be patched by PatchInlinedSmiCode, in ic-a64.cc. - InstructionAccurateScope scope(masm_, 1); - ASSERT(!info_emitted_); - ASSERT(reg.Is64Bits()); - ASSERT(!reg.Is(csp)); - reg_ = reg; - __ bind(&patch_site_); - __ tbnz(xzr, 0, target); // Never taken before patched. - } - - void EmitJumpIfEitherNotSmi(Register reg1, Register reg2, Label* target) { - // We need to use ip0, so don't allow access to the MacroAssembler. - InstructionAccurateScope scope(masm_); - __ orr(ip0, reg1, reg2); - EmitJumpIfNotSmi(ip0, target); - } - - void EmitPatchInfo() { - Assembler::BlockConstPoolScope scope(masm_); - InlineSmiCheckInfo::Emit(masm_, reg_, &patch_site_); -#ifdef DEBUG - info_emitted_ = true; -#endif - } - - private: - MacroAssembler* masm_; - Label patch_site_; - Register reg_; -#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. The actual -// argument count matches the formal parameter count expected by the -// function. -// -// The live registers are: -// - x1: the JS function object being called (i.e. ourselves). -// - cp: our context. -// - fp: our caller's frame pointer. -// - jssp: stack pointer. -// - lr: return address. -// -// The function builds a JS frame. See JavaScriptFrameConstants in -// frames-arm.h for its layout. -void FullCodeGenerator::Generate() { - CompilationInfo* info = info_; - handler_table_ = - isolate()->factory()->NewFixedArray(function()->handler_count(), TENURED); - - InitializeFeedbackVector(); - - profiling_counter_ = isolate()->factory()->NewCell( - Handle(Smi::FromInt(FLAG_interrupt_budget), isolate())); - SetFunctionPosition(function()); - Comment cmnt(masm_, "[ Function compiled by full code generator"); - - ProfileEntryHookStub::MaybeCallEntryHook(masm_); - -#ifdef DEBUG - if (strlen(FLAG_stop_at) > 0 && - info->function()->name()->IsUtf8EqualTo(CStrVector(FLAG_stop_at))) { - __ Debug("stop-at", __LINE__, BREAK); - } -#endif - - // Classic mode functions and builtins need to replace the receiver with the - // global proxy when called as functions (without an explicit receiver - // object). - if (info->is_classic_mode() && !info->is_native()) { - Label ok; - int receiver_offset = info->scope()->num_parameters() * kXRegSizeInBytes; - __ Peek(x10, receiver_offset); - __ JumpIfNotRoot(x10, Heap::kUndefinedValueRootIndex, &ok); - - __ Ldr(x10, GlobalObjectMemOperand()); - __ Ldr(x10, FieldMemOperand(x10, GlobalObject::kGlobalReceiverOffset)); - __ Poke(x10, receiver_offset); - - __ Bind(&ok); - } - - - // 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 because we do it manually below. - FrameScope frame_scope(masm_, StackFrame::MANUAL); - - // This call emits the following sequence in a way that can be patched for - // code ageing support: - // Push(lr, fp, cp, x1); - // Add(fp, jssp, 2 * kPointerSize); - info->set_prologue_offset(masm_->pc_offset()); - __ Prologue(BUILD_FUNCTION_FRAME); - info->AddNoFrameRange(0, masm_->pc_offset()); - - // Reserve space on the stack for locals. - { Comment cmnt(masm_, "[ Allocate locals"); - int locals_count = info->scope()->num_stack_slots(); - // Generators allocate locals, if any, in context slots. - ASSERT(!info->function()->is_generator() || locals_count == 0); - - if (locals_count > 0) { - __ LoadRoot(x10, Heap::kUndefinedValueRootIndex); - __ PushMultipleTimes(locals_count, x10); - } - } - - bool function_in_register_x1 = true; - - int heap_slots = info->scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS; - if (heap_slots > 0) { - // Argument to NewContext is the function, which is still in x1. - Comment cmnt(masm_, "[ Allocate context"); - if (FLAG_harmony_scoping && info->scope()->is_global_scope()) { - __ Mov(x10, Operand(info->scope()->GetScopeInfo())); - __ Push(x1, x10); - __ CallRuntime(Runtime::kNewGlobalContext, 2); - } else if (heap_slots <= FastNewContextStub::kMaximumSlots) { - FastNewContextStub stub(heap_slots); - __ CallStub(&stub); - } else { - __ Push(x1); - __ CallRuntime(Runtime::kNewFunctionContext, 1); - } - function_in_register_x1 = false; - // Context is returned in x0. It replaces the context passed to us. - // It's saved in the stack and kept live in cp. - __ Mov(cp, x0); - __ Str(x0, MemOperand(fp, StandardFrameConstants::kContextOffset)); - // Copy any necessary parameters into the context. - int num_parameters = info->scope()->num_parameters(); - for (int i = 0; i < num_parameters; i++) { - Variable* var = scope()->parameter(i); - if (var->IsContextSlot()) { - int parameter_offset = StandardFrameConstants::kCallerSPOffset + - (num_parameters - 1 - i) * kPointerSize; - // Load parameter from stack. - __ Ldr(x10, MemOperand(fp, parameter_offset)); - // Store it in the context. - MemOperand target = ContextMemOperand(cp, var->index()); - __ Str(x10, target); - - // Update the write barrier. - __ RecordWriteContextSlot( - cp, target.offset(), x10, x11, kLRHasBeenSaved, kDontSaveFPRegs); - } - } - } - - Variable* arguments = scope()->arguments(); - if (arguments != NULL) { - // Function uses arguments object. - Comment cmnt(masm_, "[ Allocate arguments object"); - if (!function_in_register_x1) { - // Load this again, if it's used by the local context below. - __ Ldr(x3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); - } else { - __ Mov(x3, x1); - } - // Receiver is just before the parameters on the caller's stack. - int num_parameters = info->scope()->num_parameters(); - int offset = num_parameters * kPointerSize; - __ Add(x2, fp, StandardFrameConstants::kCallerSPOffset + offset); - __ Mov(x1, Operand(Smi::FromInt(num_parameters))); - __ Push(x3, x2, x1); - - // Arguments to ArgumentsAccessStub: - // function, receiver address, parameter count. - // The stub will rewrite receiver and parameter count if the previous - // stack frame was an arguments adapter frame. - ArgumentsAccessStub::Type type; - if (!is_classic_mode()) { - type = ArgumentsAccessStub::NEW_STRICT; - } else if (function()->has_duplicate_parameters()) { - type = ArgumentsAccessStub::NEW_NON_STRICT_SLOW; - } else { - type = ArgumentsAccessStub::NEW_NON_STRICT_FAST; - } - ArgumentsAccessStub stub(type); - __ CallStub(&stub); - - SetVar(arguments, x0, x1, x2); - } - - if (FLAG_trace) { - __ CallRuntime(Runtime::kTraceEnter, 0); - } - - - // Visit the declarations and body unless there is an illegal - // redeclaration. - if (scope()->HasIllegalRedeclaration()) { - Comment cmnt(masm_, "[ Declarations"); - scope()->VisitIllegalRedeclaration(this); - - } else { - PrepareForBailoutForId(BailoutId::FunctionEntry(), NO_REGISTERS); - { Comment cmnt(masm_, "[ Declarations"); - if (scope()->is_function_scope() && scope()->function() != NULL) { - VariableDeclaration* function = scope()->function(); - ASSERT(function->proxy()->var()->mode() == CONST || - function->proxy()->var()->mode() == CONST_HARMONY); - ASSERT(function->proxy()->var()->location() != Variable::UNALLOCATED); - VisitVariableDeclaration(function); - } - VisitDeclarations(scope()->declarations()); - } - } - - { Comment cmnt(masm_, "[ Stack check"); - PrepareForBailoutForId(BailoutId::Declarations(), NO_REGISTERS); - Label ok; - ASSERT(jssp.Is(__ StackPointer())); - __ CompareRoot(jssp, Heap::kStackLimitRootIndex); - __ B(hs, &ok); - PredictableCodeSizeScope predictable(masm_, - Assembler::kCallSizeWithRelocation); - __ Call(isolate()->builtins()->StackCheck(), RelocInfo::CODE_TARGET); - __ Bind(&ok); - } - - { Comment cmnt(masm_, "[ Body"); - ASSERT(loop_depth() == 0); - VisitStatements(function()->body()); - ASSERT(loop_depth() == 0); - } - - // Always emit a 'return undefined' in case control fell off the end of - // the body. - { Comment cmnt(masm_, "[ return ;"); - __ LoadRoot(x0, Heap::kUndefinedValueRootIndex); - } - EmitReturnSequence(); - - // Force emit the constant pool, so it doesn't get emitted in the middle - // of the back edge table. - masm()->CheckConstPool(true, false); -} - - -void FullCodeGenerator::ClearAccumulator() { - __ Mov(x0, Operand(Smi::FromInt(0))); -} - - -void FullCodeGenerator::EmitProfilingCounterDecrement(int delta) { - __ Mov(x2, Operand(profiling_counter_)); - __ Ldr(x3, FieldMemOperand(x2, Cell::kValueOffset)); - __ Subs(x3, x3, Operand(Smi::FromInt(delta))); - __ Str(x3, FieldMemOperand(x2, Cell::kValueOffset)); -} - - -void FullCodeGenerator::EmitProfilingCounterReset() { - int reset_value = FLAG_interrupt_budget; - if (isolate()->IsDebuggerActive()) { - // Detect debug break requests as soon as possible. - reset_value = FLAG_interrupt_budget >> 4; - } - __ Mov(x2, Operand(profiling_counter_)); - __ Mov(x3, Operand(Smi::FromInt(reset_value))); - __ Str(x3, FieldMemOperand(x2, Cell::kValueOffset)); -} - - -void FullCodeGenerator::EmitBackEdgeBookkeeping(IterationStatement* stmt, - Label* back_edge_target) { - ASSERT(jssp.Is(__ StackPointer())); - Comment cmnt(masm_, "[ Back edge bookkeeping"); - // Block literal pools whilst emitting back edge code. - Assembler::BlockConstPoolScope block_const_pool(masm_); - Label ok; - - ASSERT(back_edge_target->is_bound()); - int distance = masm_->SizeOfCodeGeneratedSince(back_edge_target); - int weight = Min(kMaxBackEdgeWeight, - Max(1, distance / kCodeSizeMultiplier)); - EmitProfilingCounterDecrement(weight); - __ B(pl, &ok); - __ 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); - PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS); - // Record a mapping of the OSR id to this PC. This is used if the OSR - // entry becomes the target of a bailout. We don't expect it to be, but - // we want it to work if it is. - PrepareForBailoutForId(stmt->OsrEntryId(), NO_REGISTERS); -} - - -void FullCodeGenerator::EmitReturnSequence() { - Comment cmnt(masm_, "[ Return sequence"); - - if (return_label_.is_bound()) { - __ B(&return_label_); - - } else { - __ Bind(&return_label_); - if (FLAG_trace) { - // Push the return value on the stack as the parameter. - // Runtime::TraceExit returns its parameter in x0. - __ Push(result_register()); - __ CallRuntime(Runtime::kTraceExit, 1); - ASSERT(x0.Is(result_register())); - } - // 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; - __ B(pl, &ok); - __ Push(x0); - __ Call(isolate()->builtins()->InterruptCheck(), - RelocInfo::CODE_TARGET); - __ Pop(x0); - EmitProfilingCounterReset(); - __ Bind(&ok); - - // Make sure that the constant pool is not emitted inside of the return - // sequence. This sequence can get patched when the debugger is used. See - // debug-a64.cc:BreakLocationIterator::SetDebugBreakAtReturn(). - { - InstructionAccurateScope scope(masm_, - Assembler::kJSRetSequenceInstructions); - CodeGenerator::RecordPositions(masm_, function()->end_position() - 1); - __ RecordJSReturn(); - // This code is generated using Assembler methods rather than Macro - // Assembler methods because it will be patched later on, and so the size - // of the generated code must be consistent. - const Register& current_sp = __ StackPointer(); - // Nothing ensures 16 bytes alignment here. - ASSERT(!current_sp.Is(csp)); - __ mov(current_sp, fp); - int no_frame_start = masm_->pc_offset(); - __ ldp(fp, lr, MemOperand(current_sp, 2 * kXRegSizeInBytes, PostIndex)); - // Drop the arguments and receiver and return. - // TODO(all): This implementation is overkill as it supports 2**31+1 - // arguments, consider how to improve it without creating a security - // hole. - __ LoadLiteral(ip0, 3 * kInstructionSize); - __ add(current_sp, current_sp, ip0); - __ ret(); - __ dc64(kXRegSizeInBytes * (info_->scope()->num_parameters() + 1)); - info_->AddNoFrameRange(no_frame_start, masm_->pc_offset()); - } - } -} - - -void FullCodeGenerator::EffectContext::Plug(Variable* var) const { - ASSERT(var->IsStackAllocated() || var->IsContextSlot()); -} - - -void FullCodeGenerator::AccumulatorValueContext::Plug(Variable* var) const { - ASSERT(var->IsStackAllocated() || var->IsContextSlot()); - codegen()->GetVar(result_register(), var); -} - - -void FullCodeGenerator::StackValueContext::Plug(Variable* var) const { - ASSERT(var->IsStackAllocated() || var->IsContextSlot()); - codegen()->GetVar(result_register(), var); - __ Push(result_register()); -} - - -void FullCodeGenerator::TestContext::Plug(Variable* var) const { - ASSERT(var->IsStackAllocated() || var->IsContextSlot()); - // For simplicity we always test the accumulator register. - codegen()->GetVar(result_register(), var); - codegen()->PrepareForBailoutBeforeSplit(condition(), false, NULL, NULL); - codegen()->DoTest(this); -} - - -void FullCodeGenerator::EffectContext::Plug(Heap::RootListIndex index) const { - // Root values have no side effects. -} - - -void FullCodeGenerator::AccumulatorValueContext::Plug( - Heap::RootListIndex index) const { - __ LoadRoot(result_register(), index); -} - - -void FullCodeGenerator::StackValueContext::Plug( - Heap::RootListIndex index) const { - __ LoadRoot(result_register(), index); - __ Push(result_register()); -} - - -void FullCodeGenerator::TestContext::Plug(Heap::RootListIndex index) const { - codegen()->PrepareForBailoutBeforeSplit(condition(), true, true_label_, - false_label_); - if (index == Heap::kUndefinedValueRootIndex || - index == Heap::kNullValueRootIndex || - index == Heap::kFalseValueRootIndex) { - if (false_label_ != fall_through_) __ B(false_label_); - } else if (index == Heap::kTrueValueRootIndex) { - if (true_label_ != fall_through_) __ B(true_label_); - } else { - __ LoadRoot(result_register(), index); - codegen()->DoTest(this); - } -} - - -void FullCodeGenerator::EffectContext::Plug(Handle lit) const { -} - - -void FullCodeGenerator::AccumulatorValueContext::Plug( - Handle lit) const { - __ Mov(result_register(), Operand(lit)); -} - - -void FullCodeGenerator::StackValueContext::Plug(Handle lit) const { - // Immediates cannot be pushed directly. - __ Mov(result_register(), Operand(lit)); - __ Push(result_register()); -} - - -void FullCodeGenerator::TestContext::Plug(Handle lit) const { - codegen()->PrepareForBailoutBeforeSplit(condition(), - true, - true_label_, - false_label_); - ASSERT(!lit->IsUndetectableObject()); // There are no undetectable literals. - if (lit->IsUndefined() || lit->IsNull() || lit->IsFalse()) { - if (false_label_ != fall_through_) __ B(false_label_); - } else if (lit->IsTrue() || lit->IsJSObject()) { - if (true_label_ != fall_through_) __ B(true_label_); - } else if (lit->IsString()) { - if (String::cast(*lit)->length() == 0) { - if (false_label_ != fall_through_) __ B(false_label_); - } else { - if (true_label_ != fall_through_) __ B(true_label_); - } - } else if (lit->IsSmi()) { - if (Smi::cast(*lit)->value() == 0) { - if (false_label_ != fall_through_) __ B(false_label_); - } else { - if (true_label_ != fall_through_) __ B(true_label_); - } - } else { - // For simplicity we always test the accumulator register. - __ Mov(result_register(), Operand(lit)); - codegen()->DoTest(this); - } -} - - -void FullCodeGenerator::EffectContext::DropAndPlug(int count, - Register reg) const { - ASSERT(count > 0); - __ Drop(count); -} - - -void FullCodeGenerator::AccumulatorValueContext::DropAndPlug( - int count, - Register reg) const { - ASSERT(count > 0); - __ Drop(count); - __ Move(result_register(), reg); -} - - -void FullCodeGenerator::StackValueContext::DropAndPlug(int count, - Register reg) const { - ASSERT(count > 0); - if (count > 1) __ Drop(count - 1); - __ Poke(reg, 0); -} - - -void FullCodeGenerator::TestContext::DropAndPlug(int count, - Register reg) const { - ASSERT(count > 0); - // For simplicity we always test the accumulator register. - __ Drop(count); - __ Mov(result_register(), reg); - codegen()->PrepareForBailoutBeforeSplit(condition(), false, NULL, NULL); - codegen()->DoTest(this); -} - - -void FullCodeGenerator::EffectContext::Plug(Label* materialize_true, - Label* materialize_false) const { - ASSERT(materialize_true == materialize_false); - __ Bind(materialize_true); -} - - -void FullCodeGenerator::AccumulatorValueContext::Plug( - Label* materialize_true, - Label* materialize_false) const { - Label done; - __ Bind(materialize_true); - __ LoadRoot(result_register(), Heap::kTrueValueRootIndex); - __ B(&done); - __ Bind(materialize_false); - __ LoadRoot(result_register(), Heap::kFalseValueRootIndex); - __ Bind(&done); -} - - -void FullCodeGenerator::StackValueContext::Plug( - Label* materialize_true, - Label* materialize_false) const { - Label done; - __ Bind(materialize_true); - __ LoadRoot(x10, Heap::kTrueValueRootIndex); - __ B(&done); - __ Bind(materialize_false); - __ LoadRoot(x10, Heap::kFalseValueRootIndex); - __ Bind(&done); - __ Push(x10); -} - - -void FullCodeGenerator::TestContext::Plug(Label* materialize_true, - Label* materialize_false) const { - ASSERT(materialize_true == true_label_); - ASSERT(materialize_false == false_label_); -} - - -void FullCodeGenerator::EffectContext::Plug(bool flag) const { -} - - -void FullCodeGenerator::AccumulatorValueContext::Plug(bool flag) const { - Heap::RootListIndex value_root_index = - flag ? Heap::kTrueValueRootIndex : Heap::kFalseValueRootIndex; - __ LoadRoot(result_register(), value_root_index); -} - - -void FullCodeGenerator::StackValueContext::Plug(bool flag) const { - Heap::RootListIndex value_root_index = - flag ? Heap::kTrueValueRootIndex : Heap::kFalseValueRootIndex; - __ LoadRoot(x10, value_root_index); - __ Push(x10); -} - - -void FullCodeGenerator::TestContext::Plug(bool flag) const { - codegen()->PrepareForBailoutBeforeSplit(condition(), - true, - true_label_, - false_label_); - if (flag) { - if (true_label_ != fall_through_) { - __ B(true_label_); - } - } else { - if (false_label_ != fall_through_) { - __ B(false_label_); - } - } -} - - -void FullCodeGenerator::DoTest(Expression* condition, - Label* if_true, - Label* if_false, - Label* fall_through) { - Handle ic = ToBooleanStub::GetUninitialized(isolate()); - CallIC(ic, condition->test_id()); - __ CompareAndSplit(result_register(), 0, ne, if_true, if_false, fall_through); -} - - -// If (cond), branch to if_true. -// If (!cond), branch to if_false. -// fall_through is used as an optimization in cases where only one branch -// instruction is necessary. -void FullCodeGenerator::Split(Condition cond, - Label* if_true, - Label* if_false, - Label* fall_through) { - if (if_false == fall_through) { - __ B(cond, if_true); - } else if (if_true == fall_through) { - ASSERT(if_false != fall_through); - __ B(InvertCondition(cond), if_false); - } else { - __ B(cond, if_true); - __ B(if_false); - } -} - - -MemOperand FullCodeGenerator::StackOperand(Variable* var) { - // Offset is negative because higher indexes are at lower addresses. - int offset = -var->index() * kXRegSizeInBytes; - // Adjust by a (parameter or local) base offset. - if (var->IsParameter()) { - offset += (info_->scope()->num_parameters() + 1) * kPointerSize; - } else { - offset += JavaScriptFrameConstants::kLocal0Offset; - } - return MemOperand(fp, offset); -} - - -MemOperand FullCodeGenerator::VarOperand(Variable* var, Register scratch) { - ASSERT(var->IsContextSlot() || var->IsStackAllocated()); - if (var->IsContextSlot()) { - int context_chain_length = scope()->ContextChainLength(var->scope()); - __ LoadContext(scratch, context_chain_length); - return ContextMemOperand(scratch, var->index()); - } else { - return StackOperand(var); - } -} - - -void FullCodeGenerator::GetVar(Register dest, Variable* var) { - // Use destination as scratch. - MemOperand location = VarOperand(var, dest); - __ Ldr(dest, location); -} - - -void FullCodeGenerator::SetVar(Variable* var, - Register src, - Register scratch0, - Register scratch1) { - ASSERT(var->IsContextSlot() || var->IsStackAllocated()); - ASSERT(!AreAliased(src, scratch0, scratch1)); - MemOperand location = VarOperand(var, scratch0); - __ Str(src, location); - - // Emit the write barrier code if the location is in the heap. - if (var->IsContextSlot()) { - // scratch0 contains the correct context. - __ RecordWriteContextSlot(scratch0, - location.offset(), - src, - scratch1, - kLRHasBeenSaved, - kDontSaveFPRegs); - } -} - - -void FullCodeGenerator::PrepareForBailoutBeforeSplit(Expression* expr, - bool should_normalize, - Label* if_true, - Label* if_false) { - // Only prepare for bailouts before splits if we're in a test - // context. Otherwise, we let the Visit function deal with the - // preparation to avoid preparing with the same AST id twice. - if (!context()->IsTest() || !info_->IsOptimizable()) return; - - // TODO(all): Investigate to see if there is something to work on here. - Label skip; - if (should_normalize) { - __ B(&skip); - } - PrepareForBailout(expr, TOS_REG); - if (should_normalize) { - __ CompareRoot(x0, Heap::kTrueValueRootIndex); - Split(eq, if_true, if_false, NULL); - __ Bind(&skip); - } -} - - -void FullCodeGenerator::EmitDebugCheckDeclarationContext(Variable* variable) { - // The variable in the declaration always resides in the current function - // context. - ASSERT_EQ(0, scope()->ContextChainLength(variable->scope())); - if (generate_debug_code_) { - // Check that we're not inside a with or catch context. - __ Ldr(x1, FieldMemOperand(cp, HeapObject::kMapOffset)); - __ CompareRoot(x1, Heap::kWithContextMapRootIndex); - __ Check(ne, kDeclarationInWithContext); - __ CompareRoot(x1, Heap::kCatchContextMapRootIndex); - __ Check(ne, kDeclarationInCatchContext); - } -} - - -void FullCodeGenerator::VisitVariableDeclaration( - VariableDeclaration* declaration) { - // If it was not possible to allocate the variable at compile time, we - // need to "declare" it at runtime to make sure it actually exists in the - // local context. - VariableProxy* proxy = declaration->proxy(); - VariableMode mode = declaration->mode(); - Variable* variable = proxy->var(); - bool hole_init = (mode == CONST) || (mode == CONST_HARMONY) || (mode == LET); - - switch (variable->location()) { - case Variable::UNALLOCATED: - globals_->Add(variable->name(), zone()); - globals_->Add(variable->binding_needs_init() - ? isolate()->factory()->the_hole_value() - : isolate()->factory()->undefined_value(), - zone()); - break; - - case Variable::PARAMETER: - case Variable::LOCAL: - if (hole_init) { - Comment cmnt(masm_, "[ VariableDeclaration"); - __ LoadRoot(x10, Heap::kTheHoleValueRootIndex); - __ Str(x10, StackOperand(variable)); - } - break; - - case Variable::CONTEXT: - if (hole_init) { - Comment cmnt(masm_, "[ VariableDeclaration"); - EmitDebugCheckDeclarationContext(variable); - __ LoadRoot(x10, Heap::kTheHoleValueRootIndex); - __ Str(x10, ContextMemOperand(cp, variable->index())); - // No write barrier since the_hole_value is in old space. - PrepareForBailoutForId(proxy->id(), NO_REGISTERS); - } - break; - - case Variable::LOOKUP: { - Comment cmnt(masm_, "[ VariableDeclaration"); - __ Mov(x2, Operand(variable->name())); - // Declaration nodes are always introduced in one of four modes. - ASSERT(IsDeclaredVariableMode(mode)); - PropertyAttributes attr = IsImmutableVariableMode(mode) ? READ_ONLY - : NONE; - __ Mov(x1, Operand(Smi::FromInt(attr))); - // Push initial value, if any. - // Note: For variables we must not push an initial value (such as - // 'undefined') because we may have a (legal) redeclaration and we - // must not destroy the current value. - if (hole_init) { - __ LoadRoot(x0, Heap::kTheHoleValueRootIndex); - __ Push(cp, x2, x1, x0); - } else { - // Pushing 0 (xzr) indicates no initial value. - __ Push(cp, x2, x1, xzr); - } - __ CallRuntime(Runtime::kDeclareContextSlot, 4); - break; - } - } -} - - -void FullCodeGenerator::VisitFunctionDeclaration( - FunctionDeclaration* declaration) { - VariableProxy* proxy = declaration->proxy(); - Variable* variable = proxy->var(); - switch (variable->location()) { - case Variable::UNALLOCATED: { - globals_->Add(variable->name(), zone()); - Handle function = - Compiler::BuildFunctionInfo(declaration->fun(), script()); - // Check for stack overflow exception. - if (function.is_null()) return SetStackOverflow(); - globals_->Add(function, zone()); - break; - } - - case Variable::PARAMETER: - case Variable::LOCAL: { - Comment cmnt(masm_, "[ Function Declaration"); - VisitForAccumulatorValue(declaration->fun()); - __ Str(result_register(), StackOperand(variable)); - break; - } - - case Variable::CONTEXT: { - Comment cmnt(masm_, "[ Function Declaration"); - EmitDebugCheckDeclarationContext(variable); - VisitForAccumulatorValue(declaration->fun()); - __ Str(result_register(), ContextMemOperand(cp, variable->index())); - int offset = Context::SlotOffset(variable->index()); - // We know that we have written a function, which is not a smi. - __ RecordWriteContextSlot(cp, - offset, - result_register(), - x2, - kLRHasBeenSaved, - kDontSaveFPRegs, - EMIT_REMEMBERED_SET, - OMIT_SMI_CHECK); - PrepareForBailoutForId(proxy->id(), NO_REGISTERS); - break; - } - - case Variable::LOOKUP: { - Comment cmnt(masm_, "[ Function Declaration"); - __ Mov(x2, Operand(variable->name())); - __ Mov(x1, Operand(Smi::FromInt(NONE))); - __ Push(cp, x2, x1); - // Push initial value for function declaration. - VisitForStackValue(declaration->fun()); - __ CallRuntime(Runtime::kDeclareContextSlot, 4); - break; - } - } -} - - -void FullCodeGenerator::VisitModuleDeclaration(ModuleDeclaration* declaration) { - Variable* variable = declaration->proxy()->var(); - ASSERT(variable->location() == Variable::CONTEXT); - ASSERT(variable->interface()->IsFrozen()); - - Comment cmnt(masm_, "[ ModuleDeclaration"); - EmitDebugCheckDeclarationContext(variable); - - // Load instance object. - __ LoadContext(x1, scope_->ContextChainLength(scope_->GlobalScope())); - __ Ldr(x1, ContextMemOperand(x1, variable->interface()->Index())); - __ Ldr(x1, ContextMemOperand(x1, Context::EXTENSION_INDEX)); - - // Assign it. - __ Str(x1, ContextMemOperand(cp, variable->index())); - // We know that we have written a module, which is not a smi. - __ RecordWriteContextSlot(cp, - Context::SlotOffset(variable->index()), - x1, - x3, - kLRHasBeenSaved, - kDontSaveFPRegs, - EMIT_REMEMBERED_SET, - OMIT_SMI_CHECK); - PrepareForBailoutForId(declaration->proxy()->id(), NO_REGISTERS); - - // Traverse info body. - Visit(declaration->module()); -} - - -void FullCodeGenerator::VisitImportDeclaration(ImportDeclaration* declaration) { - VariableProxy* proxy = declaration->proxy(); - Variable* variable = proxy->var(); - switch (variable->location()) { - case Variable::UNALLOCATED: - // TODO(rossberg) - break; - - case Variable::CONTEXT: { - Comment cmnt(masm_, "[ ImportDeclaration"); - EmitDebugCheckDeclarationContext(variable); - // TODO(rossberg) - break; - } - - case Variable::PARAMETER: - case Variable::LOCAL: - case Variable::LOOKUP: - UNREACHABLE(); - } -} - - -void FullCodeGenerator::VisitExportDeclaration(ExportDeclaration* declaration) { - // TODO(rossberg) -} - - -void FullCodeGenerator::DeclareGlobals(Handle pairs) { - // Call the runtime to declare the globals. - __ Mov(x11, Operand(pairs)); - Register flags = xzr; - if (Smi::FromInt(DeclareGlobalsFlags())) { - flags = x10; - __ Mov(flags, Operand(Smi::FromInt(DeclareGlobalsFlags()))); - } - __ Push(cp, x11, flags); - __ CallRuntime(Runtime::kDeclareGlobals, 3); - // Return value is ignored. -} - - -void FullCodeGenerator::DeclareModules(Handle descriptions) { - // Call the runtime to declare the modules. - __ Push(descriptions); - __ CallRuntime(Runtime::kDeclareModules, 1); - // Return value is ignored. -} - - -void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { - ASM_LOCATION("FullCodeGenerator::VisitSwitchStatement"); - 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()); - PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS); - - 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 '==='. - __ Peek(x1, 0); // Switch value. - - JumpPatchSite patch_site(masm_); - if (ShouldInlineSmiCase(Token::EQ_STRICT)) { - Label slow_case; - patch_site.EmitJumpIfEitherNotSmi(x0, x1, &slow_case); - __ Cmp(x1, x0); - __ B(ne, &next_test); - __ Drop(1); // Switch value is no longer needed. - __ B(clause->body_target()); - __ Bind(&slow_case); - } - - // Record position before stub call for type feedback. - SetSourcePosition(clause->position()); - Handle ic = CompareIC::GetUninitialized(isolate(), Token::EQ_STRICT); - CallIC(ic, clause->CompareId()); - patch_site.EmitPatchInfo(); - - Label skip; - __ B(&skip); - PrepareForBailout(clause, TOS_REG); - __ JumpIfNotRoot(x0, Heap::kTrueValueRootIndex, &next_test); - __ Drop(1); - __ B(clause->body_target()); - __ Bind(&skip); - - __ Cbnz(x0, &next_test); - __ Drop(1); // Switch value is no longer needed. - __ B(clause->body_target()); - } - - // Discard the test value and jump to the default if present, otherwise to - // the end of the statement. - __ Bind(&next_test); - __ Drop(1); // Switch value is no longer needed. - if (default_clause == NULL) { - __ B(nested_statement.break_label()); - } else { - __ B(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()); - PrepareForBailoutForId(clause->EntryId(), NO_REGISTERS); - VisitStatements(clause->statements()); - } - - __ Bind(nested_statement.break_label()); - PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS); -} - - -void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { - ASM_LOCATION("FullCodeGenerator::VisitForInStatement"); - Comment cmnt(masm_, "[ ForInStatement"); - int slot = stmt->ForInFeedbackSlot(); - // TODO(all): This visitor probably needs better comments and a revisit. - SetStatementPosition(stmt); - - Label loop, exit; - ForIn loop_statement(this, stmt); - increment_loop_depth(); - - // Get the object to enumerate over. If the object is null or undefined, skip - // over the loop. See ECMA-262 version 5, section 12.6.4. - VisitForAccumulatorValue(stmt->enumerable()); - __ JumpIfRoot(x0, Heap::kUndefinedValueRootIndex, &exit); - Register null_value = x15; - __ LoadRoot(null_value, Heap::kNullValueRootIndex); - __ Cmp(x0, null_value); - __ B(eq, &exit); - - PrepareForBailoutForId(stmt->PrepareId(), TOS_REG); - - // Convert the object to a JS object. - Label convert, done_convert; - __ JumpIfSmi(x0, &convert); - __ JumpIfObjectType(x0, x10, x11, FIRST_SPEC_OBJECT_TYPE, &done_convert, ge); - __ Bind(&convert); - __ Push(x0); - __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); - __ Bind(&done_convert); - __ Push(x0); - - // Check for proxies. - Label call_runtime; - STATIC_ASSERT(FIRST_JS_PROXY_TYPE == FIRST_SPEC_OBJECT_TYPE); - __ JumpIfObjectType(x0, x10, x11, LAST_JS_PROXY_TYPE, &call_runtime, le); - - // Check cache validity in generated code. This is a fast case for - // the JSObject::IsSimpleEnum cache validity checks. If we cannot - // guarantee cache validity, call the runtime system to check cache - // validity or get the property names in a fixed array. - __ CheckEnumCache(x0, null_value, x10, x11, x12, x13, &call_runtime); - - // The enum cache is valid. Load the map of the object being - // iterated over and use the cache for the iteration. - Label use_cache; - __ Ldr(x0, FieldMemOperand(x0, HeapObject::kMapOffset)); - __ B(&use_cache); - - // Get the set of properties to enumerate. - __ Bind(&call_runtime); - __ Push(x0); // Duplicate the enumerable object on the stack. - __ CallRuntime(Runtime::kGetPropertyNamesFast, 1); - - // If we got a map from the runtime call, we can do a fast - // modification check. Otherwise, we got a fixed array, and we have - // to do a slow check. - Label fixed_array, no_descriptors; - __ Ldr(x2, FieldMemOperand(x0, HeapObject::kMapOffset)); - __ JumpIfNotRoot(x2, Heap::kMetaMapRootIndex, &fixed_array); - - // We got a map in register x0. Get the enumeration cache from it. - __ Bind(&use_cache); - - __ EnumLengthUntagged(x1, x0); - __ Cbz(x1, &no_descriptors); - - __ LoadInstanceDescriptors(x0, x2); - __ Ldr(x2, FieldMemOperand(x2, DescriptorArray::kEnumCacheOffset)); - __ Ldr(x2, - FieldMemOperand(x2, DescriptorArray::kEnumCacheBridgeCacheOffset)); - - // Set up the four remaining stack slots. - __ Push(x0); // Map. - __ Mov(x0, Operand(Smi::FromInt(0))); - // Push enumeration cache, enumeration cache length (as smi) and zero. - __ SmiTag(x1); - __ Push(x2, x1, x0); - __ B(&loop); - - __ Bind(&no_descriptors); - __ Drop(1); - __ B(&exit); - - // We got a fixed array in register x0. Iterate through that. - __ Bind(&fixed_array); - - Handle feedback = Handle( - Smi::FromInt(TypeFeedbackInfo::kForInFastCaseMarker), - isolate()); - StoreFeedbackVectorSlot(slot, feedback); - __ LoadObject(x1, FeedbackVector()); - __ Mov(x10, Operand(Smi::FromInt(TypeFeedbackInfo::kForInSlowCaseMarker))); - __ Str(x10, FieldMemOperand(x1, FixedArray::OffsetOfElementAt(slot))); - - __ Mov(x1, Operand(Smi::FromInt(1))); // Smi indicates slow check. - __ Peek(x10, 0); // Get enumerated object. - STATIC_ASSERT(FIRST_JS_PROXY_TYPE == FIRST_SPEC_OBJECT_TYPE); - // TODO(all): similar check was done already. Can we avoid it here? - __ CompareObjectType(x10, x11, x12, LAST_JS_PROXY_TYPE); - ASSERT(Smi::FromInt(0) == 0); - __ CzeroX(x1, le); // Zero indicates proxy. - __ Push(x1, x0); // Smi and array - __ Ldr(x1, FieldMemOperand(x0, FixedArray::kLengthOffset)); - __ Push(x1, xzr); // Fixed array length (as smi) and initial index. - - // Generate code for doing the condition check. - PrepareForBailoutForId(stmt->BodyId(), NO_REGISTERS); - __ Bind(&loop); - // Load the current count to x0, load the length to x1. - __ PeekPair(x0, x1, 0); - __ Cmp(x0, x1); // Compare to the array length. - __ B(hs, loop_statement.break_label()); - - // Get the current entry of the array into register r3. - __ Peek(x10, 2 * kXRegSizeInBytes); - __ Add(x10, x10, Operand::UntagSmiAndScale(x0, kPointerSizeLog2)); - __ Ldr(x3, MemOperand(x10, FixedArray::kHeaderSize - kHeapObjectTag)); - - // Get the expected map from the stack or a smi in the - // permanent slow case into register x10. - __ Peek(x2, 3 * kXRegSizeInBytes); - - // Check if the expected map still matches that of the enumerable. - // If not, we may have to filter the key. - Label update_each; - __ Peek(x1, 4 * kXRegSizeInBytes); - __ Ldr(x11, FieldMemOperand(x1, HeapObject::kMapOffset)); - __ Cmp(x11, x2); - __ B(eq, &update_each); - - // For proxies, no filtering is done. - // TODO(rossberg): What if only a prototype is a proxy? Not specified yet. - STATIC_ASSERT(kSmiTag == 0); - __ Cbz(x2, &update_each); - - // Convert the entry to a string or (smi) 0 if it isn't a property - // any more. If the property has been removed while iterating, we - // just skip it. - __ Push(x1, x3); - __ InvokeBuiltin(Builtins::FILTER_KEY, CALL_FUNCTION); - __ Mov(x3, x0); - __ Cbz(x0, loop_statement.continue_label()); - - // Update the 'each' property or variable from the possibly filtered - // entry in register x3. - __ Bind(&update_each); - __ Mov(result_register(), x3); - // Perform the assignment as if via '='. - { EffectContext context(this); - EmitAssignment(stmt->each()); - } - - // 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()); - // TODO(all): We could use a callee saved register to avoid popping. - __ Pop(x0); - __ Add(x0, x0, Operand(Smi::FromInt(1))); - __ Push(x0); - - EmitBackEdgeBookkeeping(stmt, &loop); - __ B(&loop); - - // Remove the pointers stored on the stack. - __ Bind(loop_statement.break_label()); - __ Drop(5); - - // Exit and decrement the loop depth. - PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS); - __ Bind(&exit); - decrement_loop_depth(); -} - - -void FullCodeGenerator::VisitForOfStatement(ForOfStatement* stmt) { - Comment cmnt(masm_, "[ ForOfStatement"); - SetStatementPosition(stmt); - - Iteration loop_statement(this, stmt); - increment_loop_depth(); - - // var iterator = iterable[@@iterator]() - VisitForAccumulatorValue(stmt->assign_iterator()); - - // As with for-in, skip the loop if the iterator is null or undefined. - Register iterator = x0; - __ JumpIfRoot(iterator, Heap::kUndefinedValueRootIndex, - loop_statement.break_label()); - __ JumpIfRoot(iterator, Heap::kNullValueRootIndex, - loop_statement.break_label()); - - // Convert the iterator to a JS object. - Label convert, done_convert; - __ JumpIfSmi(iterator, &convert); - __ CompareObjectType(iterator, x1, x1, FIRST_SPEC_OBJECT_TYPE); - __ B(ge, &done_convert); - __ Bind(&convert); - __ Push(iterator); - __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); - __ Bind(&done_convert); - __ Push(iterator); - - // Loop entry. - __ Bind(loop_statement.continue_label()); - - // result = iterator.next() - VisitForEffect(stmt->next_result()); - - // if (result.done) break; - Label result_not_done; - VisitForControl(stmt->result_done(), - loop_statement.break_label(), - &result_not_done, - &result_not_done); - __ Bind(&result_not_done); - - // each = result.value - VisitForEffect(stmt->assign_each()); - - // Generate code for the body of the loop. - Visit(stmt->body()); - - // Check stack before looping. - PrepareForBailoutForId(stmt->BackEdgeId(), NO_REGISTERS); - EmitBackEdgeBookkeeping(stmt, loop_statement.continue_label()); - __ B(loop_statement.continue_label()); - - // Exit and decrement the loop depth. - PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS); - __ Bind(loop_statement.break_label()); - decrement_loop_depth(); -} - - -void FullCodeGenerator::EmitNewClosure(Handle info, - bool pretenure) { - // Use the fast case closure allocation code that allocates in new space for - // nested functions that don't need literals cloning. If we're running with - // the --always-opt or the --prepare-always-opt flag, we need to use the - // runtime function so that the new function we are creating here gets a - // chance to have its code optimized and doesn't just get a copy of the - // existing unoptimized code. - if (!FLAG_always_opt && - !FLAG_prepare_always_opt && - !pretenure && - scope()->is_function_scope() && - info->num_literals() == 0) { - FastNewClosureStub stub(info->language_mode(), info->is_generator()); - __ Mov(x2, Operand(info)); - __ CallStub(&stub); - } else { - __ Mov(x11, Operand(info)); - __ LoadRoot(x10, pretenure ? Heap::kTrueValueRootIndex - : Heap::kFalseValueRootIndex); - __ Push(cp, x11, x10); - __ CallRuntime(Runtime::kNewClosure, 3); - } - context()->Plug(x0); -} - - -void FullCodeGenerator::VisitVariableProxy(VariableProxy* expr) { - Comment cmnt(masm_, "[ VariableProxy"); - EmitVariableLoad(expr); -} - - -void FullCodeGenerator::EmitLoadGlobalCheckExtensions(Variable* var, - TypeofState typeof_state, - Label* slow) { - Register current = cp; - Register next = x10; - Register temp = x11; - - Scope* s = scope(); - while (s != NULL) { - if (s->num_heap_slots() > 0) { - if (s->calls_non_strict_eval()) { - // Check that extension is NULL. - __ Ldr(temp, ContextMemOperand(current, Context::EXTENSION_INDEX)); - __ Cbnz(temp, slow); - } - // Load next context in chain. - __ Ldr(next, ContextMemOperand(current, Context::PREVIOUS_INDEX)); - // Walk the rest of the chain without clobbering cp. - current = next; - } - // If no outer scope calls eval, we do not need to check more - // context extensions. - if (!s->outer_scope_calls_non_strict_eval() || s->is_eval_scope()) break; - s = s->outer_scope(); - } - - if (s->is_eval_scope()) { - Label loop, fast; - __ Mov(next, current); - - __ Bind(&loop); - // Terminate at native context. - __ Ldr(temp, FieldMemOperand(next, HeapObject::kMapOffset)); - __ JumpIfRoot(temp, Heap::kNativeContextMapRootIndex, &fast); - // Check that extension is NULL. - __ Ldr(temp, ContextMemOperand(next, Context::EXTENSION_INDEX)); - __ Cbnz(temp, slow); - // Load next context in chain. - __ Ldr(next, ContextMemOperand(next, Context::PREVIOUS_INDEX)); - __ B(&loop); - __ Bind(&fast); - } - - __ Ldr(x0, GlobalObjectMemOperand()); - __ Mov(x2, Operand(var->name())); - ContextualMode mode = (typeof_state == INSIDE_TYPEOF) ? NOT_CONTEXTUAL - : CONTEXTUAL; - CallLoadIC(mode); -} - - -MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions(Variable* var, - Label* slow) { - ASSERT(var->IsContextSlot()); - Register context = cp; - Register next = x10; - Register temp = x11; - - for (Scope* s = scope(); s != var->scope(); s = s->outer_scope()) { - if (s->num_heap_slots() > 0) { - if (s->calls_non_strict_eval()) { - // Check that extension is NULL. - __ Ldr(temp, ContextMemOperand(context, Context::EXTENSION_INDEX)); - __ Cbnz(temp, slow); - } - __ Ldr(next, ContextMemOperand(context, Context::PREVIOUS_INDEX)); - // Walk the rest of the chain without clobbering cp. - context = next; - } - } - // Check that last extension is NULL. - __ Ldr(temp, ContextMemOperand(context, Context::EXTENSION_INDEX)); - __ Cbnz(temp, slow); - - // This function is used only for loads, not stores, so it's safe to - // return an cp-based operand (the write barrier cannot be allowed to - // destroy the cp register). - return ContextMemOperand(context, var->index()); -} - - -void FullCodeGenerator::EmitDynamicLookupFastCase(Variable* var, - TypeofState typeof_state, - Label* slow, - Label* done) { - // Generate fast-case code for variables that might be shadowed by - // eval-introduced variables. Eval is used a lot without - // introducing variables. In those cases, we do not want to - // perform a runtime call for all variables in the scope - // containing the eval. - if (var->mode() == DYNAMIC_GLOBAL) { - EmitLoadGlobalCheckExtensions(var, typeof_state, slow); - __ B(done); - } else if (var->mode() == DYNAMIC_LOCAL) { - Variable* local = var->local_if_not_shadowed(); - __ Ldr(x0, ContextSlotOperandCheckExtensions(local, slow)); - if (local->mode() == LET || - local->mode() == CONST || - local->mode() == CONST_HARMONY) { - __ JumpIfNotRoot(x0, Heap::kTheHoleValueRootIndex, done); - if (local->mode() == CONST) { - __ LoadRoot(x0, Heap::kUndefinedValueRootIndex); - } else { // LET || CONST_HARMONY - __ Mov(x0, Operand(var->name())); - __ Push(x0); - __ CallRuntime(Runtime::kThrowReferenceError, 1); - } - } - __ B(done); - } -} - - -void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy) { - // Record position before possible IC call. - SetSourcePosition(proxy->position()); - Variable* var = proxy->var(); - - // Three cases: global variables, lookup variables, and all other types of - // variables. - switch (var->location()) { - case Variable::UNALLOCATED: { - Comment cmnt(masm_, "Global variable"); - // Use inline caching. Variable name is passed in x2 and the global - // object (receiver) in x0. - __ Ldr(x0, GlobalObjectMemOperand()); - __ Mov(x2, Operand(var->name())); - CallLoadIC(CONTEXTUAL); - context()->Plug(x0); - break; - } - - case Variable::PARAMETER: - case Variable::LOCAL: - case Variable::CONTEXT: { - Comment cmnt(masm_, var->IsContextSlot() - ? "Context variable" - : "Stack variable"); - if (var->binding_needs_init()) { - // var->scope() may be NULL when the proxy is located in eval code and - // refers to a potential outside binding. Currently those bindings are - // always looked up dynamically, i.e. in that case - // var->location() == LOOKUP. - // always holds. - ASSERT(var->scope() != NULL); - - // Check if the binding really needs an initialization check. The check - // can be skipped in the following situation: we have a LET or CONST - // binding in harmony mode, both the Variable and the VariableProxy have - // the same declaration scope (i.e. they are both in global code, in the - // same function or in the same eval code) and the VariableProxy is in - // the source physically located after the initializer of the variable. - // - // We cannot skip any initialization checks for CONST in non-harmony - // mode because const variables may be declared but never initialized: - // if (false) { const x; }; var y = x; - // - // The condition on the declaration scopes is a conservative check for - // nested functions that access a binding and are called before the - // binding is initialized: - // function() { f(); let x = 1; function f() { x = 2; } } - // - bool skip_init_check; - if (var->scope()->DeclarationScope() != scope()->DeclarationScope()) { - skip_init_check = false; - } else { - // Check that we always have valid source position. - ASSERT(var->initializer_position() != RelocInfo::kNoPosition); - ASSERT(proxy->position() != RelocInfo::kNoPosition); - skip_init_check = var->mode() != CONST && - var->initializer_position() < proxy->position(); - } - - if (!skip_init_check) { - // Let and const need a read barrier. - GetVar(x0, var); - Label done; - __ JumpIfNotRoot(x0, Heap::kTheHoleValueRootIndex, &done); - if (var->mode() == LET || var->mode() == CONST_HARMONY) { - // Throw a reference error when using an uninitialized let/const - // binding in harmony mode. - __ Mov(x0, Operand(var->name())); - __ Push(x0); - __ CallRuntime(Runtime::kThrowReferenceError, 1); - __ Bind(&done); - } else { - // Uninitalized const bindings outside of harmony mode are unholed. - ASSERT(var->mode() == CONST); - __ LoadRoot(x0, Heap::kUndefinedValueRootIndex); - __ Bind(&done); - } - context()->Plug(x0); - break; - } - } - context()->Plug(var); - break; - } - - case Variable::LOOKUP: { - Label done, slow; - // Generate code for loading from variables potentially shadowed by - // eval-introduced variables. - EmitDynamicLookupFastCase(var, NOT_INSIDE_TYPEOF, &slow, &done); - __ Bind(&slow); - Comment cmnt(masm_, "Lookup variable"); - __ Mov(x1, Operand(var->name())); - __ Push(cp, x1); // Context and name. - __ CallRuntime(Runtime::kLoadContextSlot, 2); - __ Bind(&done); - context()->Plug(x0); - break; - } - } -} - - -void FullCodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) { - Comment cmnt(masm_, "[ RegExpLiteral"); - Label materialized; - // Registers will be used as follows: - // x5 = materialized value (RegExp literal) - // x4 = JS function, literals array - // x3 = literal index - // x2 = RegExp pattern - // x1 = RegExp flags - // x0 = RegExp literal clone - __ Ldr(x10, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); - __ Ldr(x4, FieldMemOperand(x10, JSFunction::kLiteralsOffset)); - int literal_offset = - FixedArray::kHeaderSize + expr->literal_index() * kPointerSize; - __ Ldr(x5, FieldMemOperand(x4, literal_offset)); - __ JumpIfNotRoot(x5, Heap::kUndefinedValueRootIndex, &materialized); - - // Create regexp literal using runtime function. - // Result will be in x0. - __ Mov(x3, Operand(Smi::FromInt(expr->literal_index()))); - __ Mov(x2, Operand(expr->pattern())); - __ Mov(x1, Operand(expr->flags())); - __ Push(x4, x3, x2, x1); - __ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4); - __ Mov(x5, x0); - - __ Bind(&materialized); - int size = JSRegExp::kSize + JSRegExp::kInObjectFieldCount * kPointerSize; - Label allocated, runtime_allocate; - __ Allocate(size, x0, x2, x3, &runtime_allocate, TAG_OBJECT); - __ B(&allocated); - - __ Bind(&runtime_allocate); - __ Mov(x10, Operand(Smi::FromInt(size))); - __ Push(x5, x10); - __ CallRuntime(Runtime::kAllocateInNewSpace, 1); - __ Pop(x5); - - __ Bind(&allocated); - // After this, registers are used as follows: - // x0: Newly allocated regexp. - // x5: Materialized regexp. - // x10, x11, x12: temps. - __ CopyFields(x0, x5, CPURegList(x10, x11, x12), size / kPointerSize); - context()->Plug(x0); -} - - -void FullCodeGenerator::EmitAccessor(Expression* expression) { - if (expression == NULL) { - __ LoadRoot(x10, Heap::kNullValueRootIndex); - __ Push(x10); - } else { - VisitForStackValue(expression); - } -} - - -void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { - Comment cmnt(masm_, "[ ObjectLiteral"); - - expr->BuildConstantProperties(isolate()); - Handle constant_properties = expr->constant_properties(); - __ Ldr(x3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); - __ Ldr(x3, FieldMemOperand(x3, JSFunction::kLiteralsOffset)); - __ Mov(x2, Operand(Smi::FromInt(expr->literal_index()))); - __ Mov(x1, Operand(constant_properties)); - int flags = expr->fast_elements() - ? ObjectLiteral::kFastElements - : ObjectLiteral::kNoFlags; - flags |= expr->has_function() - ? ObjectLiteral::kHasFunction - : ObjectLiteral::kNoFlags; - __ Mov(x0, Operand(Smi::FromInt(flags))); - int properties_count = constant_properties->length() / 2; - const int max_cloned_properties = - FastCloneShallowObjectStub::kMaximumClonedProperties; - if ((FLAG_track_double_fields && expr->may_store_doubles()) || - (expr->depth() > 1) || Serializer::enabled() || - (flags != ObjectLiteral::kFastElements) || - (properties_count > max_cloned_properties)) { - __ Push(x3, x2, x1, x0); - __ CallRuntime(Runtime::kCreateObjectLiteral, 4); - } else { - FastCloneShallowObjectStub stub(properties_count); - __ CallStub(&stub); - } - - // If result_saved is true the result is on top of the stack. If - // result_saved is false the result is in x0. - bool result_saved = false; - - // Mark all computed expressions that are bound to a key that - // is shadowed by a later occurrence of the same key. For the - // marked expressions, no store code is emitted. - expr->CalculateEmitStore(zone()); - - AccessorTable accessor_table(zone()); - for (int i = 0; i < expr->properties()->length(); i++) { - ObjectLiteral::Property* property = expr->properties()->at(i); - if (property->IsCompileTimeValue()) continue; - - Literal* key = property->key(); - Expression* value = property->value(); - if (!result_saved) { - __ Push(x0); // Save result on stack - result_saved = true; - } - switch (property->kind()) { - case ObjectLiteral::Property::CONSTANT: - UNREACHABLE(); - case ObjectLiteral::Property::MATERIALIZED_LITERAL: - ASSERT(!CompileTimeValue::IsCompileTimeValue(property->value())); - // Fall through. - case ObjectLiteral::Property::COMPUTED: - if (key->value()->IsInternalizedString()) { - if (property->emit_store()) { - VisitForAccumulatorValue(value); - __ Mov(x2, Operand(key->value())); - __ Peek(x1, 0); - CallStoreIC(key->LiteralFeedbackId()); - PrepareForBailoutForId(key->id(), NO_REGISTERS); - } else { - VisitForEffect(value); - } - break; - } - // Duplicate receiver on stack. - __ Peek(x0, 0); - __ Push(x0); - VisitForStackValue(key); - VisitForStackValue(value); - if (property->emit_store()) { - __ Mov(x0, Operand(Smi::FromInt(NONE))); // PropertyAttributes - __ Push(x0); - __ CallRuntime(Runtime::kSetProperty, 4); - } else { - __ Drop(3); - } - break; - case ObjectLiteral::Property::PROTOTYPE: - // Duplicate receiver on stack. - __ Peek(x0, 0); - // TODO(jbramley): This push shouldn't be necessary if we don't call the - // runtime below. In that case, skip it. - __ Push(x0); - VisitForStackValue(value); - if (property->emit_store()) { - __ CallRuntime(Runtime::kSetPrototype, 2); - } else { - __ Drop(2); - } - break; - case ObjectLiteral::Property::GETTER: - accessor_table.lookup(key)->second->getter = value; - break; - case ObjectLiteral::Property::SETTER: - accessor_table.lookup(key)->second->setter = value; - 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) { - __ Peek(x10, 0); // Duplicate receiver. - __ Push(x10); - VisitForStackValue(it->first); - EmitAccessor(it->second->getter); - EmitAccessor(it->second->setter); - __ Mov(x10, Operand(Smi::FromInt(NONE))); - __ Push(x10); - __ CallRuntime(Runtime::kDefineOrRedefineAccessorProperty, 5); - } - - if (expr->has_function()) { - ASSERT(result_saved); - __ Peek(x0, 0); - __ Push(x0); - __ CallRuntime(Runtime::kToFastProperties, 1); - } - - if (result_saved) { - context()->PlugTOS(); - } else { - context()->Plug(x0); - } -} - - -void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { - Comment cmnt(masm_, "[ ArrayLiteral"); - - expr->BuildConstantElements(isolate()); - int flags = (expr->depth() == 1) ? ArrayLiteral::kShallowElements - : ArrayLiteral::kNoFlags; - - ZoneList* subexprs = expr->values(); - int length = subexprs->length(); - Handle constant_elements = expr->constant_elements(); - ASSERT_EQ(2, constant_elements->length()); - ElementsKind constant_elements_kind = - static_cast(Smi::cast(constant_elements->get(0))->value()); - bool has_fast_elements = IsFastObjectElementsKind(constant_elements_kind); - Handle constant_elements_values( - FixedArrayBase::cast(constant_elements->get(1))); - - AllocationSiteMode allocation_site_mode = TRACK_ALLOCATION_SITE; - if (has_fast_elements && !FLAG_allocation_site_pretenuring) { - // If the only customer of allocation sites is transitioning, then - // we can turn it off if we don't have anywhere else to transition to. - allocation_site_mode = DONT_TRACK_ALLOCATION_SITE; - } - - __ Ldr(x3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); - __ Ldr(x3, FieldMemOperand(x3, JSFunction::kLiteralsOffset)); - // TODO(jbramley): Can these Operand constructors be implicit? - __ Mov(x2, Operand(Smi::FromInt(expr->literal_index()))); - __ Mov(x1, Operand(constant_elements)); - if (has_fast_elements && constant_elements_values->map() == - isolate()->heap()->fixed_cow_array_map()) { - FastCloneShallowArrayStub stub( - FastCloneShallowArrayStub::COPY_ON_WRITE_ELEMENTS, - allocation_site_mode, - length); - __ CallStub(&stub); - __ IncrementCounter( - isolate()->counters()->cow_arrays_created_stub(), 1, x10, x11); - } else if ((expr->depth() > 1) || Serializer::enabled() || - length > FastCloneShallowArrayStub::kMaximumClonedLength) { - __ Mov(x0, Operand(Smi::FromInt(flags))); - __ Push(x3, x2, x1, x0); - __ CallRuntime(Runtime::kCreateArrayLiteral, 4); - } else { - ASSERT(IsFastSmiOrObjectElementsKind(constant_elements_kind) || - FLAG_smi_only_arrays); - FastCloneShallowArrayStub::Mode mode = - FastCloneShallowArrayStub::CLONE_ANY_ELEMENTS; - - if (has_fast_elements) { - mode = FastCloneShallowArrayStub::CLONE_ELEMENTS; - } - - FastCloneShallowArrayStub stub(mode, allocation_site_mode, length); - __ CallStub(&stub); - } - - bool result_saved = false; // Is the result saved to the stack? - - // Emit code to evaluate all the non-constant subexpressions and to store - // them into the newly cloned array. - for (int i = 0; i < length; i++) { - Expression* subexpr = subexprs->at(i); - // 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) { - __ Push(x0); - __ Push(Smi::FromInt(expr->literal_index())); - result_saved = true; - } - VisitForAccumulatorValue(subexpr); - - if (IsFastObjectElementsKind(constant_elements_kind)) { - int offset = FixedArray::kHeaderSize + (i * kPointerSize); - __ Peek(x6, kPointerSize); // Copy of array literal. - __ Ldr(x1, FieldMemOperand(x6, JSObject::kElementsOffset)); - __ Str(result_register(), FieldMemOperand(x1, offset)); - // Update the write barrier for the array store. - __ RecordWriteField(x1, offset, result_register(), x10, - kLRHasBeenSaved, kDontSaveFPRegs, - EMIT_REMEMBERED_SET, INLINE_SMI_CHECK); - } else { - __ Mov(x3, Operand(Smi::FromInt(i))); - StoreArrayLiteralElementStub stub; - __ CallStub(&stub); - } - - PrepareForBailoutForId(expr->GetIdForElement(i), NO_REGISTERS); - } - - if (result_saved) { - __ Drop(1); // literal index - context()->PlugTOS(); - } else { - context()->Plug(x0); - } -} - - -void FullCodeGenerator::VisitAssignment(Assignment* expr) { - Comment cmnt(masm_, "[ Assignment"); - // Invalid left-hand sides are rewritten to have a 'throw ReferenceError' - // on the left-hand side. - if (!expr->target()->IsValidLeftHandSide()) { - VisitForEffect(expr->target()); - return; - } - - // Left-hand side can only be a property, a global or a (parameter or local) - // slot. - enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY }; - LhsKind assign_type = VARIABLE; - Property* property = expr->target()->AsProperty(); - if (property != NULL) { - assign_type = (property->key()->IsPropertyName()) - ? NAMED_PROPERTY - : KEYED_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 accumulator. - VisitForAccumulatorValue(property->obj()); - __ Push(result_register()); - } else { - VisitForStackValue(property->obj()); - } - break; - case KEYED_PROPERTY: - if (expr->is_compound()) { - VisitForStackValue(property->obj()); - VisitForAccumulatorValue(property->key()); - __ Peek(x1, 0); - __ Push(x0); - } else { - VisitForStackValue(property->obj()); - VisitForStackValue(property->key()); - } - break; - } - - // For compound assignments we need another deoptimization point after the - // variable/property load. - if (expr->is_compound()) { - { AccumulatorValueContext context(this); - switch (assign_type) { - case VARIABLE: - EmitVariableLoad(expr->target()->AsVariableProxy()); - PrepareForBailout(expr->target(), TOS_REG); - break; - case NAMED_PROPERTY: - EmitNamedPropertyLoad(property); - PrepareForBailoutForId(property->LoadId(), TOS_REG); - break; - case KEYED_PROPERTY: - EmitKeyedPropertyLoad(property); - PrepareForBailoutForId(property->LoadId(), TOS_REG); - break; - } - } - - Token::Value op = expr->binary_op(); - __ Push(x0); // Left operand goes on the stack. - VisitForAccumulatorValue(expr->value()); - - OverwriteMode mode = expr->value()->ResultOverwriteAllowed() - ? OVERWRITE_RIGHT - : NO_OVERWRITE; - SetSourcePosition(expr->position() + 1); - AccumulatorValueContext context(this); - if (ShouldInlineSmiCase(op)) { - EmitInlineSmiBinaryOp(expr->binary_operation(), - op, - mode, - expr->target(), - expr->value()); - } else { - EmitBinaryOp(expr->binary_operation(), op, mode); - } - - // Deoptimization point in case the binary operation may have side effects. - PrepareForBailout(expr->binary_operation(), TOS_REG); - } else { - VisitForAccumulatorValue(expr->value()); - } - - // Record source position before possible IC call. - SetSourcePosition(expr->position()); - - // Store the value. - switch (assign_type) { - case VARIABLE: - EmitVariableAssignment(expr->target()->AsVariableProxy()->var(), - expr->op()); - PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); - context()->Plug(x0); - break; - case NAMED_PROPERTY: - EmitNamedPropertyAssignment(expr); - break; - case KEYED_PROPERTY: - EmitKeyedPropertyAssignment(expr); - break; - } -} - - -void FullCodeGenerator::EmitNamedPropertyLoad(Property* prop) { - SetSourcePosition(prop->position()); - Literal* key = prop->key()->AsLiteral(); - __ Mov(x2, Operand(key->value())); - // Call load IC. It has arguments receiver and property name x0 and x2. - CallLoadIC(NOT_CONTEXTUAL, prop->PropertyFeedbackId()); -} - - -void FullCodeGenerator::EmitKeyedPropertyLoad(Property* prop) { - SetSourcePosition(prop->position()); - // Call keyed load IC. It has arguments key and receiver in r0 and r1. - Handle ic = isolate()->builtins()->KeyedLoadIC_Initialize(); - CallIC(ic, prop->PropertyFeedbackId()); -} - - -void FullCodeGenerator::EmitInlineSmiBinaryOp(BinaryOperation* expr, - Token::Value op, - OverwriteMode mode, - Expression* left_expr, - Expression* right_expr) { - Label done, both_smis, stub_call; - - // Get the arguments. - Register left = x1; - Register right = x0; - Register result = x0; - __ Pop(left); - - // Perform combined smi check on both operands. - __ Orr(x10, left, right); - JumpPatchSite patch_site(masm_); - patch_site.EmitJumpIfSmi(x10, &both_smis); - - __ Bind(&stub_call); - BinaryOpICStub stub(op, mode); - { - Assembler::BlockConstPoolScope scope(masm_); - CallIC(stub.GetCode(isolate()), expr->BinaryOperationFeedbackId()); - patch_site.EmitPatchInfo(); - } - __ B(&done); - - __ Bind(&both_smis); - // Smi case. This code works in the same way as the smi-smi case in the type - // recording binary operation stub, see - // BinaryOpStub::GenerateSmiSmiOperation for comments. - // TODO(all): That doesn't exist any more. Where are the comments? - // - // The set of operations that needs to be supported here is controlled by - // FullCodeGenerator::ShouldInlineSmiCase(). - switch (op) { - case Token::SAR: - __ Ubfx(right, right, kSmiShift, 5); - __ Asr(result, left, right); - __ Bic(result, result, kSmiShiftMask); - break; - case Token::SHL: - __ Ubfx(right, right, kSmiShift, 5); - __ Lsl(result, left, right); - break; - case Token::SHR: { - Label right_not_zero; - __ Cbnz(right, &right_not_zero); - __ Tbnz(left, kXSignBit, &stub_call); - __ Bind(&right_not_zero); - __ Ubfx(right, right, kSmiShift, 5); - __ Lsr(result, left, right); - __ Bic(result, result, kSmiShiftMask); - break; - } - case Token::ADD: - __ Adds(x10, left, right); - __ B(vs, &stub_call); - __ Mov(result, x10); - break; - case Token::SUB: - __ Subs(x10, left, right); - __ B(vs, &stub_call); - __ Mov(result, x10); - break; - case Token::MUL: { - Label not_minus_zero, done; - __ Smulh(x10, left, right); - __ Cbnz(x10, ¬_minus_zero); - __ Eor(x11, left, right); - __ Tbnz(x11, kXSignBit, &stub_call); - STATIC_ASSERT(kSmiTag == 0); - __ Mov(result, x10); - __ B(&done); - __ Bind(¬_minus_zero); - __ Cls(x11, x10); - __ Cmp(x11, kXRegSize - kSmiShift); - __ B(lt, &stub_call); - __ SmiTag(result, x10); - __ Bind(&done); - break; - } - case Token::BIT_OR: - __ Orr(result, left, right); - break; - case Token::BIT_AND: - __ And(result, left, right); - break; - case Token::BIT_XOR: - __ Eor(result, left, right); - break; - default: - UNREACHABLE(); - } - - __ Bind(&done); - context()->Plug(x0); -} - - -void FullCodeGenerator::EmitBinaryOp(BinaryOperation* expr, - Token::Value op, - OverwriteMode mode) { - __ Pop(x1); - BinaryOpICStub stub(op, mode); - JumpPatchSite patch_site(masm_); // Unbound, signals no inlined smi code. - { - Assembler::BlockConstPoolScope scope(masm_); - CallIC(stub.GetCode(isolate()), expr->BinaryOperationFeedbackId()); - patch_site.EmitPatchInfo(); - } - context()->Plug(x0); -} - - -void FullCodeGenerator::EmitAssignment(Expression* expr) { - // Invalid left-hand sides are rewritten to have a 'throw - // ReferenceError' on the left-hand side. - if (!expr->IsValidLeftHandSide()) { - VisitForEffect(expr); - return; - } - - // Left-hand side can only be a property, a global or a (parameter or local) - // slot. - enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY }; - LhsKind assign_type = VARIABLE; - Property* prop = expr->AsProperty(); - if (prop != NULL) { - assign_type = (prop->key()->IsPropertyName()) - ? NAMED_PROPERTY - : KEYED_PROPERTY; - } - - switch (assign_type) { - case VARIABLE: { - Variable* var = expr->AsVariableProxy()->var(); - EffectContext context(this); - EmitVariableAssignment(var, Token::ASSIGN); - break; - } - case NAMED_PROPERTY: { - __ Push(x0); // Preserve value. - VisitForAccumulatorValue(prop->obj()); - // TODO(all): We could introduce a VisitForRegValue(reg, expr) to avoid - // this copy. - __ Mov(x1, x0); - __ Pop(x0); // Restore value. - __ Mov(x2, Operand(prop->key()->AsLiteral()->value())); - CallStoreIC(); - break; - } - case KEYED_PROPERTY: { - __ Push(x0); // Preserve value. - VisitForStackValue(prop->obj()); - VisitForAccumulatorValue(prop->key()); - __ Mov(x1, x0); - __ Pop(x2, x0); - Handle ic = is_classic_mode() - ? isolate()->builtins()->KeyedStoreIC_Initialize() - : isolate()->builtins()->KeyedStoreIC_Initialize_Strict(); - CallIC(ic); - break; - } - } - context()->Plug(x0); -} - - -void FullCodeGenerator::EmitStoreToStackLocalOrContextSlot( - Variable* var, MemOperand location) { - __ Str(result_register(), location); - if (var->IsContextSlot()) { - // RecordWrite may destroy all its register arguments. - __ Mov(x10, result_register()); - int offset = Context::SlotOffset(var->index()); - __ RecordWriteContextSlot( - x1, offset, x10, x11, kLRHasBeenSaved, kDontSaveFPRegs); - } -} - - -void FullCodeGenerator::EmitCallStoreContextSlot( - Handle name, LanguageMode mode) { - __ Mov(x11, Operand(name)); - __ Mov(x10, Operand(Smi::FromInt(mode))); - // jssp[0] : mode. - // jssp[8] : name. - // jssp[16] : context. - // jssp[24] : value. - __ Push(x0, cp, x11, x10); - __ CallRuntime(Runtime::kStoreContextSlot, 4); -} - - -void FullCodeGenerator::EmitVariableAssignment(Variable* var, - Token::Value op) { - ASM_LOCATION("FullCodeGenerator::EmitVariableAssignment"); - if (var->IsUnallocated()) { - // Global var, const, or let. - __ Mov(x2, Operand(var->name())); - __ Ldr(x1, GlobalObjectMemOperand()); - CallStoreIC(); - - } else if (op == Token::INIT_CONST) { - // Const initializers need a write barrier. - ASSERT(!var->IsParameter()); // No const parameters. - if (var->IsLookupSlot()) { - __ Push(x0); - __ Mov(x0, Operand(var->name())); - __ Push(cp, x0); // Context and name. - __ CallRuntime(Runtime::kInitializeConstContextSlot, 3); - } else { - ASSERT(var->IsStackLocal() || var->IsContextSlot()); - Label skip; - MemOperand location = VarOperand(var, x1); - __ Ldr(x10, location); - __ JumpIfNotRoot(x10, Heap::kTheHoleValueRootIndex, &skip); - EmitStoreToStackLocalOrContextSlot(var, location); - __ Bind(&skip); - } - - } else if (var->mode() == LET && op != Token::INIT_LET) { - // Non-initializing assignment to let variable needs a write barrier. - if (var->IsLookupSlot()) { - EmitCallStoreContextSlot(var->name(), language_mode()); - } else { - ASSERT(var->IsStackAllocated() || var->IsContextSlot()); - Label assign; - MemOperand location = VarOperand(var, x1); - __ Ldr(x10, location); - __ JumpIfNotRoot(x10, Heap::kTheHoleValueRootIndex, &assign); - __ Mov(x10, Operand(var->name())); - __ Push(x10); - __ CallRuntime(Runtime::kThrowReferenceError, 1); - // Perform the assignment. - __ Bind(&assign); - EmitStoreToStackLocalOrContextSlot(var, location); - } - - } else if (!var->is_const_mode() || op == Token::INIT_CONST_HARMONY) { - // Assignment to var or initializing assignment to let/const - // in harmony mode. - if (var->IsLookupSlot()) { - EmitCallStoreContextSlot(var->name(), language_mode()); - } else { - ASSERT(var->IsStackAllocated() || var->IsContextSlot()); - MemOperand location = VarOperand(var, x1); - if (FLAG_debug_code && op == Token::INIT_LET) { - __ Ldr(x10, location); - __ CompareRoot(x10, Heap::kTheHoleValueRootIndex); - __ Check(eq, kLetBindingReInitialization); - } - EmitStoreToStackLocalOrContextSlot(var, location); - } - } - // Non-initializing assignments to consts are ignored. -} - - -void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { - ASM_LOCATION("FullCodeGenerator::EmitNamedPropertyAssignment"); - // Assignment to a property, using a named store IC. - Property* prop = expr->target()->AsProperty(); - ASSERT(prop != NULL); - ASSERT(prop->key()->AsLiteral() != NULL); - - // Record source code position before IC call. - SetSourcePosition(expr->position()); - __ Mov(x2, Operand(prop->key()->AsLiteral()->value())); - __ Pop(x1); - - CallStoreIC(expr->AssignmentFeedbackId()); - - PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); - context()->Plug(x0); -} - - -void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) { - ASM_LOCATION("FullCodeGenerator::EmitKeyedPropertyAssignment"); - // Assignment to a property, using a keyed store IC. - - // Record source code position before IC call. - SetSourcePosition(expr->position()); - // TODO(all): Could we pass this in registers rather than on the stack? - __ Pop(x1, x2); // Key and object holding the property. - - Handle ic = is_classic_mode() - ? isolate()->builtins()->KeyedStoreIC_Initialize() - : isolate()->builtins()->KeyedStoreIC_Initialize_Strict(); - CallIC(ic, expr->AssignmentFeedbackId()); - - PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); - context()->Plug(x0); -} - - -void FullCodeGenerator::VisitProperty(Property* expr) { - Comment cmnt(masm_, "[ Property"); - Expression* key = expr->key(); - - if (key->IsPropertyName()) { - VisitForAccumulatorValue(expr->obj()); - EmitNamedPropertyLoad(expr); - PrepareForBailoutForId(expr->LoadId(), TOS_REG); - context()->Plug(x0); - } else { - VisitForStackValue(expr->obj()); - VisitForAccumulatorValue(expr->key()); - __ Pop(x1); - EmitKeyedPropertyLoad(expr); - context()->Plug(x0); - } -} - - -void FullCodeGenerator::CallIC(Handle code, - TypeFeedbackId ast_id) { - ic_total_count_++; - // All calls must have a predictable size in full-codegen code to ensure that - // the debugger can patch them correctly. - __ Call(code, RelocInfo::CODE_TARGET, ast_id); -} - - -// Code common for calls using the IC. -void FullCodeGenerator::EmitCallWithIC(Call* expr) { - ASM_LOCATION("EmitCallWithIC"); - - Expression* callee = expr->expression(); - ZoneList* args = expr->arguments(); - int arg_count = args->length(); - - CallFunctionFlags flags; - // Get the target function. - if (callee->IsVariableProxy()) { - { StackValueContext context(this); - EmitVariableLoad(callee->AsVariableProxy()); - PrepareForBailout(callee, NO_REGISTERS); - } - // Push undefined as receiver. This is patched in the method prologue if it - // is a classic mode method. - __ Push(isolate()->factory()->undefined_value()); - flags = NO_CALL_FUNCTION_FLAGS; - } else { - // Load the function from the receiver. - ASSERT(callee->IsProperty()); - __ Peek(x0, 0); - EmitNamedPropertyLoad(callee->AsProperty()); - PrepareForBailoutForId(callee->AsProperty()->LoadId(), TOS_REG); - // Push the target function under the receiver. - __ Pop(x10); - __ Push(x0, x10); - flags = CALL_AS_METHOD; - } - - // Load the arguments. - { PreservePositionScope scope(masm()->positions_recorder()); - for (int i = 0; i < arg_count; i++) { - VisitForStackValue(args->at(i)); - } - } - - // Record source position for debugger. - SetSourcePosition(expr->position()); - CallFunctionStub stub(arg_count, flags); - __ Peek(x1, (arg_count + 1) * kPointerSize); - __ CallStub(&stub); - - RecordJSReturnSite(expr); - - // Restore context register. - __ Ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); - - context()->DropAndPlug(1, x0); -} - - -// Code common for calls using the IC. -void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr, - Expression* key) { - // Load the key. - VisitForAccumulatorValue(key); - - Expression* callee = expr->expression(); - ZoneList* args = expr->arguments(); - int arg_count = args->length(); - - // Load the function from the receiver. - ASSERT(callee->IsProperty()); - __ Peek(x1, 0); - EmitKeyedPropertyLoad(callee->AsProperty()); - PrepareForBailoutForId(callee->AsProperty()->LoadId(), TOS_REG); - - // Push the target function under the receiver. - __ Pop(x10); - __ Push(x0, x10); - - { PreservePositionScope scope(masm()->positions_recorder()); - for (int i = 0; i < arg_count; i++) { - VisitForStackValue(args->at(i)); - } - } - - // Record source position for debugger. - SetSourcePosition(expr->position()); - CallFunctionStub stub(arg_count, CALL_AS_METHOD); - __ Peek(x1, (arg_count + 1) * kPointerSize); - __ CallStub(&stub); - - RecordJSReturnSite(expr); - // Restore context register. - __ Ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); - - context()->DropAndPlug(1, x0); -} - - -void FullCodeGenerator::EmitCallWithStub(Call* expr) { - // Code common for calls using the call stub. - ZoneList* args = expr->arguments(); - int arg_count = args->length(); - { PreservePositionScope scope(masm()->positions_recorder()); - for (int i = 0; i < arg_count; i++) { - VisitForStackValue(args->at(i)); - } - } - // Record source position for debugger. - SetSourcePosition(expr->position()); - - Handle uninitialized = - TypeFeedbackInfo::UninitializedSentinel(isolate()); - StoreFeedbackVectorSlot(expr->CallFeedbackSlot(), uninitialized); - __ LoadObject(x2, FeedbackVector()); - __ Mov(x3, Operand(Smi::FromInt(expr->CallFeedbackSlot()))); - - // Record call targets in unoptimized code. - CallFunctionStub stub(arg_count, RECORD_CALL_TARGET); - __ Peek(x1, (arg_count + 1) * kXRegSizeInBytes); - __ CallStub(&stub); - RecordJSReturnSite(expr); - // Restore context register. - __ Ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); - context()->DropAndPlug(1, x0); -} - - -void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) { - ASM_LOCATION("FullCodeGenerator::EmitResolvePossiblyDirectEval"); - // Prepare to push a copy of the first argument or undefined if it doesn't - // exist. - if (arg_count > 0) { - __ Peek(x10, arg_count * kXRegSizeInBytes); - } else { - __ LoadRoot(x10, Heap::kUndefinedValueRootIndex); - } - - // Prepare to push the receiver of the enclosing function. - int receiver_offset = 2 + info_->scope()->num_parameters(); - __ Ldr(x11, MemOperand(fp, receiver_offset * kPointerSize)); - - // Push. - __ Push(x10, x11); - - // Prepare to push the language mode. - __ Mov(x10, Operand(Smi::FromInt(language_mode()))); - // Prepare to push the start position of the scope the calls resides in. - __ Mov(x11, Operand(Smi::FromInt(scope()->start_position()))); - - // Push. - __ Push(x10, x11); - - // Do the runtime call. - __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 5); -} - - -void FullCodeGenerator::VisitCall(Call* expr) { -#ifdef DEBUG - // We want to verify that RecordJSReturnSite gets called on all paths - // through this function. Avoid early returns. - expr->return_is_recorded_ = false; -#endif - - Comment cmnt(masm_, "[ Call"); - Expression* callee = expr->expression(); - Call::CallType call_type = expr->GetCallType(isolate()); - - if (call_type == Call::POSSIBLY_EVAL_CALL) { - // In a call to eval, we first call %ResolvePossiblyDirectEval to - // resolve the function we need to call and the receiver of the - // call. Then we call the resolved function using the given - // arguments. - ZoneList* args = expr->arguments(); - int arg_count = args->length(); - - { - PreservePositionScope pos_scope(masm()->positions_recorder()); - VisitForStackValue(callee); - __ LoadRoot(x10, Heap::kUndefinedValueRootIndex); - __ Push(x10); // Reserved receiver slot. - - // Push the arguments. - for (int i = 0; i < arg_count; i++) { - VisitForStackValue(args->at(i)); - } - - // Push a copy of the function (found below the arguments) and - // resolve eval. - __ Peek(x10, (arg_count + 1) * kPointerSize); - __ Push(x10); - EmitResolvePossiblyDirectEval(arg_count); - - // The runtime call returns a pair of values in x0 (function) and - // x1 (receiver). Touch up the stack with the right values. - __ PokePair(x1, x0, arg_count * kPointerSize); - } - - // Record source position for debugger. - SetSourcePosition(expr->position()); - - // Call the evaluated function. - CallFunctionStub stub(arg_count, NO_CALL_FUNCTION_FLAGS); - __ Peek(x1, (arg_count + 1) * kXRegSizeInBytes); - __ CallStub(&stub); - RecordJSReturnSite(expr); - // Restore context register. - __ Ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); - context()->DropAndPlug(1, x0); - - } else if (call_type == Call::GLOBAL_CALL) { - EmitCallWithIC(expr); - - } else if (call_type == Call::LOOKUP_SLOT_CALL) { - // Call to a lookup slot (dynamically introduced variable). - VariableProxy* proxy = callee->AsVariableProxy(); - Label slow, done; - - { PreservePositionScope scope(masm()->positions_recorder()); - // Generate code for loading from variables potentially shadowed - // by eval-introduced variables. - EmitDynamicLookupFastCase(proxy->var(), NOT_INSIDE_TYPEOF, &slow, &done); - } - - __ Bind(&slow); - // Call the runtime to find the function to call (returned in x0) - // and the object holding it (returned in x1). - __ Push(context_register()); - __ Mov(x10, Operand(proxy->name())); - __ Push(x10); - __ CallRuntime(Runtime::kLoadContextSlot, 2); - __ Push(x0, x1); // Receiver, function. - - // If fast case code has been generated, emit code to push the - // function and receiver and have the slow path jump around this - // code. - if (done.is_linked()) { - Label call; - __ B(&call); - __ Bind(&done); - // Push function. - __ Push(x0); - // The receiver is implicitly the global receiver. Indicate this - // by passing the undefined to the call function stub. - __ LoadRoot(x1, Heap::kUndefinedValueRootIndex); - __ Push(x1); - __ Bind(&call); - } - - // The receiver is either the global receiver or an object found - // by LoadContextSlot. - EmitCallWithStub(expr); - } else if (call_type == Call::PROPERTY_CALL) { - Property* property = callee->AsProperty(); - { PreservePositionScope scope(masm()->positions_recorder()); - VisitForStackValue(property->obj()); - } - if (property->key()->IsPropertyName()) { - EmitCallWithIC(expr); - } else { - EmitKeyedCallWithIC(expr, property->key()); - } - - } else { - ASSERT(call_type == Call::OTHER_CALL); - // Call to an arbitrary expression not handled specially above. - { PreservePositionScope scope(masm()->positions_recorder()); - VisitForStackValue(callee); - } - __ LoadRoot(x1, Heap::kUndefinedValueRootIndex); - __ Push(x1); - // Emit function call. - EmitCallWithStub(expr); - } - -#ifdef DEBUG - // RecordJSReturnSite should have been called. - ASSERT(expr->return_is_recorded_); -#endif -} - - -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. - 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. - SetSourcePosition(expr->position()); - - // Load function and argument count into x1 and x0. - __ Mov(x0, arg_count); - __ Peek(x1, arg_count * kXRegSizeInBytes); - - // Record call targets in unoptimized code. - Handle uninitialized = - TypeFeedbackInfo::UninitializedSentinel(isolate()); - StoreFeedbackVectorSlot(expr->CallNewFeedbackSlot(), uninitialized); - __ LoadObject(x2, FeedbackVector()); - __ Mov(x3, Operand(Smi::FromInt(expr->CallNewFeedbackSlot()))); - - CallConstructStub stub(RECORD_CALL_TARGET); - __ Call(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL); - PrepareForBailoutForId(expr->ReturnId(), TOS_REG); - context()->Plug(x0); -} - - -void FullCodeGenerator::EmitIsSmi(CallRuntime* expr) { - ZoneList* args = expr->arguments(); - ASSERT(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); - - PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); - __ TestAndSplit(x0, kSmiTagMask, if_true, if_false, fall_through); - - context()->Plug(if_true, if_false); -} - - -void FullCodeGenerator::EmitIsNonNegativeSmi(CallRuntime* expr) { - ZoneList* args = expr->arguments(); - ASSERT(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); - - PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); - __ TestAndSplit(x0, kSmiTagMask | (0x80000000UL << kSmiShift), if_true, - if_false, fall_through); - - context()->Plug(if_true, if_false); -} - - -void FullCodeGenerator::EmitIsObject(CallRuntime* expr) { - ZoneList* args = expr->arguments(); - ASSERT(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(x0, if_false); - __ JumpIfRoot(x0, Heap::kNullValueRootIndex, if_true); - __ Ldr(x10, FieldMemOperand(x0, HeapObject::kMapOffset)); - // Undetectable objects behave like undefined when tested with typeof. - __ Ldrb(x11, FieldMemOperand(x10, Map::kBitFieldOffset)); - __ Tbnz(x11, Map::kIsUndetectable, if_false); - __ Ldrb(x12, FieldMemOperand(x10, Map::kInstanceTypeOffset)); - __ Cmp(x12, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE); - __ B(lt, if_false); - __ Cmp(x12, LAST_NONCALLABLE_SPEC_OBJECT_TYPE); - PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); - Split(le, if_true, if_false, fall_through); - - context()->Plug(if_true, if_false); -} - - -void FullCodeGenerator::EmitIsSpecObject(CallRuntime* expr) { - ZoneList* args = expr->arguments(); - ASSERT(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(x0, if_false); - __ CompareObjectType(x0, x10, x11, FIRST_SPEC_OBJECT_TYPE); - PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); - Split(ge, if_true, if_false, fall_through); - - context()->Plug(if_true, if_false); -} - - -void FullCodeGenerator::EmitIsUndetectableObject(CallRuntime* expr) { - ASM_LOCATION("FullCodeGenerator::EmitIsUndetectableObject"); - ZoneList* args = expr->arguments(); - ASSERT(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(x0, if_false); - __ Ldr(x10, FieldMemOperand(x0, HeapObject::kMapOffset)); - __ Ldrb(x11, FieldMemOperand(x10, Map::kBitFieldOffset)); - __ Tst(x11, 1 << Map::kIsUndetectable); - PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); - Split(ne, if_true, if_false, fall_through); - - context()->Plug(if_true, if_false); -} - - -void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf( - CallRuntime* expr) { - ZoneList* args = expr->arguments(); - ASSERT(args->length() == 1); - VisitForAccumulatorValue(args->at(0)); - - Label materialize_true, materialize_false, skip_lookup; - Label* if_true = NULL; - Label* if_false = NULL; - Label* fall_through = NULL; - context()->PrepareTest(&materialize_true, &materialize_false, - &if_true, &if_false, &fall_through); - - Register object = x0; - __ AssertNotSmi(object); - - Register map = x10; - Register bitfield2 = x11; - __ Ldr(map, FieldMemOperand(object, HeapObject::kMapOffset)); - __ Ldrb(bitfield2, FieldMemOperand(map, Map::kBitField2Offset)); - __ Tbnz(bitfield2, Map::kStringWrapperSafeForDefaultValueOf, &skip_lookup); - - // Check for fast case object. Generate false result for slow case object. - Register props = x12; - Register props_map = x12; - Register hash_table_map = x13; - __ Ldr(props, FieldMemOperand(object, JSObject::kPropertiesOffset)); - __ Ldr(props_map, FieldMemOperand(props, HeapObject::kMapOffset)); - __ LoadRoot(hash_table_map, Heap::kHashTableMapRootIndex); - __ Cmp(props_map, hash_table_map); - __ B(eq, if_false); - - // Look for valueOf name in the descriptor array, and indicate false if found. - // Since we omit an enumeration index check, if it is added via a transition - // that shares its descriptor array, this is a false positive. - Label loop, done; - - // Skip loop if no descriptors are valid. - Register descriptors = x12; - Register descriptors_length = x13; - __ NumberOfOwnDescriptors(descriptors_length, map); - __ Cbz(descriptors_length, &done); - - __ LoadInstanceDescriptors(map, descriptors); - - // Calculate the end of the descriptor array. - Register descriptors_end = x14; - __ Mov(x15, DescriptorArray::kDescriptorSize); - __ Mul(descriptors_length, descriptors_length, x15); - // Calculate location of the first key name. - __ Add(descriptors, descriptors, - DescriptorArray::kFirstOffset - kHeapObjectTag); - // Calculate the end of the descriptor array. - __ Add(descriptors_end, descriptors, - Operand(descriptors_length, LSL, kPointerSizeLog2)); - - // Loop through all the keys in the descriptor array. If one of these is the - // string "valueOf" the result is false. - Register valueof_string = x1; - int descriptor_size = DescriptorArray::kDescriptorSize * kPointerSize; - __ Mov(valueof_string, Operand(isolate()->factory()->value_of_string())); - __ Bind(&loop); - __ Ldr(x15, MemOperand(descriptors, descriptor_size, PostIndex)); - __ Cmp(x15, valueof_string); - __ B(eq, if_false); - __ Cmp(descriptors, descriptors_end); - __ B(ne, &loop); - - __ Bind(&done); - - // Set the bit in the map to indicate that there is no local valueOf field. - __ Ldrb(x2, FieldMemOperand(map, Map::kBitField2Offset)); - __ Orr(x2, x2, 1 << Map::kStringWrapperSafeForDefaultValueOf); - __ Strb(x2, FieldMemOperand(map, Map::kBitField2Offset)); - - __ Bind(&skip_lookup); - - // If a valueOf property is not found on the object check that its prototype - // is the unmodified String prototype. If not result is false. - Register prototype = x1; - Register global_idx = x2; - Register native_context = x2; - Register string_proto = x3; - Register proto_map = x4; - __ Ldr(prototype, FieldMemOperand(map, Map::kPrototypeOffset)); - __ JumpIfSmi(prototype, if_false); - __ Ldr(proto_map, FieldMemOperand(prototype, HeapObject::kMapOffset)); - __ Ldr(global_idx, GlobalObjectMemOperand()); - __ Ldr(native_context, - FieldMemOperand(global_idx, GlobalObject::kNativeContextOffset)); - __ Ldr(string_proto, - ContextMemOperand(native_context, - Context::STRING_FUNCTION_PROTOTYPE_MAP_INDEX)); - __ Cmp(proto_map, string_proto); - - PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); - Split(eq, if_true, if_false, fall_through); - - context()->Plug(if_true, if_false); -} - - -void FullCodeGenerator::EmitIsFunction(CallRuntime* expr) { - ZoneList* args = expr->arguments(); - ASSERT(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(x0, if_false); - __ CompareObjectType(x0, x10, x11, JS_FUNCTION_TYPE); - PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); - Split(eq, if_true, if_false, fall_through); - - context()->Plug(if_true, if_false); -} - - -void FullCodeGenerator::EmitIsMinusZero(CallRuntime* expr) { - ZoneList* args = expr->arguments(); - ASSERT(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); - - // Only a HeapNumber can be -0.0, so return false if we have something else. - __ CheckMap(x0, x1, Heap::kHeapNumberMapRootIndex, if_false, DO_SMI_CHECK); - - // Test the bit pattern. - __ Ldr(x10, FieldMemOperand(x0, HeapNumber::kValueOffset)); - __ Cmp(x10, 1); // Set V on 0x8000000000000000. - - PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); - Split(vs, if_true, if_false, fall_through); - - context()->Plug(if_true, if_false); -} - - -void FullCodeGenerator::EmitIsArray(CallRuntime* expr) { - ZoneList* args = expr->arguments(); - ASSERT(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(x0, if_false); - __ CompareObjectType(x0, x10, x11, JS_ARRAY_TYPE); - PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); - Split(eq, if_true, if_false, fall_through); - - context()->Plug(if_true, if_false); -} - - -void FullCodeGenerator::EmitIsRegExp(CallRuntime* expr) { - ZoneList* args = expr->arguments(); - ASSERT(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(x0, if_false); - __ CompareObjectType(x0, x10, x11, JS_REGEXP_TYPE); - PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); - Split(eq, if_true, if_false, fall_through); - - context()->Plug(if_true, if_false); -} - - - -void FullCodeGenerator::EmitIsConstructCall(CallRuntime* expr) { - ASSERT(expr->arguments()->length() == 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); - - // Get the frame pointer for the calling frame. - __ Ldr(x2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); - - // Skip the arguments adaptor frame if it exists. - Label check_frame_marker; - __ Ldr(x1, MemOperand(x2, StandardFrameConstants::kContextOffset)); - __ Cmp(x1, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); - __ B(ne, &check_frame_marker); - __ Ldr(x2, MemOperand(x2, StandardFrameConstants::kCallerFPOffset)); - - // Check the marker in the calling frame. - __ Bind(&check_frame_marker); - __ Ldr(x1, MemOperand(x2, StandardFrameConstants::kMarkerOffset)); - __ Cmp(x1, Operand(Smi::FromInt(StackFrame::CONSTRUCT))); - PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); - Split(eq, if_true, if_false, fall_through); - - context()->Plug(if_true, if_false); -} - - -void FullCodeGenerator::EmitObjectEquals(CallRuntime* expr) { - ZoneList* args = expr->arguments(); - ASSERT(args->length() == 2); - - // Load the two objects into registers and perform the comparison. - VisitForStackValue(args->at(0)); - VisitForAccumulatorValue(args->at(1)); - - 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); - - __ Pop(x1); - __ Cmp(x0, x1); - PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); - Split(eq, if_true, if_false, fall_through); - - context()->Plug(if_true, if_false); -} - - -void FullCodeGenerator::EmitArguments(CallRuntime* expr) { - ZoneList* args = expr->arguments(); - ASSERT(args->length() == 1); - - // ArgumentsAccessStub expects the key in x1. - VisitForAccumulatorValue(args->at(0)); - __ Mov(x1, x0); - __ Mov(x0, Operand(Smi::FromInt(info_->scope()->num_parameters()))); - ArgumentsAccessStub stub(ArgumentsAccessStub::READ_ELEMENT); - __ CallStub(&stub); - context()->Plug(x0); -} - - -void FullCodeGenerator::EmitArgumentsLength(CallRuntime* expr) { - ASSERT(expr->arguments()->length() == 0); - Label exit; - // Get the number of formal parameters. - __ Mov(x0, Operand(Smi::FromInt(info_->scope()->num_parameters()))); - - // Check if the calling frame is an arguments adaptor frame. - __ Ldr(x12, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); - __ Ldr(x13, MemOperand(x12, StandardFrameConstants::kContextOffset)); - __ Cmp(x13, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); - __ B(ne, &exit); - - // Arguments adaptor case: Read the arguments length from the - // adaptor frame. - __ Ldr(x0, MemOperand(x12, ArgumentsAdaptorFrameConstants::kLengthOffset)); - - __ Bind(&exit); - context()->Plug(x0); -} - - -void FullCodeGenerator::EmitClassOf(CallRuntime* expr) { - ASM_LOCATION("FullCodeGenerator::EmitClassOf"); - ZoneList* args = expr->arguments(); - ASSERT(args->length() == 1); - Label done, null, function, non_function_constructor; - - VisitForAccumulatorValue(args->at(0)); - - // If the object is a smi, we return null. - __ JumpIfSmi(x0, &null); - - // Check that the object is a JS object but take special care of JS - // functions to make sure they have 'Function' as their class. - // Assume that there are only two callable types, and one of them is at - // either end of the type range for JS object types. Saves extra comparisons. - STATIC_ASSERT(NUM_OF_CALLABLE_SPEC_OBJECT_TYPES == 2); - __ CompareObjectType(x0, x10, x11, FIRST_SPEC_OBJECT_TYPE); - // x10: object's map. - // x11: object's type. - __ B(lt, &null); - STATIC_ASSERT(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE == - FIRST_SPEC_OBJECT_TYPE + 1); - __ B(eq, &function); - - __ Cmp(x11, LAST_SPEC_OBJECT_TYPE); - STATIC_ASSERT(LAST_NONCALLABLE_SPEC_OBJECT_TYPE == - LAST_SPEC_OBJECT_TYPE - 1); - __ B(eq, &function); - // Assume that there is no larger type. - STATIC_ASSERT(LAST_NONCALLABLE_SPEC_OBJECT_TYPE == LAST_TYPE - 1); - - // Check if the constructor in the map is a JS function. - __ Ldr(x12, FieldMemOperand(x10, Map::kConstructorOffset)); - __ JumpIfNotObjectType(x12, x13, x14, JS_FUNCTION_TYPE, - &non_function_constructor); - - // x12 now contains the constructor function. Grab the - // instance class name from there. - __ Ldr(x13, FieldMemOperand(x12, JSFunction::kSharedFunctionInfoOffset)); - __ Ldr(x0, - FieldMemOperand(x13, SharedFunctionInfo::kInstanceClassNameOffset)); - __ B(&done); - - // Functions have class 'Function'. - __ Bind(&function); - __ LoadRoot(x0, Heap::kfunction_class_stringRootIndex); - __ B(&done); - - // Objects with a non-function constructor have class 'Object'. - __ Bind(&non_function_constructor); - __ LoadRoot(x0, Heap::kObject_stringRootIndex); - __ B(&done); - - // Non-JS objects have class null. - __ Bind(&null); - __ LoadRoot(x0, Heap::kNullValueRootIndex); - - // All done. - __ Bind(&done); - - context()->Plug(x0); -} - - -void FullCodeGenerator::EmitLog(CallRuntime* expr) { - // Conditionally generate a log call. - // Args: - // 0 (literal string): The type of logging (corresponds to the flags). - // This is used to determine whether or not to generate the log call. - // 1 (string): Format string. Access the string at argument index 2 - // with '%2s' (see Logger::LogRuntime for all the formats). - // 2 (array): Arguments to the format string. - ZoneList* args = expr->arguments(); - ASSERT_EQ(args->length(), 3); - if (CodeGenerator::ShouldGenerateLog(isolate(), args->at(0))) { - VisitForStackValue(args->at(1)); - VisitForStackValue(args->at(2)); - __ CallRuntime(Runtime::kLog, 2); - } - - // Finally, we're expected to leave a value on the top of the stack. - __ LoadRoot(x0, Heap::kUndefinedValueRootIndex); - context()->Plug(x0); -} - - -void FullCodeGenerator::EmitSubString(CallRuntime* expr) { - // Load the arguments on the stack and call the stub. - SubStringStub stub; - ZoneList* args = expr->arguments(); - ASSERT(args->length() == 3); - VisitForStackValue(args->at(0)); - VisitForStackValue(args->at(1)); - VisitForStackValue(args->at(2)); - __ CallStub(&stub); - context()->Plug(x0); -} - - -void FullCodeGenerator::EmitRegExpExec(CallRuntime* expr) { - // Load the arguments on the stack and call the stub. - RegExpExecStub stub; - ZoneList* args = expr->arguments(); - ASSERT(args->length() == 4); - VisitForStackValue(args->at(0)); - VisitForStackValue(args->at(1)); - VisitForStackValue(args->at(2)); - VisitForStackValue(args->at(3)); - __ CallStub(&stub); - context()->Plug(x0); -} - - -void FullCodeGenerator::EmitValueOf(CallRuntime* expr) { - ASM_LOCATION("FullCodeGenerator::EmitValueOf"); - ZoneList* args = expr->arguments(); - ASSERT(args->length() == 1); - VisitForAccumulatorValue(args->at(0)); // Load the object. - - Label done; - // If the object is a smi return the object. - __ JumpIfSmi(x0, &done); - // If the object is not a value type, return the object. - __ JumpIfNotObjectType(x0, x10, x11, JS_VALUE_TYPE, &done); - __ Ldr(x0, FieldMemOperand(x0, JSValue::kValueOffset)); - - __ Bind(&done); - context()->Plug(x0); -} - - -void FullCodeGenerator::EmitDateField(CallRuntime* expr) { - ZoneList* args = expr->arguments(); - ASSERT(args->length() == 2); - ASSERT_NE(NULL, args->at(1)->AsLiteral()); - Smi* index = Smi::cast(*(args->at(1)->AsLiteral()->value())); - - VisitForAccumulatorValue(args->at(0)); // Load the object. - - Label runtime, done, not_date_object; - Register object = x0; - Register result = x0; - Register stamp_addr = x10; - Register stamp_cache = x11; - - __ JumpIfSmi(object, ¬_date_object); - __ JumpIfNotObjectType(object, x10, x10, JS_DATE_TYPE, ¬_date_object); - - if (index->value() == 0) { - __ Ldr(result, FieldMemOperand(object, JSDate::kValueOffset)); - __ B(&done); - } else { - if (index->value() < JSDate::kFirstUncachedField) { - ExternalReference stamp = ExternalReference::date_cache_stamp(isolate()); - __ Mov(x10, Operand(stamp)); - __ Ldr(stamp_addr, MemOperand(x10)); - __ Ldr(stamp_cache, FieldMemOperand(object, JSDate::kCacheStampOffset)); - __ Cmp(stamp_addr, stamp_cache); - __ B(ne, &runtime); - __ Ldr(result, FieldMemOperand(object, JSDate::kValueOffset + - kPointerSize * index->value())); - __ B(&done); - } - - __ Bind(&runtime); - __ Mov(x1, Operand(index)); - __ CallCFunction(ExternalReference::get_date_field_function(isolate()), 2); - __ B(&done); - } - - __ Bind(¬_date_object); - __ CallRuntime(Runtime::kThrowNotDateError, 0); - __ Bind(&done); - context()->Plug(x0); -} - - -void FullCodeGenerator::EmitOneByteSeqStringSetChar(CallRuntime* expr) { - ZoneList* args = expr->arguments(); - ASSERT_EQ(3, args->length()); - - Register string = x0; - Register index = x1; - Register value = x2; - Register scratch = x10; - - VisitForStackValue(args->at(1)); // index - VisitForStackValue(args->at(2)); // value - VisitForAccumulatorValue(args->at(0)); // string - __ Pop(value, index); - - if (FLAG_debug_code) { - __ AssertSmi(value, kNonSmiValue); - __ AssertSmi(index, kNonSmiIndex); - static const uint32_t one_byte_seq_type = kSeqStringTag | kOneByteStringTag; - __ EmitSeqStringSetCharCheck(string, index, kIndexIsSmi, scratch, - one_byte_seq_type); - } - - __ Add(scratch, string, SeqOneByteString::kHeaderSize - kHeapObjectTag); - __ SmiUntag(value); - __ SmiUntag(index); - __ Strb(value, MemOperand(scratch, index)); - context()->Plug(string); -} - - -void FullCodeGenerator::EmitTwoByteSeqStringSetChar(CallRuntime* expr) { - ZoneList* args = expr->arguments(); - ASSERT_EQ(3, args->length()); - - Register string = x0; - Register index = x1; - Register value = x2; - Register scratch = x10; - - VisitForStackValue(args->at(1)); // index - VisitForStackValue(args->at(2)); // value - VisitForAccumulatorValue(args->at(0)); // string - __ Pop(value, index); - - if (FLAG_debug_code) { - __ AssertSmi(value, kNonSmiValue); - __ AssertSmi(index, kNonSmiIndex); - static const uint32_t two_byte_seq_type = kSeqStringTag | kTwoByteStringTag; - __ EmitSeqStringSetCharCheck(string, index, kIndexIsSmi, scratch, - two_byte_seq_type); - } - - __ Add(scratch, string, SeqTwoByteString::kHeaderSize - kHeapObjectTag); - __ SmiUntag(value); - __ SmiUntag(index); - __ Strh(value, MemOperand(scratch, index, LSL, 1)); - context()->Plug(string); -} - - -void FullCodeGenerator::EmitMathPow(CallRuntime* expr) { - // Load the arguments on the stack and call the MathPow stub. - ZoneList* args = expr->arguments(); - ASSERT(args->length() == 2); - VisitForStackValue(args->at(0)); - VisitForStackValue(args->at(1)); - MathPowStub stub(MathPowStub::ON_STACK); - __ CallStub(&stub); - context()->Plug(x0); -} - - -void FullCodeGenerator::EmitSetValueOf(CallRuntime* expr) { - ZoneList* args = expr->arguments(); - ASSERT(args->length() == 2); - VisitForStackValue(args->at(0)); // Load the object. - VisitForAccumulatorValue(args->at(1)); // Load the value. - __ Pop(x1); - // x0 = value. - // x1 = object. - - Label done; - // If the object is a smi, return the value. - __ JumpIfSmi(x1, &done); - - // If the object is not a value type, return the value. - __ JumpIfNotObjectType(x1, x10, x11, JS_VALUE_TYPE, &done); - - // Store the value. - __ Str(x0, FieldMemOperand(x1, JSValue::kValueOffset)); - // Update the write barrier. Save the value as it will be - // overwritten by the write barrier code and is needed afterward. - __ Mov(x10, x0); - __ RecordWriteField( - x1, JSValue::kValueOffset, x10, x11, kLRHasBeenSaved, kDontSaveFPRegs); - - __ Bind(&done); - context()->Plug(x0); -} - - -void FullCodeGenerator::EmitNumberToString(CallRuntime* expr) { - ZoneList* args = expr->arguments(); - ASSERT_EQ(args->length(), 1); - - // Load the argument into x0 and call the stub. - VisitForAccumulatorValue(args->at(0)); - - NumberToStringStub stub; - __ CallStub(&stub); - context()->Plug(x0); -} - - -void FullCodeGenerator::EmitStringCharFromCode(CallRuntime* expr) { - ZoneList* args = expr->arguments(); - ASSERT(args->length() == 1); - - VisitForAccumulatorValue(args->at(0)); - - Label done; - Register code = x0; - Register result = x1; - - StringCharFromCodeGenerator generator(code, result); - generator.GenerateFast(masm_); - __ B(&done); - - NopRuntimeCallHelper call_helper; - generator.GenerateSlow(masm_, call_helper); - - __ Bind(&done); - context()->Plug(result); -} - - -void FullCodeGenerator::EmitStringCharCodeAt(CallRuntime* expr) { - ZoneList* args = expr->arguments(); - ASSERT(args->length() == 2); - - VisitForStackValue(args->at(0)); - VisitForAccumulatorValue(args->at(1)); - - Register object = x1; - Register index = x0; - Register result = x3; - - __ Pop(object); - - Label need_conversion; - Label index_out_of_range; - Label done; - StringCharCodeAtGenerator generator(object, - index, - result, - &need_conversion, - &need_conversion, - &index_out_of_range, - STRING_INDEX_IS_NUMBER); - generator.GenerateFast(masm_); - __ B(&done); - - __ Bind(&index_out_of_range); - // When the index is out of range, the spec requires us to return NaN. - __ LoadRoot(result, Heap::kNanValueRootIndex); - __ B(&done); - - __ Bind(&need_conversion); - // Load the undefined value into the result register, which will - // trigger conversion. - __ LoadRoot(result, Heap::kUndefinedValueRootIndex); - __ B(&done); - - NopRuntimeCallHelper call_helper; - generator.GenerateSlow(masm_, call_helper); - - __ Bind(&done); - context()->Plug(result); -} - - -void FullCodeGenerator::EmitStringCharAt(CallRuntime* expr) { - ZoneList* args = expr->arguments(); - ASSERT(args->length() == 2); - - VisitForStackValue(args->at(0)); - VisitForAccumulatorValue(args->at(1)); - - Register object = x1; - Register index = x0; - Register result = x0; - - __ Pop(object); - - Label need_conversion; - Label index_out_of_range; - Label done; - StringCharAtGenerator generator(object, - index, - x3, - result, - &need_conversion, - &need_conversion, - &index_out_of_range, - STRING_INDEX_IS_NUMBER); - generator.GenerateFast(masm_); - __ B(&done); - - __ Bind(&index_out_of_range); - // When the index is out of range, the spec requires us to return - // the empty string. - __ LoadRoot(result, Heap::kempty_stringRootIndex); - __ B(&done); - - __ Bind(&need_conversion); - // Move smi zero into the result register, which will trigger conversion. - __ Mov(result, Operand(Smi::FromInt(0))); - __ B(&done); - - NopRuntimeCallHelper call_helper; - generator.GenerateSlow(masm_, call_helper); - - __ Bind(&done); - context()->Plug(result); -} - - -void FullCodeGenerator::EmitStringAdd(CallRuntime* expr) { - ASM_LOCATION("FullCodeGenerator::EmitStringAdd"); - ZoneList* args = expr->arguments(); - ASSERT_EQ(2, args->length()); - - VisitForStackValue(args->at(0)); - VisitForAccumulatorValue(args->at(1)); - - __ Pop(x1); - StringAddStub stub(STRING_ADD_CHECK_BOTH, NOT_TENURED); - __ CallStub(&stub); - - context()->Plug(x0); -} - - -void FullCodeGenerator::EmitStringCompare(CallRuntime* expr) { - ZoneList* args = expr->arguments(); - ASSERT_EQ(2, args->length()); - VisitForStackValue(args->at(0)); - VisitForStackValue(args->at(1)); - - StringCompareStub stub; - __ CallStub(&stub); - context()->Plug(x0); -} - - -void FullCodeGenerator::EmitMathLog(CallRuntime* expr) { - // Load the argument on the stack and call the runtime function. - ZoneList* args = expr->arguments(); - ASSERT(args->length() == 1); - VisitForStackValue(args->at(0)); - __ CallRuntime(Runtime::kMath_log, 1); - context()->Plug(x0); -} - - -void FullCodeGenerator::EmitMathSqrt(CallRuntime* expr) { - // Load the argument on the stack and call the runtime function. - ZoneList* args = expr->arguments(); - ASSERT(args->length() == 1); - VisitForStackValue(args->at(0)); - __ CallRuntime(Runtime::kMath_sqrt, 1); - context()->Plug(x0); -} - - -void FullCodeGenerator::EmitCallFunction(CallRuntime* expr) { - ASM_LOCATION("FullCodeGenerator::EmitCallFunction"); - ZoneList* args = expr->arguments(); - ASSERT(args->length() >= 2); - - int arg_count = args->length() - 2; // 2 ~ receiver and function. - for (int i = 0; i < arg_count + 1; i++) { - VisitForStackValue(args->at(i)); - } - VisitForAccumulatorValue(args->last()); // Function. - - Label runtime, done; - // Check for non-function argument (including proxy). - __ JumpIfSmi(x0, &runtime); - __ JumpIfNotObjectType(x0, x1, x1, JS_FUNCTION_TYPE, &runtime); - - // InvokeFunction requires the function in x1. Move it in there. - __ Mov(x1, x0); - ParameterCount count(arg_count); - __ InvokeFunction(x1, count, CALL_FUNCTION, NullCallWrapper()); - __ Ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); - __ B(&done); - - __ Bind(&runtime); - __ Push(x0); - __ CallRuntime(Runtime::kCall, args->length()); - __ Bind(&done); - - context()->Plug(x0); -} - - -void FullCodeGenerator::EmitRegExpConstructResult(CallRuntime* expr) { - RegExpConstructResultStub stub; - ZoneList* args = expr->arguments(); - ASSERT(args->length() == 3); - VisitForStackValue(args->at(0)); - VisitForStackValue(args->at(1)); - VisitForAccumulatorValue(args->at(2)); - __ Pop(x1, x2); - __ CallStub(&stub); - context()->Plug(x0); -} - - -void FullCodeGenerator::EmitGetFromCache(CallRuntime* expr) { - ZoneList* args = expr->arguments(); - ASSERT_EQ(2, args->length()); - ASSERT_NE(NULL, args->at(0)->AsLiteral()); - int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->value()))->value(); - - Handle jsfunction_result_caches( - isolate()->native_context()->jsfunction_result_caches()); - if (jsfunction_result_caches->length() <= cache_id) { - __ Abort(kAttemptToUseUndefinedCache); - __ LoadRoot(x0, Heap::kUndefinedValueRootIndex); - context()->Plug(x0); - return; - } - - VisitForAccumulatorValue(args->at(1)); - - Register key = x0; - Register cache = x1; - __ Ldr(cache, GlobalObjectMemOperand()); - __ Ldr(cache, FieldMemOperand(cache, GlobalObject::kNativeContextOffset)); - __ Ldr(cache, ContextMemOperand(cache, - Context::JSFUNCTION_RESULT_CACHES_INDEX)); - __ Ldr(cache, - FieldMemOperand(cache, FixedArray::OffsetOfElementAt(cache_id))); - - Label done; - __ Ldrsw(x2, UntagSmiFieldMemOperand(cache, - JSFunctionResultCache::kFingerOffset)); - __ Add(x3, cache, FixedArray::kHeaderSize - kHeapObjectTag); - __ Add(x3, x3, Operand(x2, LSL, kPointerSizeLog2)); - - // Load the key and data from the cache. - __ Ldp(x2, x3, MemOperand(x3)); - - __ Cmp(key, x2); - __ CmovX(x0, x3, eq); - __ B(eq, &done); - - // Call runtime to perform the lookup. - __ Push(cache, key); - __ CallRuntime(Runtime::kGetFromCache, 2); - - __ Bind(&done); - context()->Plug(x0); -} - - -void FullCodeGenerator::EmitHasCachedArrayIndex(CallRuntime* expr) { - ZoneList* args = expr->arguments(); - 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); - - __ Ldr(x10, FieldMemOperand(x0, String::kHashFieldOffset)); - __ Tst(x10, String::kContainsCachedArrayIndexMask); - PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); - Split(eq, if_true, if_false, fall_through); - - context()->Plug(if_true, if_false); -} - - -void FullCodeGenerator::EmitGetCachedArrayIndex(CallRuntime* expr) { - ZoneList* args = expr->arguments(); - ASSERT(args->length() == 1); - VisitForAccumulatorValue(args->at(0)); - - __ AssertString(x0); - - __ Ldr(x10, FieldMemOperand(x0, String::kHashFieldOffset)); - __ IndexFromHash(x10, x0); - - context()->Plug(x0); -} - - -void FullCodeGenerator::EmitFastAsciiArrayJoin(CallRuntime* expr) { - ASM_LOCATION("FullCodeGenerator::EmitFastAsciiArrayJoin"); - - ZoneList* args = expr->arguments(); - ASSERT(args->length() == 2); - VisitForStackValue(args->at(1)); - VisitForAccumulatorValue(args->at(0)); - - Register array = x0; - Register result = x0; - Register elements = x1; - Register element = x2; - Register separator = x3; - Register array_length = x4; - Register result_pos = x5; - Register map = x6; - Register string_length = x10; - Register elements_end = x11; - Register string = x12; - Register scratch1 = x13; - Register scratch2 = x14; - Register scratch3 = x7; - Register separator_length = x15; - - Label bailout, done, one_char_separator, long_separator, - non_trivial_array, not_size_one_array, loop, - empty_separator_loop, one_char_separator_loop, - one_char_separator_loop_entry, long_separator_loop; - - // The separator operand is on the stack. - __ Pop(separator); - - // Check that the array is a JSArray. - __ JumpIfSmi(array, &bailout); - __ JumpIfNotObjectType(array, map, scratch1, JS_ARRAY_TYPE, &bailout); - - // Check that the array has fast elements. - __ CheckFastElements(map, scratch1, &bailout); - - // If the array has length zero, return the empty string. - // Load and untag the length of the array. - // It is an unsigned value, so we can skip sign extension. - // We assume little endianness. - __ Ldrsw(array_length, - UntagSmiFieldMemOperand(array, JSArray::kLengthOffset)); - __ Cbnz(array_length, &non_trivial_array); - __ LoadRoot(result, Heap::kempty_stringRootIndex); - __ B(&done); - - __ Bind(&non_trivial_array); - // Get the FixedArray containing array's elements. - __ Ldr(elements, FieldMemOperand(array, JSArray::kElementsOffset)); - - // Check that all array elements are sequential ASCII strings, and - // accumulate the sum of their lengths. - __ Mov(string_length, 0); - __ Add(element, elements, FixedArray::kHeaderSize - kHeapObjectTag); - __ Add(elements_end, element, Operand(array_length, LSL, kPointerSizeLog2)); - // Loop condition: while (element < elements_end). - // Live values in registers: - // elements: Fixed array of strings. - // array_length: Length of the fixed array of strings (not smi) - // separator: Separator string - // string_length: Accumulated sum of string lengths (not smi). - // element: Current array element. - // elements_end: Array end. - if (FLAG_debug_code) { - __ Cmp(array_length, Operand(0)); - __ Assert(gt, kNoEmptyArraysHereInEmitFastAsciiArrayJoin); - } - __ Bind(&loop); - __ Ldr(string, MemOperand(element, kPointerSize, PostIndex)); - __ JumpIfSmi(string, &bailout); - __ Ldr(scratch1, FieldMemOperand(string, HeapObject::kMapOffset)); - __ Ldrb(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset)); - __ JumpIfInstanceTypeIsNotSequentialAscii(scratch1, scratch2, &bailout); - __ Ldrsw(scratch1, - UntagSmiFieldMemOperand(string, SeqOneByteString::kLengthOffset)); - __ Adds(string_length, string_length, scratch1); - __ B(vs, &bailout); - __ Cmp(element, elements_end); - __ B(lt, &loop); - - // If array_length is 1, return elements[0], a string. - __ Cmp(array_length, 1); - __ B(ne, ¬_size_one_array); - __ Ldr(result, FieldMemOperand(elements, FixedArray::kHeaderSize)); - __ B(&done); - - __ Bind(¬_size_one_array); - - // Live values in registers: - // separator: Separator string - // array_length: Length of the array (not smi). - // string_length: Sum of string lengths (not smi). - // elements: FixedArray of strings. - - // Check that the separator is a flat ASCII string. - __ JumpIfSmi(separator, &bailout); - __ Ldr(scratch1, FieldMemOperand(separator, HeapObject::kMapOffset)); - __ Ldrb(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset)); - __ JumpIfInstanceTypeIsNotSequentialAscii(scratch1, scratch2, &bailout); - - // Add (separator length times array_length) - separator length to the - // string_length to get the length of the result string. - // Load the separator length as untagged. - // We assume little endianness, and that the length is positive. - __ Ldrsw(separator_length, - UntagSmiFieldMemOperand(separator, - SeqOneByteString::kLengthOffset)); - __ Sub(string_length, string_length, separator_length); - __ Umaddl(string_length, array_length.W(), separator_length.W(), - string_length); - - // Get first element in the array. - __ Add(element, elements, FixedArray::kHeaderSize - kHeapObjectTag); - // Live values in registers: - // element: First array element - // separator: Separator string - // string_length: Length of result string (not smi) - // array_length: Length of the array (not smi). - __ AllocateAsciiString(result, string_length, scratch1, scratch2, scratch3, - &bailout); - - // Prepare for looping. Set up elements_end to end of the array. Set - // result_pos to the position of the result where to write the first - // character. - // TODO(all): useless unless AllocateAsciiString trashes the register. - __ Add(elements_end, element, Operand(array_length, LSL, kPointerSizeLog2)); - __ Add(result_pos, result, SeqOneByteString::kHeaderSize - kHeapObjectTag); - - // Check the length of the separator. - __ Cmp(separator_length, 1); - __ B(eq, &one_char_separator); - __ B(gt, &long_separator); - - // Empty separator case - __ Bind(&empty_separator_loop); - // Live values in registers: - // result_pos: the position to which we are currently copying characters. - // element: Current array element. - // elements_end: Array end. - - // Copy next array element to the result. - __ Ldr(string, MemOperand(element, kPointerSize, PostIndex)); - __ Ldrsw(string_length, - UntagSmiFieldMemOperand(string, String::kLengthOffset)); - __ Add(string, string, SeqOneByteString::kHeaderSize - kHeapObjectTag); - __ CopyBytes(result_pos, string, string_length, scratch1); - __ Cmp(element, elements_end); - __ B(lt, &empty_separator_loop); // End while (element < elements_end). - __ B(&done); - - // One-character separator case - __ Bind(&one_char_separator); - // Replace separator with its ASCII character value. - __ Ldrb(separator, FieldMemOperand(separator, SeqOneByteString::kHeaderSize)); - // Jump into the loop after the code that copies the separator, so the first - // element is not preceded by a separator - __ B(&one_char_separator_loop_entry); - - __ Bind(&one_char_separator_loop); - // Live values in registers: - // result_pos: the position to which we are currently copying characters. - // element: Current array element. - // elements_end: Array end. - // separator: Single separator ASCII char (in lower byte). - - // Copy the separator character to the result. - __ Strb(separator, MemOperand(result_pos, 1, PostIndex)); - - // Copy next array element to the result. - __ Bind(&one_char_separator_loop_entry); - __ Ldr(string, MemOperand(element, kPointerSize, PostIndex)); - __ Ldrsw(string_length, - UntagSmiFieldMemOperand(string, String::kLengthOffset)); - __ Add(string, string, SeqOneByteString::kHeaderSize - kHeapObjectTag); - __ CopyBytes(result_pos, string, string_length, scratch1); - __ Cmp(element, elements_end); - __ B(lt, &one_char_separator_loop); // End while (element < elements_end). - __ B(&done); - - // Long separator case (separator is more than one character). Entry is at the - // label long_separator below. - __ Bind(&long_separator_loop); - // Live values in registers: - // result_pos: the position to which we are currently copying characters. - // element: Current array element. - // elements_end: Array end. - // separator: Separator string. - - // Copy the separator to the result. - // TODO(all): hoist next two instructions. - __ Ldrsw(string_length, - UntagSmiFieldMemOperand(separator, String::kLengthOffset)); - __ Add(string, separator, SeqOneByteString::kHeaderSize - kHeapObjectTag); - __ CopyBytes(result_pos, string, string_length, scratch1); - - __ Bind(&long_separator); - __ Ldr(string, MemOperand(element, kPointerSize, PostIndex)); - __ Ldrsw(string_length, - UntagSmiFieldMemOperand(string, String::kLengthOffset)); - __ Add(string, string, SeqOneByteString::kHeaderSize - kHeapObjectTag); - __ CopyBytes(result_pos, string, string_length, scratch1); - __ Cmp(element, elements_end); - __ B(lt, &long_separator_loop); // End while (element < elements_end). - __ B(&done); - - __ Bind(&bailout); - // Returning undefined will force slower code to handle it. - __ LoadRoot(result, Heap::kUndefinedValueRootIndex); - __ Bind(&done); - context()->Plug(result); -} - - -void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { - Handle name = expr->name(); - if (name->length() > 0 && name->Get(0) == '_') { - Comment cmnt(masm_, "[ InlineRuntimeCall"); - EmitInlineRuntimeCall(expr); - return; - } - - Comment cmnt(masm_, "[ CallRunTime"); - ZoneList* args = expr->arguments(); - int arg_count = args->length(); - - if (expr->is_jsruntime()) { - // Push the builtins object as the receiver. - __ Ldr(x10, GlobalObjectMemOperand()); - __ Ldr(x0, FieldMemOperand(x10, GlobalObject::kBuiltinsOffset)); - __ Push(x0); - - // Load the function from the receiver. - __ Mov(x2, Operand(name)); - CallLoadIC(NOT_CONTEXTUAL, expr->CallRuntimeFeedbackId()); - - // Push the target function under the receiver. - __ Pop(x10); - __ Push(x0, x10); - - int arg_count = args->length(); - for (int i = 0; i < arg_count; i++) { - VisitForStackValue(args->at(i)); - } - - // Record source position of the IC call. - SetSourcePosition(expr->position()); - CallFunctionStub stub(arg_count, NO_CALL_FUNCTION_FLAGS); - __ Peek(x1, (arg_count + 1) * kPointerSize); - __ CallStub(&stub); - - // Restore context register. - __ Ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); - - context()->DropAndPlug(1, x0); - } else { - // Push the arguments ("left-to-right"). - for (int i = 0; i < arg_count; i++) { - VisitForStackValue(args->at(i)); - } - - // Call the C runtime function. - __ CallRuntime(expr->function(), arg_count); - context()->Plug(x0); - } -} - - -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()); - StrictModeFlag strict_mode_flag = (language_mode() == CLASSIC_MODE) - ? kNonStrictMode : kStrictMode; - __ Mov(x10, Operand(Smi::FromInt(strict_mode_flag))); - __ Push(x10); - __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION); - context()->Plug(x0); - } else if (proxy != NULL) { - Variable* var = proxy->var(); - // Delete of an unqualified identifier is disallowed in strict mode - // but "delete this" is allowed. - ASSERT(language_mode() == CLASSIC_MODE || var->is_this()); - if (var->IsUnallocated()) { - __ Ldr(x12, GlobalObjectMemOperand()); - __ Mov(x11, Operand(var->name())); - __ Mov(x10, Operand(Smi::FromInt(kNonStrictMode))); - __ Push(x12, x11, x10); - __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION); - context()->Plug(x0); - } else if (var->IsStackAllocated() || var->IsContextSlot()) { - // Result of deleting non-global, non-dynamic variables is false. - // The subexpression does not have side effects. - context()->Plug(var->is_this()); - } else { - // Non-global variable. Call the runtime to try to delete from the - // context where the variable was introduced. - __ Mov(x2, Operand(var->name())); - __ Push(context_register(), x2); - __ CallRuntime(Runtime::kDeleteContextSlot, 2); - context()->Plug(x0); - } - } else { - // Result of deleting non-property, non-variable reference is true. - // The subexpression may have side effects. - VisitForEffect(expr->expression()); - context()->Plug(true); - } - break; - break; - } - case Token::VOID: { - Comment cmnt(masm_, "[ UnaryOperation (VOID)"); - VisitForEffect(expr->expression()); - context()->Plug(Heap::kUndefinedValueRootIndex); - 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 { - ASSERT(context()->IsAccumulatorValue() || context()->IsStackValue()); - // TODO(jbramley): This could be much more efficient using (for - // example) the CSEL instruction. - Label materialize_true, materialize_false, done; - VisitForControl(expr->expression(), - &materialize_false, - &materialize_true, - &materialize_true); - - __ Bind(&materialize_true); - PrepareForBailoutForId(expr->MaterializeTrueId(), NO_REGISTERS); - __ LoadRoot(result_register(), Heap::kTrueValueRootIndex); - __ B(&done); - - __ Bind(&materialize_false); - PrepareForBailoutForId(expr->MaterializeFalseId(), NO_REGISTERS); - __ LoadRoot(result_register(), Heap::kFalseValueRootIndex); - __ B(&done); - - __ Bind(&done); - if (context()->IsStackValue()) { - __ Push(result_register()); - } - } - break; - } - case Token::TYPEOF: { - Comment cmnt(masm_, "[ UnaryOperation (TYPEOF)"); - { - StackValueContext context(this); - VisitForTypeofValue(expr->expression()); - } - __ CallRuntime(Runtime::kTypeof, 1); - context()->Plug(x0); - break; - } - default: - UNREACHABLE(); - } -} - - -void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { - Comment cmnt(masm_, "[ CountOperation"); - SetSourcePosition(expr->position()); - - // Invalid left-hand sides are rewritten to have a 'throw ReferenceError' - // as the left-hand side. - if (!expr->expression()->IsValidLeftHandSide()) { - VisitForEffect(expr->expression()); - return; - } - - // Expression can only be a property, a global or a (parameter or local) - // slot. - enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY }; - LhsKind assign_type = VARIABLE; - Property* prop = expr->expression()->AsProperty(); - // In case of a property we use the uninitialized expression context - // of the key to detect a named property. - if (prop != NULL) { - assign_type = - (prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY; - } - - // Evaluate expression and get value. - if (assign_type == VARIABLE) { - ASSERT(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()) { - __ Push(xzr); - } - if (assign_type == NAMED_PROPERTY) { - // Put the object both on the stack and in the accumulator. - VisitForAccumulatorValue(prop->obj()); - __ Push(x0); - EmitNamedPropertyLoad(prop); - } else { - // KEYED_PROPERTY - VisitForStackValue(prop->obj()); - VisitForAccumulatorValue(prop->key()); - __ Peek(x1, 0); - __ Push(x0); - EmitKeyedPropertyLoad(prop); - } - } - - // We need a second deoptimization point after loading the value - // in case evaluating the property load my have a side effect. - if (assign_type == VARIABLE) { - PrepareForBailout(expr->expression(), TOS_REG); - } else { - PrepareForBailoutForId(prop->LoadId(), TOS_REG); - } - - // Inline smi case if we are in a loop. - Label stub_call, done; - JumpPatchSite patch_site(masm_); - - int count_value = expr->op() == Token::INC ? 1 : -1; - if (ShouldInlineSmiCase(expr->op())) { - Label slow; - patch_site.EmitJumpIfNotSmi(x0, &slow); - - // 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: - __ Push(x0); - break; - case NAMED_PROPERTY: - __ Poke(x0, kPointerSize); - break; - case KEYED_PROPERTY: - __ Poke(x0, kPointerSize * 2); - break; - } - } - } - - __ Adds(x0, x0, Operand(Smi::FromInt(count_value))); - __ B(vc, &done); - // Call stub. Undo operation first. - __ Sub(x0, x0, Operand(Smi::FromInt(count_value))); - __ B(&stub_call); - __ Bind(&slow); - } - ToNumberStub convert_stub; - __ CallStub(&convert_stub); - - // 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: - __ Push(x0); - break; - case NAMED_PROPERTY: - __ Poke(x0, kXRegSizeInBytes); - break; - case KEYED_PROPERTY: - __ Poke(x0, 2 * kXRegSizeInBytes); - break; - } - } - } - - __ Bind(&stub_call); - __ Mov(x1, x0); - __ Mov(x0, Operand(Smi::FromInt(count_value))); - - // Record position before stub call. - SetSourcePosition(expr->position()); - - { - Assembler::BlockConstPoolScope scope(masm_); - BinaryOpICStub stub(Token::ADD, NO_OVERWRITE); - CallIC(stub.GetCode(isolate()), expr->CountBinOpFeedbackId()); - patch_site.EmitPatchInfo(); - } - __ Bind(&done); - - // Store the value returned in x0. - switch (assign_type) { - case VARIABLE: - if (expr->is_postfix()) { - { EffectContext context(this); - EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(), - Token::ASSIGN); - PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); - context.Plug(x0); - } - // For all contexts except EffectConstant We have the result on - // top of the stack. - if (!context()->IsEffect()) { - context()->PlugTOS(); - } - } else { - EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(), - Token::ASSIGN); - PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); - context()->Plug(x0); - } - break; - case NAMED_PROPERTY: { - __ Mov(x2, Operand(prop->key()->AsLiteral()->value())); - __ Pop(x1); - CallStoreIC(expr->CountStoreFeedbackId()); - PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); - if (expr->is_postfix()) { - if (!context()->IsEffect()) { - context()->PlugTOS(); - } - } else { - context()->Plug(x0); - } - break; - } - case KEYED_PROPERTY: { - __ Pop(x1); // Key. - __ Pop(x2); // Receiver. - Handle ic = is_classic_mode() - ? isolate()->builtins()->KeyedStoreIC_Initialize() - : isolate()->builtins()->KeyedStoreIC_Initialize_Strict(); - CallIC(ic, expr->CountStoreFeedbackId()); - PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); - if (expr->is_postfix()) { - if (!context()->IsEffect()) { - context()->PlugTOS(); - } - } else { - context()->Plug(x0); - } - break; - } - } -} - - -void FullCodeGenerator::VisitForTypeofValue(Expression* expr) { - ASSERT(!context()->IsEffect()); - ASSERT(!context()->IsTest()); - VariableProxy* proxy = expr->AsVariableProxy(); - if (proxy != NULL && proxy->var()->IsUnallocated()) { - Comment cmnt(masm_, "Global variable"); - __ Ldr(x0, GlobalObjectMemOperand()); - __ Mov(x2, Operand(proxy->name())); - // Use a regular load, not a contextual load, to avoid a reference - // error. - CallLoadIC(NOT_CONTEXTUAL); - PrepareForBailout(expr, TOS_REG); - context()->Plug(x0); - } else if (proxy != NULL && proxy->var()->IsLookupSlot()) { - Label done, slow; - - // Generate code for loading from variables potentially shadowed - // by eval-introduced variables. - EmitDynamicLookupFastCase(proxy->var(), INSIDE_TYPEOF, &slow, &done); - - __ Bind(&slow); - __ Mov(x0, Operand(proxy->name())); - __ Push(cp, x0); - __ CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2); - PrepareForBailout(expr, TOS_REG); - __ Bind(&done); - - context()->Plug(x0); - } else { - // This expression cannot throw a reference error at the top level. - VisitInDuplicateContext(expr); - } -} - - -void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr, - Expression* sub_expr, - Handle check) { - ASM_LOCATION("FullCodeGenerator::EmitLiteralCompareTypeof"); - Comment cmnt(masm_, "[ EmitLiteralCompareTypeof"); - 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); - } - PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); - - if (check->Equals(isolate()->heap()->number_string())) { - ASM_LOCATION("FullCodeGenerator::EmitLiteralCompareTypeof number_string"); - __ JumpIfSmi(x0, if_true); - __ Ldr(x0, FieldMemOperand(x0, HeapObject::kMapOffset)); - __ CompareRoot(x0, Heap::kHeapNumberMapRootIndex); - Split(eq, if_true, if_false, fall_through); - } else if (check->Equals(isolate()->heap()->string_string())) { - ASM_LOCATION("FullCodeGenerator::EmitLiteralCompareTypeof string_string"); - __ JumpIfSmi(x0, if_false); - // Check for undetectable objects => false. - __ JumpIfObjectType(x0, x0, x1, FIRST_NONSTRING_TYPE, if_false, ge); - __ Ldrb(x1, FieldMemOperand(x0, Map::kBitFieldOffset)); - __ TestAndSplit(x1, 1 << Map::kIsUndetectable, if_true, if_false, - fall_through); - } else if (check->Equals(isolate()->heap()->symbol_string())) { - ASM_LOCATION("FullCodeGenerator::EmitLiteralCompareTypeof symbol_string"); - __ JumpIfSmi(x0, if_false); - __ CompareObjectType(x0, x0, x1, SYMBOL_TYPE); - Split(eq, if_true, if_false, fall_through); - } else if (check->Equals(isolate()->heap()->boolean_string())) { - ASM_LOCATION("FullCodeGenerator::EmitLiteralCompareTypeof boolean_string"); - __ JumpIfRoot(x0, Heap::kTrueValueRootIndex, if_true); - __ CompareRoot(x0, Heap::kFalseValueRootIndex); - Split(eq, if_true, if_false, fall_through); - } else if (FLAG_harmony_typeof && - check->Equals(isolate()->heap()->null_string())) { - ASM_LOCATION("FullCodeGenerator::EmitLiteralCompareTypeof null_string"); - __ CompareRoot(x0, Heap::kNullValueRootIndex); - Split(eq, if_true, if_false, fall_through); - } else if (check->Equals(isolate()->heap()->undefined_string())) { - ASM_LOCATION( - "FullCodeGenerator::EmitLiteralCompareTypeof undefined_string"); - __ JumpIfRoot(x0, Heap::kUndefinedValueRootIndex, if_true); - __ JumpIfSmi(x0, if_false); - // Check for undetectable objects => true. - __ Ldr(x0, FieldMemOperand(x0, HeapObject::kMapOffset)); - __ Ldrb(x1, FieldMemOperand(x0, Map::kBitFieldOffset)); - __ TestAndSplit(x1, 1 << Map::kIsUndetectable, if_false, if_true, - fall_through); - } else if (check->Equals(isolate()->heap()->function_string())) { - ASM_LOCATION("FullCodeGenerator::EmitLiteralCompareTypeof function_string"); - __ JumpIfSmi(x0, if_false); - STATIC_ASSERT(NUM_OF_CALLABLE_SPEC_OBJECT_TYPES == 2); - __ JumpIfObjectType(x0, x10, x11, JS_FUNCTION_TYPE, if_true); - __ CompareAndSplit(x11, JS_FUNCTION_PROXY_TYPE, eq, if_true, if_false, - fall_through); - - } else if (check->Equals(isolate()->heap()->object_string())) { - ASM_LOCATION("FullCodeGenerator::EmitLiteralCompareTypeof object_string"); - __ JumpIfSmi(x0, if_false); - if (!FLAG_harmony_typeof) { - __ JumpIfRoot(x0, Heap::kNullValueRootIndex, if_true); - } - // Check for JS objects => true. - Register map = x10; - __ JumpIfObjectType(x0, map, x11, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE, - if_false, lt); - __ CompareInstanceType(map, x11, LAST_NONCALLABLE_SPEC_OBJECT_TYPE); - __ B(gt, if_false); - // Check for undetectable objects => false. - __ Ldrb(x10, FieldMemOperand(map, Map::kBitFieldOffset)); - - __ TestAndSplit(x10, 1 << Map::kIsUndetectable, if_true, if_false, - fall_through); - - } else { - ASM_LOCATION("FullCodeGenerator::EmitLiteralCompareTypeof other"); - if (if_false != fall_through) __ B(if_false); - } - context()->Plug(if_true, if_false); -} - - -void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { - Comment cmnt(masm_, "[ CompareOperation"); - SetSourcePosition(expr->position()); - - // Try to generate an optimized comparison with a literal value. - // TODO(jbramley): This only checks common values like NaN or undefined. - // Should it also handle A64 immediate operands? - if (TryLiteralCompare(expr)) { - return; - } - - // Assign labels according to context()->PrepareTest. - Label materialize_true; - Label 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()); - __ InvokeBuiltin(Builtins::IN, CALL_FUNCTION); - PrepareForBailoutBeforeSplit(expr, false, NULL, NULL); - __ CompareRoot(x0, Heap::kTrueValueRootIndex); - Split(eq, if_true, if_false, fall_through); - break; - - case Token::INSTANCEOF: { - VisitForStackValue(expr->right()); - InstanceofStub stub(InstanceofStub::kNoFlags); - __ CallStub(&stub); - PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); - // The stub returns 0 for true. - __ CompareAndSplit(x0, 0, eq, if_true, if_false, fall_through); - break; - } - - default: { - VisitForAccumulatorValue(expr->right()); - Condition cond = CompareIC::ComputeCondition(op); - - // Pop the stack value. - __ Pop(x1); - - JumpPatchSite patch_site(masm_); - if (ShouldInlineSmiCase(op)) { - Label slow_case; - patch_site.EmitJumpIfEitherNotSmi(x0, x1, &slow_case); - __ Cmp(x1, x0); - Split(cond, if_true, if_false, NULL); - __ Bind(&slow_case); - } - - // Record position and call the compare IC. - SetSourcePosition(expr->position()); - Handle ic = CompareIC::GetUninitialized(isolate(), op); - CallIC(ic, expr->CompareOperationFeedbackId()); - patch_site.EmitPatchInfo(); - PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); - __ CompareAndSplit(x0, 0, cond, 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) { - ASM_LOCATION("FullCodeGenerator::EmitLiteralCompareNil"); - 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); - PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); - - if (expr->op() == Token::EQ_STRICT) { - Heap::RootListIndex nil_value = nil == kNullValue ? - Heap::kNullValueRootIndex : - Heap::kUndefinedValueRootIndex; - __ CompareRoot(x0, nil_value); - Split(eq, if_true, if_false, fall_through); - } else { - Handle ic = CompareNilICStub::GetUninitialized(isolate(), nil); - CallIC(ic, expr->CompareOperationFeedbackId()); - __ CompareAndSplit(x0, 0, ne, if_true, if_false, fall_through); - } - - context()->Plug(if_true, if_false); -} - - -void FullCodeGenerator::VisitThisFunction(ThisFunction* expr) { - __ Ldr(x0, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); - context()->Plug(x0); -} - - -void FullCodeGenerator::VisitYield(Yield* expr) { - Comment cmnt(masm_, "[ Yield"); - // Evaluate yielded value first; the initial iterator definition depends on - // this. It stays on the stack while we update the iterator. - VisitForStackValue(expr->expression()); - - // TODO(jbramley): Tidy this up once the merge is done, using named registers - // and suchlike. The implementation changes a little by bleeding_edge so I - // don't want to spend too much time on it now. - - switch (expr->yield_kind()) { - case Yield::SUSPEND: - // Pop value from top-of-stack slot; box result into result register. - EmitCreateIteratorResult(false); - __ Push(result_register()); - // Fall through. - case Yield::INITIAL: { - Label suspend, continuation, post_runtime, resume; - - __ B(&suspend); - - // TODO(jbramley): This label is bound here because the following code - // looks at its pos(). Is it possible to do something more efficient here, - // perhaps using Adr? - __ Bind(&continuation); - __ B(&resume); - - __ Bind(&suspend); - VisitForAccumulatorValue(expr->generator_object()); - ASSERT((continuation.pos() > 0) && Smi::IsValid(continuation.pos())); - __ Mov(x1, Operand(Smi::FromInt(continuation.pos()))); - __ Str(x1, FieldMemOperand(x0, JSGeneratorObject::kContinuationOffset)); - __ Str(cp, FieldMemOperand(x0, JSGeneratorObject::kContextOffset)); - __ Mov(x1, cp); - __ RecordWriteField(x0, JSGeneratorObject::kContextOffset, x1, x2, - kLRHasBeenSaved, kDontSaveFPRegs); - __ Add(x1, fp, StandardFrameConstants::kExpressionsOffset); - __ Cmp(__ StackPointer(), x1); - __ B(eq, &post_runtime); - __ Push(x0); // generator object - __ CallRuntime(Runtime::kSuspendJSGeneratorObject, 1); - __ Ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); - __ Bind(&post_runtime); - __ Pop(result_register()); - EmitReturnSequence(); - - __ Bind(&resume); - context()->Plug(result_register()); - break; - } - - case Yield::FINAL: { - VisitForAccumulatorValue(expr->generator_object()); - __ Mov(x1, Operand(Smi::FromInt(JSGeneratorObject::kGeneratorClosed))); - __ Str(x1, FieldMemOperand(result_register(), - JSGeneratorObject::kContinuationOffset)); - // Pop value from top-of-stack slot, box result into result register. - EmitCreateIteratorResult(true); - EmitUnwindBeforeReturn(); - EmitReturnSequence(); - break; - } - - case Yield::DELEGATING: { - VisitForStackValue(expr->generator_object()); - - // Initial stack layout is as follows: - // [sp + 1 * kPointerSize] iter - // [sp + 0 * kPointerSize] g - - Label l_catch, l_try, l_suspend, l_continuation, l_resume; - Label l_next, l_call, l_loop; - // Initial send value is undefined. - __ LoadRoot(x0, Heap::kUndefinedValueRootIndex); - __ B(&l_next); - - // catch (e) { receiver = iter; f = 'throw'; arg = e; goto l_call; } - __ Bind(&l_catch); - handler_table()->set(expr->index(), Smi::FromInt(l_catch.pos())); - __ LoadRoot(x2, Heap::kthrow_stringRootIndex); // "throw" - __ Peek(x3, 1 * kPointerSize); // iter - __ Push(x2, x3, x0); // "throw", iter, except - __ B(&l_call); - - // try { received = %yield result } - // Shuffle the received result above a try handler and yield it without - // re-boxing. - __ Bind(&l_try); - __ Pop(x0); // result - __ PushTryHandler(StackHandler::CATCH, expr->index()); - const int handler_size = StackHandlerConstants::kSize; - __ Push(x0); // result - __ B(&l_suspend); - - // TODO(jbramley): This label is bound here because the following code - // looks at its pos(). Is it possible to do something more efficient here, - // perhaps using Adr? - __ Bind(&l_continuation); - __ B(&l_resume); - - __ Bind(&l_suspend); - const int generator_object_depth = kPointerSize + handler_size; - __ Peek(x0, generator_object_depth); - __ Push(x0); // g - ASSERT((l_continuation.pos() > 0) && Smi::IsValid(l_continuation.pos())); - __ Mov(x1, Operand(Smi::FromInt(l_continuation.pos()))); - __ Str(x1, FieldMemOperand(x0, JSGeneratorObject::kContinuationOffset)); - __ Str(cp, FieldMemOperand(x0, JSGeneratorObject::kContextOffset)); - __ Mov(x1, cp); - __ RecordWriteField(x0, JSGeneratorObject::kContextOffset, x1, x2, - kLRHasBeenSaved, kDontSaveFPRegs); - __ CallRuntime(Runtime::kSuspendJSGeneratorObject, 1); - __ Ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); - __ Pop(x0); // result - EmitReturnSequence(); - __ Bind(&l_resume); // received in x0 - __ PopTryHandler(); - - // receiver = iter; f = 'next'; arg = received; - __ Bind(&l_next); - __ LoadRoot(x2, Heap::knext_stringRootIndex); // "next" - __ Peek(x3, 1 * kPointerSize); // iter - __ Push(x2, x3, x0); // "next", iter, received - - // result = receiver[f](arg); - __ Bind(&l_call); - __ Peek(x1, 1 * kPointerSize); - __ Peek(x0, 2 * kPointerSize); - Handle ic = isolate()->builtins()->KeyedLoadIC_Initialize(); - CallIC(ic, TypeFeedbackId::None()); - __ Mov(x1, x0); - __ Poke(x1, 2 * kPointerSize); - CallFunctionStub stub(1, CALL_AS_METHOD); - __ CallStub(&stub); - - __ Ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); - __ Drop(1); // The function is still on the stack; drop it. - - // if (!result.done) goto l_try; - __ Bind(&l_loop); - __ Push(x0); // save result - __ LoadRoot(x2, Heap::kdone_stringRootIndex); // "done" - CallLoadIC(NOT_CONTEXTUAL); // result.done in x0 - // The ToBooleanStub argument (result.done) is in x0. - Handle bool_ic = ToBooleanStub::GetUninitialized(isolate()); - CallIC(bool_ic); - __ Cbz(x0, &l_try); - - // result.value - __ Pop(x0); // result - __ LoadRoot(x2, Heap::kvalue_stringRootIndex); // "value" - CallLoadIC(NOT_CONTEXTUAL); // result.value in x0 - context()->DropAndPlug(2, x0); // drop iter and g - break; - } - } -} - - -void FullCodeGenerator::EmitGeneratorResume(Expression *generator, - Expression *value, - JSGeneratorObject::ResumeMode resume_mode) { - ASM_LOCATION("FullCodeGenerator::EmitGeneratorResume"); - Register value_reg = x0; - Register generator_object = x1; - Register the_hole = x2; - Register operand_stack_size = w3; - Register function = x4; - - // The value stays in x0, and is ultimately read by the resumed generator, as - // if the CallRuntime(Runtime::kSuspendJSGeneratorObject) returned it. Or it - // is read to throw the value when the resumed generator is already closed. r1 - // will hold the generator object until the activation has been resumed. - VisitForStackValue(generator); - VisitForAccumulatorValue(value); - __ Pop(generator_object); - - // Check generator state. - Label wrong_state, closed_state, done; - __ Ldr(x10, FieldMemOperand(generator_object, - JSGeneratorObject::kContinuationOffset)); - STATIC_ASSERT(JSGeneratorObject::kGeneratorExecuting < 0); - STATIC_ASSERT(JSGeneratorObject::kGeneratorClosed == 0); - __ CompareAndBranch(x10, Operand(Smi::FromInt(0)), eq, &closed_state); - __ CompareAndBranch(x10, Operand(Smi::FromInt(0)), lt, &wrong_state); - - // Load suspended function and context. - __ Ldr(cp, FieldMemOperand(generator_object, - JSGeneratorObject::kContextOffset)); - __ Ldr(function, FieldMemOperand(generator_object, - JSGeneratorObject::kFunctionOffset)); - - // Load receiver and store as the first argument. - __ Ldr(x10, FieldMemOperand(generator_object, - JSGeneratorObject::kReceiverOffset)); - __ Push(x10); - - // Push holes for the rest of the arguments to the generator function. - __ Ldr(x10, FieldMemOperand(function, JSFunction::kSharedFunctionInfoOffset)); - - // The number of arguments is stored as an int32_t, and -1 is a marker - // (SharedFunctionInfo::kDontAdaptArgumentsSentinel), so we need sign - // extension to correctly handle it. However, in this case, we operate on - // 32-bit W registers, so extension isn't required. - __ Ldr(w10, FieldMemOperand(x10, - SharedFunctionInfo::kFormalParameterCountOffset)); - __ LoadRoot(the_hole, Heap::kTheHoleValueRootIndex); - - // TODO(jbramley): Write a variant of PushMultipleTimes which takes a register - // instead of a constant count, and use it to replace this loop. - Label push_argument_holes, push_frame; - __ Bind(&push_argument_holes); - __ Subs(w10, w10, 1); - __ B(mi, &push_frame); - __ Push(the_hole); - __ B(&push_argument_holes); - - // Enter a new JavaScript frame, and initialize its slots as they were when - // the generator was suspended. - Label resume_frame; - __ Bind(&push_frame); - __ Bl(&resume_frame); - __ B(&done); - - __ Bind(&resume_frame); - __ Push(lr, // Return address. - fp, // Caller's frame pointer. - cp, // Callee's context. - function); // Callee's JS Function. - __ Add(fp, __ StackPointer(), kPointerSize * 2); - - // Load and untag the operand stack size. - __ Ldr(x10, FieldMemOperand(generator_object, - JSGeneratorObject::kOperandStackOffset)); - __ Ldr(operand_stack_size, - UntagSmiFieldMemOperand(x10, FixedArray::kLengthOffset)); - - // If we are sending a value and there is no operand stack, we can jump back - // in directly. - if (resume_mode == JSGeneratorObject::NEXT) { - Label slow_resume; - __ Cbnz(operand_stack_size, &slow_resume); - __ Ldr(x10, FieldMemOperand(function, JSFunction::kCodeEntryOffset)); - __ Ldrsw(x11, - UntagSmiFieldMemOperand(generator_object, - JSGeneratorObject::kContinuationOffset)); - __ Add(x10, x10, x11); - __ Mov(x12, Operand(Smi::FromInt(JSGeneratorObject::kGeneratorExecuting))); - __ Str(x12, FieldMemOperand(generator_object, - JSGeneratorObject::kContinuationOffset)); - __ Br(x10); - - __ Bind(&slow_resume); - } - - // Otherwise, we push holes for the operand stack and call the runtime to fix - // up the stack and the handlers. - // TODO(jbramley): Write a variant of PushMultipleTimes which takes a register - // instead of a constant count, and use it to replace this loop. - Label push_operand_holes, call_resume; - __ Bind(&push_operand_holes); - __ Subs(operand_stack_size, operand_stack_size, 1); - __ B(mi, &call_resume); - __ Push(the_hole); - __ B(&push_operand_holes); - - __ Bind(&call_resume); - __ Mov(x10, Operand(Smi::FromInt(resume_mode))); - __ Push(generator_object, result_register(), x10); - __ CallRuntime(Runtime::kResumeJSGeneratorObject, 3); - // Not reached: the runtime call returns elsewhere. - __ Unreachable(); - - // Reach here when generator is closed. - __ Bind(&closed_state); - if (resume_mode == JSGeneratorObject::NEXT) { - // Return completed iterator result when generator is closed. - __ LoadRoot(x10, Heap::kUndefinedValueRootIndex); - __ Push(x10); - // Pop value from top-of-stack slot; box result into result register. - EmitCreateIteratorResult(true); - } else { - // Throw the provided value. - __ Push(value_reg); - __ CallRuntime(Runtime::kThrow, 1); - } - __ B(&done); - - // Throw error if we attempt to operate on a running generator. - __ Bind(&wrong_state); - __ Push(generator_object); - __ CallRuntime(Runtime::kThrowGeneratorStateError, 1); - - __ Bind(&done); - context()->Plug(result_register()); -} - - -void FullCodeGenerator::EmitCreateIteratorResult(bool done) { - Label gc_required; - Label allocated; - - Handle map(isolate()->native_context()->generator_result_map()); - - // Allocate and populate an object with this form: { value: VAL, done: DONE } - - Register result = x0; - __ Allocate(map->instance_size(), result, x10, x11, &gc_required, TAG_OBJECT); - __ B(&allocated); - - __ Bind(&gc_required); - __ Push(Smi::FromInt(map->instance_size())); - __ CallRuntime(Runtime::kAllocateInNewSpace, 1); - __ Ldr(context_register(), - MemOperand(fp, StandardFrameConstants::kContextOffset)); - - __ Bind(&allocated); - Register map_reg = x1; - Register result_value = x2; - Register boolean_done = x3; - Register empty_fixed_array = x4; - __ Mov(map_reg, Operand(map)); - __ Pop(result_value); - __ Mov(boolean_done, Operand(isolate()->factory()->ToBoolean(done))); - __ Mov(empty_fixed_array, Operand(isolate()->factory()->empty_fixed_array())); - ASSERT_EQ(map->instance_size(), 5 * kPointerSize); - // TODO(jbramley): Use Stp if possible. - __ Str(map_reg, FieldMemOperand(result, HeapObject::kMapOffset)); - __ Str(empty_fixed_array, - FieldMemOperand(result, JSObject::kPropertiesOffset)); - __ Str(empty_fixed_array, FieldMemOperand(result, JSObject::kElementsOffset)); - __ Str(result_value, - FieldMemOperand(result, - JSGeneratorObject::kResultValuePropertyOffset)); - __ Str(boolean_done, - FieldMemOperand(result, - JSGeneratorObject::kResultDonePropertyOffset)); - - // Only the value field needs a write barrier, as the other values are in the - // root set. - __ RecordWriteField(result, JSGeneratorObject::kResultValuePropertyOffset, - x10, x11, kLRHasBeenSaved, kDontSaveFPRegs); -} - - -// TODO(all): I don't like this method. -// It seems to me that in too many places x0 is used in place of this. -// Also, this function is not suitable for all places where x0 should be -// abstracted (eg. when used as an argument). But some places assume that the -// first argument register is x0, and use this function instead. -// Considering that most of the register allocation is hard-coded in the -// FullCodeGen, that it is unlikely we will need to change it extensively, and -// that abstracting the allocation through functions would not yield any -// performance benefit, I think the existence of this function is debatable. -Register FullCodeGenerator::result_register() { - return x0; -} - - -Register FullCodeGenerator::context_register() { - return cp; -} - - -void FullCodeGenerator::StoreToFrameField(int frame_offset, Register value) { - ASSERT(POINTER_SIZE_ALIGN(frame_offset) == frame_offset); - __ Str(value, MemOperand(fp, frame_offset)); -} - - -void FullCodeGenerator::LoadContextField(Register dst, int context_index) { - __ Ldr(dst, ContextMemOperand(cp, context_index)); -} - - -void FullCodeGenerator::PushFunctionArgumentForContextAllocation() { - Scope* declaration_scope = scope()->DeclarationScope(); - if (declaration_scope->is_global_scope() || - declaration_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. Pass a smi sentinel and let the runtime look up the empty - // function. - ASSERT(kSmiTag == 0); - __ Push(xzr); - } else if (declaration_scope->is_eval_scope()) { - // Contexts created by a call to eval have the same closure as the - // context calling eval, not the anonymous closure containing the eval - // code. Fetch it from the context. - __ Ldr(x10, ContextMemOperand(cp, Context::CLOSURE_INDEX)); - __ Push(x10); - } else { - ASSERT(declaration_scope->is_function_scope()); - __ Ldr(x10, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); - __ Push(x10); - } -} - - -void FullCodeGenerator::EnterFinallyBlock() { - ASM_LOCATION("FullCodeGenerator::EnterFinallyBlock"); - ASSERT(!result_register().is(x10)); - // Preserve the result register while executing finally block. - // Also cook the return address in lr to the stack (smi encoded Code* delta). - __ Sub(x10, lr, Operand(masm_->CodeObject())); - __ SmiTag(x10); - __ Push(result_register(), x10); - - // Store pending message while executing finally block. - ExternalReference pending_message_obj = - ExternalReference::address_of_pending_message_obj(isolate()); - __ Mov(x10, Operand(pending_message_obj)); - __ Ldr(x10, MemOperand(x10)); - - ExternalReference has_pending_message = - ExternalReference::address_of_has_pending_message(isolate()); - __ Mov(x11, Operand(has_pending_message)); - __ Ldr(x11, MemOperand(x11)); - __ SmiTag(x11); - - __ Push(x10, x11); - - ExternalReference pending_message_script = - ExternalReference::address_of_pending_message_script(isolate()); - __ Mov(x10, Operand(pending_message_script)); - __ Ldr(x10, MemOperand(x10)); - __ Push(x10); -} - - -void FullCodeGenerator::ExitFinallyBlock() { - ASM_LOCATION("FullCodeGenerator::ExitFinallyBlock"); - ASSERT(!result_register().is(x10)); - - // Restore pending message from stack. - __ Pop(x10, x11, x12); - ExternalReference pending_message_script = - ExternalReference::address_of_pending_message_script(isolate()); - __ Mov(x13, Operand(pending_message_script)); - __ Str(x10, MemOperand(x13)); - - __ SmiUntag(x11); - ExternalReference has_pending_message = - ExternalReference::address_of_has_pending_message(isolate()); - __ Mov(x13, Operand(has_pending_message)); - __ Str(x11, MemOperand(x13)); - - ExternalReference pending_message_obj = - ExternalReference::address_of_pending_message_obj(isolate()); - __ Mov(x13, Operand(pending_message_obj)); - __ Str(x12, MemOperand(x13)); - - // Restore result register and cooked return address from the stack. - __ Pop(x10, result_register()); - - // Uncook the return address (see EnterFinallyBlock). - __ SmiUntag(x10); - __ Add(x11, x10, Operand(masm_->CodeObject())); - __ Br(x11); -} - - -#undef __ - - -void BackEdgeTable::PatchAt(Code* unoptimized_code, - Address pc, - BackEdgeState target_state, - Code* replacement_code) { - // Turn the jump into a nop. - Address branch_address = pc - 3 * kInstructionSize; - PatchingAssembler patcher(branch_address, 1); - - switch (target_state) { - case INTERRUPT: - // - // .. .. .. .. b.pl ok - // .. .. .. .. ldr x16, pc+ - // .. .. .. .. blr x16 - // ... more instructions. - // ok-label - // Jump offset is 6 instructions. - ASSERT(Instruction::Cast(branch_address) - ->IsNop(Assembler::INTERRUPT_CODE_NOP)); - patcher.b(6, pl); - break; - case ON_STACK_REPLACEMENT: - case OSR_AFTER_STACK_CHECK: - // - // .. .. .. .. mov x0, x0 (NOP) - // .. .. .. .. ldr x16, pc+ - // .. .. .. .. blr x16 - ASSERT(Instruction::Cast(branch_address)->IsCondBranchImm()); - ASSERT(Instruction::Cast(branch_address)->ImmPCOffset() == - 6 * kInstructionSize); - patcher.nop(Assembler::INTERRUPT_CODE_NOP); - break; - } - - // Replace the call address. - Instruction* load = Instruction::Cast(pc)->preceding(2); - Address interrupt_address_pointer = - reinterpret_cast
(load) + load->ImmPCOffset(); - ASSERT((Memory::uint64_at(interrupt_address_pointer) == - reinterpret_cast(unoptimized_code->GetIsolate() - ->builtins() - ->OnStackReplacement() - ->entry())) || - (Memory::uint64_at(interrupt_address_pointer) == - reinterpret_cast(unoptimized_code->GetIsolate() - ->builtins() - ->InterruptCheck() - ->entry())) || - (Memory::uint64_at(interrupt_address_pointer) == - reinterpret_cast(unoptimized_code->GetIsolate() - ->builtins() - ->OsrAfterStackCheck() - ->entry())) || - (Memory::uint64_at(interrupt_address_pointer) == - reinterpret_cast(unoptimized_code->GetIsolate() - ->builtins() - ->OnStackReplacement() - ->entry()))); - Memory::uint64_at(interrupt_address_pointer) = - reinterpret_cast(replacement_code->entry()); - - unoptimized_code->GetHeap()->incremental_marking()->RecordCodeTargetPatch( - unoptimized_code, reinterpret_cast
(load), replacement_code); -} - - -BackEdgeTable::BackEdgeState BackEdgeTable::GetBackEdgeState( - Isolate* isolate, - Code* unoptimized_code, - Address pc) { - // TODO(jbramley): There should be some extra assertions here (as in the ARM - // back-end), but this function is gone in bleeding_edge so it might not - // matter anyway. - Instruction* jump_or_nop = Instruction::Cast(pc)->preceding(3); - - if (jump_or_nop->IsNop(Assembler::INTERRUPT_CODE_NOP)) { - Instruction* load = Instruction::Cast(pc)->preceding(2); - uint64_t entry = Memory::uint64_at(reinterpret_cast
(load) + - load->ImmPCOffset()); - if (entry == reinterpret_cast( - isolate->builtins()->OnStackReplacement()->entry())) { - return ON_STACK_REPLACEMENT; - } else if (entry == reinterpret_cast( - isolate->builtins()->OsrAfterStackCheck()->entry())) { - return OSR_AFTER_STACK_CHECK; - } else { - UNREACHABLE(); - } - } - - return INTERRUPT; -} - - -#define __ ACCESS_MASM(masm()) - - -FullCodeGenerator::NestedStatement* FullCodeGenerator::TryFinally::Exit( - int* stack_depth, - int* context_length) { - ASM_LOCATION("FullCodeGenerator::TryFinally::Exit"); - // The macros used here must preserve the result register. - - // Because the handler block contains the context of the finally - // code, we can restore it directly from there for the finally code - // rather than iteratively unwinding contexts via their previous - // links. - __ Drop(*stack_depth); // Down to the handler block. - if (*context_length > 0) { - // Restore the context to its dedicated register and the stack. - __ Peek(cp, StackHandlerConstants::kContextOffset); - __ Str(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); - } - __ PopTryHandler(); - __ Bl(finally_entry_); - - *stack_depth = 0; - *context_length = 0; - return previous_; -} - - -#undef __ - - -} } // namespace v8::internal - -#endif // V8_TARGET_ARCH_A64 diff --git a/deps/v8/src/a64/ic-a64.cc b/deps/v8/src/a64/ic-a64.cc deleted file mode 100644 index 93d7857b05..0000000000 --- a/deps/v8/src/a64/ic-a64.cc +++ /dev/null @@ -1,1413 +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 "v8.h" - -#if V8_TARGET_ARCH_A64 - -#include "a64/assembler-a64.h" -#include "code-stubs.h" -#include "codegen.h" -#include "disasm.h" -#include "ic-inl.h" -#include "runtime.h" -#include "stub-cache.h" - -namespace v8 { -namespace internal { - - -#define __ ACCESS_MASM(masm) - - -// "type" holds an instance type on entry and is not clobbered. -// Generated code branch on "global_object" if type is any kind of global -// JS object. -static void GenerateGlobalInstanceTypeCheck(MacroAssembler* masm, - Register type, - Label* global_object) { - __ Cmp(type, JS_GLOBAL_OBJECT_TYPE); - __ Ccmp(type, JS_BUILTINS_OBJECT_TYPE, ZFlag, ne); - __ Ccmp(type, JS_GLOBAL_PROXY_TYPE, ZFlag, ne); - __ B(eq, global_object); -} - - -// Generated code falls through if the receiver is a regular non-global -// JS object with slow properties and no interceptors. -// -// "receiver" holds the receiver on entry and is unchanged. -// "elements" holds the property dictionary on fall through. -static void GenerateNameDictionaryReceiverCheck(MacroAssembler* masm, - Register receiver, - Register elements, - Register scratch0, - Register scratch1, - Label* miss) { - ASSERT(!AreAliased(receiver, elements, scratch0, scratch1)); - - // Check that the receiver isn't a smi. - __ JumpIfSmi(receiver, miss); - - // Check that the receiver is a valid JS object. - // Let t be the object instance type, we want: - // FIRST_SPEC_OBJECT_TYPE <= t <= LAST_SPEC_OBJECT_TYPE. - // Since LAST_SPEC_OBJECT_TYPE is the last possible instance type we only - // check the lower bound. - STATIC_ASSERT(LAST_TYPE == LAST_SPEC_OBJECT_TYPE); - - __ JumpIfObjectType(receiver, scratch0, scratch1, FIRST_SPEC_OBJECT_TYPE, - miss, lt); - - // scratch0 now contains the map of the receiver and scratch1 the object type. - Register map = scratch0; - Register type = scratch1; - - // Check if the receiver is a global JS object. - GenerateGlobalInstanceTypeCheck(masm, type, miss); - - // Check that the object does not require access checks. - __ Ldrb(scratch1, FieldMemOperand(map, Map::kBitFieldOffset)); - __ Tbnz(scratch1, Map::kIsAccessCheckNeeded, miss); - __ Tbnz(scratch1, Map::kHasNamedInterceptor, miss); - - // Check that the properties dictionary is valid. - __ Ldr(elements, FieldMemOperand(receiver, JSObject::kPropertiesOffset)); - __ Ldr(scratch1, FieldMemOperand(elements, HeapObject::kMapOffset)); - __ JumpIfNotRoot(scratch1, Heap::kHashTableMapRootIndex, miss); -} - - -// Helper function used from LoadIC GenerateNormal. -// -// elements: Property dictionary. It is not clobbered if a jump to the miss -// label is done. -// name: Property name. It is not clobbered if a jump to the miss label is -// done -// result: Register for the result. It is only updated if a jump to the miss -// label is not done. -// The scratch registers need to be different from elements, name and result. -// The generated code assumes that the receiver has slow properties, -// is not a global object and does not have interceptors. -static void GenerateDictionaryLoad(MacroAssembler* masm, - Label* miss, - Register elements, - Register name, - Register result, - Register scratch1, - Register scratch2) { - ASSERT(!AreAliased(elements, name, scratch1, scratch2)); - ASSERT(!AreAliased(result, scratch1, scratch2)); - - Label done; - - // Probe the dictionary. - NameDictionaryLookupStub::GeneratePositiveLookup(masm, - miss, - &done, - elements, - name, - scratch1, - scratch2); - - // If probing finds an entry check that the value is a normal property. - __ Bind(&done); - - static const int kElementsStartOffset = NameDictionary::kHeaderSize + - NameDictionary::kElementsStartIndex * kPointerSize; - static const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize; - __ Ldr(scratch1, FieldMemOperand(scratch2, kDetailsOffset)); - __ Tst(scratch1, Operand(Smi::FromInt(PropertyDetails::TypeField::kMask))); - __ B(ne, miss); - - // Get the value at the masked, scaled index and return. - __ Ldr(result, - FieldMemOperand(scratch2, kElementsStartOffset + 1 * kPointerSize)); -} - - -// Helper function used from StoreIC::GenerateNormal. -// -// elements: Property dictionary. It is not clobbered if a jump to the miss -// label is done. -// name: Property name. It is not clobbered if a jump to the miss label is -// done -// value: The value to store (never clobbered). -// -// The generated code assumes that the receiver has slow properties, -// is not a global object and does not have interceptors. -static void GenerateDictionaryStore(MacroAssembler* masm, - Label* miss, - Register elements, - Register name, - Register value, - Register scratch1, - Register scratch2) { - ASSERT(!AreAliased(elements, name, value, scratch1, scratch2)); - - Label done; - - // Probe the dictionary. - NameDictionaryLookupStub::GeneratePositiveLookup(masm, - miss, - &done, - elements, - name, - scratch1, - scratch2); - - // If probing finds an entry in the dictionary check that the value - // is a normal property that is not read only. - __ Bind(&done); - - static const int kElementsStartOffset = NameDictionary::kHeaderSize + - NameDictionary::kElementsStartIndex * kPointerSize; - static const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize; - static const int kTypeAndReadOnlyMask = - PropertyDetails::TypeField::kMask | - PropertyDetails::AttributesField::encode(READ_ONLY); - __ Ldrsw(scratch1, UntagSmiFieldMemOperand(scratch2, kDetailsOffset)); - __ Tst(scratch1, kTypeAndReadOnlyMask); - __ B(ne, miss); - - // Store the value at the masked, scaled index and return. - static const int kValueOffset = kElementsStartOffset + kPointerSize; - __ Add(scratch2, scratch2, kValueOffset - kHeapObjectTag); - __ Str(value, MemOperand(scratch2)); - - // Update the write barrier. Make sure not to clobber the value. - __ Mov(scratch1, value); - __ RecordWrite( - elements, scratch2, scratch1, kLRHasNotBeenSaved, kDontSaveFPRegs); -} - - -// Checks the receiver for special cases (value type, slow case bits). -// Falls through for regular JS object and return the map of the -// receiver in 'map_scratch' if the receiver is not a SMI. -static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm, - Register receiver, - Register map_scratch, - Register scratch, - int interceptor_bit, - Label* slow) { - ASSERT(!AreAliased(map_scratch, scratch)); - - // Check that the object isn't a smi. - __ JumpIfSmi(receiver, slow); - // Get the map of the receiver. - __ Ldr(map_scratch, FieldMemOperand(receiver, HeapObject::kMapOffset)); - // Check bit field. - __ Ldrb(scratch, FieldMemOperand(map_scratch, Map::kBitFieldOffset)); - __ Tbnz(scratch, Map::kIsAccessCheckNeeded, slow); - __ Tbnz(scratch, interceptor_bit, slow); - - // Check that the object is some kind of JS object EXCEPT JS Value type. - // In the case that the object is a value-wrapper object, we enter the - // runtime system to make sure that indexing into string objects work - // as intended. - STATIC_ASSERT(JS_OBJECT_TYPE > JS_VALUE_TYPE); - __ Ldrb(scratch, FieldMemOperand(map_scratch, Map::kInstanceTypeOffset)); - __ Cmp(scratch, JS_OBJECT_TYPE); - __ B(lt, slow); -} - - -// Loads an indexed element from a fast case array. -// If not_fast_array is NULL, doesn't perform the elements map check. -// -// receiver - holds the receiver on entry. -// Unchanged unless 'result' is the same register. -// -// key - holds the smi key on entry. -// Unchanged unless 'result' is the same register. -// -// elements - holds the elements of the receiver on exit. -// -// elements_map - holds the elements map on exit if the not_fast_array branch is -// taken. Otherwise, this is used as a scratch register. -// -// result - holds the result on exit if the load succeeded. -// Allowed to be the the same as 'receiver' or 'key'. -// Unchanged on bailout so 'receiver' and 'key' can be safely -// used by further computation. -static void GenerateFastArrayLoad(MacroAssembler* masm, - Register receiver, - Register key, - Register elements, - Register elements_map, - Register scratch2, - Register result, - Label* not_fast_array, - Label* slow) { - ASSERT(!AreAliased(receiver, key, elements, elements_map, scratch2)); - - // Check for fast array. - __ Ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset)); - if (not_fast_array != NULL) { - // Check that the object is in fast mode and writable. - __ Ldr(elements_map, FieldMemOperand(elements, HeapObject::kMapOffset)); - __ JumpIfNotRoot(elements_map, Heap::kFixedArrayMapRootIndex, - not_fast_array); - } else { - __ AssertFastElements(elements); - } - - // The elements_map register is only used for the not_fast_array path, which - // was handled above. From this point onward it is a scratch register. - Register scratch1 = elements_map; - - // Check that the key (index) is within bounds. - __ Ldr(scratch1, FieldMemOperand(elements, FixedArray::kLengthOffset)); - __ Cmp(key, scratch1); - __ B(hs, slow); - - // Fast case: Do the load. - __ Add(scratch1, elements, FixedArray::kHeaderSize - kHeapObjectTag); - __ SmiUntag(scratch2, key); - __ Ldr(scratch2, MemOperand(scratch1, scratch2, LSL, kPointerSizeLog2)); - - // In case the loaded value is the_hole we have to consult GetProperty - // to ensure the prototype chain is searched. - __ JumpIfRoot(scratch2, Heap::kTheHoleValueRootIndex, slow); - - // Move the value to the result register. - // 'result' can alias with 'receiver' or 'key' but these two must be - // preserved if we jump to 'slow'. - __ Mov(result, scratch2); -} - - -// Checks whether a key is an array index string or a unique name. -// Falls through if a key is a unique name. -// The map of the key is returned in 'map_scratch'. -// If the jump to 'index_string' is done the hash of the key is left -// in 'hash_scratch'. -static void GenerateKeyNameCheck(MacroAssembler* masm, - Register key, - Register map_scratch, - Register hash_scratch, - Label* index_string, - Label* not_unique) { - ASSERT(!AreAliased(key, map_scratch, hash_scratch)); - - // Is the key a name? - Label unique; - __ JumpIfObjectType(key, map_scratch, hash_scratch, LAST_UNIQUE_NAME_TYPE, - not_unique, hi); - STATIC_ASSERT(LAST_UNIQUE_NAME_TYPE == FIRST_NONSTRING_TYPE); - __ B(eq, &unique); - - // Is the string an array index with cached numeric value? - __ Ldr(hash_scratch.W(), FieldMemOperand(key, Name::kHashFieldOffset)); - __ TestAndBranchIfAllClear(hash_scratch, - Name::kContainsCachedArrayIndexMask, - index_string); - - // Is the string internalized? We know it's a string, so a single bit test is - // enough. - __ Ldrb(hash_scratch, FieldMemOperand(map_scratch, Map::kInstanceTypeOffset)); - STATIC_ASSERT(kInternalizedTag == 0); - __ TestAndBranchIfAnySet(hash_scratch, kIsNotInternalizedMask, not_unique); - - __ Bind(&unique); - // Fall through if the key is a unique name. -} - - -// Neither 'object' nor 'key' are modified by this function. -// -// If the 'unmapped_case' or 'slow_case' exit is taken, the 'map' register is -// left with the object's elements map. Otherwise, it is used as a scratch -// register. -static MemOperand GenerateMappedArgumentsLookup(MacroAssembler* masm, - Register object, - Register key, - Register map, - Register scratch1, - Register scratch2, - Label* unmapped_case, - Label* slow_case) { - ASSERT(!AreAliased(object, key, map, scratch1, scratch2)); - - Heap* heap = masm->isolate()->heap(); - - // Check that the receiver is a JSObject. Because of the elements - // map check later, we do not need to check for interceptors or - // whether it requires access checks. - __ JumpIfSmi(object, slow_case); - // Check that the object is some kind of JSObject. - __ JumpIfObjectType(object, map, scratch1, FIRST_JS_RECEIVER_TYPE, - slow_case, lt); - - // Check that the key is a positive smi. - __ JumpIfNotSmi(key, slow_case); - __ Tbnz(key, kXSignBit, slow_case); - - // Load the elements object and check its map. - Handle arguments_map(heap->non_strict_arguments_elements_map()); - __ Ldr(map, FieldMemOperand(object, JSObject::kElementsOffset)); - __ CheckMap(map, scratch1, arguments_map, slow_case, DONT_DO_SMI_CHECK); - - // Check if element is in the range of mapped arguments. If not, jump - // to the unmapped lookup. - __ Ldr(scratch1, FieldMemOperand(map, FixedArray::kLengthOffset)); - __ Sub(scratch1, scratch1, Operand(Smi::FromInt(2))); - __ Cmp(key, scratch1); - __ B(hs, unmapped_case); - - // Load element index and check whether it is the hole. - static const int offset = - FixedArray::kHeaderSize + 2 * kPointerSize - kHeapObjectTag; - - __ Add(scratch1, map, offset); - __ SmiUntag(scratch2, key); - __ Ldr(scratch1, MemOperand(scratch1, scratch2, LSL, kPointerSizeLog2)); - __ JumpIfRoot(scratch1, Heap::kTheHoleValueRootIndex, unmapped_case); - - // Load value from context and return it. - __ Ldr(scratch2, FieldMemOperand(map, FixedArray::kHeaderSize)); - __ SmiUntag(scratch1); - __ Add(scratch2, scratch2, Context::kHeaderSize - kHeapObjectTag); - return MemOperand(scratch2, scratch1, LSL, kPointerSizeLog2); -} - - -// The 'parameter_map' register must be loaded with the parameter map of the -// arguments object and is overwritten. -static MemOperand GenerateUnmappedArgumentsLookup(MacroAssembler* masm, - Register key, - Register parameter_map, - Register scratch, - Label* slow_case) { - ASSERT(!AreAliased(key, parameter_map, scratch)); - - // Element is in arguments backing store, which is referenced by the - // second element of the parameter_map. - const int kBackingStoreOffset = FixedArray::kHeaderSize + kPointerSize; - Register backing_store = parameter_map; - __ Ldr(backing_store, FieldMemOperand(parameter_map, kBackingStoreOffset)); - Handle fixed_array_map(masm->isolate()->heap()->fixed_array_map()); - __ CheckMap( - backing_store, scratch, fixed_array_map, slow_case, DONT_DO_SMI_CHECK); - __ Ldr(scratch, FieldMemOperand(backing_store, FixedArray::kLengthOffset)); - __ Cmp(key, scratch); - __ B(hs, slow_case); - - __ Add(backing_store, - backing_store, - FixedArray::kHeaderSize - kHeapObjectTag); - __ SmiUntag(scratch, key); - return MemOperand(backing_store, scratch, LSL, kPointerSizeLog2); -} - - -void LoadIC::GenerateMegamorphic(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- x2 : name - // -- lr : return address - // -- x0 : receiver - // ----------------------------------- - - // Probe the stub cache. - Code::Flags flags = Code::ComputeHandlerFlags(Code::LOAD_IC); - masm->isolate()->stub_cache()->GenerateProbe( - masm, flags, x0, x2, x3, x4, x5, x6); - - // Cache miss: Jump to runtime. - GenerateMiss(masm); -} - - -void LoadIC::GenerateNormal(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- x2 : name - // -- lr : return address - // -- x0 : receiver - // ----------------------------------- - Label miss; - - GenerateNameDictionaryReceiverCheck(masm, x0, x1, x3, x4, &miss); - - // x1 now holds the property dictionary. - GenerateDictionaryLoad(masm, &miss, x1, x2, x0, x3, x4); - __ Ret(); - - // Cache miss: Jump to runtime. - __ Bind(&miss); - GenerateMiss(masm); -} - - -void LoadIC::GenerateMiss(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- x2 : name - // -- lr : return address - // -- x0 : receiver - // ----------------------------------- - Isolate* isolate = masm->isolate(); - ASM_LOCATION("LoadIC::GenerateMiss"); - - __ IncrementCounter(isolate->counters()->load_miss(), 1, x3, x4); - - // TODO(jbramley): Does the target actually expect an argument in x3, or is - // this inherited from ARM's push semantics? - __ Mov(x3, x0); - __ Push(x3, x2); - - // Perform tail call to the entry. - ExternalReference ref = - ExternalReference(IC_Utility(kLoadIC_Miss), isolate); - __ TailCallExternalReference(ref, 2, 1); -} - - -void LoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) { - // ---------- S t a t e -------------- - // -- x2 : name - // -- lr : return address - // -- x0 : receiver - // ----------------------------------- - - // TODO(jbramley): Does the target actually expect an argument in x3, or is - // this inherited from ARM's push semantics? - __ Mov(x3, x0); - __ Push(x3, x2); - - __ TailCallRuntime(Runtime::kGetProperty, 2, 1); -} - - -void KeyedLoadIC::GenerateNonStrictArguments(MacroAssembler* masm) { - // ---------- S t a t e -------------- - // -- lr : return address - // -- x0 : key - // -- x1 : receiver - // ----------------------------------- - Register result = x0; - Register key = x0; - Register receiver = x1; - Label miss, unmapped; - - Register map_scratch = x2; - MemOperand mapped_location = GenerateMappedArgumentsLookup( - masm, receiver, key, map_scratch, x3, x4, &unmapped, &miss); - __ Ldr(result, mapped_location); - __ Ret(); - - __ Bind(&unmapped); - // Parameter map is left in map_scratch when a jump on unmapped is done. - MemOperand unmapped_location = - GenerateUnmappedArgumentsLookup(masm, key, map_scratch, x3, &miss); - __ Ldr(x2, unmapped_location); - __ JumpIfRoot(x2, Heap::kTheHoleValueRootIndex, &miss); - // Move the result in x0. x0 must be preserved on miss. - __ Mov(result, x2); - __ Ret(); - - __ Bind(&miss); - GenerateMiss(masm); -} - - -void KeyedStoreIC::GenerateNonStrictArguments(MacroAssembler* masm) { - ASM_LOCATION("KeyedStoreIC::GenerateNonStrictArguments"); - // ---------- S t a t e -------------- - // -- lr : return address - // -- x0 : value - // -- x1 : key - // -- x2 : receiver - // ----------------------------------- - - Label slow, notin; - - Register value = x0; - Register key = x1; - Register receiver = x2; - Register map = x3; - - // These registers are used by GenerateMappedArgumentsLookup to build a - // MemOperand. They are live for as long as the MemOperand is live. - Register mapped1 = x4; - Register mapped2 = x5; - - MemOperand mapped = - GenerateMappedArgumentsLookup(masm, receiver, key, map, - mapped1, mapped2, - ¬in, &slow); - Operand mapped_offset = mapped.OffsetAsOperand(); - __ Str(value, mapped); - __ Add(x10, mapped.base(), mapped_offset); - __ Mov(x11, value); - __ RecordWrite(mapped.base(), x10, x11, kLRHasNotBeenSaved, kDontSaveFPRegs); - __ Ret(); - - __ Bind(¬in); - - // These registers are used by GenerateMappedArgumentsLookup to build a - // MemOperand. They are live for as long as the MemOperand is live. - Register unmapped1 = map; // This is assumed to alias 'map'. - Register unmapped2 = x4; - MemOperand unmapped = - GenerateUnmappedArgumentsLookup(masm, key, unmapped1, unmapped2, &slow); - Operand unmapped_offset = unmapped.OffsetAsOperand(); - __ Str(value, unmapped); - __ Add(x10, unmapped.base(), unmapped_offset); - __ Mov(x11, value); - __ RecordWrite(unmapped.base(), x10, x11, - kLRHasNotBeenSaved, kDontSaveFPRegs); - __ Ret(); - __ Bind(&slow); - GenerateMiss(masm); -} - - -void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) { - // ---------- S t a t e -------------- - // -- lr : return address - // -- x0 : key - // -- x1 : receiver - // ----------------------------------- - Isolate* isolate = masm->isolate(); - - __ IncrementCounter(isolate->counters()->keyed_load_miss(), 1, x10, x11); - - __ Push(x1, x0); - - // Perform tail call to the entry. - ExternalReference ref = - ExternalReference(IC_Utility(kKeyedLoadIC_Miss), isolate); - - __ TailCallExternalReference(ref, 2, 1); -} - - -void KeyedLoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) { - // ---------- S t a t e -------------- - // -- lr : return address - // -- x0 : key - // -- x1 : receiver - // ----------------------------------- - Register key = x0; - Register receiver = x1; - - __ Push(receiver, key); - __ TailCallRuntime(Runtime::kKeyedGetProperty, 2, 1); -} - - -static void GenerateKeyedLoadWithSmiKey(MacroAssembler* masm, - Register key, - Register receiver, - Register scratch1, - Register scratch2, - Register scratch3, - Register scratch4, - Register scratch5, - Label *slow) { - ASSERT(!AreAliased( - key, receiver, scratch1, scratch2, scratch3, scratch4, scratch5)); - - Isolate* isolate = masm->isolate(); - Label check_number_dictionary; - // If we can load the value, it should be returned in x0. - Register result = x0; - - GenerateKeyedLoadReceiverCheck( - masm, receiver, scratch1, scratch2, Map::kHasIndexedInterceptor, slow); - - // Check the receiver's map to see if it has fast elements. - __ CheckFastElements(scratch1, scratch2, &check_number_dictionary); - - GenerateFastArrayLoad( - masm, receiver, key, scratch3, scratch2, scratch1, result, NULL, slow); - __ IncrementCounter( - isolate->counters()->keyed_load_generic_smi(), 1, scratch1, scratch2); - __ Ret(); - - __ Bind(&check_number_dictionary); - __ Ldr(scratch3, FieldMemOperand(receiver, JSObject::kElementsOffset)); - __ Ldr(scratch2, FieldMemOperand(scratch3, JSObject::kMapOffset)); - - // Check whether we have a number dictionary. - __ JumpIfNotRoot(scratch2, Heap::kHashTableMapRootIndex, slow); - - __ LoadFromNumberDictionary( - slow, scratch3, key, result, scratch1, scratch2, scratch4, scratch5); - __ Ret(); -} - -static void GenerateKeyedLoadWithNameKey(MacroAssembler* masm, - Register key, - Register receiver, - Register scratch1, - Register scratch2, - Register scratch3, - Register scratch4, - Register scratch5, - Label *slow) { - ASSERT(!AreAliased( - key, receiver, scratch1, scratch2, scratch3, scratch4, scratch5)); - - Isolate* isolate = masm->isolate(); - Label probe_dictionary, property_array_property; - // If we can load the value, it should be returned in x0. - Register result = x0; - - GenerateKeyedLoadReceiverCheck( - masm, receiver, scratch1, scratch2, Map::kHasNamedInterceptor, slow); - - // If the receiver is a fast-case object, check the keyed lookup cache. - // Otherwise probe the dictionary. - __ Ldr(scratch2, FieldMemOperand(receiver, JSObject::kPropertiesOffset)); - __ Ldr(scratch3, FieldMemOperand(scratch2, HeapObject::kMapOffset)); - __ JumpIfRoot(scratch3, Heap::kHashTableMapRootIndex, &probe_dictionary); - - // We keep the map of the receiver in scratch1. - Register receiver_map = scratch1; - - // Load the map of the receiver, compute the keyed lookup cache hash - // based on 32 bits of the map pointer and the name hash. - __ Ldr(receiver_map, FieldMemOperand(receiver, HeapObject::kMapOffset)); - __ Mov(scratch2, Operand(receiver_map, ASR, KeyedLookupCache::kMapHashShift)); - __ Ldr(scratch3.W(), FieldMemOperand(key, Name::kHashFieldOffset)); - __ Eor(scratch2, scratch2, Operand(scratch3, ASR, Name::kHashShift)); - int mask = KeyedLookupCache::kCapacityMask & KeyedLookupCache::kHashMask; - __ And(scratch2, scratch2, mask); - - // Load the key (consisting of map and unique name) from the cache and - // check for match. - Label load_in_object_property; - static const int kEntriesPerBucket = KeyedLookupCache::kEntriesPerBucket; - Label hit_on_nth_entry[kEntriesPerBucket]; - ExternalReference cache_keys = - ExternalReference::keyed_lookup_cache_keys(isolate); - - __ Mov(scratch3, Operand(cache_keys)); - __ Add(scratch3, scratch3, Operand(scratch2, LSL, kPointerSizeLog2 + 1)); - - for (int i = 0; i < kEntriesPerBucket - 1; i++) { - Label try_next_entry; - // Load map and make scratch3 pointing to the next entry. - __ Ldr(scratch4, MemOperand(scratch3, kPointerSize * 2, PostIndex)); - __ Cmp(receiver_map, scratch4); - __ B(ne, &try_next_entry); - __ Ldr(scratch4, MemOperand(scratch3, -kPointerSize)); // Load name - __ Cmp(key, scratch4); - __ B(eq, &hit_on_nth_entry[i]); - __ Bind(&try_next_entry); - } - - // Last entry. - __ Ldr(scratch4, MemOperand(scratch3, kPointerSize, PostIndex)); - __ Cmp(receiver_map, scratch4); - __ B(ne, slow); - __ Ldr(scratch4, MemOperand(scratch3)); - __ Cmp(key, scratch4); - __ B(ne, slow); - - // Get field offset. - ExternalReference cache_field_offsets = - ExternalReference::keyed_lookup_cache_field_offsets(isolate); - - // Hit on nth entry. - for (int i = kEntriesPerBucket - 1; i >= 0; i--) { - __ Bind(&hit_on_nth_entry[i]); - __ Mov(scratch3, Operand(cache_field_offsets)); - if (i != 0) { - __ Add(scratch2, scratch2, i); - } - __ Ldr(scratch4.W(), MemOperand(scratch3, scratch2, LSL, 2)); - __ Ldrb(scratch5, - FieldMemOperand(receiver_map, Map::kInObjectPropertiesOffset)); - __ Subs(scratch4, scratch4, scratch5); - __ B(ge, &property_array_property); - if (i != 0) { - __ B(&load_in_object_property); - } - } - - // Load in-object property. - __ Bind(&load_in_object_property); - __ Ldrb(scratch5, FieldMemOperand(receiver_map, Map::kInstanceSizeOffset)); - __ Add(scratch5, scratch5, scratch4); // Index from start of object. - __ Sub(receiver, receiver, kHeapObjectTag); // Remove the heap tag. - __ Ldr(result, MemOperand(receiver, scratch5, LSL, kPointerSizeLog2)); - __ IncrementCounter(isolate->counters()->keyed_load_generic_lookup_cache(), - 1, scratch1, scratch2); - __ Ret(); - - // Load property array property. - __ Bind(&property_array_property); - __ Ldr(scratch1, FieldMemOperand(receiver, JSObject::kPropertiesOffset)); - __ Add(scratch1, scratch1, FixedArray::kHeaderSize - kHeapObjectTag); - __ Ldr(result, MemOperand(scratch1, scratch4, LSL, kPointerSizeLog2)); - __ IncrementCounter(isolate->counters()->keyed_load_generic_lookup_cache(), - 1, scratch1, scratch2); - __ Ret(); - - // Do a quick inline probe of the receiver's dictionary, if it exists. - __ Bind(&probe_dictionary); - __ Ldr(scratch1, FieldMemOperand(receiver, HeapObject::kMapOffset)); - __ Ldrb(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset)); - GenerateGlobalInstanceTypeCheck(masm, scratch1, slow); - // Load the property. - GenerateDictionaryLoad(masm, slow, scratch2, key, result, scratch1, scratch3); - __ IncrementCounter(isolate->counters()->keyed_load_generic_symbol(), - 1, scratch1, scratch2); - __ Ret(); -} - - -void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { - // ---------- S t a t e -------------- - // -- lr : return address - // -- x0 : key - // -- x1 : receiver - // ----------------------------------- - Label slow, check_name, index_smi, index_name; - - Register key = x0; - Register receiver = x1; - - __ JumpIfNotSmi(key, &check_name); - __ Bind(&index_smi); - // Now the key is known to be a smi. This place is also jumped to from below - // where a numeric string is converted to a smi. - GenerateKeyedLoadWithSmiKey(masm, key, receiver, x2, x3, x4, x5, x6, &slow); - - // Slow case, key and receiver still in x0 and x1. - __ Bind(&slow); - __ IncrementCounter( - masm->isolate()->counters()->keyed_load_generic_slow(), 1, x2, x3); - GenerateRuntimeGetProperty(masm); - - __ Bind(&check_name); - GenerateKeyNameCheck(masm, key, x2, x3, &index_name, &slow); - - GenerateKeyedLoadWithNameKey(masm, key, receiver, x2, x3, x4, x5, x6, &slow); - - __ Bind(&index_name); - __ IndexFromHash(x3, key); - // Now jump to the place where smi keys are handled. - __ B(&index_smi); -} - - -void KeyedLoadIC::GenerateString(MacroAssembler* masm) { - // ---------- S t a t e -------------- - // -- lr : return address - // -- x0 : key (index) - // -- x1 : receiver - // ----------------------------------- - Label miss; - - Register index = x0; - Register receiver = x1; - Register result = x0; - Register scratch = x3; - - StringCharAtGenerator char_at_generator(receiver, - index, - scratch, - result, - &miss, // When not a string. - &miss, // When not a number. - &miss, // When index out of range. - STRING_INDEX_IS_ARRAY_INDEX); - char_at_generator.GenerateFast(masm); - __ Ret(); - - StubRuntimeCallHelper call_helper; - char_at_generator.GenerateSlow(masm, call_helper); - - __ Bind(&miss); - GenerateMiss(masm); -} - - -void KeyedLoadIC::GenerateIndexedInterceptor(MacroAssembler* masm) { - // ---------- S t a t e -------------- - // -- lr : return address - // -- x0 : key - // -- x1 : receiver - // ----------------------------------- - Label slow; - Register key = x0; - Register receiver = x1; - - // Check that the receiver isn't a smi. - __ JumpIfSmi(receiver, &slow); - - // Check that the key is an array index, that is Uint32. - __ TestAndBranchIfAnySet(key, kSmiTagMask | kSmiSignMask, &slow); - - // Get the map of the receiver. - Register map = x2; - __ Ldr(map, FieldMemOperand(receiver, HeapObject::kMapOffset)); - - // Check that it has indexed interceptor and access checks - // are not enabled for this object. - __ Ldrb(x3, FieldMemOperand(map, Map::kBitFieldOffset)); - ASSERT(kSlowCaseBitFieldMask == - ((1 << Map::kIsAccessCheckNeeded) | (1 << Map::kHasIndexedInterceptor))); - __ Tbnz(x3, Map::kIsAccessCheckNeeded, &slow); - __ Tbz(x3, Map::kHasIndexedInterceptor, &slow); - - // Everything is fine, call runtime. - __ Push(receiver, key); - __ TailCallExternalReference( - ExternalReference(IC_Utility(kKeyedLoadPropertyWithInterceptor), - masm->isolate()), - 2, - 1); - - __ Bind(&slow); - GenerateMiss(masm); -} - - -void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) { - ASM_LOCATION("KeyedStoreIC::GenerateMiss"); - // ---------- S t a t e -------------- - // -- x0 : value - // -- x1 : key - // -- x2 : receiver - // -- lr : return address - // ----------------------------------- - - // Push receiver, key and value for runtime call. - __ Push(x2, x1, x0); - - ExternalReference ref = - ExternalReference(IC_Utility(kKeyedStoreIC_Miss), masm->isolate()); - __ TailCallExternalReference(ref, 3, 1); -} - - -void KeyedStoreIC::GenerateSlow(MacroAssembler* masm) { - ASM_LOCATION("KeyedStoreIC::GenerateSlow"); - // ---------- S t a t e -------------- - // -- lr : return address - // -- x0 : value - // -- x1 : key - // -- x2 : receiver - // ----------------------------------- - - // Push receiver, key and value for runtime call. - __ Push(x2, x1, x0); - - // The slow case calls into the runtime to complete the store without causing - // an IC miss that would otherwise cause a transition to the generic stub. - ExternalReference ref = - ExternalReference(IC_Utility(kKeyedStoreIC_Slow), masm->isolate()); - __ TailCallExternalReference(ref, 3, 1); -} - - -void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm, - StrictModeFlag strict_mode) { - ASM_LOCATION("KeyedStoreIC::GenerateRuntimeSetProperty"); - // ---------- S t a t e -------------- - // -- x0 : value - // -- x1 : key - // -- x2 : receiver - // -- lr : return address - // ----------------------------------- - - // Push receiver, key and value for runtime call. - __ Push(x2, x1, x0); - - // Push PropertyAttributes(NONE) and strict_mode for runtime call. - STATIC_ASSERT(NONE == 0); - __ Mov(x10, Operand(Smi::FromInt(strict_mode))); - __ Push(xzr, x10); - - __ TailCallRuntime(Runtime::kSetProperty, 5, 1); -} - - -static void KeyedStoreGenerateGenericHelper( - MacroAssembler* masm, - Label* fast_object, - Label* fast_double, - Label* slow, - KeyedStoreCheckMap check_map, - KeyedStoreIncrementLength increment_length, - Register value, - Register key, - Register receiver, - Register receiver_map, - Register elements_map, - Register elements) { - ASSERT(!AreAliased( - value, key, receiver, receiver_map, elements_map, elements, x10, x11)); - - Label transition_smi_elements; - Label transition_double_elements; - Label fast_double_without_map_check; - Label non_double_value; - Label finish_store; - - __ Bind(fast_object); - if (check_map == kCheckMap) { - __ Ldr(elements_map, FieldMemOperand(elements, HeapObject::kMapOffset)); - __ Cmp(elements_map, - Operand(masm->isolate()->factory()->fixed_array_map())); - __ B(ne, fast_double); - } - - // HOLECHECK: guards "A[i] = V" - // We have to go to the runtime if the current value is the hole because there - // may be a callback on the element. - Label holecheck_passed; - // TODO(all): This address calculation is repeated later (for the store - // itself). We should keep the result to avoid doing the work twice. - __ Add(x10, elements, FixedArray::kHeaderSize - kHeapObjectTag); - __ Add(x10, x10, Operand::UntagSmiAndScale(key, kPointerSizeLog2)); - __ Ldr(x11, MemOperand(x10)); - __ JumpIfNotRoot(x11, Heap::kTheHoleValueRootIndex, &holecheck_passed); - __ JumpIfDictionaryInPrototypeChain(receiver, elements_map, x10, slow); - __ bind(&holecheck_passed); - - // Smi stores don't require further checks. - __ JumpIfSmi(value, &finish_store); - - // Escape to elements kind transition case. - __ CheckFastObjectElements(receiver_map, x10, &transition_smi_elements); - - __ Bind(&finish_store); - if (increment_length == kIncrementLength) { - // Add 1 to receiver->length. - __ Add(x10, key, Operand(Smi::FromInt(1))); - __ Str(x10, FieldMemOperand(receiver, JSArray::kLengthOffset)); - } - - Register address = x11; - __ Add(address, elements, FixedArray::kHeaderSize - kHeapObjectTag); - __ Add(address, address, Operand::UntagSmiAndScale(key, kPointerSizeLog2)); - __ Str(value, MemOperand(address)); - - Label dont_record_write; - __ JumpIfSmi(value, &dont_record_write); - - // Update write barrier for the elements array address. - __ Mov(x10, value); // Preserve the value which is returned. - __ RecordWrite(elements, - address, - x10, - kLRHasNotBeenSaved, - kDontSaveFPRegs, - EMIT_REMEMBERED_SET, - OMIT_SMI_CHECK); - - __ Bind(&dont_record_write); - __ Ret(); - - - __ Bind(fast_double); - if (check_map == kCheckMap) { - // Check for fast double array case. If this fails, call through to the - // runtime. - __ JumpIfNotRoot(elements_map, Heap::kFixedDoubleArrayMapRootIndex, slow); - } - - // HOLECHECK: guards "A[i] double hole?" - // We have to see if the double version of the hole is present. If so go to - // the runtime. - // TODO(all): This address calculation was done earlier. We should keep the - // result to avoid doing the work twice. - __ Add(x10, elements, FixedDoubleArray::kHeaderSize - kHeapObjectTag); - __ Add(x10, x10, Operand::UntagSmiAndScale(key, kPointerSizeLog2)); - __ Ldr(x11, MemOperand(x10)); - __ CompareAndBranch(x11, kHoleNanInt64, ne, &fast_double_without_map_check); - __ JumpIfDictionaryInPrototypeChain(receiver, elements_map, x10, slow); - - __ Bind(&fast_double_without_map_check); - __ StoreNumberToDoubleElements(value, - key, - elements, - x10, - d0, - d1, - &transition_double_elements); - if (increment_length == kIncrementLength) { - // Add 1 to receiver->length. - __ Add(x10, key, Operand(Smi::FromInt(1))); - __ Str(x10, FieldMemOperand(receiver, JSArray::kLengthOffset)); - } - __ Ret(); - - - __ Bind(&transition_smi_elements); - // Transition the array appropriately depending on the value type. - __ Ldr(x10, FieldMemOperand(value, HeapObject::kMapOffset)); - __ JumpIfNotRoot(x10, Heap::kHeapNumberMapRootIndex, &non_double_value); - - // Value is a double. Transition FAST_SMI_ELEMENTS -> - // FAST_DOUBLE_ELEMENTS and complete the store. - __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, - FAST_DOUBLE_ELEMENTS, - receiver_map, - x10, - slow); - ASSERT(receiver_map.Is(x3)); // Transition code expects map in x3. - AllocationSiteMode mode = AllocationSite::GetMode(FAST_SMI_ELEMENTS, - FAST_DOUBLE_ELEMENTS); - ElementsTransitionGenerator::GenerateSmiToDouble(masm, mode, slow); - __ Ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset)); - __ B(&fast_double_without_map_check); - - __ Bind(&non_double_value); - // Value is not a double, FAST_SMI_ELEMENTS -> FAST_ELEMENTS. - __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, - FAST_ELEMENTS, - receiver_map, - x10, - slow); - ASSERT(receiver_map.Is(x3)); // Transition code expects map in x3. - mode = AllocationSite::GetMode(FAST_SMI_ELEMENTS, FAST_ELEMENTS); - ElementsTransitionGenerator::GenerateMapChangeElementsTransition(masm, mode, - slow); - __ Ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset)); - __ B(&finish_store); - - __ Bind(&transition_double_elements); - // Elements are FAST_DOUBLE_ELEMENTS, but value is an Object that's not a - // HeapNumber. Make sure that the receiver is a Array with FAST_ELEMENTS and - // transition array from FAST_DOUBLE_ELEMENTS to FAST_ELEMENTS - __ LoadTransitionedArrayMapConditional(FAST_DOUBLE_ELEMENTS, - FAST_ELEMENTS, - receiver_map, - x10, - slow); - ASSERT(receiver_map.Is(x3)); // Transition code expects map in x3. - mode = AllocationSite::GetMode(FAST_DOUBLE_ELEMENTS, FAST_ELEMENTS); - ElementsTransitionGenerator::GenerateDoubleToObject(masm, mode, slow); - __ Ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset)); - __ B(&finish_store); -} - - -void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm, - StrictModeFlag strict_mode) { - ASM_LOCATION("KeyedStoreIC::GenerateGeneric"); - // ---------- S t a t e -------------- - // -- x0 : value - // -- x1 : key - // -- x2 : receiver - // -- lr : return address - // ----------------------------------- - Label slow; - Label array; - Label fast_object; - Label extra; - Label fast_object_grow; - Label fast_double_grow; - Label fast_double; - - Register value = x0; - Register key = x1; - Register receiver = x2; - Register receiver_map = x3; - Register elements = x4; - Register elements_map = x5; - - __ JumpIfNotSmi(key, &slow); - __ JumpIfSmi(receiver, &slow); - __ Ldr(receiver_map, FieldMemOperand(receiver, HeapObject::kMapOffset)); - - // Check that the receiver does not require access checks and is not observed. - // The generic stub does not perform map checks or handle observed objects. - __ Ldrb(x10, FieldMemOperand(receiver_map, Map::kBitFieldOffset)); - __ TestAndBranchIfAnySet( - x10, (1 << Map::kIsAccessCheckNeeded) | (1 << Map::kIsObserved), &slow); - - // Check if the object is a JS array or not. - Register instance_type = x10; - __ CompareInstanceType(receiver_map, instance_type, JS_ARRAY_TYPE); - __ B(eq, &array); - // Check that the object is some kind of JSObject. - __ Cmp(instance_type, FIRST_JS_OBJECT_TYPE); - __ B(lt, &slow); - - // Object case: Check key against length in the elements array. - __ Ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset)); - // Check array bounds. Both the key and the length of FixedArray are smis. - __ Ldrsw(x10, UntagSmiFieldMemOperand(elements, FixedArray::kLengthOffset)); - __ Cmp(x10, Operand::UntagSmi(key)); - __ B(hi, &fast_object); - - - __ Bind(&slow); - // Slow case, handle jump to runtime. - // Live values: - // x0: value - // x1: key - // x2: receiver - GenerateRuntimeSetProperty(masm, strict_mode); - - - __ Bind(&extra); - // Extra capacity case: Check if there is extra capacity to - // perform the store and update the length. Used for adding one - // element to the array by writing to array[array.length]. - - // Check for room in the elements backing store. - // Both the key and the length of FixedArray are smis. - __ Ldrsw(x10, UntagSmiFieldMemOperand(elements, FixedArray::kLengthOffset)); - __ Cmp(x10, Operand::UntagSmi(key)); - __ B(ls, &slow); - - __ Ldr(elements_map, FieldMemOperand(elements, HeapObject::kMapOffset)); - __ Cmp(elements_map, Operand(masm->isolate()->factory()->fixed_array_map())); - __ B(eq, &fast_object_grow); - __ Cmp(elements_map, - Operand(masm->isolate()->factory()->fixed_double_array_map())); - __ B(eq, &fast_double_grow); - __ B(&slow); - - - __ Bind(&array); - // Array case: Get the length and the elements array from the JS - // array. Check that the array is in fast mode (and writable); if it - // is the length is always a smi. - - __ Ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset)); - - // Check the key against the length in the array. - __ Ldrsw(x10, UntagSmiFieldMemOperand(receiver, JSArray::kLengthOffset)); - __ Cmp(x10, Operand::UntagSmi(key)); - __ B(eq, &extra); // We can handle the case where we are appending 1 element. - __ B(lo, &slow); - - KeyedStoreGenerateGenericHelper(masm, &fast_object, &fast_double, - &slow, kCheckMap, kDontIncrementLength, - value, key, receiver, receiver_map, - elements_map, elements); - KeyedStoreGenerateGenericHelper(masm, &fast_object_grow, &fast_double_grow, - &slow, kDontCheckMap, kIncrementLength, - value, key, receiver, receiver_map, - elements_map, elements); -} - - -void StoreIC::GenerateMegamorphic(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- x0 : value - // -- x1 : receiver - // -- x2 : name - // -- lr : return address - // ----------------------------------- - - // Probe the stub cache. - Code::Flags flags = Code::ComputeHandlerFlags(Code::STORE_IC); - masm->isolate()->stub_cache()->GenerateProbe( - masm, flags, x1, x2, x3, x4, x5, x6); - - // Cache miss: Jump to runtime. - GenerateMiss(masm); -} - - -void StoreIC::GenerateMiss(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- x0 : value - // -- x1 : receiver - // -- x2 : name - // -- lr : return address - // ----------------------------------- - - __ Push(x1, x2, x0); - - // Tail call to the entry. - ExternalReference ref = - ExternalReference(IC_Utility(kStoreIC_Miss), masm->isolate()); - __ TailCallExternalReference(ref, 3, 1); -} - - -void StoreIC::GenerateNormal(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- x0 : value - // -- x1 : receiver - // -- x2 : name - // -- lr : return address - // ----------------------------------- - Label miss; - Register value = x0; - Register receiver = x1; - Register name = x2; - Register dictionary = x3; - - GenerateNameDictionaryReceiverCheck( - masm, receiver, dictionary, x4, x5, &miss); - - GenerateDictionaryStore(masm, &miss, dictionary, name, value, x4, x5); - Counters* counters = masm->isolate()->counters(); - __ IncrementCounter(counters->store_normal_hit(), 1, x4, x5); - __ Ret(); - - // Cache miss: Jump to runtime. - __ Bind(&miss); - __ IncrementCounter(counters->store_normal_miss(), 1, x4, x5); - GenerateMiss(masm); -} - - -void StoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm, - StrictModeFlag strict_mode) { - ASM_LOCATION("StoreIC::GenerateRuntimeSetProperty"); - // ----------- S t a t e ------------- - // -- x0 : value - // -- x1 : receiver - // -- x2 : name - // -- lr : return address - // ----------------------------------- - - __ Push(x1, x2, x0); - - __ Mov(x11, Operand(Smi::FromInt(NONE))); // PropertyAttributes - __ Mov(x10, Operand(Smi::FromInt(strict_mode))); - __ Push(x11, x10); - - // Do tail-call to runtime routine. - __ TailCallRuntime(Runtime::kSetProperty, 5, 1); -} - - -void StoreIC::GenerateSlow(MacroAssembler* masm) { - // ---------- S t a t e -------------- - // -- x0 : value - // -- x1 : receiver - // -- x2 : name - // -- lr : return address - // ----------------------------------- - - // Push receiver, name and value for runtime call. - __ Push(x1, x2, x0); - - // The slow case calls into the runtime to complete the store without causing - // an IC miss that would otherwise cause a transition to the generic stub. - ExternalReference ref = - ExternalReference(IC_Utility(kStoreIC_Slow), masm->isolate()); - __ TailCallExternalReference(ref, 3, 1); -} - - -Condition CompareIC::ComputeCondition(Token::Value op) { - switch (op) { - case Token::EQ_STRICT: - case Token::EQ: - return eq; - case Token::LT: - return lt; - case Token::GT: - return gt; - case Token::LTE: - return le; - case Token::GTE: - return ge; - default: - UNREACHABLE(); - return al; - } -} - - -bool CompareIC::HasInlinedSmiCode(Address address) { - // The address of the instruction following the call. - Address info_address = - Assembler::return_address_from_call_start(address); - - InstructionSequence* patch_info = InstructionSequence::At(info_address); - return patch_info->IsInlineData(); -} - - -// Activate a SMI fast-path by patching the instructions generated by -// JumpPatchSite::EmitJumpIf(Not)Smi(), using the information encoded by -// JumpPatchSite::EmitPatchInfo(). -void PatchInlinedSmiCode(Address address, InlinedSmiCheck check) { - // The patch information is encoded in the instruction stream using - // instructions which have no side effects, so we can safely execute them. - // The patch information is encoded directly after the call to the helper - // function which is requesting this patch operation. - Address info_address = - Assembler::return_address_from_call_start(address); - InlineSmiCheckInfo info(info_address); - - // Check and decode the patch information instruction. - if (!info.HasSmiCheck()) { - return; - } - - if (FLAG_trace_ic) { - PrintF("[ Patching ic at %p, marker=%p, SMI check=%p\n", - address, info_address, reinterpret_cast(info.SmiCheck())); - } - - // Patch and activate code generated by JumpPatchSite::EmitJumpIfNotSmi() - // and JumpPatchSite::EmitJumpIfSmi(). - // Changing - // tb(n)z xzr, #0, - // to - // tb(!n)z test_reg, #0, - Instruction* to_patch = info.SmiCheck(); - PatchingAssembler patcher(to_patch, 1); - ASSERT(to_patch->IsTestBranch()); - ASSERT(to_patch->ImmTestBranchBit5() == 0); - ASSERT(to_patch->ImmTestBranchBit40() == 0); - - STATIC_ASSERT(kSmiTag == 0); - STATIC_ASSERT(kSmiTagMask == 1); - - int branch_imm = to_patch->ImmTestBranch(); - Register smi_reg; - if (check == ENABLE_INLINED_SMI_CHECK) { - ASSERT(to_patch->Rt() == xzr.code()); - smi_reg = info.SmiRegister(); - } else { - ASSERT(check == DISABLE_INLINED_SMI_CHECK); - ASSERT(to_patch->Rt() != xzr.code()); - smi_reg = xzr; - } - - if (to_patch->Mask(TestBranchMask) == TBZ) { - // This is JumpIfNotSmi(smi_reg, branch_imm). - patcher.tbnz(smi_reg, 0, branch_imm); - } else { - ASSERT(to_patch->Mask(TestBranchMask) == TBNZ); - // This is JumpIfSmi(smi_reg, branch_imm). - patcher.tbz(smi_reg, 0, branch_imm); - } -} - - -} } // namespace v8::internal - -#endif // V8_TARGET_ARCH_A64 diff --git a/deps/v8/src/a64/instructions-a64.cc b/deps/v8/src/a64/instructions-a64.cc deleted file mode 100644 index 4496d56753..0000000000 --- a/deps/v8/src/a64/instructions-a64.cc +++ /dev/null @@ -1,334 +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 "v8.h" - -#if V8_TARGET_ARCH_A64 - -#define A64_DEFINE_FP_STATICS - -#include "a64/instructions-a64.h" -#include "a64/assembler-a64-inl.h" - -namespace v8 { -namespace internal { - - -bool Instruction::IsLoad() const { - if (Mask(LoadStoreAnyFMask) != LoadStoreAnyFixed) { - return false; - } - - if (Mask(LoadStorePairAnyFMask) == LoadStorePairAnyFixed) { - return Mask(LoadStorePairLBit) != 0; - } else { - LoadStoreOp op = static_cast(Mask(LoadStoreOpMask)); - switch (op) { - case LDRB_w: - case LDRH_w: - case LDR_w: - case LDR_x: - case LDRSB_w: - case LDRSB_x: - case LDRSH_w: - case LDRSH_x: - case LDRSW_x: - case LDR_s: - case LDR_d: return true; - default: return false; - } - } -} - - -bool Instruction::IsStore() const { - if (Mask(LoadStoreAnyFMask) != LoadStoreAnyFixed) { - return false; - } - - if (Mask(LoadStorePairAnyFMask) == LoadStorePairAnyFixed) { - return Mask(LoadStorePairLBit) == 0; - } else { - LoadStoreOp op = static_cast(Mask(LoadStoreOpMask)); - switch (op) { - case STRB_w: - case STRH_w: - case STR_w: - case STR_x: - case STR_s: - case STR_d: return true; - default: return false; - } - } -} - - -static uint64_t RotateRight(uint64_t value, - unsigned int rotate, - unsigned int width) { - ASSERT(width <= 64); - rotate &= 63; - return ((value & ((1UL << rotate) - 1UL)) << (width - rotate)) | - (value >> rotate); -} - - -static uint64_t RepeatBitsAcrossReg(unsigned reg_size, - uint64_t value, - unsigned width) { - ASSERT((width == 2) || (width == 4) || (width == 8) || (width == 16) || - (width == 32)); - ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); - uint64_t result = value & ((1UL << width) - 1UL); - for (unsigned i = width; i < reg_size; i *= 2) { - result |= (result << i); - } - return result; -} - - -// Logical immediates can't encode zero, so a return value of zero is used to -// indicate a failure case. Specifically, where the constraints on imm_s are not -// met. -uint64_t Instruction::ImmLogical() { - unsigned reg_size = SixtyFourBits() ? kXRegSize : kWRegSize; - int64_t n = BitN(); - int64_t imm_s = ImmSetBits(); - int64_t imm_r = ImmRotate(); - - // An integer is constructed from the n, imm_s and imm_r bits according to - // the following table: - // - // N imms immr size S R - // 1 ssssss rrrrrr 64 UInt(ssssss) UInt(rrrrrr) - // 0 0sssss xrrrrr 32 UInt(sssss) UInt(rrrrr) - // 0 10ssss xxrrrr 16 UInt(ssss) UInt(rrrr) - // 0 110sss xxxrrr 8 UInt(sss) UInt(rrr) - // 0 1110ss xxxxrr 4 UInt(ss) UInt(rr) - // 0 11110s xxxxxr 2 UInt(s) UInt(r) - // (s bits must not be all set) - // - // A pattern is constructed of size bits, where the least significant S+1 - // bits are set. The pattern is rotated right by R, and repeated across a - // 32 or 64-bit value, depending on destination register width. - // - - if (n == 1) { - if (imm_s == 0x3F) { - return 0; - } - uint64_t bits = (1UL << (imm_s + 1)) - 1; - return RotateRight(bits, imm_r, 64); - } else { - if ((imm_s >> 1) == 0x1F) { - return 0; - } - for (int width = 0x20; width >= 0x2; width >>= 1) { - if ((imm_s & width) == 0) { - int mask = width - 1; - if ((imm_s & mask) == mask) { - return 0; - } - uint64_t bits = (1UL << ((imm_s & mask) + 1)) - 1; - return RepeatBitsAcrossReg(reg_size, - RotateRight(bits, imm_r & mask, width), - width); - } - } - } - UNREACHABLE(); - return 0; -} - - -float Instruction::ImmFP32() { - // ImmFP: abcdefgh (8 bits) - // Single: aBbb.bbbc.defg.h000.0000.0000.0000.0000 (32 bits) - // where B is b ^ 1 - uint32_t bits = ImmFP(); - uint32_t bit7 = (bits >> 7) & 0x1; - uint32_t bit6 = (bits >> 6) & 0x1; - uint32_t bit5_to_0 = bits & 0x3f; - uint32_t result = (bit7 << 31) | ((32 - bit6) << 25) | (bit5_to_0 << 19); - - return rawbits_to_float(result); -} - - -double Instruction::ImmFP64() { - // ImmFP: abcdefgh (8 bits) - // Double: aBbb.bbbb.bbcd.efgh.0000.0000.0000.0000 - // 0000.0000.0000.0000.0000.0000.0000.0000 (64 bits) - // where B is b ^ 1 - uint32_t bits = ImmFP(); - uint64_t bit7 = (bits >> 7) & 0x1; - uint64_t bit6 = (bits >> 6) & 0x1; - uint64_t bit5_to_0 = bits & 0x3f; - uint64_t result = (bit7 << 63) | ((256 - bit6) << 54) | (bit5_to_0 << 48); - - return rawbits_to_double(result); -} - - -LSDataSize CalcLSPairDataSize(LoadStorePairOp op) { - switch (op) { - case STP_x: - case LDP_x: - case STP_d: - case LDP_d: return LSDoubleWord; - default: return LSWord; - } -} - - -ptrdiff_t Instruction::ImmPCOffset() { - ptrdiff_t offset; - if (IsPCRelAddressing()) { - // PC-relative addressing. Only ADR is supported. - offset = ImmPCRel(); - } else if (BranchType() != UnknownBranchType) { - // All PC-relative branches. - // Relative branch offsets are instruction-size-aligned. - offset = ImmBranch() << kInstructionSizeLog2; - } else { - // Load literal (offset from PC). - ASSERT(IsLdrLiteral()); - // The offset is always shifted by 2 bits, even for loads to 64-bits - // registers. - offset = ImmLLiteral() << kInstructionSizeLog2; - } - return offset; -} - - -Instruction* Instruction::ImmPCOffsetTarget() { - return this + ImmPCOffset(); -} - - -bool Instruction::IsValidImmPCOffset(ImmBranchType branch_type, - int32_t offset) { - return is_intn(offset, ImmBranchRangeBitwidth(branch_type)); -} - - -bool Instruction::IsTargetInImmPCOffsetRange(Instruction* target) { - int offset = target - this; - return IsValidImmPCOffset(BranchType(), offset); -} - - -void Instruction::SetImmPCOffsetTarget(Instruction* target) { - if (IsPCRelAddressing()) { - SetPCRelImmTarget(target); - } else if (BranchType() != UnknownBranchType) { - SetBranchImmTarget(target); - } else { - SetImmLLiteral(target); - } -} - - -void Instruction::SetPCRelImmTarget(Instruction* target) { - // ADRP is not supported, so 'this' must point to an ADR instruction. - ASSERT(Mask(PCRelAddressingMask) == ADR); - - Instr imm = Assembler::ImmPCRelAddress(target - this); - - SetInstructionBits(Mask(~ImmPCRel_mask) | imm); -} - - -void Instruction::SetBranchImmTarget(Instruction* target) { - ASSERT(((target - this) & 3) == 0); - Instr branch_imm = 0; - uint32_t imm_mask = 0; - int offset = (target - this) >> kInstructionSizeLog2; - switch (BranchType()) { - case CondBranchType: { - branch_imm = Assembler::ImmCondBranch(offset); - imm_mask = ImmCondBranch_mask; - break; - } - case UncondBranchType: { - branch_imm = Assembler::ImmUncondBranch(offset); - imm_mask = ImmUncondBranch_mask; - break; - } - case CompareBranchType: { - branch_imm = Assembler::ImmCmpBranch(offset); - imm_mask = ImmCmpBranch_mask; - break; - } - case TestBranchType: { - branch_imm = Assembler::ImmTestBranch(offset); - imm_mask = ImmTestBranch_mask; - break; - } - default: UNREACHABLE(); - } - SetInstructionBits(Mask(~imm_mask) | branch_imm); -} - - -void Instruction::SetImmLLiteral(Instruction* source) { - ASSERT(((source - this) & 3) == 0); - int offset = (source - this) >> kLiteralEntrySizeLog2; - Instr imm = Assembler::ImmLLiteral(offset); - Instr mask = ImmLLiteral_mask; - - SetInstructionBits(Mask(~mask) | imm); -} - - -// TODO(jbramley): We can't put this inline in the class because things like -// xzr and Register are not defined in that header. Consider adding -// instructions-a64-inl.h to work around this. -bool InstructionSequence::IsInlineData() const { - // Inline data is encoded as a single movz instruction which writes to xzr - // (x31). - return IsMovz() && SixtyFourBits() && (Rd() == xzr.code()); - // TODO(all): If we extend ::InlineData() to support bigger data, we need - // to update this method too. -} - - -// TODO(jbramley): We can't put this inline in the class because things like -// xzr and Register are not defined in that header. Consider adding -// instructions-a64-inl.h to work around this. -uint64_t InstructionSequence::InlineData() const { - ASSERT(IsInlineData()); - uint64_t payload = ImmMoveWide(); - // TODO(all): If we extend ::InlineData() to support bigger data, we need - // to update this method too. - return payload; -} - - -} } // namespace v8::internal - -#endif // V8_TARGET_ARCH_A64 diff --git a/deps/v8/src/a64/instructions-a64.h b/deps/v8/src/a64/instructions-a64.h deleted file mode 100644 index 472d4bf9fd..0000000000 --- a/deps/v8/src/a64/instructions-a64.h +++ /dev/null @@ -1,516 +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. - -#ifndef V8_A64_INSTRUCTIONS_A64_H_ -#define V8_A64_INSTRUCTIONS_A64_H_ - -#include "globals.h" -#include "utils.h" -#include "a64/constants-a64.h" -#include "a64/utils-a64.h" - -namespace v8 { -namespace internal { - - -// ISA constants. -------------------------------------------------------------- - -typedef uint32_t Instr; - -// The following macros initialize a float/double variable with a bit pattern -// without using static initializers: If A64_DEFINE_FP_STATICS is defined, the -// symbol is defined as uint32_t/uint64_t initialized with the desired bit -// pattern. Otherwise, the same symbol is declared as an external float/double. -#if defined(A64_DEFINE_FP_STATICS) -#define DEFINE_FLOAT(name, value) extern const uint32_t name = value -#define DEFINE_DOUBLE(name, value) extern const uint64_t name = value -#else -#define DEFINE_FLOAT(name, value) extern const float name -#define DEFINE_DOUBLE(name, value) extern const double name -#endif // defined(A64_DEFINE_FP_STATICS) - -DEFINE_FLOAT(kFP32PositiveInfinity, 0x7f800000); -DEFINE_FLOAT(kFP32NegativeInfinity, 0xff800000); -DEFINE_DOUBLE(kFP64PositiveInfinity, 0x7ff0000000000000UL); -DEFINE_DOUBLE(kFP64NegativeInfinity, 0xfff0000000000000UL); - -// This value is a signalling NaN as both a double and as a float (taking the -// least-significant word). -DEFINE_DOUBLE(kFP64SignallingNaN, 0x7ff000007f800001); -DEFINE_FLOAT(kFP32SignallingNaN, 0x7f800001); - -// A similar value, but as a quiet NaN. -DEFINE_DOUBLE(kFP64QuietNaN, 0x7ff800007fc00001); -DEFINE_FLOAT(kFP32QuietNaN, 0x7fc00001); - -#undef DEFINE_FLOAT -#undef DEFINE_DOUBLE - - -enum LSDataSize { - LSByte = 0, - LSHalfword = 1, - LSWord = 2, - LSDoubleWord = 3 -}; - -LSDataSize CalcLSPairDataSize(LoadStorePairOp op); - -enum ImmBranchType { - UnknownBranchType = 0, - CondBranchType = 1, - UncondBranchType = 2, - CompareBranchType = 3, - TestBranchType = 4 -}; - -enum AddrMode { - Offset, - PreIndex, - PostIndex -}; - -enum FPRounding { - // The first four values are encodable directly by FPCR. - FPTieEven = 0x0, - FPPositiveInfinity = 0x1, - FPNegativeInfinity = 0x2, - FPZero = 0x3, - - // The final rounding mode is only available when explicitly specified by the - // instruction (such as with fcvta). It cannot be set in FPCR. - FPTieAway -}; - -enum Reg31Mode { - Reg31IsStackPointer, - Reg31IsZeroRegister -}; - -// Instructions. --------------------------------------------------------------- - -class Instruction { - public: - Instr InstructionBits() const { - Instr bits; - memcpy(&bits, this, sizeof(bits)); - return bits; - } - - void SetInstructionBits(Instr new_instr) { - memcpy(this, &new_instr, sizeof(new_instr)); - } - - int Bit(int pos) const { - return (InstructionBits() >> pos) & 1; - } - - uint32_t Bits(int msb, int lsb) const { - return unsigned_bitextract_32(msb, lsb, InstructionBits()); - } - - int32_t SignedBits(int msb, int lsb) const { - int32_t bits = *(reinterpret_cast(this)); - return signed_bitextract_32(msb, lsb, bits); - } - - Instr Mask(uint32_t mask) const { - return InstructionBits() & mask; - } - - Instruction* following(int count = 1) { - return this + count * kInstructionSize; - } - - Instruction* preceding(int count = 1) { - return this - count * kInstructionSize; - } - - #define DEFINE_GETTER(Name, HighBit, LowBit, Func) \ - int64_t Name() const { return Func(HighBit, LowBit); } - INSTRUCTION_FIELDS_LIST(DEFINE_GETTER) - #undef DEFINE_GETTER - - // ImmPCRel is a compound field (not present in INSTRUCTION_FIELDS_LIST), - // formed from ImmPCRelLo and ImmPCRelHi. - int ImmPCRel() const { - int const offset = ((ImmPCRelHi() << ImmPCRelLo_width) | ImmPCRelLo()); - int const width = ImmPCRelLo_width + ImmPCRelHi_width; - return signed_bitextract_32(width-1, 0, offset); - } - - uint64_t ImmLogical(); - float ImmFP32(); - double ImmFP64(); - - LSDataSize SizeLSPair() const { - return CalcLSPairDataSize( - static_cast(Mask(LoadStorePairMask))); - } - - // Helpers. - bool IsCondBranchImm() const { - return Mask(ConditionalBranchFMask) == ConditionalBranchFixed; - } - - bool IsUncondBranchImm() const { - return Mask(UnconditionalBranchFMask) == UnconditionalBranchFixed; - } - - bool IsCompareBranch() const { - return Mask(CompareBranchFMask) == CompareBranchFixed; - } - - bool IsTestBranch() const { - return Mask(TestBranchFMask) == TestBranchFixed; - } - - bool IsLdrLiteral() const { - return Mask(LoadLiteralFMask) == LoadLiteralFixed; - } - - bool IsLdrLiteralX() const { - return Mask(LoadLiteralMask) == LDR_x_lit; - } - - bool IsPCRelAddressing() const { - return Mask(PCRelAddressingFMask) == PCRelAddressingFixed; - } - - bool IsLogicalImmediate() const { - return Mask(LogicalImmediateFMask) == LogicalImmediateFixed; - } - - bool IsAddSubImmediate() const { - return Mask(AddSubImmediateFMask) == AddSubImmediateFixed; - } - - bool IsAddSubExtended() const { - return Mask(AddSubExtendedFMask) == AddSubExtendedFixed; - } - - // Match any loads or stores, including pairs. - bool IsLoadOrStore() const { - return Mask(LoadStoreAnyFMask) == LoadStoreAnyFixed; - } - - // Match any loads, including pairs. - bool IsLoad() const; - // Match any stores, including pairs. - bool IsStore() const; - - // Indicate whether Rd can be the stack pointer or the zero register. This - // does not check that the instruction actually has an Rd field. - Reg31Mode RdMode() const { - // The following instructions use csp or wsp as Rd: - // Add/sub (immediate) when not setting the flags. - // Add/sub (extended) when not setting the flags. - // Logical (immediate) when not setting the flags. - // Otherwise, r31 is the zero register. - if (IsAddSubImmediate() || IsAddSubExtended()) { - if (Mask(AddSubSetFlagsBit)) { - return Reg31IsZeroRegister; - } else { - return Reg31IsStackPointer; - } - } - if (IsLogicalImmediate()) { - // Of the logical (immediate) instructions, only ANDS (and its aliases) - // can set the flags. The others can all write into csp. - // Note that some logical operations are not available to - // immediate-operand instructions, so we have to combine two masks here. - if (Mask(LogicalImmediateMask & LogicalOpMask) == ANDS) { - return Reg31IsZeroRegister; - } else { - return Reg31IsStackPointer; - } - } - return Reg31IsZeroRegister; - } - - // Indicate whether Rn can be the stack pointer or the zero register. This - // does not check that the instruction actually has an Rn field. - Reg31Mode RnMode() const { - // The following instructions use csp or wsp as Rn: - // All loads and stores. - // Add/sub (immediate). - // Add/sub (extended). - // Otherwise, r31 is the zero register. - if (IsLoadOrStore() || IsAddSubImmediate() || IsAddSubExtended()) { - return Reg31IsStackPointer; - } - return Reg31IsZeroRegister; - } - - ImmBranchType BranchType() const { - if (IsCondBranchImm()) { - return CondBranchType; - } else if (IsUncondBranchImm()) { - return UncondBranchType; - } else if (IsCompareBranch()) { - return CompareBranchType; - } else if (IsTestBranch()) { - return TestBranchType; - } else { - return UnknownBranchType; - } - } - - static int ImmBranchRangeBitwidth(ImmBranchType branch_type) { - switch (branch_type) { - case UncondBranchType: - return ImmUncondBranch_width; - case CondBranchType: - return ImmCondBranch_width; - case CompareBranchType: - return ImmCmpBranch_width; - case TestBranchType: - return ImmTestBranch_width; - default: - UNREACHABLE(); - return 0; - } - } - - // The range of the branch instruction, expressed as 'instr +- range'. - static int32_t ImmBranchRange(ImmBranchType branch_type) { - return - (1 << (ImmBranchRangeBitwidth(branch_type) + kInstructionSizeLog2)) / 2 - - kInstructionSize; - } - - int ImmBranch() const { - switch (BranchType()) { - case CondBranchType: return ImmCondBranch(); - case UncondBranchType: return ImmUncondBranch(); - case CompareBranchType: return ImmCmpBranch(); - case TestBranchType: return ImmTestBranch(); - default: UNREACHABLE(); - } - return 0; - } - - bool IsBranchAndLinkToRegister() const { - return Mask(UnconditionalBranchToRegisterMask) == BLR; - } - - bool IsMovz() const { - return (Mask(MoveWideImmediateMask) == MOVZ_x) || - (Mask(MoveWideImmediateMask) == MOVZ_w); - } - - bool IsMovk() const { - return (Mask(MoveWideImmediateMask) == MOVK_x) || - (Mask(MoveWideImmediateMask) == MOVK_w); - } - - bool IsMovn() const { - return (Mask(MoveWideImmediateMask) == MOVN_x) || - (Mask(MoveWideImmediateMask) == MOVN_w); - } - - bool IsNop(int n) { - // A marking nop is an instruction - // mov r, r - // which is encoded as - // orr r, xzr, r - return (Mask(LogicalShiftedMask) == ORR_x) && - (Rd() == Rm()) && - (Rd() == n); - } - - // Find the PC offset encoded in this instruction. 'this' may be a branch or - // a PC-relative addressing instruction. - // The offset returned is unscaled. - ptrdiff_t ImmPCOffset(); - - // Find the target of this instruction. 'this' may be a branch or a - // PC-relative addressing instruction. - Instruction* ImmPCOffsetTarget(); - - static bool IsValidImmPCOffset(ImmBranchType branch_type, int32_t offset); - bool IsTargetInImmPCOffsetRange(Instruction* target); - // Patch a PC-relative offset to refer to 'target'. 'this' may be a branch or - // a PC-relative addressing instruction. - void SetImmPCOffsetTarget(Instruction* target); - // Patch a literal load instruction to load from 'source'. - void SetImmLLiteral(Instruction* source); - - uint8_t* LiteralAddress() { - int offset = ImmLLiteral() << kLiteralEntrySizeLog2; - return reinterpret_cast(this) + offset; - } - - uint32_t Literal32() { - uint32_t literal; - memcpy(&literal, LiteralAddress(), sizeof(literal)); - - return literal; - } - - uint64_t Literal64() { - uint64_t literal; - memcpy(&literal, LiteralAddress(), sizeof(literal)); - - return literal; - } - - float LiteralFP32() { - return rawbits_to_float(Literal32()); - } - - double LiteralFP64() { - return rawbits_to_double(Literal64()); - } - - Instruction* NextInstruction() { - return this + kInstructionSize; - } - - Instruction* InstructionAtOffset(int64_t offset) { - ASSERT(IsAligned(reinterpret_cast(this) + offset, - kInstructionSize)); - return this + offset; - } - - template static Instruction* Cast(T src) { - return reinterpret_cast(src); - } - - - void SetPCRelImmTarget(Instruction* target); - void SetBranchImmTarget(Instruction* target); -}; - - -// Where Instruction looks at instructions generated by the Assembler, -// InstructionSequence looks at instructions sequences generated by the -// MacroAssembler. -class InstructionSequence : public Instruction { - public: - static InstructionSequence* At(Address address) { - return reinterpret_cast(address); - } - - // Sequences generated by MacroAssembler::InlineData(). - bool IsInlineData() const; - uint64_t InlineData() const; -}; - - -// Simulator/Debugger debug instructions --------------------------------------- -// Each debug marker is represented by a HLT instruction. The immediate comment -// field in the instruction is used to identify the type of debug marker. Each -// marker encodes arguments in a different way, as described below. - -// Indicate to the Debugger that the instruction is a redirected call. -const Instr kImmExceptionIsRedirectedCall = 0xca11; - -// Represent unreachable code. This is used as a guard in parts of the code that -// should not be reachable, such as in data encoded inline in the instructions. -const Instr kImmExceptionIsUnreachable = 0xdebf; - -// A pseudo 'printf' instruction. The arguments will be passed to the platform -// printf method. -const Instr kImmExceptionIsPrintf = 0xdeb1; -// Parameters are stored in A64 registers as if the printf pseudo-instruction -// was a call to the real printf method: -// -// x0: The format string, then either of: -// x1-x7: Optional arguments. -// d0-d7: Optional arguments. -// -// Floating-point and integer arguments are passed in separate sets of -// registers in AAPCS64 (even for varargs functions), so it is not possible to -// determine the type of location of each arguments without some information -// about the values that were passed in. This information could be retrieved -// from the printf format string, but the format string is not trivial to -// parse so we encode the relevant information with the HLT instruction. -// - Type -// Either kRegister or kFPRegister, but stored as a uint32_t because there's -// no way to guarantee the size of the CPURegister::RegisterType enum. -const unsigned kPrintfTypeOffset = 1 * kInstructionSize; -const unsigned kPrintfLength = 2 * kInstructionSize; - -// A pseudo 'debug' instruction. -const Instr kImmExceptionIsDebug = 0xdeb0; -// Parameters are inlined in the code after a debug pseudo-instruction: -// - Debug code. -// - Debug parameters. -// - Debug message string. This is a NULL-terminated ASCII string, padded to -// kInstructionSize so that subsequent instructions are correctly aligned. -// - A kImmExceptionIsUnreachable marker, to catch accidental execution of the -// string data. -const unsigned kDebugCodeOffset = 1 * kInstructionSize; -const unsigned kDebugParamsOffset = 2 * kInstructionSize; -const unsigned kDebugMessageOffset = 3 * kInstructionSize; - -// Debug parameters. -// Used without a TRACE_ option, the Debugger will print the arguments only -// once. Otherwise TRACE_ENABLE and TRACE_DISABLE will enable or disable tracing -// before every instruction for the specified LOG_ parameters. -// -// TRACE_OVERRIDE enables the specified LOG_ parameters, and disabled any -// others that were not specified. -// -// For example: -// -// __ debug("print registers and fp registers", 0, LOG_REGS | LOG_FP_REGS); -// will print the registers and fp registers only once. -// -// __ debug("trace disasm", 1, TRACE_ENABLE | LOG_DISASM); -// starts disassembling the code. -// -// __ debug("trace rets", 2, TRACE_ENABLE | LOG_REGS); -// adds the general purpose registers to the trace. -// -// __ debug("stop regs", 3, TRACE_DISABLE | LOG_REGS); -// stops tracing the registers. -const unsigned kDebuggerTracingDirectivesMask = 3 << 6; -enum DebugParameters { - NO_PARAM = 0, - BREAK = 1 << 0, - LOG_DISASM = 1 << 1, // Use only with TRACE. Disassemble the code. - LOG_REGS = 1 << 2, // Log general purpose registers. - LOG_FP_REGS = 1 << 3, // Log floating-point registers. - LOG_SYS_REGS = 1 << 4, // Log the status flags. - LOG_WRITE = 1 << 5, // Log any memory write. - - LOG_STATE = LOG_REGS | LOG_FP_REGS | LOG_SYS_REGS, - LOG_ALL = LOG_DISASM | LOG_STATE | LOG_WRITE, - - // Trace control. - TRACE_ENABLE = 1 << 6, - TRACE_DISABLE = 2 << 6, - TRACE_OVERRIDE = 3 << 6 -}; - - -} } // namespace v8::internal - - -#endif // V8_A64_INSTRUCTIONS_A64_H_ diff --git a/deps/v8/src/a64/instrument-a64.cc b/deps/v8/src/a64/instrument-a64.cc deleted file mode 100644 index 93892d9360..0000000000 --- a/deps/v8/src/a64/instrument-a64.cc +++ /dev/null @@ -1,618 +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 "a64/instrument-a64.h" - -namespace v8 { -namespace internal { - -Counter::Counter(const char* name, CounterType type) - : count_(0), enabled_(false), type_(type) { - ASSERT(name != NULL); - strncpy(name_, name, kCounterNameMaxLength); -} - - -void Counter::Enable() { - enabled_ = true; -} - - -void Counter::Disable() { - enabled_ = false; -} - - -bool Counter::IsEnabled() { - return enabled_; -} - - -void Counter::Increment() { - if (enabled_) { - count_++; - } -} - - -uint64_t Counter::count() { - uint64_t result = count_; - if (type_ == Gauge) { - // If the counter is a Gauge, reset the count after reading. - count_ = 0; - } - return result; -} - - -const char* Counter::name() { - return name_; -} - - -CounterType Counter::type() { - return type_; -} - - -typedef struct { - const char* name; - CounterType type; -} CounterDescriptor; - - -static const CounterDescriptor kCounterList[] = { - {"Instruction", Cumulative}, - - {"Move Immediate", Gauge}, - {"Add/Sub DP", Gauge}, - {"Logical DP", Gauge}, - {"Other Int DP", Gauge}, - {"FP DP", Gauge}, - - {"Conditional Select", Gauge}, - {"Conditional Compare", Gauge}, - - {"Unconditional Branch", Gauge}, - {"Compare and Branch", Gauge}, - {"Test and Branch", Gauge}, - {"Conditional Branch", Gauge}, - - {"Load Integer", Gauge}, - {"Load FP", Gauge}, - {"Load Pair", Gauge}, - {"Load Literal", Gauge}, - - {"Store Integer", Gauge}, - {"Store FP", Gauge}, - {"Store Pair", Gauge}, - - {"PC Addressing", Gauge}, - {"Other", Gauge}, - {"SP Adjust", Gauge}, -}; - - -Instrument::Instrument(const char* datafile, uint64_t sample_period) - : output_stream_(stderr), sample_period_(sample_period) { - - // Set up the output stream. If datafile is non-NULL, use that file. If it - // can't be opened, or datafile is NULL, use stderr. - if (datafile != NULL) { - output_stream_ = fopen(datafile, "w"); - if (output_stream_ == NULL) { - fprintf(stderr, "Can't open output file %s. Using stderr.\n", datafile); - output_stream_ = stderr; - } - } - - static const int num_counters = - sizeof(kCounterList) / sizeof(CounterDescriptor); - - // Dump an instrumentation description comment at the top of the file. - fprintf(output_stream_, "# counters=%d\n", num_counters); - fprintf(output_stream_, "# sample_period=%" PRIu64 "\n", sample_period_); - - // Construct Counter objects from counter description array. - for (int i = 0; i < num_counters; i++) { - Counter* counter = new Counter(kCounterList[i].name, kCounterList[i].type); - counters_.push_back(counter); - } - - DumpCounterNames(); -} - - -Instrument::~Instrument() { - // Dump any remaining instruction data to the output file. - DumpCounters(); - - // Free all the counter objects. - std::list::iterator it; - for (it = counters_.begin(); it != counters_.end(); it++) { - delete *it; - } - - if (output_stream_ != stderr) { - fclose(output_stream_); - } -} - - -void Instrument::Update() { - // Increment the instruction counter, and dump all counters if a sample period - // has elapsed. - static Counter* counter = GetCounter("Instruction"); - ASSERT(counter->type() == Cumulative); - counter->Increment(); - - if (counter->IsEnabled() && (counter->count() % sample_period_) == 0) { - DumpCounters(); - } -} - - -void Instrument::DumpCounters() { - // Iterate through the counter objects, dumping their values to the output - // stream. - std::list::const_iterator it; - for (it = counters_.begin(); it != counters_.end(); it++) { - fprintf(output_stream_, "%" PRIu64 ",", (*it)->count()); - } - fprintf(output_stream_, "\n"); - fflush(output_stream_); -} - - -void Instrument::DumpCounterNames() { - // Iterate through the counter objects, dumping the counter names to the - // output stream. - std::list::const_iterator it; - for (it = counters_.begin(); it != counters_.end(); it++) { - fprintf(output_stream_, "%s,", (*it)->name()); - } - fprintf(output_stream_, "\n"); - fflush(output_stream_); -} - - -void Instrument::HandleInstrumentationEvent(unsigned event) { - switch (event) { - case InstrumentStateEnable: Enable(); break; - case InstrumentStateDisable: Disable(); break; - default: DumpEventMarker(event); - } -} - - -void Instrument::DumpEventMarker(unsigned marker) { - // Dumpan event marker to the output stream as a specially formatted comment - // line. - static Counter* counter = GetCounter("Instruction"); - - fprintf(output_stream_, "# %c%c @ %" PRId64 "\n", marker & 0xff, - (marker >> 8) & 0xff, counter->count()); -} - - -Counter* Instrument::GetCounter(const char* name) { - // Get a Counter object by name from the counter list. - std::list::const_iterator it; - for (it = counters_.begin(); it != counters_.end(); it++) { - if (strcmp((*it)->name(), name) == 0) { - return *it; - } - } - - // A Counter by that name does not exist: print an error message to stderr - // and the output file, and exit. - static const char* error_message = - "# Error: Unknown counter \"%s\". Exiting.\n"; - fprintf(stderr, error_message, name); - fprintf(output_stream_, error_message, name); - exit(1); -} - - -void Instrument::Enable() { - std::list::iterator it; - for (it = counters_.begin(); it != counters_.end(); it++) { - (*it)->Enable(); - } -} - - -void Instrument::Disable() { - std::list::iterator it; - for (it = counters_.begin(); it != counters_.end(); it++) { - (*it)->Disable(); - } -} - - -void Instrument::VisitPCRelAddressing(Instruction* instr) { - Update(); - static Counter* counter = GetCounter("PC Addressing"); - counter->Increment(); -} - - -void Instrument::VisitAddSubImmediate(Instruction* instr) { - Update(); - static Counter* sp_counter = GetCounter("SP Adjust"); - static Counter* add_sub_counter = GetCounter("Add/Sub DP"); - if (((instr->Mask(AddSubOpMask) == SUB) || - (instr->Mask(AddSubOpMask) == ADD)) && - (instr->Rd() == 31) && (instr->Rn() == 31)) { - // Count adjustments to the C stack pointer caused by V8 needing two SPs. - sp_counter->Increment(); - } else { - add_sub_counter->Increment(); - } -} - - -void Instrument::VisitLogicalImmediate(Instruction* instr) { - Update(); - static Counter* counter = GetCounter("Logical DP"); - counter->Increment(); -} - - -void Instrument::VisitMoveWideImmediate(Instruction* instr) { - Update(); - static Counter* counter = GetCounter("Move Immediate"); - - if (instr->IsMovn() && (instr->Rd() == kZeroRegCode)) { - unsigned imm = instr->ImmMoveWide(); - HandleInstrumentationEvent(imm); - } else { - counter->Increment(); - } -} - - -void Instrument::VisitBitfield(Instruction* instr) { - Update(); - static Counter* counter = GetCounter("Other Int DP"); - counter->Increment(); -} - - -void Instrument::VisitExtract(Instruction* instr) { - Update(); - static Counter* counter = GetCounter("Other Int DP"); - counter->Increment(); -} - - -void Instrument::VisitUnconditionalBranch(Instruction* instr) { - Update(); - static Counter* counter = GetCounter("Unconditional Branch"); - counter->Increment(); -} - - -void Instrument::VisitUnconditionalBranchToRegister(Instruction* instr) { - Update(); - static Counter* counter = GetCounter("Unconditional Branch"); - counter->Increment(); -} - - -void Instrument::VisitCompareBranch(Instruction* instr) { - Update(); - static Counter* counter = GetCounter("Compare and Branch"); - counter->Increment(); -} - - -void Instrument::VisitTestBranch(Instruction* instr) { - Update(); - static Counter* counter = GetCounter("Test and Branch"); - counter->Increment(); -} - - -void Instrument::VisitConditionalBranch(Instruction* instr) { - Update(); - static Counter* counter = GetCounter("Conditional Branch"); - counter->Increment(); -} - - -void Instrument::VisitSystem(Instruction* instr) { - Update(); - static Counter* counter = GetCounter("Other"); - counter->Increment(); -} - - -void Instrument::VisitException(Instruction* instr) { - Update(); - static Counter* counter = GetCounter("Other"); - counter->Increment(); -} - - -void Instrument::InstrumentLoadStorePair(Instruction* instr) { - static Counter* load_pair_counter = GetCounter("Load Pair"); - static Counter* store_pair_counter = GetCounter("Store Pair"); - if (instr->Mask(LoadStorePairLBit) != 0) { - load_pair_counter->Increment(); - } else { - store_pair_counter->Increment(); - } -} - - -void Instrument::VisitLoadStorePairPostIndex(Instruction* instr) { - Update(); - InstrumentLoadStorePair(instr); -} - - -void Instrument::VisitLoadStorePairOffset(Instruction* instr) { - Update(); - InstrumentLoadStorePair(instr); -} - - -void Instrument::VisitLoadStorePairPreIndex(Instruction* instr) { - Update(); - InstrumentLoadStorePair(instr); -} - - -void Instrument::VisitLoadStorePairNonTemporal(Instruction* instr) { - Update(); - InstrumentLoadStorePair(instr); -} - - -void Instrument::VisitLoadLiteral(Instruction* instr) { - Update(); - static Counter* counter = GetCounter("Load Literal"); - counter->Increment(); -} - - -void Instrument::InstrumentLoadStore(Instruction* instr) { - static Counter* load_int_counter = GetCounter("Load Integer"); - static Counter* store_int_counter = GetCounter("Store Integer"); - static Counter* load_fp_counter = GetCounter("Load FP"); - static Counter* store_fp_counter = GetCounter("Store FP"); - - switch (instr->Mask(LoadStoreOpMask)) { - case STRB_w: // Fall through. - case STRH_w: // Fall through. - case STR_w: // Fall through. - case STR_x: store_int_counter->Increment(); break; - case STR_s: // Fall through. - case STR_d: store_fp_counter->Increment(); break; - case LDRB_w: // Fall through. - case LDRH_w: // Fall through. - case LDR_w: // Fall through. - case LDR_x: // Fall through. - case LDRSB_x: // Fall through. - case LDRSH_x: // Fall through. - case LDRSW_x: // Fall through. - case LDRSB_w: // Fall through. - case LDRSH_w: load_int_counter->Increment(); break; - case LDR_s: // Fall through. - case LDR_d: load_fp_counter->Increment(); break; - default: UNREACHABLE(); - } -} - - -void Instrument::VisitLoadStoreUnscaledOffset(Instruction* instr) { - Update(); - InstrumentLoadStore(instr); -} - - -void Instrument::VisitLoadStorePostIndex(Instruction* instr) { - Update(); - InstrumentLoadStore(instr); -} - - -void Instrument::VisitLoadStorePreIndex(Instruction* instr) { - Update(); - InstrumentLoadStore(instr); -} - - -void Instrument::VisitLoadStoreRegisterOffset(Instruction* instr) { - Update(); - InstrumentLoadStore(instr); -} - - -void Instrument::VisitLoadStoreUnsignedOffset(Instruction* instr) { - Update(); - InstrumentLoadStore(instr); -} - - -void Instrument::VisitLogicalShifted(Instruction* instr) { - Update(); - static Counter* counter = GetCounter("Logical DP"); - counter->Increment(); -} - - -void Instrument::VisitAddSubShifted(Instruction* instr) { - Update(); - static Counter* counter = GetCounter("Add/Sub DP"); - counter->Increment(); -} - - -void Instrument::VisitAddSubExtended(Instruction* instr) { - Update(); - static Counter* sp_counter = GetCounter("SP Adjust"); - static Counter* add_sub_counter = GetCounter("Add/Sub DP"); - if (((instr->Mask(AddSubOpMask) == SUB) || - (instr->Mask(AddSubOpMask) == ADD)) && - (instr->Rd() == 31) && (instr->Rn() == 31)) { - // Count adjustments to the C stack pointer caused by V8 needing two SPs. - sp_counter->Increment(); - } else { - add_sub_counter->Increment(); - } -} - - -void Instrument::VisitAddSubWithCarry(Instruction* instr) { - Update(); - static Counter* counter = GetCounter("Add/Sub DP"); - counter->Increment(); -} - - -void Instrument::VisitConditionalCompareRegister(Instruction* instr) { - Update(); - static Counter* counter = GetCounter("Conditional Compare"); - counter->Increment(); -} - - -void Instrument::VisitConditionalCompareImmediate(Instruction* instr) { - Update(); - static Counter* counter = GetCounter("Conditional Compare"); - counter->Increment(); -} - - -void Instrument::VisitConditionalSelect(Instruction* instr) { - Update(); - static Counter* counter = GetCounter("Conditional Select"); - counter->Increment(); -} - - -void Instrument::VisitDataProcessing1Source(Instruction* instr) { - Update(); - static Counter* counter = GetCounter("Other Int DP"); - counter->Increment(); -} - - -void Instrument::VisitDataProcessing2Source(Instruction* instr) { - Update(); - static Counter* counter = GetCounter("Other Int DP"); - counter->Increment(); -} - - -void Instrument::VisitDataProcessing3Source(Instruction* instr) { - Update(); - static Counter* counter = GetCounter("Other Int DP"); - counter->Increment(); -} - - -void Instrument::VisitFPCompare(Instruction* instr) { - Update(); - static Counter* counter = GetCounter("FP DP"); - counter->Increment(); -} - - -void Instrument::VisitFPConditionalCompare(Instruction* instr) { - Update(); - static Counter* counter = GetCounter("Conditional Compare"); - counter->Increment(); -} - - -void Instrument::VisitFPConditionalSelect(Instruction* instr) { - Update(); - static Counter* counter = GetCounter("Conditional Select"); - counter->Increment(); -} - - -void Instrument::VisitFPImmediate(Instruction* instr) { - Update(); - static Counter* counter = GetCounter("FP DP"); - counter->Increment(); -} - - -void Instrument::VisitFPDataProcessing1Source(Instruction* instr) { - Update(); - static Counter* counter = GetCounter("FP DP"); - counter->Increment(); -} - - -void Instrument::VisitFPDataProcessing2Source(Instruction* instr) { - Update(); - static Counter* counter = GetCounter("FP DP"); - counter->Increment(); -} - - -void Instrument::VisitFPDataProcessing3Source(Instruction* instr) { - Update(); - static Counter* counter = GetCounter("FP DP"); - counter->Increment(); -} - - -void Instrument::VisitFPIntegerConvert(Instruction* instr) { - Update(); - static Counter* counter = GetCounter("FP DP"); - counter->Increment(); -} - - -void Instrument::VisitFPFixedPointConvert(Instruction* instr) { - Update(); - static Counter* counter = GetCounter("FP DP"); - counter->Increment(); -} - - -void Instrument::VisitUnallocated(Instruction* instr) { - Update(); - static Counter* counter = GetCounter("Other"); - counter->Increment(); -} - - -void Instrument::VisitUnimplemented(Instruction* instr) { - Update(); - static Counter* counter = GetCounter("Other"); - counter->Increment(); -} - - -} } // namespace v8::internal diff --git a/deps/v8/src/a64/instrument-a64.h b/deps/v8/src/a64/instrument-a64.h deleted file mode 100644 index 08dc1b2ad1..0000000000 --- a/deps/v8/src/a64/instrument-a64.h +++ /dev/null @@ -1,108 +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. - -#ifndef V8_A64_INSTRUMENT_A64_H_ -#define V8_A64_INSTRUMENT_A64_H_ - -#include "globals.h" -#include "utils.h" -#include "a64/decoder-a64.h" -#include "a64/constants-a64.h" -#include "a64/instrument-a64.h" - -namespace v8 { -namespace internal { - -const int kCounterNameMaxLength = 256; -const uint64_t kDefaultInstrumentationSamplingPeriod = 1 << 22; - - -enum InstrumentState { - InstrumentStateDisable = 0, - InstrumentStateEnable = 1 -}; - - -enum CounterType { - Gauge = 0, // Gauge counters reset themselves after reading. - Cumulative = 1 // Cumulative counters keep their value after reading. -}; - - -class Counter { - public: - Counter(const char* name, CounterType type = Gauge); - - void Increment(); - void Enable(); - void Disable(); - bool IsEnabled(); - uint64_t count(); - const char* name(); - CounterType type(); - - private: - char name_[kCounterNameMaxLength]; - uint64_t count_; - bool enabled_; - CounterType type_; -}; - - -class Instrument: public DecoderVisitor { - public: - explicit Instrument(const char* datafile = NULL, - uint64_t sample_period = kDefaultInstrumentationSamplingPeriod); - ~Instrument(); - - // Declare all Visitor functions. - #define DECLARE(A) void Visit##A(Instruction* instr); - VISITOR_LIST(DECLARE) - #undef DECLARE - - private: - void Update(); - void Enable(); - void Disable(); - void DumpCounters(); - void DumpCounterNames(); - void DumpEventMarker(unsigned marker); - void HandleInstrumentationEvent(unsigned event); - Counter* GetCounter(const char* name); - - void InstrumentLoadStore(Instruction* instr); - void InstrumentLoadStorePair(Instruction* instr); - - std::list counters_; - - FILE *output_stream_; - uint64_t sample_period_; -}; - -} } // namespace v8::internal - -#endif // V8_A64_INSTRUMENT_A64_H_ diff --git a/deps/v8/src/a64/lithium-a64.cc b/deps/v8/src/a64/lithium-a64.cc deleted file mode 100644 index fa351e3928..0000000000 --- a/deps/v8/src/a64/lithium-a64.cc +++ /dev/null @@ -1,2449 +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 "v8.h" - -#include "lithium-allocator-inl.h" -#include "a64/lithium-a64.h" -#include "a64/lithium-codegen-a64.h" -#include "hydrogen-osr.h" - -namespace v8 { -namespace internal { - - -#define DEFINE_COMPILE(type) \ - void L##type::CompileToNative(LCodeGen* generator) { \ - generator->Do##type(this); \ - } -LITHIUM_CONCRETE_INSTRUCTION_LIST(DEFINE_COMPILE) -#undef DEFINE_COMPILE - -#ifdef DEBUG -void LInstruction::VerifyCall() { - // Call instructions can use only fixed registers as temporaries and - // outputs because all registers are blocked by the calling convention. - // Inputs operands must use a fixed register or use-at-start policy or - // a non-register policy. - ASSERT(Output() == NULL || - LUnallocated::cast(Output())->HasFixedPolicy() || - !LUnallocated::cast(Output())->HasRegisterPolicy()); - for (UseIterator it(this); !it.Done(); it.Advance()) { - LUnallocated* operand = LUnallocated::cast(it.Current()); - ASSERT(operand->HasFixedPolicy() || - operand->IsUsedAtStart()); - } - for (TempIterator it(this); !it.Done(); it.Advance()) { - LUnallocated* operand = LUnallocated::cast(it.Current()); - ASSERT(operand->HasFixedPolicy() ||!operand->HasRegisterPolicy()); - } -} -#endif - - -void LLabel::PrintDataTo(StringStream* stream) { - LGap::PrintDataTo(stream); - LLabel* rep = replacement(); - if (rep != NULL) { - stream->Add(" Dead block replaced with B%d", rep->block_id()); - } -} - - -void LAccessArgumentsAt::PrintDataTo(StringStream* stream) { - arguments()->PrintTo(stream); - stream->Add(" length "); - length()->PrintTo(stream); - stream->Add(" index "); - index()->PrintTo(stream); -} - - -void LBranch::PrintDataTo(StringStream* stream) { - stream->Add("B%d | B%d on ", true_block_id(), false_block_id()); - value()->PrintTo(stream); -} - - -void LCallJSFunction::PrintDataTo(StringStream* stream) { - stream->Add("= "); - function()->PrintTo(stream); - stream->Add("#%d / ", arity()); -} - - -void LCallWithDescriptor::PrintDataTo(StringStream* stream) { - for (int i = 0; i < InputCount(); i++) { - InputAt(i)->PrintTo(stream); - stream->Add(" "); - } - stream->Add("#%d / ", arity()); -} - - -void LCallNew::PrintDataTo(StringStream* stream) { - stream->Add("= "); - constructor()->PrintTo(stream); - stream->Add(" #%d / ", arity()); -} - - -void LCallNewArray::PrintDataTo(StringStream* stream) { - stream->Add("= "); - constructor()->PrintTo(stream); - stream->Add(" #%d / ", arity()); - ElementsKind kind = hydrogen()->elements_kind(); - stream->Add(" (%s) ", ElementsKindToString(kind)); -} - - -void LClassOfTestAndBranch::PrintDataTo(StringStream* stream) { - stream->Add("if class_of_test("); - value()->PrintTo(stream); - stream->Add(", \"%o\") then B%d else B%d", - *hydrogen()->class_name(), - true_block_id(), - false_block_id()); -} - - -void LCompareNumericAndBranch::PrintDataTo(StringStream* stream) { - stream->Add("if "); - left()->PrintTo(stream); - stream->Add(" %s ", Token::String(op())); - right()->PrintTo(stream); - stream->Add(" then B%d else B%d", true_block_id(), false_block_id()); -} - - -void LHasCachedArrayIndexAndBranch::PrintDataTo(StringStream* stream) { - stream->Add("if has_cached_array_index("); - value()->PrintTo(stream); - stream->Add(") then B%d else B%d", true_block_id(), false_block_id()); -} - - -bool LGoto::HasInterestingComment(LCodeGen* gen) const { - return !gen->IsNextEmittedBlock(block_id()); -} - - -void LGoto::PrintDataTo(StringStream* stream) { - stream->Add("B%d", block_id()); -} - - -void LInnerAllocatedObject::PrintDataTo(StringStream* stream) { - stream->Add(" = "); - base_object()->PrintTo(stream); - stream->Add(" + "); - offset()->PrintTo(stream); -} - - -void LInvokeFunction::PrintDataTo(StringStream* stream) { - stream->Add("= "); - function()->PrintTo(stream); - stream->Add(" #%d / ", arity()); -} - - -void LInstruction::PrintTo(StringStream* stream) { - stream->Add("%s ", this->Mnemonic()); - - PrintOutputOperandTo(stream); - - PrintDataTo(stream); - - if (HasEnvironment()) { - stream->Add(" "); - environment()->PrintTo(stream); - } - - if (HasPointerMap()) { - stream->Add(" "); - pointer_map()->PrintTo(stream); - } -} - - -void LInstruction::PrintDataTo(StringStream* stream) { - stream->Add("= "); - for (int i = 0; i < InputCount(); i++) { - if (i > 0) stream->Add(" "); - if (InputAt(i) == NULL) { - stream->Add("NULL"); - } else { - InputAt(i)->PrintTo(stream); - } - } -} - - -void LInstruction::PrintOutputOperandTo(StringStream* stream) { - if (HasResult()) result()->PrintTo(stream); -} - - -void LHasInstanceTypeAndBranch::PrintDataTo(StringStream* stream) { - stream->Add("if has_instance_type("); - value()->PrintTo(stream); - stream->Add(") then B%d else B%d", true_block_id(), false_block_id()); -} - - -void LIsObjectAndBranch::PrintDataTo(StringStream* stream) { - stream->Add("if is_object("); - value()->PrintTo(stream); - stream->Add(") then B%d else B%d", true_block_id(), false_block_id()); -} - - -void LIsStringAndBranch::PrintDataTo(StringStream* stream) { - stream->Add("if is_string("); - value()->PrintTo(stream); - stream->Add(") then B%d else B%d", true_block_id(), false_block_id()); -} - - -void LIsSmiAndBranch::PrintDataTo(StringStream* stream) { - stream->Add("if is_smi("); - value()->PrintTo(stream); - stream->Add(") then B%d else B%d", true_block_id(), false_block_id()); -} - - -void LTypeofIsAndBranch::PrintDataTo(StringStream* stream) { - stream->Add("if typeof "); - value()->PrintTo(stream); - stream->Add(" == \"%s\" then B%d else B%d", - hydrogen()->type_literal()->ToCString().get(), - true_block_id(), false_block_id()); -} - - -void LIsUndetectableAndBranch::PrintDataTo(StringStream* stream) { - stream->Add("if is_undetectable("); - value()->PrintTo(stream); - stream->Add(") then B%d else B%d", true_block_id(), false_block_id()); -} - - -bool LGap::IsRedundant() const { - for (int i = 0; i < 4; i++) { - if ((parallel_moves_[i] != NULL) && !parallel_moves_[i]->IsRedundant()) { - return false; - } - } - - return true; -} - - -void LGap::PrintDataTo(StringStream* stream) { - for (int i = 0; i < 4; i++) { - stream->Add("("); - if (parallel_moves_[i] != NULL) { - parallel_moves_[i]->PrintDataTo(stream); - } - stream->Add(") "); - } -} - - -void LLoadContextSlot::PrintDataTo(StringStream* stream) { - context()->PrintTo(stream); - stream->Add("[%d]", slot_index()); -} - - -void LStoreCodeEntry::PrintDataTo(StringStream* stream) { - stream->Add(" = "); - function()->PrintTo(stream); - stream->Add(".code_entry = "); - code_object()->PrintTo(stream); -} - - -void LStoreContextSlot::PrintDataTo(StringStream* stream) { - context()->PrintTo(stream); - stream->Add("[%d] <- ", slot_index()); - value()->PrintTo(stream); -} - - -void LStoreKeyedGeneric::PrintDataTo(StringStream* stream) { - object()->PrintTo(stream); - stream->Add("["); - key()->PrintTo(stream); - stream->Add("] <- "); - value()->PrintTo(stream); -} - - -void LStoreNamedField::PrintDataTo(StringStream* stream) { - object()->PrintTo(stream); - hydrogen()->access().PrintTo(stream); - stream->Add(" <- "); - value()->PrintTo(stream); -} - - -void LStoreNamedGeneric::PrintDataTo(StringStream* stream) { - object()->PrintTo(stream); - stream->Add("."); - stream->Add(String::cast(*name())->ToCString().get()); - stream->Add(" <- "); - value()->PrintTo(stream); -} - - -void LStringCompareAndBranch::PrintDataTo(StringStream* stream) { - stream->Add("if string_compare("); - left()->PrintTo(stream); - right()->PrintTo(stream); - stream->Add(") then B%d else B%d", true_block_id(), false_block_id()); -} - - -void LTransitionElementsKind::PrintDataTo(StringStream* stream) { - object()->PrintTo(stream); - stream->Add("%p -> %p", *original_map(), *transitioned_map()); -} - - -template -void LUnaryMathOperation::PrintDataTo(StringStream* stream) { - value()->PrintTo(stream); -} - - -const char* LArithmeticD::Mnemonic() const { - switch (op()) { - case Token::ADD: return "add-d"; - case Token::SUB: return "sub-d"; - case Token::MUL: return "mul-d"; - case Token::DIV: return "div-d"; - case Token::MOD: return "mod-d"; - default: - UNREACHABLE(); - return NULL; - } -} - - -const char* LArithmeticT::Mnemonic() const { - switch (op()) { - case Token::ADD: return "add-t"; - case Token::SUB: return "sub-t"; - case Token::MUL: return "mul-t"; - case Token::MOD: return "mod-t"; - case Token::DIV: return "div-t"; - case Token::BIT_AND: return "bit-and-t"; - case Token::BIT_OR: return "bit-or-t"; - case Token::BIT_XOR: return "bit-xor-t"; - case Token::ROR: return "ror-t"; - case Token::SHL: return "shl-t"; - case Token::SAR: return "sar-t"; - case Token::SHR: return "shr-t"; - default: - UNREACHABLE(); - return NULL; - } -} - - -void LChunkBuilder::Abort(BailoutReason reason) { - info()->set_bailout_reason(reason); - status_ = ABORTED; -} - - -LUnallocated* LChunkBuilder::ToUnallocated(Register reg) { - return new(zone()) LUnallocated(LUnallocated::FIXED_REGISTER, - Register::ToAllocationIndex(reg)); -} - - -LUnallocated* LChunkBuilder::ToUnallocated(DoubleRegister reg) { - return new(zone()) LUnallocated(LUnallocated::FIXED_DOUBLE_REGISTER, - DoubleRegister::ToAllocationIndex(reg)); -} - - -LOperand* LChunkBuilder::Use(HValue* value, LUnallocated* operand) { - if (value->EmitAtUses()) { - HInstruction* instr = HInstruction::cast(value); - VisitInstruction(instr); - } - operand->set_virtual_register(value->id()); - return operand; -} - - -LOperand* LChunkBuilder::UseFixed(HValue* value, Register fixed_register) { - return Use(value, ToUnallocated(fixed_register)); -} - - -LOperand* LChunkBuilder::UseFixedDouble(HValue* value, - DoubleRegister fixed_register) { - return Use(value, ToUnallocated(fixed_register)); -} - - -LOperand* LChunkBuilder::UseRegister(HValue* value) { - return Use(value, new(zone()) LUnallocated(LUnallocated::MUST_HAVE_REGISTER)); -} - - -LOperand* LChunkBuilder::UseRegisterAndClobber(HValue* value) { - return Use(value, new(zone()) LUnallocated(LUnallocated::WRITABLE_REGISTER)); -} - - -LOperand* LChunkBuilder::UseRegisterAtStart(HValue* value) { - return Use(value, - new(zone()) LUnallocated(LUnallocated::MUST_HAVE_REGISTER, - LUnallocated::USED_AT_START)); -} - - -LOperand* LChunkBuilder::UseRegisterOrConstant(HValue* value) { - return value->IsConstant() ? UseConstant(value) : UseRegister(value); -} - - -LOperand* LChunkBuilder::UseRegisterOrConstantAtStart(HValue* value) { - return value->IsConstant() ? UseConstant(value) : UseRegisterAtStart(value); -} - - -LConstantOperand* LChunkBuilder::UseConstant(HValue* value) { - return chunk_->DefineConstantOperand(HConstant::cast(value)); -} - - -LOperand* LChunkBuilder::UseAny(HValue* value) { - return value->IsConstant() - ? UseConstant(value) - : Use(value, new(zone()) LUnallocated(LUnallocated::ANY)); -} - - -LInstruction* LChunkBuilder::Define(LTemplateResultInstruction<1>* instr, - LUnallocated* result) { - result->set_virtual_register(current_instruction_->id()); - instr->set_result(result); - return instr; -} - - -LInstruction* LChunkBuilder::DefineAsRegister( - LTemplateResultInstruction<1>* instr) { - return Define(instr, - new(zone()) LUnallocated(LUnallocated::MUST_HAVE_REGISTER)); -} - - -LInstruction* LChunkBuilder::DefineAsSpilled( - LTemplateResultInstruction<1>* instr, int index) { - return Define(instr, - new(zone()) LUnallocated(LUnallocated::FIXED_SLOT, index)); -} - - -LInstruction* LChunkBuilder::DefineSameAsFirst( - LTemplateResultInstruction<1>* instr) { - return Define(instr, - new(zone()) LUnallocated(LUnallocated::SAME_AS_FIRST_INPUT)); -} - - -LInstruction* LChunkBuilder::DefineFixed( - LTemplateResultInstruction<1>* instr, Register reg) { - return Define(instr, ToUnallocated(reg)); -} - - -LInstruction* LChunkBuilder::DefineFixedDouble( - LTemplateResultInstruction<1>* instr, DoubleRegister reg) { - return Define(instr, ToUnallocated(reg)); -} - - -LInstruction* LChunkBuilder::MarkAsCall(LInstruction* instr, - HInstruction* hinstr, - CanDeoptimize can_deoptimize) { - info()->MarkAsNonDeferredCalling(); -#ifdef DEBUG - instr->VerifyCall(); -#endif - instr->MarkAsCall(); - instr = AssignPointerMap(instr); - - if (hinstr->HasObservableSideEffects()) { - ASSERT(hinstr->next()->IsSimulate()); - HSimulate* sim = HSimulate::cast(hinstr->next()); - ASSERT(instruction_pending_deoptimization_environment_ == NULL); - ASSERT(pending_deoptimization_ast_id_.IsNone()); - instruction_pending_deoptimization_environment_ = instr; - pending_deoptimization_ast_id_ = sim->ast_id(); - } - - // If instruction does not have side-effects lazy deoptimization - // after the call will try to deoptimize to the point before the call. - // Thus we still need to attach environment to this call even if - // call sequence can not deoptimize eagerly. - bool needs_environment = - (can_deoptimize == CAN_DEOPTIMIZE_EAGERLY) || - !hinstr->HasObservableSideEffects(); - if (needs_environment && !instr->HasEnvironment()) { - instr = AssignEnvironment(instr); - } - - return instr; -} - - -LInstruction* LChunkBuilder::AssignPointerMap(LInstruction* instr) { - ASSERT(!instr->HasPointerMap()); - instr->set_pointer_map(new(zone()) LPointerMap(zone())); - return instr; -} - - -LUnallocated* LChunkBuilder::TempRegister() { - LUnallocated* operand = - new(zone()) LUnallocated(LUnallocated::MUST_HAVE_REGISTER); - int vreg = allocator_->GetVirtualRegister(); - if (!allocator_->AllocationOk()) { - Abort(kOutOfVirtualRegistersWhileTryingToAllocateTempRegister); - vreg = 0; - } - operand->set_virtual_register(vreg); - return operand; -} - - -int LPlatformChunk::GetNextSpillIndex() { - return spill_slot_count_++; -} - - -LOperand* LPlatformChunk::GetNextSpillSlot(RegisterKind kind) { - int index = GetNextSpillIndex(); - if (kind == DOUBLE_REGISTERS) { - return LDoubleStackSlot::Create(index, zone()); - } else { - ASSERT(kind == GENERAL_REGISTERS); - return LStackSlot::Create(index, zone()); - } -} - - -LOperand* LChunkBuilder::FixedTemp(DoubleRegister reg) { - LUnallocated* operand = ToUnallocated(reg); - ASSERT(operand->HasFixedPolicy()); - return operand; -} - - -LPlatformChunk* LChunkBuilder::Build() { - ASSERT(is_unused()); - chunk_ = new(zone()) LPlatformChunk(info_, graph_); - LPhase phase("L_Building chunk", chunk_); - status_ = BUILDING; - - // If compiling for OSR, reserve space for the unoptimized frame, - // which will be subsumed into this frame. - if (graph()->has_osr()) { - // TODO(all): GetNextSpillIndex just increments a field. It has no other - // side effects, so we should get rid of this loop. - for (int i = graph()->osr()->UnoptimizedFrameSlots(); i > 0; i--) { - chunk_->GetNextSpillIndex(); - } - } - - const ZoneList* blocks = graph_->blocks(); - for (int i = 0; i < blocks->length(); i++) { - DoBasicBlock(blocks->at(i)); - if (is_aborted()) return NULL; - } - status_ = DONE; - return chunk_; -} - - -void LChunkBuilder::DoBasicBlock(HBasicBlock* block) { - ASSERT(is_building()); - current_block_ = block; - - if (block->IsStartBlock()) { - block->UpdateEnvironment(graph_->start_environment()); - argument_count_ = 0; - } else if (block->predecessors()->length() == 1) { - // We have a single predecessor => copy environment and outgoing - // argument count from the predecessor. - ASSERT(block->phis()->length() == 0); - HBasicBlock* pred = block->predecessors()->at(0); - HEnvironment* last_environment = pred->last_environment(); - ASSERT(last_environment != NULL); - - // Only copy the environment, if it is later used again. - if (pred->end()->SecondSuccessor() == NULL) { - ASSERT(pred->end()->FirstSuccessor() == block); - } else { - if ((pred->end()->FirstSuccessor()->block_id() > block->block_id()) || - (pred->end()->SecondSuccessor()->block_id() > block->block_id())) { - last_environment = last_environment->Copy(); - } - } - block->UpdateEnvironment(last_environment); - ASSERT(pred->argument_count() >= 0); - argument_count_ = pred->argument_count(); - } else { - // We are at a state join => process phis. - HBasicBlock* pred = block->predecessors()->at(0); - // No need to copy the environment, it cannot be used later. - HEnvironment* last_environment = pred->last_environment(); - for (int i = 0; i < block->phis()->length(); ++i) { - HPhi* phi = block->phis()->at(i); - if (phi->HasMergedIndex()) { - last_environment->SetValueAt(phi->merged_index(), phi); - } - } - for (int i = 0; i < block->deleted_phis()->length(); ++i) { - if (block->deleted_phis()->at(i) < last_environment->length()) { - last_environment->SetValueAt(block->deleted_phis()->at(i), - graph_->GetConstantUndefined()); - } - } - block->UpdateEnvironment(last_environment); - // Pick up the outgoing argument count of one of the predecessors. - argument_count_ = pred->argument_count(); - } - - // Translate hydrogen instructions to lithium ones for the current block. - HInstruction* current = block->first(); - int start = chunk_->instructions()->length(); - while ((current != NULL) && !is_aborted()) { - // Code for constants in registers is generated lazily. - if (!current->EmitAtUses()) { - VisitInstruction(current); - } - current = current->next(); - } - int end = chunk_->instructions()->length() - 1; - if (end >= start) { - block->set_first_instruction_index(start); - block->set_last_instruction_index(end); - } - block->set_argument_count(argument_count_); - current_block_ = NULL; -} - - -void LChunkBuilder::VisitInstruction(HInstruction* current) { - HInstruction* old_current = current_instruction_; - current_instruction_ = current; - - LInstruction* instr = NULL; - if (current->CanReplaceWithDummyUses()) { - if (current->OperandCount() == 0) { - instr = DefineAsRegister(new(zone()) LDummy()); - } else { - ASSERT(!current->OperandAt(0)->IsControlInstruction()); - instr = DefineAsRegister(new(zone()) - LDummyUse(UseAny(current->OperandAt(0)))); - } - for (int i = 1; i < current->OperandCount(); ++i) { - if (current->OperandAt(i)->IsControlInstruction()) continue; - LInstruction* dummy = - new(zone()) LDummyUse(UseAny(current->OperandAt(i))); - dummy->set_hydrogen_value(current); - chunk_->AddInstruction(dummy, current_block_); - } - } else { - instr = current->CompileToLithium(this); - } - - argument_count_ += current->argument_delta(); - ASSERT(argument_count_ >= 0); - - if (instr != NULL) { - // Associate the hydrogen instruction first, since we may need it for - // the ClobbersRegisters() or ClobbersDoubleRegisters() calls below. - instr->set_hydrogen_value(current); - -#if DEBUG - // Make sure that the lithium instruction has either no fixed register - // constraints in temps or the result OR no uses that are only used at - // start. If this invariant doesn't hold, the register allocator can decide - // to insert a split of a range immediately before the instruction due to an - // already allocated register needing to be used for the instruction's fixed - // register constraint. In this case, the register allocator won't see an - // interference between the split child and the use-at-start (it would if - // the it was just a plain use), so it is free to move the split child into - // the same register that is used for the use-at-start. - // See https://code.google.com/p/chromium/issues/detail?id=201590 - if (!(instr->ClobbersRegisters() && instr->ClobbersDoubleRegisters())) { - int fixed = 0; - int used_at_start = 0; - for (UseIterator it(instr); !it.Done(); it.Advance()) { - LUnallocated* operand = LUnallocated::cast(it.Current()); - if (operand->IsUsedAtStart()) ++used_at_start; - } - if (instr->Output() != NULL) { - if (LUnallocated::cast(instr->Output())->HasFixedPolicy()) ++fixed; - } - for (TempIterator it(instr); !it.Done(); it.Advance()) { - LUnallocated* operand = LUnallocated::cast(it.Current()); - if (operand->HasFixedPolicy()) ++fixed; - } - ASSERT(fixed == 0 || used_at_start == 0); - } -#endif - - if (FLAG_stress_pointer_maps && !instr->HasPointerMap()) { - instr = AssignPointerMap(instr); - } - if (FLAG_stress_environments && !instr->HasEnvironment()) { - instr = AssignEnvironment(instr); - } - chunk_->AddInstruction(instr, current_block_); - } - current_instruction_ = old_current; -} - - -LInstruction* LChunkBuilder::AssignEnvironment(LInstruction* instr) { - HEnvironment* hydrogen_env = current_block_->last_environment(); - int argument_index_accumulator = 0; - ZoneList objects_to_materialize(0, zone()); - instr->set_environment(CreateEnvironment(hydrogen_env, - &argument_index_accumulator, - &objects_to_materialize)); - return instr; -} - - -LInstruction* LChunkBuilder::DoAbnormalExit(HAbnormalExit* instr) { - // The control instruction marking the end of a block that completed - // abruptly (e.g., threw an exception). There is nothing specific to do. - return NULL; -} - - -LInstruction* LChunkBuilder::DoArithmeticD(Token::Value op, - HArithmeticBinaryOperation* instr) { - ASSERT(instr->representation().IsDouble()); - ASSERT(instr->left()->representation().IsDouble()); - ASSERT(instr->right()->representation().IsDouble()); - - if (op == Token::MOD) { - LOperand* left = UseFixedDouble(instr->left(), d0); - LOperand* right = UseFixedDouble(instr->right(), d1); - LArithmeticD* result = new(zone()) LArithmeticD(Token::MOD, left, right); - return MarkAsCall(DefineFixedDouble(result, d0), instr); - } else { - LOperand* left = UseRegisterAtStart(instr->left()); - LOperand* right = UseRegisterAtStart(instr->right()); - LArithmeticD* result = new(zone()) LArithmeticD(op, left, right); - return DefineAsRegister(result); - } -} - - -LInstruction* LChunkBuilder::DoArithmeticT(Token::Value op, - HBinaryOperation* instr) { - ASSERT((op == Token::ADD) || (op == Token::SUB) || (op == Token::MUL) || - (op == Token::DIV) || (op == Token::MOD) || (op == Token::SHR) || - (op == Token::SHL) || (op == Token::SAR) || (op == Token::ROR) || - (op == Token::BIT_OR) || (op == Token::BIT_AND) || - (op == Token::BIT_XOR)); - HValue* left = instr->left(); - HValue* right = instr->right(); - - // TODO(jbramley): Once we've implemented smi support for all arithmetic - // operations, these assertions should check IsTagged(). - ASSERT(instr->representation().IsSmiOrTagged()); - ASSERT(left->representation().IsSmiOrTagged()); - ASSERT(right->representation().IsSmiOrTagged()); - - LOperand* context = UseFixed(instr->context(), cp); - LOperand* left_operand = UseFixed(left, x1); - LOperand* right_operand = UseFixed(right, x0); - LArithmeticT* result = - new(zone()) LArithmeticT(op, context, left_operand, right_operand); - return MarkAsCall(DefineFixed(result, x0), instr); -} - - -LInstruction* LChunkBuilder::DoBoundsCheckBaseIndexInformation( - HBoundsCheckBaseIndexInformation* instr) { - UNREACHABLE(); - return NULL; -} - - -LInstruction* LChunkBuilder::DoAccessArgumentsAt(HAccessArgumentsAt* instr) { - // TODO(all): Try to improve this, like ARM r17925. - info()->MarkAsRequiresFrame(); - LOperand* args = NULL; - LOperand* length = NULL; - LOperand* index = NULL; - LOperand* temp = NULL; - - if (instr->length()->IsConstant() && instr->index()->IsConstant()) { - args = UseRegisterAtStart(instr->arguments()); - length = UseConstant(instr->length()); - index = UseConstant(instr->index()); - } else { - args = UseRegister(instr->arguments()); - length = UseRegisterAtStart(instr->length()); - index = UseRegisterOrConstantAtStart(instr->index()); - temp = TempRegister(); - } - - return DefineAsRegister( - new(zone()) LAccessArgumentsAt(args, length, index, temp)); -} - - -LInstruction* LChunkBuilder::DoAdd(HAdd* instr) { - if (instr->representation().IsSmiOrInteger32()) { - ASSERT(instr->left()->representation().Equals(instr->representation())); - ASSERT(instr->right()->representation().Equals(instr->representation())); - LOperand* left = UseRegisterAtStart(instr->BetterLeftOperand()); - LOperand* right = - UseRegisterOrConstantAtStart(instr->BetterRightOperand()); - LInstruction* result = instr->representation().IsSmi() ? - DefineAsRegister(new(zone()) LAddS(left, right)) : - DefineAsRegister(new(zone()) LAddI(left, right)); - if (instr->CheckFlag(HValue::kCanOverflow)) { - result = AssignEnvironment(result); - } - return result; - } else if (instr->representation().IsExternal()) { - ASSERT(instr->left()->representation().IsExternal()); - ASSERT(instr->right()->representation().IsInteger32()); - ASSERT(!instr->CheckFlag(HValue::kCanOverflow)); - LOperand* left = UseRegisterAtStart(instr->left()); - LOperand* right = UseRegisterOrConstantAtStart(instr->right()); - return DefineAsRegister(new(zone()) LAddE(left, right)); - } else if (instr->representation().IsDouble()) { - return DoArithmeticD(Token::ADD, instr); - } else { - ASSERT(instr->representation().IsTagged()); - return DoArithmeticT(Token::ADD, instr); - } -} - - -LInstruction* LChunkBuilder::DoAllocate(HAllocate* instr) { - info()->MarkAsDeferredCalling(); - LOperand* context = UseAny(instr->context()); - LOperand* size = UseRegisterOrConstant(instr->size()); - LOperand* temp1 = TempRegister(); - LOperand* temp2 = TempRegister(); - LAllocate* result = new(zone()) LAllocate(context, size, temp1, temp2); - return AssignPointerMap(DefineAsRegister(result)); -} - - -LInstruction* LChunkBuilder::DoApplyArguments(HApplyArguments* instr) { - LOperand* function = UseFixed(instr->function(), x1); - LOperand* receiver = UseFixed(instr->receiver(), x0); - LOperand* length = UseFixed(instr->length(), x2); - LOperand* elements = UseFixed(instr->elements(), x3); - LApplyArguments* result = new(zone()) LApplyArguments(function, - receiver, - length, - elements); - return MarkAsCall(DefineFixed(result, x0), instr, CAN_DEOPTIMIZE_EAGERLY); -} - - -LInstruction* LChunkBuilder::DoArgumentsElements(HArgumentsElements* instr) { - info()->MarkAsRequiresFrame(); - LOperand* temp = instr->from_inlined() ? NULL : TempRegister(); - return DefineAsRegister(new(zone()) LArgumentsElements(temp)); -} - - -LInstruction* LChunkBuilder::DoArgumentsLength(HArgumentsLength* instr) { - info()->MarkAsRequiresFrame(); - LOperand* value = UseRegisterAtStart(instr->value()); - return DefineAsRegister(new(zone()) LArgumentsLength(value)); -} - - -LInstruction* LChunkBuilder::DoArgumentsObject(HArgumentsObject* instr) { - // There are no real uses of the arguments object. - // arguments.length and element access are supported directly on - // stack arguments, and any real arguments object use causes a bailout. - // So this value is never used. - return NULL; -} - - -LInstruction* LChunkBuilder::DoBitwise(HBitwise* instr) { - if (instr->representation().IsSmiOrInteger32()) { - ASSERT(instr->left()->representation().Equals(instr->representation())); - ASSERT(instr->right()->representation().Equals(instr->representation())); - ASSERT(instr->CheckFlag(HValue::kTruncatingToInt32)); - - LOperand* left = UseRegisterAtStart(instr->BetterLeftOperand()); - LOperand* right = - UseRegisterOrConstantAtStart(instr->BetterRightOperand()); - return instr->representation().IsSmi() ? - DefineAsRegister(new(zone()) LBitS(left, right)) : - DefineAsRegister(new(zone()) LBitI(left, right)); - } else { - return DoArithmeticT(instr->op(), instr); - } -} - - -LInstruction* LChunkBuilder::DoBlockEntry(HBlockEntry* instr) { - // V8 expects a label to be generated for each basic block. - // This is used in some places like LAllocator::IsBlockBoundary - // in lithium-allocator.cc - return new(zone()) LLabel(instr->block()); -} - - -LInstruction* LChunkBuilder::DoBoundsCheck(HBoundsCheck* instr) { - LOperand* value = UseRegisterOrConstantAtStart(instr->index()); - LOperand* length = UseRegister(instr->length()); - return AssignEnvironment(new(zone()) LBoundsCheck(value, length)); -} - - -LInstruction* LChunkBuilder::DoBranch(HBranch* instr) { - LInstruction* goto_instr = CheckElideControlInstruction(instr); - if (goto_instr != NULL) return goto_instr; - - HValue* value = instr->value(); - Representation r = value->representation(); - HType type = value->type(); - - if (r.IsInteger32() || r.IsSmi() || r.IsDouble()) { - // These representations have simple checks that cannot deoptimize. - return new(zone()) LBranch(UseRegister(value), NULL, NULL); - } else { - ASSERT(r.IsTagged()); - if (type.IsBoolean() || type.IsSmi() || type.IsJSArray() || - type.IsHeapNumber()) { - // These types have simple checks that cannot deoptimize. - return new(zone()) LBranch(UseRegister(value), NULL, NULL); - } - - if (type.IsString()) { - // This type cannot deoptimize, but needs a scratch register. - return new(zone()) LBranch(UseRegister(value), TempRegister(), NULL); - } - - ToBooleanStub::Types expected = instr->expected_input_types(); - bool needs_temps = expected.NeedsMap() || expected.IsEmpty(); - LOperand* temp1 = needs_temps ? TempRegister() : NULL; - LOperand* temp2 = needs_temps ? TempRegister() : NULL; - - if (expected.IsGeneric() || expected.IsEmpty()) { - // The generic case cannot deoptimize because it already supports every - // possible input type. - ASSERT(needs_temps); - return new(zone()) LBranch(UseRegister(value), temp1, temp2); - } else { - return AssignEnvironment( - new(zone()) LBranch(UseRegister(value), temp1, temp2)); - } - } -} - - -LInstruction* LChunkBuilder::DoCallJSFunction( - HCallJSFunction* instr) { - LOperand* function = UseFixed(instr->function(), x1); - - LCallJSFunction* result = new(zone()) LCallJSFunction(function); - - return MarkAsCall(DefineFixed(result, x0), instr); -} - - -LInstruction* LChunkBuilder::DoCallWithDescriptor( - HCallWithDescriptor* instr) { - const CallInterfaceDescriptor* descriptor = instr->descriptor(); - - LOperand* target = UseRegisterOrConstantAtStart(instr->target()); - ZoneList ops(instr->OperandCount(), zone()); - ops.Add(target, zone()); - for (int i = 1; i < instr->OperandCount(); i++) { - LOperand* op = UseFixed(instr->OperandAt(i), - descriptor->GetParameterRegister(i - 1)); - ops.Add(op, zone()); - } - - LCallWithDescriptor* result = new(zone()) LCallWithDescriptor(descriptor, - ops, - zone()); - return MarkAsCall(DefineFixed(result, x0), instr); -} - - -LInstruction* LChunkBuilder::DoCallFunction(HCallFunction* instr) { - LOperand* context = UseFixed(instr->context(), cp); - LOperand* function = UseFixed(instr->function(), x1); - LCallFunction* call = new(zone()) LCallFunction(context, function); - return MarkAsCall(DefineFixed(call, x0), instr); -} - - -LInstruction* LChunkBuilder::DoCallNew(HCallNew* instr) { - LOperand* context = UseFixed(instr->context(), cp); - // The call to CallConstructStub will expect the constructor to be in x1. - LOperand* constructor = UseFixed(instr->constructor(), x1); - LCallNew* result = new(zone()) LCallNew(context, constructor); - return MarkAsCall(DefineFixed(result, x0), instr); -} - - -LInstruction* LChunkBuilder::DoCallNewArray(HCallNewArray* instr) { - LOperand* context = UseFixed(instr->context(), cp); - // The call to ArrayConstructCode will expect the constructor to be in x1. - LOperand* constructor = UseFixed(instr->constructor(), x1); - LCallNewArray* result = new(zone()) LCallNewArray(context, constructor); - return MarkAsCall(DefineFixed(result, x0), instr); -} - - -LInstruction* LChunkBuilder::DoCallRuntime(HCallRuntime* instr) { - LOperand* context = UseFixed(instr->context(), cp); - return MarkAsCall(DefineFixed(new(zone()) LCallRuntime(context), x0), instr); -} - - -LInstruction* LChunkBuilder::DoCallStub(HCallStub* instr) { - LOperand* context = UseFixed(instr->context(), cp); - return MarkAsCall(DefineFixed(new(zone()) LCallStub(context), x0), instr); -} - - -LInstruction* LChunkBuilder::DoCapturedObject(HCapturedObject* instr) { - instr->ReplayEnvironment(current_block_->last_environment()); - - // There are no real uses of a captured object. - return NULL; -} - - -LInstruction* LChunkBuilder::DoChange(HChange* instr) { - Representation from = instr->from(); - Representation to = instr->to(); - - if (from.IsSmi()) { - if (to.IsTagged()) { - LOperand* value = UseRegister(instr->value()); - return DefineSameAsFirst(new(zone()) LDummyUse(value)); - } - from = Representation::Tagged(); - } - - if (from.IsTagged()) { - if (to.IsDouble()) { - LOperand* value = UseRegister(instr->value()); - LOperand* temp = TempRegister(); - LNumberUntagD* res = new(zone()) LNumberUntagD(value, temp); - return AssignEnvironment(DefineAsRegister(res)); - } else if (to.IsSmi()) { - LOperand* value = UseRegister(instr->value()); - if (instr->value()->type().IsSmi()) { - return DefineSameAsFirst(new(zone()) LDummyUse(value)); - } - return AssignEnvironment(DefineSameAsFirst(new(zone()) LCheckSmi(value))); - } else { - ASSERT(to.IsInteger32()); - LInstruction* res = NULL; - - if (instr->value()->type().IsSmi() || - instr->value()->representation().IsSmi()) { - LOperand* value = UseRegisterAtStart(instr->value()); - res = DefineAsRegister(new(zone()) LSmiUntag(value, false)); - } else { - LOperand* value = UseRegister(instr->value()); - LOperand* temp1 = TempRegister(); - LOperand* temp2 = - instr->CanTruncateToInt32() ? TempRegister() : FixedTemp(d24); - res = DefineAsRegister(new(zone()) LTaggedToI(value, temp1, temp2)); - res = AssignEnvironment(res); - } - - return res; - } - } else if (from.IsDouble()) { - if (to.IsTagged()) { - info()->MarkAsDeferredCalling(); - LOperand* value = UseRegister(instr->value()); - LOperand* temp1 = TempRegister(); - LOperand* temp2 = TempRegister(); - - LNumberTagD* result = new(zone()) LNumberTagD(value, temp1, temp2); - return AssignPointerMap(DefineAsRegister(result)); - } else { - ASSERT(to.IsSmi() || to.IsInteger32()); - LOperand* value = UseRegister(instr->value()); - - if (instr->CanTruncateToInt32()) { - LTruncateDoubleToIntOrSmi* result = - new(zone()) LTruncateDoubleToIntOrSmi(value); - return DefineAsRegister(result); - } else { - LDoubleToIntOrSmi* result = new(zone()) LDoubleToIntOrSmi(value); - return AssignEnvironment(DefineAsRegister(result)); - } - } - } else if (from.IsInteger32()) { - info()->MarkAsDeferredCalling(); - if (to.IsTagged()) { - if (instr->value()->CheckFlag(HInstruction::kUint32)) { - LOperand* value = UseRegister(instr->value()); - LNumberTagU* result = new(zone()) LNumberTagU(value, - TempRegister(), - TempRegister()); - return AssignEnvironment(AssignPointerMap(DefineAsRegister(result))); - } else { - STATIC_ASSERT((kMinInt == Smi::kMinValue) && - (kMaxInt == Smi::kMaxValue)); - LOperand* value = UseRegisterAtStart(instr->value()); - return DefineAsRegister(new(zone()) LSmiTag(value)); - } - } else if (to.IsSmi()) { - LOperand* value = UseRegisterAtStart(instr->value()); - if (instr->value()->CheckFlag(HInstruction::kUint32)) { - LUint32ToSmi* result = new(zone()) LUint32ToSmi(value); - return AssignEnvironment(DefineAsRegister(result)); - } else { - // This cannot deoptimize because an A64 smi can represent any int32. - return DefineAsRegister(new(zone()) LInteger32ToSmi(value)); - } - } else { - ASSERT(to.IsDouble()); - if (instr->value()->CheckFlag(HInstruction::kUint32)) { - return DefineAsRegister( - new(zone()) LUint32ToDouble(UseRegisterAtStart(instr->value()))); - } else { - return DefineAsRegister( - new(zone()) LInteger32ToDouble(UseRegisterAtStart(instr->value()))); - } - } - } - - UNREACHABLE(); - return NULL; -} - - -LInstruction* LChunkBuilder::DoCheckValue(HCheckValue* instr) { - // We only need a temp register if the target is in new space, but we can't - // dereference the handle to test that here. - // TODO(all): Check these constraints. The temp register is not always used. - LOperand* value = UseRegister(instr->value()); - LOperand* temp = TempRegister(); - return AssignEnvironment(new(zone()) LCheckValue(value, temp)); -} - - -LInstruction* LChunkBuilder::DoCheckInstanceType(HCheckInstanceType* instr) { - LOperand* value = UseRegisterAtStart(instr->value()); - LOperand* temp = TempRegister(); - LInstruction* result = new(zone()) LCheckInstanceType(value, temp); - return AssignEnvironment(result); -} - - -LInstruction* LChunkBuilder::DoCheckMaps(HCheckMaps* instr) { - if (instr->CanOmitMapChecks()) { - // LCheckMaps does nothing in this case. - return new(zone()) LCheckMaps(NULL); - } else { - LOperand* value = UseRegisterAtStart(instr->value()); - LOperand* temp = TempRegister(); - - if (instr->has_migration_target()) { - info()->MarkAsDeferredCalling(); - LInstruction* result = new(zone()) LCheckMaps(value, temp); - return AssignPointerMap(AssignEnvironment(result)); - } else { - return AssignEnvironment(new(zone()) LCheckMaps(value, temp)); - } - } -} - - -LInstruction* LChunkBuilder::DoCheckHeapObject(HCheckHeapObject* instr) { - LOperand* value = UseRegisterAtStart(instr->value()); - return AssignEnvironment(new(zone()) LCheckNonSmi(value)); -} - - -LInstruction* LChunkBuilder::DoCheckSmi(HCheckSmi* instr) { - LOperand* value = UseRegisterAtStart(instr->value()); - return AssignEnvironment(new(zone()) LCheckSmi(value)); -} - - -LInstruction* LChunkBuilder::DoClampToUint8(HClampToUint8* instr) { - HValue* value = instr->value(); - Representation input_rep = value->representation(); - LOperand* reg = UseRegister(value); - if (input_rep.IsDouble()) { - return DefineAsRegister(new(zone()) LClampDToUint8(reg)); - } else if (input_rep.IsInteger32()) { - return DefineAsRegister(new(zone()) LClampIToUint8(reg)); - } else { - ASSERT(input_rep.IsSmiOrTagged()); - return AssignEnvironment( - DefineAsRegister(new(zone()) LClampTToUint8(reg, - TempRegister(), - FixedTemp(d24)))); - } -} - - -LInstruction* LChunkBuilder::DoClassOfTestAndBranch( - HClassOfTestAndBranch* instr) { - ASSERT(instr->value()->representation().IsTagged()); - LOperand* value = UseRegisterAtStart(instr->value()); - return new(zone()) LClassOfTestAndBranch(value, - TempRegister(), - TempRegister()); -} - - -LInstruction* LChunkBuilder::DoCompareNumericAndBranch( - HCompareNumericAndBranch* instr) { - Representation r = instr->representation(); - - // TODO(all): This instruction has been replaced by HCompareNumericAndBranch - // on bleeding_edge. We should update when we'll do the rebase. - if (r.IsSmiOrInteger32()) { - ASSERT(instr->left()->representation().Equals(r)); - ASSERT(instr->right()->representation().Equals(r)); - LOperand* left = UseRegisterOrConstantAtStart(instr->left()); - LOperand* right = UseRegisterOrConstantAtStart(instr->right()); - return new(zone()) LCompareNumericAndBranch(left, right); - } else { - ASSERT(r.IsDouble()); - ASSERT(instr->left()->representation().IsDouble()); - ASSERT(instr->right()->representation().IsDouble()); - // TODO(all): In fact the only case that we can handle more efficiently is - // when one of the operand is the constant 0. Currently the MacroAssembler - // will be able to cope with any constant by loading it into an internal - // scratch register. This means that if the constant is used more that once, - // it will be loaded multiple times. Unfortunatly crankshaft already - // duplicates constant loads, but we should modify the code below once this - // issue has been addressed in crankshaft. - LOperand* left = UseRegisterOrConstantAtStart(instr->left()); - LOperand* right = UseRegisterOrConstantAtStart(instr->right()); - return new(zone()) LCompareNumericAndBranch(left, right); - } -} - - -LInstruction* LChunkBuilder::DoCompareGeneric(HCompareGeneric* instr) { - ASSERT(instr->left()->representation().IsTagged()); - ASSERT(instr->right()->representation().IsTagged()); - LOperand* context = UseFixed(instr->context(), cp); - LOperand* left = UseFixed(instr->left(), x1); - LOperand* right = UseFixed(instr->right(), x0); - LCmpT* result = new(zone()) LCmpT(context, left, right); - return MarkAsCall(DefineFixed(result, x0), instr); -} - - -LInstruction* LChunkBuilder::DoCompareHoleAndBranch( - HCompareHoleAndBranch* instr) { - LOperand* value = UseRegister(instr->value()); - if (instr->representation().IsTagged()) { - return new(zone()) LCmpHoleAndBranchT(value); - } else { - LOperand* temp = TempRegister(); - return new(zone()) LCmpHoleAndBranchD(value, temp); - } -} - - -LInstruction* LChunkBuilder::DoCompareObjectEqAndBranch( - HCompareObjectEqAndBranch* instr) { - LInstruction* goto_instr = CheckElideControlInstruction(instr); - if (goto_instr != NULL) return goto_instr; - - LOperand* left = UseRegisterAtStart(instr->left()); - LOperand* right = UseRegisterAtStart(instr->right()); - return new(zone()) LCmpObjectEqAndBranch(left, right); -} - - -LInstruction* LChunkBuilder::DoCompareMap(HCompareMap* instr) { - LInstruction* goto_instr = CheckElideControlInstruction(instr); - if (goto_instr != NULL) return goto_instr; - - ASSERT(instr->value()->representation().IsTagged()); - LOperand* value = UseRegisterAtStart(instr->value()); - LOperand* temp = TempRegister(); - return new(zone()) LCmpMapAndBranch(value, temp); -} - - -LInstruction* LChunkBuilder::DoConstant(HConstant* instr) { - Representation r = instr->representation(); - if (r.IsSmi()) { - return DefineAsRegister(new(zone()) LConstantS); - } else if (r.IsInteger32()) { - return DefineAsRegister(new(zone()) LConstantI); - } else if (r.IsDouble()) { - return DefineAsRegister(new(zone()) LConstantD); - } else if (r.IsExternal()) { - return DefineAsRegister(new(zone()) LConstantE); - } else if (r.IsTagged()) { - return DefineAsRegister(new(zone()) LConstantT); - } else { - UNREACHABLE(); - return NULL; - } -} - - -LInstruction* LChunkBuilder::DoContext(HContext* instr) { - if (instr->HasNoUses()) return NULL; - - if (info()->IsStub()) { - return DefineFixed(new(zone()) LContext, cp); - } - - return DefineAsRegister(new(zone()) LContext); -} - - -LInstruction* LChunkBuilder::DoDateField(HDateField* instr) { - LOperand* object = UseFixed(instr->value(), x0); - LDateField* result = new(zone()) LDateField(object, instr->index()); - return MarkAsCall(DefineFixed(result, x0), instr, CAN_DEOPTIMIZE_EAGERLY); -} - - -LInstruction* LChunkBuilder::DoDebugBreak(HDebugBreak* instr) { - return new(zone()) LDebugBreak(); -} - - -LInstruction* LChunkBuilder::DoDeclareGlobals(HDeclareGlobals* instr) { - LOperand* context = UseFixed(instr->context(), cp); - return MarkAsCall(new(zone()) LDeclareGlobals(context), instr); -} - - -LInstruction* LChunkBuilder::DoDeoptimize(HDeoptimize* instr) { - return AssignEnvironment(new(zone()) LDeoptimize); -} - - -LInstruction* LChunkBuilder::DoDiv(HDiv* instr) { - if (instr->representation().IsInteger32()) { - // TODO(all): Update this case to support smi inputs. - ASSERT(instr->left()->representation().Equals(instr->representation())); - ASSERT(instr->right()->representation().Equals(instr->representation())); - if (instr->RightIsPowerOf2()) { - ASSERT(!instr->CheckFlag(HValue::kCanBeDivByZero)); - LOperand* value = UseRegister(instr->left()); - LDivI* div = new(zone()) LDivI(value, UseConstant(instr->right()), NULL); - return AssignEnvironment(DefineAsRegister(div)); - } - LOperand* dividend = UseRegister(instr->left()); - LOperand* divisor = UseRegister(instr->right()); - LOperand* temp = instr->CheckFlag(HInstruction::kAllUsesTruncatingToInt32) - ? NULL : TempRegister(); - LDivI* div = new(zone()) LDivI(dividend, divisor, temp); - return AssignEnvironment(DefineAsRegister(div)); - } else if (instr->representation().IsDouble()) { - return DoArithmeticD(Token::DIV, instr); - } else { - return DoArithmeticT(Token::DIV, instr); - } -} - - -LInstruction* LChunkBuilder::DoDummyUse(HDummyUse* instr) { - return DefineAsRegister(new(zone()) LDummyUse(UseAny(instr->value()))); -} - - -LInstruction* LChunkBuilder::DoEnterInlined(HEnterInlined* instr) { - HEnvironment* outer = current_block_->last_environment(); - HConstant* undefined = graph()->GetConstantUndefined(); - HEnvironment* inner = outer->CopyForInlining(instr->closure(), - instr->arguments_count(), - instr->function(), - undefined, - instr->inlining_kind()); - // Only replay binding of arguments object if it wasn't removed from graph. - if ((instr->arguments_var() != NULL) && - instr->arguments_object()->IsLinked()) { - inner->Bind(instr->arguments_var(), instr->arguments_object()); - } - inner->set_entry(instr); - current_block_->UpdateEnvironment(inner); - chunk_->AddInlinedClosure(instr->closure()); - return NULL; -} - - -LInstruction* LChunkBuilder::DoEnvironmentMarker(HEnvironmentMarker* instr) { - UNREACHABLE(); - return NULL; -} - - -LInstruction* LChunkBuilder::DoForceRepresentation( - HForceRepresentation* instr) { - // All HForceRepresentation instructions should be eliminated in the - // representation change phase of Hydrogen. - UNREACHABLE(); - return NULL; -} - - -LInstruction* LChunkBuilder::DoFunctionLiteral(HFunctionLiteral* instr) { - LOperand* context = UseFixed(instr->context(), cp); - return MarkAsCall( - DefineFixed(new(zone()) LFunctionLiteral(context), x0), instr); -} - - -LInstruction* LChunkBuilder::DoGetCachedArrayIndex( - HGetCachedArrayIndex* instr) { - ASSERT(instr->value()->representation().IsTagged()); - LOperand* value = UseRegisterAtStart(instr->value()); - return DefineAsRegister(new(zone()) LGetCachedArrayIndex(value)); -} - - -LInstruction* LChunkBuilder::DoGoto(HGoto* instr) { - return new(zone()) LGoto(instr->FirstSuccessor()); -} - - -LInstruction* LChunkBuilder::DoHasCachedArrayIndexAndBranch( - HHasCachedArrayIndexAndBranch* instr) { - ASSERT(instr->value()->representation().IsTagged()); - return new(zone()) LHasCachedArrayIndexAndBranch( - UseRegisterAtStart(instr->value()), TempRegister()); -} - - -LInstruction* LChunkBuilder::DoHasInstanceTypeAndBranch( - HHasInstanceTypeAndBranch* instr) { - ASSERT(instr->value()->representation().IsTagged()); - LOperand* value = UseRegisterAtStart(instr->value()); - return new(zone()) LHasInstanceTypeAndBranch(value, TempRegister()); -} - - -LInstruction* LChunkBuilder::DoInnerAllocatedObject( - HInnerAllocatedObject* instr) { - LOperand* base_object = UseRegisterAtStart(instr->base_object()); - LOperand* offset = UseRegisterOrConstantAtStart(instr->offset()); - return DefineAsRegister( - new(zone()) LInnerAllocatedObject(base_object, offset)); -} - - -LInstruction* LChunkBuilder::DoInstanceOf(HInstanceOf* instr) { - LOperand* context = UseFixed(instr->context(), cp); - LInstanceOf* result = new(zone()) LInstanceOf( - context, - UseFixed(instr->left(), InstanceofStub::left()), - UseFixed(instr->right(), InstanceofStub::right())); - return MarkAsCall(DefineFixed(result, x0), instr); -} - - -LInstruction* LChunkBuilder::DoInstanceOfKnownGlobal( - HInstanceOfKnownGlobal* instr) { - LInstanceOfKnownGlobal* result = new(zone()) LInstanceOfKnownGlobal( - UseFixed(instr->context(), cp), - UseFixed(instr->left(), InstanceofStub::left())); - return MarkAsCall(DefineFixed(result, x0), instr); -} - - -LInstruction* LChunkBuilder::DoInvokeFunction(HInvokeFunction* instr) { - LOperand* context = UseFixed(instr->context(), cp); - // The function is required (by MacroAssembler::InvokeFunction) to be in x1. - LOperand* function = UseFixed(instr->function(), x1); - LInvokeFunction* result = new(zone()) LInvokeFunction(context, function); - return MarkAsCall(DefineFixed(result, x0), instr, CANNOT_DEOPTIMIZE_EAGERLY); -} - - -LInstruction* LChunkBuilder::DoIsConstructCallAndBranch( - HIsConstructCallAndBranch* instr) { - return new(zone()) LIsConstructCallAndBranch(TempRegister(), TempRegister()); -} - - -LInstruction* LChunkBuilder::DoCompareMinusZeroAndBranch( - HCompareMinusZeroAndBranch* instr) { - LInstruction* goto_instr = CheckElideControlInstruction(instr); - if (goto_instr != NULL) return goto_instr; - LOperand* value = UseRegister(instr->value()); - LOperand* scratch = TempRegister(); - return new(zone()) LCompareMinusZeroAndBranch(value, scratch); -} - - -LInstruction* LChunkBuilder::DoIsObjectAndBranch(HIsObjectAndBranch* instr) { - ASSERT(instr->value()->representation().IsTagged()); - LOperand* value = UseRegisterAtStart(instr->value()); - LOperand* temp1 = TempRegister(); - LOperand* temp2 = TempRegister(); - return new(zone()) LIsObjectAndBranch(value, temp1, temp2); -} - - -LInstruction* LChunkBuilder::DoIsStringAndBranch(HIsStringAndBranch* instr) { - ASSERT(instr->value()->representation().IsTagged()); - LOperand* value = UseRegisterAtStart(instr->value()); - LOperand* temp = TempRegister(); - return new(zone()) LIsStringAndBranch(value, temp); -} - - -LInstruction* LChunkBuilder::DoIsSmiAndBranch(HIsSmiAndBranch* instr) { - ASSERT(instr->value()->representation().IsTagged()); - return new(zone()) LIsSmiAndBranch(UseRegisterAtStart(instr->value())); -} - - -LInstruction* LChunkBuilder::DoIsUndetectableAndBranch( - HIsUndetectableAndBranch* instr) { - ASSERT(instr->value()->representation().IsTagged()); - LOperand* value = UseRegisterAtStart(instr->value()); - return new(zone()) LIsUndetectableAndBranch(value, TempRegister()); -} - - -LInstruction* LChunkBuilder::DoLeaveInlined(HLeaveInlined* instr) { - LInstruction* pop = NULL; - HEnvironment* env = current_block_->last_environment(); - - if (env->entry()->arguments_pushed()) { - int argument_count = env->arguments_environment()->parameter_count(); - pop = new(zone()) LDrop(argument_count); - ASSERT(instr->argument_delta() == -argument_count); - } - - HEnvironment* outer = - current_block_->last_environment()->DiscardInlined(false); - current_block_->UpdateEnvironment(outer); - - return pop; -} - - -LInstruction* LChunkBuilder::DoLoadContextSlot(HLoadContextSlot* instr) { - LOperand* context = UseRegisterAtStart(instr->value()); - LInstruction* result = - DefineAsRegister(new(zone()) LLoadContextSlot(context)); - return instr->RequiresHoleCheck() ? AssignEnvironment(result) : result; -} - - -LInstruction* LChunkBuilder::DoLoadFunctionPrototype( - HLoadFunctionPrototype* instr) { - LOperand* function = UseRegister(instr->function()); - LOperand* temp = TempRegister(); - return AssignEnvironment(DefineAsRegister( - new(zone()) LLoadFunctionPrototype(function, temp))); -} - - -LInstruction* LChunkBuilder::DoLoadGlobalCell(HLoadGlobalCell* instr) { - LLoadGlobalCell* result = new(zone()) LLoadGlobalCell(); - return instr->RequiresHoleCheck() - ? AssignEnvironment(DefineAsRegister(result)) - : DefineAsRegister(result); -} - - -LInstruction* LChunkBuilder::DoLoadGlobalGeneric(HLoadGlobalGeneric* instr) { - LOperand* context = UseFixed(instr->context(), cp); - LOperand* global_object = UseFixed(instr->global_object(), x0); - LLoadGlobalGeneric* result = - new(zone()) LLoadGlobalGeneric(context, global_object); - return MarkAsCall(DefineFixed(result, x0), instr); -} - - -LInstruction* LChunkBuilder::DoLoadKeyed(HLoadKeyed* instr) { - ASSERT(instr->key()->representation().IsSmiOrInteger32()); - ElementsKind elements_kind = instr->elements_kind(); - LOperand* elements = UseRegister(instr->elements()); - LOperand* key = UseRegisterOrConstantAtStart(instr->key()); - - if (!instr->is_typed_elements()) { - if (instr->representation().IsDouble()) { - LOperand* temp = (!instr->key()->IsConstant() || - instr->RequiresHoleCheck()) - ? TempRegister() - : NULL; - - LLoadKeyedFixedDouble* result = - new(zone()) LLoadKeyedFixedDouble(elements, key, temp); - return instr->RequiresHoleCheck() - ? AssignEnvironment(DefineAsRegister(result)) - : DefineAsRegister(result); - } else { - ASSERT(instr->representation().IsSmiOrTagged() || - instr->representation().IsInteger32()); - LOperand* temp = instr->key()->IsConstant() ? NULL : TempRegister(); - LLoadKeyedFixed* result = - new(zone()) LLoadKeyedFixed(elements, key, temp); - return instr->RequiresHoleCheck() - ? AssignEnvironment(DefineAsRegister(result)) - : DefineAsRegister(result); - } - } else { - ASSERT((instr->representation().IsInteger32() && - !IsDoubleOrFloatElementsKind(instr->elements_kind())) || - (instr->representation().IsDouble() && - IsDoubleOrFloatElementsKind(instr->elements_kind()))); - - LOperand* temp = instr->key()->IsConstant() ? NULL : TempRegister(); - LLoadKeyedExternal* result = - new(zone()) LLoadKeyedExternal(elements, key, temp); - // An unsigned int array load might overflow and cause a deopt. Make sure it - // has an environment. - if (instr->RequiresHoleCheck() || - elements_kind == EXTERNAL_UINT32_ELEMENTS || - elements_kind == UINT32_ELEMENTS) { - return AssignEnvironment(DefineAsRegister(result)); - } else { - return DefineAsRegister(result); - } - } -} - - -LInstruction* LChunkBuilder::DoLoadKeyedGeneric(HLoadKeyedGeneric* instr) { - LOperand* context = UseFixed(instr->context(), cp); - LOperand* object = UseFixed(instr->object(), x1); - LOperand* key = UseFixed(instr->key(), x0); - - LInstruction* result = - DefineFixed(new(zone()) LLoadKeyedGeneric(context, object, key), x0); - return MarkAsCall(result, instr); -} - - -LInstruction* LChunkBuilder::DoLoadNamedField(HLoadNamedField* instr) { - LOperand* object = UseRegisterAtStart(instr->object()); - return DefineAsRegister(new(zone()) LLoadNamedField(object)); -} - - -LInstruction* LChunkBuilder::DoLoadNamedGeneric(HLoadNamedGeneric* instr) { - LOperand* context = UseFixed(instr->context(), cp); - LOperand* object = UseFixed(instr->object(), x0); - LInstruction* result = - DefineFixed(new(zone()) LLoadNamedGeneric(context, object), x0); - return MarkAsCall(result, instr); -} - - -LInstruction* LChunkBuilder::DoLoadRoot(HLoadRoot* instr) { - return DefineAsRegister(new(zone()) LLoadRoot); -} - - -LInstruction* LChunkBuilder::DoMapEnumLength(HMapEnumLength* instr) { - LOperand* map = UseRegisterAtStart(instr->value()); - return DefineAsRegister(new(zone()) LMapEnumLength(map)); -} - - -LInstruction* LChunkBuilder::DoMathFloorOfDiv(HMathFloorOfDiv* instr) { - HValue* right = instr->right(); - LOperand* dividend = UseRegister(instr->left()); - LOperand* divisor = UseRegister(right); - LOperand* remainder = TempRegister(); - return AssignEnvironment(DefineAsRegister( - new(zone()) LMathFloorOfDiv(dividend, divisor, remainder))); -} - - -LInstruction* LChunkBuilder::DoMathMinMax(HMathMinMax* instr) { - LOperand* left = NULL; - LOperand* right = NULL; - if (instr->representation().IsSmiOrInteger32()) { - ASSERT(instr->left()->representation().Equals(instr->representation())); - ASSERT(instr->right()->representation().Equals(instr->representation())); - left = UseRegisterAtStart(instr->BetterLeftOperand()); - right = UseRegisterOrConstantAtStart(instr->BetterRightOperand()); - } else { - ASSERT(instr->representation().IsDouble()); - ASSERT(instr->left()->representation().IsDouble()); - ASSERT(instr->right()->representation().IsDouble()); - left = UseRegisterAtStart(instr->left()); - right = UseRegisterAtStart(instr->right()); - } - return DefineAsRegister(new(zone()) LMathMinMax(left, right)); -} - - -LInstruction* LChunkBuilder::DoMod(HMod* hmod) { - HValue* hleft = hmod->left(); - HValue* hright = hmod->right(); - - // TODO(jbramley): Add smi support. - if (hmod->representation().IsInteger32()) { - ASSERT(hleft->representation().IsInteger32()); - ASSERT(hleft->representation().IsInteger32()); - LOperand* left_op; - LOperand* right_op; - - if (hmod->RightIsPowerOf2()) { - left_op = UseRegisterAtStart(hleft); - right_op = UseConstant(hright); - } else { - right_op = UseRegister(hright); - left_op = UseRegister(hleft); - } - - LModI* lmod = new(zone()) LModI(left_op, right_op); - - if (hmod->right()->CanBeZero() || - (hmod->CheckFlag(HValue::kBailoutOnMinusZero) && - hmod->left()->CanBeNegative() && hmod->CanBeZero())) { - AssignEnvironment(lmod); - } - return DefineAsRegister(lmod); - - } else if (hmod->representation().IsSmiOrTagged()) { - return DoArithmeticT(Token::MOD, hmod); - } else { - return DoArithmeticD(Token::MOD, hmod); - } -} - - -LInstruction* LChunkBuilder::DoMul(HMul* instr) { - if (instr->representation().IsSmiOrInteger32()) { - ASSERT(instr->left()->representation().Equals(instr->representation())); - ASSERT(instr->right()->representation().Equals(instr->representation())); - - bool can_overflow = instr->CheckFlag(HValue::kCanOverflow); - bool bailout_on_minus_zero = instr->CheckFlag(HValue::kBailoutOnMinusZero); - bool needs_environment = can_overflow || bailout_on_minus_zero; - - HValue* least_const = instr->BetterLeftOperand(); - HValue* most_const = instr->BetterRightOperand(); - - LOperand* left = UseRegisterAtStart(least_const); - - // LMulConstI can handle a subset of constants: - // With support for overflow detection: - // -1, 0, 1, 2 - // Without support for overflow detection: - // 2^n, -(2^n) - // 2^n + 1, -(2^n - 1) - if (most_const->IsConstant()) { - int32_t constant = HConstant::cast(most_const)->Integer32Value(); - int32_t constant_abs = (constant >= 0) ? constant : -constant; - - if (((constant >= -1) && (constant <= 2)) || - (!can_overflow && (IsPowerOf2(constant_abs) || - IsPowerOf2(constant_abs + 1) || - IsPowerOf2(constant_abs - 1)))) { - LConstantOperand* right = UseConstant(most_const); - LMulConstIS* mul = new(zone()) LMulConstIS(left, right); - if (needs_environment) AssignEnvironment(mul); - return DefineAsRegister(mul); - } - } - - // LMulI/S can handle all cases, but it requires that a register is - // allocated for the second operand. - LInstruction* result; - if (instr->representation().IsSmi()) { - // TODO(jbramley/rmcilroy): Fix LMulS so we can UseRegisterAtStart here. - LOperand* right = UseRegister(most_const); - result = DefineAsRegister(new(zone()) LMulS(left, right)); - } else { - LOperand* right = UseRegisterAtStart(most_const); - result = DefineAsRegister(new(zone()) LMulI(left, right)); - } - if (needs_environment) AssignEnvironment(result); - return result; - } else if (instr->representation().IsDouble()) { - return DoArithmeticD(Token::MUL, instr); - } else { - return DoArithmeticT(Token::MUL, instr); - } -} - - -LInstruction* LChunkBuilder::DoOsrEntry(HOsrEntry* instr) { - ASSERT(argument_count_ == 0); - allocator_->MarkAsOsrEntry(); - current_block_->last_environment()->set_ast_id(instr->ast_id()); - return AssignEnvironment(new(zone()) LOsrEntry); -} - - -LInstruction* LChunkBuilder::DoParameter(HParameter* instr) { - LParameter* result = new(zone()) LParameter; - if (instr->kind() == HParameter::STACK_PARAMETER) { - int spill_index = chunk_->GetParameterStackSlot(instr->index()); - return DefineAsSpilled(result, spill_index); - } else { - ASSERT(info()->IsStub()); - CodeStubInterfaceDescriptor* descriptor = - info()->code_stub()->GetInterfaceDescriptor(info()->isolate()); - int index = static_cast(instr->index()); - Register reg = descriptor->GetParameterRegister(index); - return DefineFixed(result, reg); - } -} - - -LInstruction* LChunkBuilder::DoPower(HPower* instr) { - ASSERT(instr->representation().IsDouble()); - // We call a C function for double power. It can't trigger a GC. - // We need to use fixed result register for the call. - Representation exponent_type = instr->right()->representation(); - ASSERT(instr->left()->representation().IsDouble()); - LOperand* left = UseFixedDouble(instr->left(), d0); - LOperand* right = exponent_type.IsInteger32() - ? UseFixed(instr->right(), x12) - : exponent_type.IsDouble() - ? UseFixedDouble(instr->right(), d1) - : UseFixed(instr->right(), x11); - LPower* result = new(zone()) LPower(left, right); - return MarkAsCall(DefineFixedDouble(result, d0), - instr, - CAN_DEOPTIMIZE_EAGERLY); -} - - -LInstruction* LChunkBuilder::DoPushArgument(HPushArgument* instr) { - LOperand* argument = UseRegister(instr->argument()); - return new(zone()) LPushArgument(argument); -} - - -LInstruction* LChunkBuilder::DoRegExpLiteral(HRegExpLiteral* instr) { - LOperand* context = UseFixed(instr->context(), cp); - return MarkAsCall( - DefineFixed(new(zone()) LRegExpLiteral(context), x0), instr); -} - - -LInstruction* LChunkBuilder::DoReturn(HReturn* instr) { - LOperand* context = info()->IsStub() - ? UseFixed(instr->context(), cp) - : NULL; - LOperand* parameter_count = UseRegisterOrConstant(instr->parameter_count()); - return new(zone()) LReturn(UseFixed(instr->value(), x0), context, - parameter_count); -} - - -LInstruction* LChunkBuilder::DoSeqStringGetChar(HSeqStringGetChar* instr) { - // TODO(all): Use UseRegisterAtStart and UseRegisterOrConstantAtStart here. - // We cannot do it now because the debug code in the implementation changes - // temp. - LOperand* string = UseRegister(instr->string()); - LOperand* index = UseRegisterOrConstant(instr->index()); - LOperand* temp = TempRegister(); - LSeqStringGetChar* result = - new(zone()) LSeqStringGetChar(string, index, temp); - return DefineAsRegister(result); -} - - -LInstruction* LChunkBuilder::DoSeqStringSetChar(HSeqStringSetChar* instr) { - LOperand* string = UseRegister(instr->string()); - LOperand* index = FLAG_debug_code - ? UseRegister(instr->index()) - : UseRegisterOrConstant(instr->index()); - LOperand* value = UseRegister(instr->value()); - LOperand* context = FLAG_debug_code ? UseFixed(instr->context(), cp) : NULL; - LOperand* temp = TempRegister(); - LSeqStringSetChar* result = - new(zone()) LSeqStringSetChar(context, string, index, value, temp); - return DefineAsRegister(result); -} - - -LInstruction* LChunkBuilder::DoShift(Token::Value op, - HBitwiseBinaryOperation* instr) { - if (instr->representation().IsTagged()) { - return DoArithmeticT(op, instr); - } - - ASSERT(instr->representation().IsInteger32() || - instr->representation().IsSmi()); - ASSERT(instr->left()->representation().Equals(instr->representation())); - ASSERT(instr->right()->representation().Equals(instr->representation())); - - LOperand* left = instr->representation().IsSmi() - ? UseRegister(instr->left()) - : UseRegisterAtStart(instr->left()); - - HValue* right_value = instr->right(); - LOperand* right = NULL; - LOperand* temp = NULL; - int constant_value = 0; - if (right_value->IsConstant()) { - right = UseConstant(right_value); - HConstant* constant = HConstant::cast(right_value); - constant_value = constant->Integer32Value() & 0x1f; - } else { - right = UseRegisterAtStart(right_value); - if (op == Token::ROR) { - temp = TempRegister(); - } - } - - // Shift operations can only deoptimize if we do a logical shift by 0 and the - // result cannot be truncated to int32. - bool does_deopt = false; - if ((op == Token::SHR) && (constant_value == 0)) { - if (FLAG_opt_safe_uint32_operations) { - does_deopt = !instr->CheckFlag(HInstruction::kUint32); - } else { - does_deopt = !instr->CheckUsesForFlag(HValue::kTruncatingToInt32); - } - } - - LInstruction* result; - if (instr->representation().IsInteger32()) { - result = DefineAsRegister(new(zone()) LShiftI(op, left, right, does_deopt)); - } else { - ASSERT(instr->representation().IsSmi()); - result = DefineAsRegister( - new(zone()) LShiftS(op, left, right, temp, does_deopt)); - } - - return does_deopt ? AssignEnvironment(result) : result; -} - - -LInstruction* LChunkBuilder::DoRor(HRor* instr) { - return DoShift(Token::ROR, instr); -} - - -LInstruction* LChunkBuilder::DoSar(HSar* instr) { - return DoShift(Token::SAR, instr); -} - - -LInstruction* LChunkBuilder::DoShl(HShl* instr) { - return DoShift(Token::SHL, instr); -} - - -LInstruction* LChunkBuilder::DoShr(HShr* instr) { - return DoShift(Token::SHR, instr); -} - - -LInstruction* LChunkBuilder::DoSimulate(HSimulate* instr) { - instr->ReplayEnvironment(current_block_->last_environment()); - - // If there is an instruction pending deoptimization environment create a - // lazy bailout instruction to capture the environment. - if (pending_deoptimization_ast_id_ == instr->ast_id()) { - LInstruction* result = new(zone()) LLazyBailout; - result = AssignEnvironment(result); - // Store the lazy deopt environment with the instruction if needed. Right - // now it is only used for LInstanceOfKnownGlobal. - instruction_pending_deoptimization_environment_-> - SetDeferredLazyDeoptimizationEnvironment(result->environment()); - instruction_pending_deoptimization_environment_ = NULL; - pending_deoptimization_ast_id_ = BailoutId::None(); - return result; - } - - return NULL; -} - - -LInstruction* LChunkBuilder::DoStackCheck(HStackCheck* instr) { - if (instr->is_function_entry()) { - LOperand* context = UseFixed(instr->context(), cp); - return MarkAsCall(new(zone()) LStackCheck(context), instr); - } else { - ASSERT(instr->is_backwards_branch()); - LOperand* context = UseAny(instr->context()); - return AssignEnvironment( - AssignPointerMap(new(zone()) LStackCheck(context))); - } -} - - -LInstruction* LChunkBuilder::DoStoreCodeEntry(HStoreCodeEntry* instr) { - LOperand* function = UseRegister(instr->function()); - LOperand* code_object = UseRegisterAtStart(instr->code_object()); - LOperand* temp = TempRegister(); - return new(zone()) LStoreCodeEntry(function, code_object, temp); -} - - -LInstruction* LChunkBuilder::DoStoreContextSlot(HStoreContextSlot* instr) { - LOperand* temp = TempRegister(); - LOperand* context; - LOperand* value; - if (instr->NeedsWriteBarrier()) { - // TODO(all): Replace these constraints when RecordWriteStub has been - // rewritten. - context = UseRegisterAndClobber(instr->context()); - value = UseRegisterAndClobber(instr->value()); - } else { - context = UseRegister(instr->context()); - value = UseRegister(instr->value()); - } - LInstruction* result = new(zone()) LStoreContextSlot(context, value, temp); - return instr->RequiresHoleCheck() ? AssignEnvironment(result) : result; -} - - -LInstruction* LChunkBuilder::DoStoreGlobalCell(HStoreGlobalCell* instr) { - LOperand* value = UseRegister(instr->value()); - if (instr->RequiresHoleCheck()) { - return AssignEnvironment(new(zone()) LStoreGlobalCell(value, - TempRegister(), - TempRegister())); - } else { - return new(zone()) LStoreGlobalCell(value, TempRegister(), NULL); - } -} - - -LInstruction* LChunkBuilder::DoStoreKeyed(HStoreKeyed* instr) { - LOperand* temp = NULL; - LOperand* elements = NULL; - LOperand* val = NULL; - LOperand* key = NULL; - - if (!instr->is_typed_elements() && - instr->value()->representation().IsTagged() && - instr->NeedsWriteBarrier()) { - // RecordWrite() will clobber all registers. - elements = UseRegisterAndClobber(instr->elements()); - val = UseRegisterAndClobber(instr->value()); - key = UseRegisterAndClobber(instr->key()); - } else { - elements = UseRegister(instr->elements()); - val = UseRegister(instr->value()); - key = UseRegisterOrConstantAtStart(instr->key()); - } - - if (instr->is_typed_elements()) { - ASSERT((instr->value()->representation().IsInteger32() && - !IsDoubleOrFloatElementsKind(instr->elements_kind())) || - (instr->value()->representation().IsDouble() && - IsDoubleOrFloatElementsKind(instr->elements_kind()))); - ASSERT((instr->is_fixed_typed_array() && - instr->elements()->representation().IsTagged()) || - (instr->is_external() && - instr->elements()->representation().IsExternal())); - temp = instr->key()->IsConstant() ? NULL : TempRegister(); - return new(zone()) LStoreKeyedExternal(elements, key, val, temp); - - } else if (instr->value()->representation().IsDouble()) { - ASSERT(instr->elements()->representation().IsTagged()); - - // The constraint used here is UseRegister, even though the StoreKeyed - // instruction may canonicalize the value in the register if it is a NaN. - temp = TempRegister(); - return new(zone()) LStoreKeyedFixedDouble(elements, key, val, temp); - - } else { - ASSERT(instr->elements()->representation().IsTagged()); - ASSERT(instr->value()->representation().IsSmiOrTagged() || - instr->value()->representation().IsInteger32()); - - temp = TempRegister(); - return new(zone()) LStoreKeyedFixed(elements, key, val, temp); - } -} - - -LInstruction* LChunkBuilder::DoStoreKeyedGeneric(HStoreKeyedGeneric* instr) { - LOperand* context = UseFixed(instr->context(), cp); - LOperand* object = UseFixed(instr->object(), x2); - LOperand* key = UseFixed(instr->key(), x1); - LOperand* value = UseFixed(instr->value(), x0); - - ASSERT(instr->object()->representation().IsTagged()); - ASSERT(instr->key()->representation().IsTagged()); - ASSERT(instr->value()->representation().IsTagged()); - - return MarkAsCall( - new(zone()) LStoreKeyedGeneric(context, object, key, value), instr); -} - - -LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) { - // TODO(jbramley): Optimize register usage in this instruction. For now, it - // allocates everything that it might need because it keeps changing in the - // merge and keeping it valid is time-consuming. - - // TODO(jbramley): It might be beneficial to allow value to be a constant in - // some cases. x64 makes use of this with FLAG_track_fields, for example. - - LOperand* object = UseRegister(instr->object()); - LOperand* value = UseRegisterAndClobber(instr->value()); - LOperand* temp0 = TempRegister(); - LOperand* temp1 = TempRegister(); - - LStoreNamedField* result = - new(zone()) LStoreNamedField(object, value, temp0, temp1); - if (FLAG_track_heap_object_fields && - instr->field_representation().IsHeapObject() && - !instr->value()->type().IsHeapObject()) { - return AssignEnvironment(result); - } - return result; -} - - -LInstruction* LChunkBuilder::DoStoreNamedGeneric(HStoreNamedGeneric* instr) { - LOperand* context = UseFixed(instr->context(), cp); - LOperand* object = UseFixed(instr->object(), x1); - LOperand* value = UseFixed(instr->value(), x0); - LInstruction* result = new(zone()) LStoreNamedGeneric(context, object, value); - return MarkAsCall(result, instr); -} - - -LInstruction* LChunkBuilder::DoStringAdd(HStringAdd* instr) { - LOperand* context = UseFixed(instr->context(), cp); - LOperand* left = UseFixed(instr->left(), x1); - LOperand* right = UseFixed(instr->right(), x0); - - LStringAdd* result = new(zone()) LStringAdd(context, left, right); - return MarkAsCall(DefineFixed(result, x0), instr); -} - - -LInstruction* LChunkBuilder::DoStringCharCodeAt(HStringCharCodeAt* instr) { - LOperand* string = UseRegisterAndClobber(instr->string()); - LOperand* index = UseRegisterAndClobber(instr->index()); - LOperand* context = UseAny(instr->context()); - LStringCharCodeAt* result = - new(zone()) LStringCharCodeAt(context, string, index); - return AssignEnvironment(AssignPointerMap(DefineAsRegister(result))); -} - - -LInstruction* LChunkBuilder::DoStringCharFromCode(HStringCharFromCode* instr) { - // TODO(all) use at start and remove assert in codegen - LOperand* char_code = UseRegister(instr->value()); - LOperand* context = UseAny(instr->context()); - LStringCharFromCode* result = - new(zone()) LStringCharFromCode(context, char_code); - return AssignPointerMap(DefineAsRegister(result)); -} - - -LInstruction* LChunkBuilder::DoStringCompareAndBranch( - HStringCompareAndBranch* instr) { - ASSERT(instr->left()->representation().IsTagged()); - ASSERT(instr->right()->representation().IsTagged()); - LOperand* context = UseFixed(instr->context(), cp); - LOperand* left = UseFixed(instr->left(), x1); - LOperand* right = UseFixed(instr->right(), x0); - LStringCompareAndBranch* result = - new(zone()) LStringCompareAndBranch(context, left, right); - return MarkAsCall(result, instr); -} - - -LInstruction* LChunkBuilder::DoSub(HSub* instr) { - if (instr->representation().IsSmiOrInteger32()) { - ASSERT(instr->left()->representation().Equals(instr->representation())); - ASSERT(instr->right()->representation().Equals(instr->representation())); - LOperand *left; - if (instr->left()->IsConstant() && - (HConstant::cast(instr->left())->Integer32Value() == 0)) { - left = UseConstant(instr->left()); - } else { - left = UseRegisterAtStart(instr->left()); - } - LOperand* right = UseRegisterOrConstantAtStart(instr->right()); - LInstruction* result = instr->representation().IsSmi() ? - DefineAsRegister(new(zone()) LSubS(left, right)) : - DefineAsRegister(new(zone()) LSubI(left, right)); - if (instr->CheckFlag(HValue::kCanOverflow)) { - result = AssignEnvironment(result); - } - return result; - } else if (instr->representation().IsDouble()) { - return DoArithmeticD(Token::SUB, instr); - } else { - return DoArithmeticT(Token::SUB, instr); - } -} - - -LInstruction* LChunkBuilder::DoThisFunction(HThisFunction* instr) { - if (instr->HasNoUses()) { - return NULL; - } else { - return DefineAsRegister(new(zone()) LThisFunction); - } -} - - -LInstruction* LChunkBuilder::DoToFastProperties(HToFastProperties* instr) { - LOperand* object = UseFixed(instr->value(), x0); - LToFastProperties* result = new(zone()) LToFastProperties(object); - return MarkAsCall(DefineFixed(result, x0), instr); -} - - -LInstruction* LChunkBuilder::DoTransitionElementsKind( - HTransitionElementsKind* instr) { - LOperand* object = UseRegister(instr->object()); - if (IsSimpleMapChangeTransition(instr->from_kind(), instr->to_kind())) { - LTransitionElementsKind* result = - new(zone()) LTransitionElementsKind(object, NULL, - TempRegister(), TempRegister()); - return result; - } else { - LOperand* context = UseFixed(instr->context(), cp); - LTransitionElementsKind* result = - new(zone()) LTransitionElementsKind(object, context, TempRegister()); - return AssignPointerMap(result); - } -} - - -LInstruction* LChunkBuilder::DoTrapAllocationMemento( - HTrapAllocationMemento* instr) { - LOperand* object = UseRegister(instr->object()); - LOperand* temp1 = TempRegister(); - LOperand* temp2 = TempRegister(); - LTrapAllocationMemento* result = - new(zone()) LTrapAllocationMemento(object, temp1, temp2); - return AssignEnvironment(result); -} - - -LInstruction* LChunkBuilder::DoTypeof(HTypeof* instr) { - LOperand* context = UseFixed(instr->context(), cp); - // TODO(jbramley): In ARM, this uses UseFixed to force the input to x0. - // However, LCodeGen::DoTypeof just pushes it to the stack (for CallRuntime) - // anyway, so the input doesn't have to be in x0. We might be able to improve - // the ARM back-end a little by relaxing this restriction. - LTypeof* result = - new(zone()) LTypeof(context, UseRegisterAtStart(instr->value())); - return MarkAsCall(DefineFixed(result, x0), instr); -} - - -LInstruction* LChunkBuilder::DoTypeofIsAndBranch(HTypeofIsAndBranch* instr) { - LInstruction* goto_instr = CheckElideControlInstruction(instr); - if (goto_instr != NULL) return goto_instr; - - // We only need temp registers in some cases, but we can't dereference the - // instr->type_literal() handle to test that here. - LOperand* temp1 = TempRegister(); - LOperand* temp2 = TempRegister(); - - return new(zone()) LTypeofIsAndBranch( - UseRegister(instr->value()), temp1, temp2); -} - - -LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) { - switch (instr->op()) { - case kMathAbs: { - Representation r = instr->representation(); - if (r.IsTagged()) { - // The tagged case might need to allocate a HeapNumber for the result, - // so it is handled by a separate LInstruction. - LOperand* context = UseFixed(instr->context(), cp); - LOperand* input = UseRegister(instr->value()); - LOperand* temp1 = TempRegister(); - LOperand* temp2 = TempRegister(); - LOperand* temp3 = TempRegister(); - LMathAbsTagged* result = - new(zone()) LMathAbsTagged(context, input, temp1, temp2, temp3); - return AssignEnvironment(AssignPointerMap(DefineAsRegister(result))); - } else { - LOperand* input = UseRegisterAtStart(instr->value()); - LMathAbs* result = new(zone()) LMathAbs(input); - if (r.IsDouble()) { - // The Double case can never fail so it doesn't need an environment. - return DefineAsRegister(result); - } else { - ASSERT(r.IsInteger32() || r.IsSmi()); - // The Integer32 and Smi cases need an environment because they can - // deoptimize on minimum representable number. - return AssignEnvironment(DefineAsRegister(result)); - } - } - } - case kMathExp: { - ASSERT(instr->representation().IsDouble()); - ASSERT(instr->value()->representation().IsDouble()); - LOperand* input = UseRegister(instr->value()); - // TODO(all): Implement TempFPRegister. - LOperand* double_temp1 = FixedTemp(d24); // This was chosen arbitrarily. - LOperand* temp1 = TempRegister(); - LOperand* temp2 = TempRegister(); - LOperand* temp3 = TempRegister(); - LMathExp* result = new(zone()) LMathExp(input, double_temp1, - temp1, temp2, temp3); - return DefineAsRegister(result); - } - case kMathFloor: { - ASSERT(instr->representation().IsInteger32()); - ASSERT(instr->value()->representation().IsDouble()); - // TODO(jbramley): A64 can easily handle a double argument with frintm, - // but we're never asked for it here. At the moment, we fall back to the - // runtime if the result doesn't fit, like the other architectures. - LOperand* input = UseRegisterAtStart(instr->value()); - LMathFloor* result = new(zone()) LMathFloor(input); - return AssignEnvironment(AssignPointerMap(DefineAsRegister(result))); - } - case kMathLog: { - ASSERT(instr->representation().IsDouble()); - ASSERT(instr->value()->representation().IsDouble()); - LOperand* input = UseFixedDouble(instr->value(), d0); - LMathLog* result = new(zone()) LMathLog(input); - return MarkAsCall(DefineFixedDouble(result, d0), instr); - } - case kMathPowHalf: { - ASSERT(instr->representation().IsDouble()); - ASSERT(instr->value()->representation().IsDouble()); - LOperand* input = UseRegister(instr->value()); - return DefineAsRegister(new(zone()) LMathPowHalf(input)); - } - case kMathRound: { - ASSERT(instr->representation().IsInteger32()); - ASSERT(instr->value()->representation().IsDouble()); - // TODO(jbramley): As with kMathFloor, we can probably handle double - // results fairly easily, but we are never asked for them. - LOperand* input = UseRegister(instr->value()); - LOperand* temp = FixedTemp(d24); // Choosen arbitrarily. - LMathRound* result = new(zone()) LMathRound(input, temp); - return AssignEnvironment(DefineAsRegister(result)); - } - case kMathSqrt: { - ASSERT(instr->representation().IsDouble()); - ASSERT(instr->value()->representation().IsDouble()); - LOperand* input = UseRegisterAtStart(instr->value()); - return DefineAsRegister(new(zone()) LMathSqrt(input)); - } - default: - UNREACHABLE(); - return NULL; - } -} - - -LInstruction* LChunkBuilder::DoUnknownOSRValue(HUnknownOSRValue* instr) { - // Use an index that corresponds to the location in the unoptimized frame, - // which the optimized frame will subsume. - int env_index = instr->index(); - int spill_index = 0; - if (instr->environment()->is_parameter_index(env_index)) { - spill_index = chunk_->GetParameterStackSlot(env_index); - } else { - spill_index = env_index - instr->environment()->first_local_index(); - if (spill_index > LUnallocated::kMaxFixedSlotIndex) { - Abort(kTooManySpillSlotsNeededForOSR); - spill_index = 0; - } - } - return DefineAsSpilled(new(zone()) LUnknownOSRValue, spill_index); -} - - -LInstruction* LChunkBuilder::DoUseConst(HUseConst* instr) { - return NULL; -} - - -LInstruction* LChunkBuilder::DoForInPrepareMap(HForInPrepareMap* instr) { - LOperand* context = UseFixed(instr->context(), cp); - // Assign object to a fixed register different from those already used in - // LForInPrepareMap. - LOperand* object = UseFixed(instr->enumerable(), x0); - LForInPrepareMap* result = new(zone()) LForInPrepareMap(context, object); - return MarkAsCall(DefineFixed(result, x0), instr, CAN_DEOPTIMIZE_EAGERLY); -} - - -LInstruction* LChunkBuilder::DoForInCacheArray(HForInCacheArray* instr) { - LOperand* map = UseRegister(instr->map()); - return AssignEnvironment(DefineAsRegister(new(zone()) LForInCacheArray(map))); -} - - -LInstruction* LChunkBuilder::DoCheckMapValue(HCheckMapValue* instr) { - LOperand* value = UseRegisterAtStart(instr->value()); - LOperand* map = UseRegister(instr->map()); - LOperand* temp = TempRegister(); - return AssignEnvironment(new(zone()) LCheckMapValue(value, map, temp)); -} - - -LInstruction* LChunkBuilder::DoLoadFieldByIndex(HLoadFieldByIndex* instr) { - LOperand* object = UseRegisterAtStart(instr->object()); - LOperand* index = UseRegister(instr->index()); - return DefineAsRegister(new(zone()) LLoadFieldByIndex(object, index)); -} - - -LInstruction* LChunkBuilder::DoWrapReceiver(HWrapReceiver* instr) { - LOperand* receiver = UseRegister(instr->receiver()); - LOperand* function = UseRegister(instr->function()); - LWrapReceiver* result = new(zone()) LWrapReceiver(receiver, function); - return AssignEnvironment(DefineAsRegister(result)); -} - - -} } // namespace v8::internal diff --git a/deps/v8/src/a64/lithium-a64.h b/deps/v8/src/a64/lithium-a64.h deleted file mode 100644 index 33d11e6c5d..0000000000 --- a/deps/v8/src/a64/lithium-a64.h +++ /dev/null @@ -1,2967 +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. - -#ifndef V8_A64_LITHIUM_A64_H_ -#define V8_A64_LITHIUM_A64_H_ - -#include "hydrogen.h" -#include "lithium-allocator.h" -#include "lithium.h" -#include "safepoint-table.h" -#include "utils.h" - -namespace v8 { -namespace internal { - -// Forward declarations. -class LCodeGen; - -#define LITHIUM_CONCRETE_INSTRUCTION_LIST(V) \ - V(AccessArgumentsAt) \ - V(AddE) \ - V(AddI) \ - V(AddS) \ - V(Allocate) \ - V(ApplyArguments) \ - V(ArgumentsElements) \ - V(ArgumentsLength) \ - V(ArithmeticD) \ - V(ArithmeticT) \ - V(BitI) \ - V(BitS) \ - V(BoundsCheck) \ - V(Branch) \ - V(CallFunction) \ - V(CallJSFunction) \ - V(CallNew) \ - V(CallNewArray) \ - V(CallRuntime) \ - V(CallStub) \ - V(CallWithDescriptor) \ - V(CheckInstanceType) \ - V(CheckMapValue) \ - V(CheckMaps) \ - V(CheckNonSmi) \ - V(CheckSmi) \ - V(CheckValue) \ - V(ClampDToUint8) \ - V(ClampIToUint8) \ - V(ClampTToUint8) \ - V(ClassOfTestAndBranch) \ - V(CmpHoleAndBranchD) \ - V(CmpHoleAndBranchT) \ - V(CmpMapAndBranch) \ - V(CmpObjectEqAndBranch) \ - V(CmpT) \ - V(CompareMinusZeroAndBranch) \ - V(CompareNumericAndBranch) \ - V(ConstantD) \ - V(ConstantE) \ - V(ConstantI) \ - V(ConstantS) \ - V(ConstantT) \ - V(Context) \ - V(DateField) \ - V(DebugBreak) \ - V(DeclareGlobals) \ - V(Deoptimize) \ - V(DivI) \ - V(DoubleToIntOrSmi) \ - V(Drop) \ - V(Dummy) \ - V(DummyUse) \ - V(ForInCacheArray) \ - V(ForInPrepareMap) \ - V(FunctionLiteral) \ - V(GetCachedArrayIndex) \ - V(Goto) \ - V(HasCachedArrayIndexAndBranch) \ - V(HasInstanceTypeAndBranch) \ - V(InnerAllocatedObject) \ - V(InstanceOf) \ - V(InstanceOfKnownGlobal) \ - V(InstructionGap) \ - V(Integer32ToDouble) \ - V(Integer32ToSmi) \ - V(InvokeFunction) \ - V(IsConstructCallAndBranch) \ - V(IsObjectAndBranch) \ - V(IsSmiAndBranch) \ - V(IsStringAndBranch) \ - V(IsUndetectableAndBranch) \ - V(Label) \ - V(LazyBailout) \ - V(LoadContextSlot) \ - V(LoadFieldByIndex) \ - V(LoadFunctionPrototype) \ - V(LoadGlobalCell) \ - V(LoadGlobalGeneric) \ - V(LoadKeyedExternal) \ - V(LoadKeyedFixed) \ - V(LoadKeyedFixedDouble) \ - V(LoadKeyedGeneric) \ - V(LoadNamedField) \ - V(LoadNamedGeneric) \ - V(LoadRoot) \ - V(MapEnumLength) \ - V(MathAbs) \ - V(MathAbsTagged) \ - V(MathExp) \ - V(MathFloor) \ - V(MathFloorOfDiv) \ - V(MathLog) \ - V(MathMinMax) \ - V(MathPowHalf) \ - V(MathRound) \ - V(MathSqrt) \ - V(ModI) \ - V(MulConstIS) \ - V(MulI) \ - V(MulS) \ - V(NumberTagD) \ - V(NumberTagU) \ - V(NumberUntagD) \ - V(OsrEntry) \ - V(Parameter) \ - V(Power) \ - V(PushArgument) \ - V(RegExpLiteral) \ - V(Return) \ - V(SeqStringGetChar) \ - V(SeqStringSetChar) \ - V(ShiftI) \ - V(ShiftS) \ - V(SmiTag) \ - V(SmiUntag) \ - V(StackCheck) \ - V(StoreCodeEntry) \ - V(StoreContextSlot) \ - V(StoreGlobalCell) \ - V(StoreKeyedExternal) \ - V(StoreKeyedFixed) \ - V(StoreKeyedFixedDouble) \ - V(StoreKeyedGeneric) \ - V(StoreNamedField) \ - V(StoreNamedGeneric) \ - V(StringAdd) \ - V(StringCharCodeAt) \ - V(StringCharFromCode) \ - V(StringCompareAndBranch) \ - V(SubI) \ - V(SubS) \ - V(TaggedToI) \ - V(ThisFunction) \ - V(ToFastProperties) \ - V(TransitionElementsKind) \ - V(TrapAllocationMemento) \ - V(TruncateDoubleToIntOrSmi) \ - V(Typeof) \ - V(TypeofIsAndBranch) \ - V(Uint32ToDouble) \ - V(Uint32ToSmi) \ - V(UnknownOSRValue) \ - V(WrapReceiver) - - -#define DECLARE_CONCRETE_INSTRUCTION(type, mnemonic) \ - virtual Opcode opcode() const V8_FINAL V8_OVERRIDE { \ - return LInstruction::k##type; \ - } \ - virtual void CompileToNative(LCodeGen* generator) V8_FINAL V8_OVERRIDE; \ - virtual const char* Mnemonic() const V8_FINAL V8_OVERRIDE { \ - return mnemonic; \ - } \ - static L##type* cast(LInstruction* instr) { \ - ASSERT(instr->Is##type()); \ - return reinterpret_cast(instr); \ - } - - -#define DECLARE_HYDROGEN_ACCESSOR(type) \ - H##type* hydrogen() const { \ - return H##type::cast(this->hydrogen_value()); \ - } - - -class LInstruction : public ZoneObject { - public: - LInstruction() - : environment_(NULL), - hydrogen_value_(NULL), - bit_field_(IsCallBits::encode(false)) { } - - virtual ~LInstruction() { } - - virtual void CompileToNative(LCodeGen* generator) = 0; - virtual const char* Mnemonic() const = 0; - virtual void PrintTo(StringStream* stream); - virtual void PrintDataTo(StringStream* stream); - virtual void PrintOutputOperandTo(StringStream* stream); - - enum Opcode { - // Declare a unique enum value for each instruction. -#define DECLARE_OPCODE(type) k##type, - LITHIUM_CONCRETE_INSTRUCTION_LIST(DECLARE_OPCODE) - kNumberOfInstructions -#undef DECLARE_OPCODE - }; - - virtual Opcode opcode() const = 0; - - // Declare non-virtual type testers for all leaf IR classes. -#define DECLARE_PREDICATE(type) \ - bool Is##type() const { return opcode() == k##type; } - LITHIUM_CONCRETE_INSTRUCTION_LIST(DECLARE_PREDICATE) -#undef DECLARE_PREDICATE - - // Declare virtual predicates for instructions that don't have - // an opcode. - virtual bool IsGap() const { return false; } - - virtual bool IsControl() const { return false; } - - void set_environment(LEnvironment* env) { environment_ = env; } - LEnvironment* environment() const { return environment_; } - bool HasEnvironment() const { return environment_ != NULL; } - - void set_pointer_map(LPointerMap* p) { pointer_map_.set(p); } - LPointerMap* pointer_map() const { return pointer_map_.get(); } - bool HasPointerMap() const { return pointer_map_.is_set(); } - - void set_hydrogen_value(HValue* value) { hydrogen_value_ = value; } - HValue* hydrogen_value() const { return hydrogen_value_; } - - virtual void SetDeferredLazyDeoptimizationEnvironment(LEnvironment* env) { } - - void MarkAsCall() { bit_field_ = IsCallBits::update(bit_field_, true); } - bool IsCall() const { return IsCallBits::decode(bit_field_); } - - // Interface to the register allocator and iterators. - bool ClobbersTemps() const { return IsCall(); } - bool ClobbersRegisters() const { return IsCall(); } - virtual bool ClobbersDoubleRegisters() const { return IsCall(); } - bool IsMarkedAsCall() const { return IsCall(); } - - virtual bool HasResult() const = 0; - virtual LOperand* result() const = 0; - - virtual int InputCount() = 0; - virtual LOperand* InputAt(int i) = 0; - virtual int TempCount() = 0; - virtual LOperand* TempAt(int i) = 0; - - LOperand* FirstInput() { return InputAt(0); } - LOperand* Output() { return HasResult() ? result() : NULL; } - - virtual bool HasInterestingComment(LCodeGen* gen) const { return true; } - -#ifdef DEBUG - void VerifyCall(); -#endif - - private: - class IsCallBits: public BitField {}; - - LEnvironment* environment_; - SetOncePointer pointer_map_; - HValue* hydrogen_value_; - int32_t bit_field_; -}; - - -// R = number of result operands (0 or 1). -template -class LTemplateResultInstruction : public LInstruction { - public: - // Allow 0 or 1 output operands. - STATIC_ASSERT(R == 0 || R == 1); - virtual bool HasResult() const V8_FINAL V8_OVERRIDE { - return (R != 0) && (result() != NULL); - } - void set_result(LOperand* operand) { results_[0] = operand; } - LOperand* result() const { return results_[0]; } - - protected: - EmbeddedContainer results_; -}; - - -// R = number of result operands (0 or 1). -// I = number of input operands. -// T = number of temporary operands. -template -class LTemplateInstruction : public LTemplateResultInstruction { - protected: - EmbeddedContainer inputs_; - EmbeddedContainer temps_; - - private: - // Iterator support. - virtual int InputCount() V8_FINAL V8_OVERRIDE { return I; } - virtual LOperand* InputAt(int i) V8_FINAL V8_OVERRIDE { return inputs_[i]; } - - virtual int TempCount() V8_FINAL V8_OVERRIDE { return T; } - virtual LOperand* TempAt(int i) V8_FINAL V8_OVERRIDE { return temps_[i]; } -}; - - -class LUnknownOSRValue V8_FINAL : public LTemplateInstruction<1, 0, 0> { - public: - virtual bool HasInterestingComment(LCodeGen* gen) const V8_OVERRIDE { - return false; - } - DECLARE_CONCRETE_INSTRUCTION(UnknownOSRValue, "unknown-osr-value") -}; - - -template -class LControlInstruction : public LTemplateInstruction<0, I, T> { - public: - LControlInstruction() : false_label_(NULL), true_label_(NULL) { } - - virtual bool IsControl() const V8_FINAL V8_OVERRIDE { return true; } - - int SuccessorCount() { return hydrogen()->SuccessorCount(); } - HBasicBlock* SuccessorAt(int i) { return hydrogen()->SuccessorAt(i); } - - int TrueDestination(LChunk* chunk) { - return chunk->LookupDestination(true_block_id()); - } - - int FalseDestination(LChunk* chunk) { - return chunk->LookupDestination(false_block_id()); - } - - Label* TrueLabel(LChunk* chunk) { - if (true_label_ == NULL) { - true_label_ = chunk->GetAssemblyLabel(TrueDestination(chunk)); - } - return true_label_; - } - - Label* FalseLabel(LChunk* chunk) { - if (false_label_ == NULL) { - false_label_ = chunk->GetAssemblyLabel(FalseDestination(chunk)); - } - return false_label_; - } - - protected: - int true_block_id() { return SuccessorAt(0)->block_id(); } - int false_block_id() { return SuccessorAt(1)->block_id(); } - - private: - DECLARE_HYDROGEN_ACCESSOR(ControlInstruction); - - Label* false_label_; - Label* true_label_; -}; - - -class LGap : public LTemplateInstruction<0, 0, 0> { - public: - explicit LGap(HBasicBlock* block) - : block_(block) { - parallel_moves_[BEFORE] = NULL; - parallel_moves_[START] = NULL; - parallel_moves_[END] = NULL; - parallel_moves_[AFTER] = NULL; - } - - // Can't use the DECLARE-macro here because of sub-classes. - virtual bool IsGap() const V8_OVERRIDE { return true; } - virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE; - static LGap* cast(LInstruction* instr) { - ASSERT(instr->IsGap()); - return reinterpret_cast(instr); - } - - bool IsRedundant() const; - - HBasicBlock* block() const { return block_; } - - enum InnerPosition { - BEFORE, - START, - END, - AFTER, - FIRST_INNER_POSITION = BEFORE, - LAST_INNER_POSITION = AFTER - }; - - LParallelMove* GetOrCreateParallelMove(InnerPosition pos, Zone* zone) { - if (parallel_moves_[pos] == NULL) { - parallel_moves_[pos] = new(zone) LParallelMove(zone); - } - return parallel_moves_[pos]; - } - - LParallelMove* GetParallelMove(InnerPosition pos) { - return parallel_moves_[pos]; - } - - private: - LParallelMove* parallel_moves_[LAST_INNER_POSITION + 1]; - HBasicBlock* block_; -}; - - -class LInstructionGap V8_FINAL : public LGap { - public: - explicit LInstructionGap(HBasicBlock* block) : LGap(block) { } - - virtual bool HasInterestingComment(LCodeGen* gen) const V8_OVERRIDE { - return !IsRedundant(); - } - - DECLARE_CONCRETE_INSTRUCTION(InstructionGap, "gap") -}; - - -class LDrop V8_FINAL : public LTemplateInstruction<0, 0, 0> { - public: - explicit LDrop(int count) : count_(count) { } - - int count() const { return count_; } - - DECLARE_CONCRETE_INSTRUCTION(Drop, "drop") - - private: - int count_; -}; - - -class LDummy V8_FINAL : public LTemplateInstruction<1, 0, 0> { - public: - explicit LDummy() { } - DECLARE_CONCRETE_INSTRUCTION(Dummy, "dummy") -}; - - -class LDummyUse V8_FINAL : public LTemplateInstruction<1, 1, 0> { - public: - explicit LDummyUse(LOperand* value) { - inputs_[0] = value; - } - DECLARE_CONCRETE_INSTRUCTION(DummyUse, "dummy-use") -}; - - -class LGoto V8_FINAL : public LTemplateInstruction<0, 0, 0> { - public: - explicit LGoto(HBasicBlock* block) : block_(block) { } - - virtual bool HasInterestingComment(LCodeGen* gen) const V8_OVERRIDE; - DECLARE_CONCRETE_INSTRUCTION(Goto, "goto") - virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE; - virtual bool IsControl() const V8_OVERRIDE { return true; } - - int block_id() const { return block_->block_id(); } - - private: - HBasicBlock* block_; -}; - - -class LLazyBailout V8_FINAL : public LTemplateInstruction<0, 0, 0> { - public: - LLazyBailout() : gap_instructions_size_(0) { } - - DECLARE_CONCRETE_INSTRUCTION(LazyBailout, "lazy-bailout") - - void set_gap_instructions_size(int gap_instructions_size) { - gap_instructions_size_ = gap_instructions_size; - } - int gap_instructions_size() { return gap_instructions_size_; } - - private: - int gap_instructions_size_; -}; - - -class LLabel V8_FINAL : public LGap { - public: - explicit LLabel(HBasicBlock* block) - : LGap(block), replacement_(NULL) { } - - virtual bool HasInterestingComment(LCodeGen* gen) const V8_OVERRIDE { - return false; - } - DECLARE_CONCRETE_INSTRUCTION(Label, "label") - - virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE; - - int block_id() const { return block()->block_id(); } - bool is_loop_header() const { return block()->IsLoopHeader(); } - bool is_osr_entry() const { return block()->is_osr_entry(); } - Label* label() { return &label_; } - LLabel* replacement() const { return replacement_; } - void set_replacement(LLabel* label) { replacement_ = label; } - bool HasReplacement() const { return replacement_ != NULL; } - - private: - Label label_; - LLabel* replacement_; -}; - - -class LOsrEntry V8_FINAL : public LTemplateInstruction<0, 0, 0> { - public: - LOsrEntry() {} - - virtual bool HasInterestingComment(LCodeGen* gen) const V8_OVERRIDE { - return false; - } - DECLARE_CONCRETE_INSTRUCTION(OsrEntry, "osr-entry") -}; - - -class LAccessArgumentsAt V8_FINAL : public LTemplateInstruction<1, 3, 1> { - public: - LAccessArgumentsAt(LOperand* arguments, - LOperand* length, - LOperand* index, - LOperand* temp) { - inputs_[0] = arguments; - inputs_[1] = length; - inputs_[2] = index; - temps_[0] = temp; - } - - DECLARE_CONCRETE_INSTRUCTION(AccessArgumentsAt, "access-arguments-at") - - LOperand* arguments() { return inputs_[0]; } - LOperand* length() { return inputs_[1]; } - LOperand* index() { return inputs_[2]; } - LOperand* temp() { return temps_[0]; } - - virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE; -}; - - -class LAddE V8_FINAL : public LTemplateInstruction<1, 2, 0> { - public: - LAddE(LOperand* left, LOperand* right) { - inputs_[0] = left; - inputs_[1] = right; - } - - LOperand* left() { return inputs_[0]; } - LOperand* right() { return inputs_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(AddE, "add-e") - DECLARE_HYDROGEN_ACCESSOR(Add) -}; - - -class LAddI V8_FINAL : public LTemplateInstruction<1, 2, 0> { - public: - LAddI(LOperand* left, LOperand* right) { - inputs_[0] = left; - inputs_[1] = right; - } - - LOperand* left() { return inputs_[0]; } - LOperand* right() { return inputs_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(AddI, "add-i") - DECLARE_HYDROGEN_ACCESSOR(Add) -}; - - -class LAddS V8_FINAL : public LTemplateInstruction<1, 2, 0> { - public: - LAddS(LOperand* left, LOperand* right) { - inputs_[0] = left; - inputs_[1] = right; - } - - LOperand* left() { return inputs_[0]; } - LOperand* right() { return inputs_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(AddS, "add-s") - DECLARE_HYDROGEN_ACCESSOR(Add) -}; - - -class LAllocate V8_FINAL : public LTemplateInstruction<1, 2, 2> { - public: - LAllocate(LOperand* context, - LOperand* size, - LOperand* temp1, - LOperand* temp2) { - inputs_[0] = context; - inputs_[1] = size; - temps_[0] = temp1; - temps_[1] = temp2; - } - - LOperand* context() { return inputs_[0]; } - LOperand* size() { return inputs_[1]; } - LOperand* temp1() { return temps_[0]; } - LOperand* temp2() { return temps_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(Allocate, "allocate") - DECLARE_HYDROGEN_ACCESSOR(Allocate) -}; - - -class LApplyArguments V8_FINAL : public LTemplateInstruction<1, 4, 0> { - public: - LApplyArguments(LOperand* function, - LOperand* receiver, - LOperand* length, - LOperand* elements) { - inputs_[0] = function; - inputs_[1] = receiver; - inputs_[2] = length; - inputs_[3] = elements; - } - - DECLARE_CONCRETE_INSTRUCTION(ApplyArguments, "apply-arguments") - - LOperand* function() { return inputs_[0]; } - LOperand* receiver() { return inputs_[1]; } - LOperand* length() { return inputs_[2]; } - LOperand* elements() { return inputs_[3]; } -}; - - -class LArgumentsElements V8_FINAL : public LTemplateInstruction<1, 0, 1> { - public: - explicit LArgumentsElements(LOperand* temp) { - temps_[0] = temp; - } - - LOperand* temp() { return temps_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(ArgumentsElements, "arguments-elements") - DECLARE_HYDROGEN_ACCESSOR(ArgumentsElements) -}; - - -class LArgumentsLength V8_FINAL : public LTemplateInstruction<1, 1, 0> { - public: - explicit LArgumentsLength(LOperand* elements) { - inputs_[0] = elements; - } - - LOperand* elements() { return inputs_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(ArgumentsLength, "arguments-length") -}; - - -class LArithmeticD V8_FINAL : public LTemplateInstruction<1, 2, 0> { - public: - LArithmeticD(Token::Value op, - LOperand* left, - LOperand* right) - : op_(op) { - inputs_[0] = left; - inputs_[1] = right; - } - - Token::Value op() const { return op_; } - LOperand* left() { return inputs_[0]; } - LOperand* right() { return inputs_[1]; } - - virtual Opcode opcode() const V8_OVERRIDE { - return LInstruction::kArithmeticD; - } - virtual void CompileToNative(LCodeGen* generator) V8_OVERRIDE; - virtual const char* Mnemonic() const V8_OVERRIDE; - - private: - Token::Value op_; -}; - - -class LArithmeticT V8_FINAL : public LTemplateInstruction<1, 3, 0> { - public: - LArithmeticT(Token::Value op, - LOperand* context, - LOperand* left, - LOperand* right) - : op_(op) { - inputs_[0] = context; - inputs_[1] = left; - inputs_[2] = right; - } - - LOperand* context() { return inputs_[0]; } - LOperand* left() { return inputs_[1]; } - LOperand* right() { return inputs_[2]; } - Token::Value op() const { return op_; } - - virtual Opcode opcode() const V8_OVERRIDE { - return LInstruction::kArithmeticT; - } - virtual void CompileToNative(LCodeGen* generator) V8_OVERRIDE; - virtual const char* Mnemonic() const V8_OVERRIDE; - - private: - Token::Value op_; -}; - - -class LBoundsCheck V8_FINAL : public LTemplateInstruction<0, 2, 0> { - public: - explicit LBoundsCheck(LOperand* index, LOperand* length) { - inputs_[0] = index; - inputs_[1] = length; - } - - LOperand* index() { return inputs_[0]; } - LOperand* length() { return inputs_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(BoundsCheck, "bounds-check") - DECLARE_HYDROGEN_ACCESSOR(BoundsCheck) -}; - - -class LBitI V8_FINAL : public LTemplateInstruction<1, 2, 0> { - public: - LBitI(LOperand* left, LOperand* right) { - inputs_[0] = left; - inputs_[1] = right; - } - - LOperand* left() { return inputs_[0]; } - LOperand* right() { return inputs_[1]; } - - Token::Value op() const { return hydrogen()->op(); } - - DECLARE_CONCRETE_INSTRUCTION(BitI, "bit-i") - DECLARE_HYDROGEN_ACCESSOR(Bitwise) -}; - - -class LBitS V8_FINAL : public LTemplateInstruction<1, 2, 0> { - public: - LBitS(LOperand* left, LOperand* right) { - inputs_[0] = left; - inputs_[1] = right; - } - - LOperand* left() { return inputs_[0]; } - LOperand* right() { return inputs_[1]; } - - Token::Value op() const { return hydrogen()->op(); } - - DECLARE_CONCRETE_INSTRUCTION(BitS, "bit-s") - DECLARE_HYDROGEN_ACCESSOR(Bitwise) -}; - - -class LBranch V8_FINAL : public LControlInstruction<1, 2> { - public: - explicit LBranch(LOperand* value, LOperand *temp1, LOperand *temp2) { - inputs_[0] = value; - temps_[0] = temp1; - temps_[1] = temp2; - } - - LOperand* value() { return inputs_[0]; } - LOperand* temp1() { return temps_[0]; } - LOperand* temp2() { return temps_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(Branch, "branch") - DECLARE_HYDROGEN_ACCESSOR(Branch) - - virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE; -}; - - -class LCallJSFunction V8_FINAL : public LTemplateInstruction<1, 1, 0> { - public: - explicit LCallJSFunction(LOperand* function) { - inputs_[0] = function; - } - - LOperand* function() { return inputs_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(CallJSFunction, "call-js-function") - DECLARE_HYDROGEN_ACCESSOR(CallJSFunction) - - virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE; - - int arity() const { return hydrogen()->argument_count() - 1; } -}; - - -class LCallFunction V8_FINAL : public LTemplateInstruction<1, 2, 0> { - public: - LCallFunction(LOperand* context, LOperand* function) { - inputs_[0] = context; - inputs_[1] = function; - } - - LOperand* context() { return inputs_[0]; } - LOperand* function() { return inputs_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(CallFunction, "call-function") - DECLARE_HYDROGEN_ACCESSOR(CallFunction) - - int arity() const { return hydrogen()->argument_count() - 1; } -}; - - -class LCallNew V8_FINAL : public LTemplateInstruction<1, 2, 0> { - public: - LCallNew(LOperand* context, LOperand* constructor) { - inputs_[0] = context; - inputs_[1] = constructor; - } - - LOperand* context() { return inputs_[0]; } - LOperand* constructor() { return inputs_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(CallNew, "call-new") - DECLARE_HYDROGEN_ACCESSOR(CallNew) - - virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE; - - int arity() const { return hydrogen()->argument_count() - 1; } -}; - - -class LCallNewArray V8_FINAL : public LTemplateInstruction<1, 2, 0> { - public: - LCallNewArray(LOperand* context, LOperand* constructor) { - inputs_[0] = context; - inputs_[1] = constructor; - } - - LOperand* context() { return inputs_[0]; } - LOperand* constructor() { return inputs_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(CallNewArray, "call-new-array") - DECLARE_HYDROGEN_ACCESSOR(CallNewArray) - - virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE; - - int arity() const { return hydrogen()->argument_count() - 1; } -}; - - -class LCallRuntime V8_FINAL : public LTemplateInstruction<1, 1, 0> { - public: - explicit LCallRuntime(LOperand* context) { - inputs_[0] = context; - } - - LOperand* context() { return inputs_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(CallRuntime, "call-runtime") - DECLARE_HYDROGEN_ACCESSOR(CallRuntime) - - virtual bool ClobbersDoubleRegisters() const V8_OVERRIDE { - return save_doubles() == kDontSaveFPRegs; - } - - const Runtime::Function* function() const { return hydrogen()->function(); } - int arity() const { return hydrogen()->argument_count(); } - SaveFPRegsMode save_doubles() const { return hydrogen()->save_doubles(); } -}; - - -class LCallStub V8_FINAL : public LTemplateInstruction<1, 1, 0> { - public: - explicit LCallStub(LOperand* context) { - inputs_[0] = context; - } - - LOperand* context() { return inputs_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(CallStub, "call-stub") - DECLARE_HYDROGEN_ACCESSOR(CallStub) -}; - - -class LCheckInstanceType V8_FINAL : public LTemplateInstruction<0, 1, 1> { - public: - explicit LCheckInstanceType(LOperand* value, LOperand* temp) { - inputs_[0] = value; - temps_[0] = temp; - } - - LOperand* value() { return inputs_[0]; } - LOperand* temp() { return temps_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(CheckInstanceType, "check-instance-type") - DECLARE_HYDROGEN_ACCESSOR(CheckInstanceType) -}; - - -class LCheckMaps V8_FINAL : public LTemplateInstruction<0, 1, 1> { - public: - explicit LCheckMaps(LOperand* value, LOperand* temp = NULL) { - inputs_[0] = value; - temps_[0] = temp; - } - - LOperand* value() { return inputs_[0]; } - LOperand* temp() { return temps_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(CheckMaps, "check-maps") - DECLARE_HYDROGEN_ACCESSOR(CheckMaps) -}; - - -class LCheckNonSmi V8_FINAL : public LTemplateInstruction<0, 1, 0> { - public: - explicit LCheckNonSmi(LOperand* value) { - inputs_[0] = value; - } - - LOperand* value() { return inputs_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(CheckNonSmi, "check-non-smi") - DECLARE_HYDROGEN_ACCESSOR(CheckHeapObject) -}; - - -class LCheckSmi V8_FINAL : public LTemplateInstruction<1, 1, 0> { - public: - explicit LCheckSmi(LOperand* value) { - inputs_[0] = value; - } - - LOperand* value() { return inputs_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(CheckSmi, "check-smi") -}; - - -class LCheckValue V8_FINAL : public LTemplateInstruction<0, 1, 1> { - public: - LCheckValue(LOperand* value, LOperand* temp) { - inputs_[0] = value; - temps_[0] = temp; - } - - LOperand* value() { return inputs_[0]; } - LOperand* temp() { return temps_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(CheckValue, "check-value") - DECLARE_HYDROGEN_ACCESSOR(CheckValue) -}; - - -class LClampDToUint8 V8_FINAL : public LTemplateInstruction<1, 1, 0> { - public: - explicit LClampDToUint8(LOperand* unclamped) { - inputs_[0] = unclamped; - } - - LOperand* unclamped() { return inputs_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(ClampDToUint8, "clamp-d-to-uint8") -}; - - -class LClampIToUint8 V8_FINAL : public LTemplateInstruction<1, 1, 0> { - public: - explicit LClampIToUint8(LOperand* unclamped) { - inputs_[0] = unclamped; - } - - LOperand* unclamped() { return inputs_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(ClampIToUint8, "clamp-i-to-uint8") -}; - - -class LClampTToUint8 V8_FINAL : public LTemplateInstruction<1, 1, 2> { - public: - LClampTToUint8(LOperand* unclamped, LOperand* temp1, LOperand* temp2) { - inputs_[0] = unclamped; - temps_[0] = temp1; - temps_[1] = temp2; - } - - LOperand* unclamped() { return inputs_[0]; } - LOperand* temp1() { return temps_[0]; } - LOperand* temp2() { return temps_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(ClampTToUint8, "clamp-t-to-uint8") -}; - - -class LClassOfTestAndBranch V8_FINAL : public LControlInstruction<1, 2> { - public: - LClassOfTestAndBranch(LOperand* value, LOperand* temp1, LOperand* temp2) { - inputs_[0] = value; - temps_[0] = temp1; - temps_[1] = temp2; - } - - LOperand* value() { return inputs_[0]; } - LOperand* temp1() { return temps_[0]; } - LOperand* temp2() { return temps_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(ClassOfTestAndBranch, - "class-of-test-and-branch") - DECLARE_HYDROGEN_ACCESSOR(ClassOfTestAndBranch) - - virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE; -}; - - -class LCmpHoleAndBranchD V8_FINAL : public LControlInstruction<1, 1> { - public: - explicit LCmpHoleAndBranchD(LOperand* object, LOperand* temp) { - inputs_[0] = object; - temps_[0] = temp; - } - - LOperand* object() { return inputs_[0]; } - LOperand* temp() { return temps_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(CmpHoleAndBranchD, "cmp-hole-and-branch-d") - DECLARE_HYDROGEN_ACCESSOR(CompareHoleAndBranch) -}; - - -class LCmpHoleAndBranchT V8_FINAL : public LControlInstruction<1, 0> { - public: - explicit LCmpHoleAndBranchT(LOperand* object) { - inputs_[0] = object; - } - - LOperand* object() { return inputs_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(CmpHoleAndBranchT, "cmp-hole-and-branch-t") - DECLARE_HYDROGEN_ACCESSOR(CompareHoleAndBranch) -}; - - -class LCmpMapAndBranch V8_FINAL : public LControlInstruction<1, 1> { - public: - LCmpMapAndBranch(LOperand* value, LOperand* temp) { - inputs_[0] = value; - temps_[0] = temp; - } - - LOperand* value() { return inputs_[0]; } - LOperand* temp() { return temps_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(CmpMapAndBranch, "cmp-map-and-branch") - DECLARE_HYDROGEN_ACCESSOR(CompareMap) - - Handle map() const { return hydrogen()->map().handle(); } -}; - - -class LCmpObjectEqAndBranch V8_FINAL : public LControlInstruction<2, 0> { - public: - LCmpObjectEqAndBranch(LOperand* left, LOperand* right) { - inputs_[0] = left; - inputs_[1] = right; - } - - LOperand* left() { return inputs_[0]; } - LOperand* right() { return inputs_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(CmpObjectEqAndBranch, "cmp-object-eq-and-branch") - DECLARE_HYDROGEN_ACCESSOR(CompareObjectEqAndBranch) -}; - - -class LCmpT V8_FINAL : public LTemplateInstruction<1, 3, 0> { - public: - LCmpT(LOperand* context, LOperand* left, LOperand* right) { - inputs_[0] = context; - inputs_[1] = left; - inputs_[2] = right; - } - - LOperand* context() { return inputs_[0]; } - LOperand* left() { return inputs_[1]; } - LOperand* right() { return inputs_[2]; } - - DECLARE_CONCRETE_INSTRUCTION(CmpT, "cmp-t") - DECLARE_HYDROGEN_ACCESSOR(CompareGeneric) - - Token::Value op() const { return hydrogen()->token(); } -}; - - -class LCompareMinusZeroAndBranch V8_FINAL : public LControlInstruction<1, 1> { - public: - LCompareMinusZeroAndBranch(LOperand* value, LOperand* temp) { - inputs_[0] = value; - temps_[0] = temp; - } - - LOperand* value() { return inputs_[0]; } - LOperand* temp() { return temps_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(CompareMinusZeroAndBranch, - "cmp-minus-zero-and-branch") - DECLARE_HYDROGEN_ACCESSOR(CompareMinusZeroAndBranch) -}; - - -class LCompareNumericAndBranch V8_FINAL : public LControlInstruction<2, 0> { - public: - LCompareNumericAndBranch(LOperand* left, LOperand* right) { - inputs_[0] = left; - inputs_[1] = right; - } - - LOperand* left() { return inputs_[0]; } - LOperand* right() { return inputs_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(CompareNumericAndBranch, - "compare-numeric-and-branch") - DECLARE_HYDROGEN_ACCESSOR(CompareNumericAndBranch) - - Token::Value op() const { return hydrogen()->token(); } - bool is_double() const { - return hydrogen()->representation().IsDouble(); - } - - virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE; -}; - - -class LConstantD V8_FINAL : public LTemplateInstruction<1, 0, 0> { - public: - DECLARE_CONCRETE_INSTRUCTION(ConstantD, "constant-d") - DECLARE_HYDROGEN_ACCESSOR(Constant) - - double value() const { return hydrogen()->DoubleValue(); } -}; - - -class LConstantE V8_FINAL : public LTemplateInstruction<1, 0, 0> { - public: - DECLARE_CONCRETE_INSTRUCTION(ConstantE, "constant-e") - DECLARE_HYDROGEN_ACCESSOR(Constant) - - ExternalReference value() const { - return hydrogen()->ExternalReferenceValue(); - } -}; - - -class LConstantI V8_FINAL : public LTemplateInstruction<1, 0, 0> { - public: - DECLARE_CONCRETE_INSTRUCTION(ConstantI, "constant-i") - DECLARE_HYDROGEN_ACCESSOR(Constant) - - int32_t value() const { return hydrogen()->Integer32Value(); } -}; - - -class LConstantS V8_FINAL : public LTemplateInstruction<1, 0, 0> { - public: - DECLARE_CONCRETE_INSTRUCTION(ConstantS, "constant-s") - DECLARE_HYDROGEN_ACCESSOR(Constant) - - Smi* value() const { return Smi::FromInt(hydrogen()->Integer32Value()); } -}; - - -class LConstantT V8_FINAL : public LTemplateInstruction<1, 0, 0> { - public: - DECLARE_CONCRETE_INSTRUCTION(ConstantT, "constant-t") - DECLARE_HYDROGEN_ACCESSOR(Constant) - - Handle value(Isolate* isolate) const { - return hydrogen()->handle(isolate); - } -}; - - -class LContext V8_FINAL : public LTemplateInstruction<1, 0, 0> { - public: - DECLARE_CONCRETE_INSTRUCTION(Context, "context") - DECLARE_HYDROGEN_ACCESSOR(Context) -}; - - -class LDateField V8_FINAL : public LTemplateInstruction<1, 1, 0> { - public: - LDateField(LOperand* date, Smi* index) : index_(index) { - inputs_[0] = date; - } - - LOperand* date() { return inputs_[0]; } - Smi* index() const { return index_; } - - DECLARE_CONCRETE_INSTRUCTION(DateField, "date-field") - DECLARE_HYDROGEN_ACCESSOR(DateField) - - private: - Smi* index_; -}; - - -class LDebugBreak V8_FINAL : public LTemplateInstruction<0, 0, 0> { - public: - DECLARE_CONCRETE_INSTRUCTION(DebugBreak, "break") -}; - - -class LDeclareGlobals V8_FINAL : public LTemplateInstruction<0, 1, 0> { - public: - explicit LDeclareGlobals(LOperand* context) { - inputs_[0] = context; - } - - LOperand* context() { return inputs_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(DeclareGlobals, "declare-globals") - DECLARE_HYDROGEN_ACCESSOR(DeclareGlobals) -}; - - -class LDeoptimize V8_FINAL : public LTemplateInstruction<0, 0, 0> { - public: - DECLARE_CONCRETE_INSTRUCTION(Deoptimize, "deoptimize") - DECLARE_HYDROGEN_ACCESSOR(Deoptimize) -}; - - -class LDivI V8_FINAL : public LTemplateInstruction<1, 2, 1> { - public: - LDivI(LOperand* left, LOperand* right, LOperand* temp) { - inputs_[0] = left; - inputs_[1] = right; - temps_[0] = temp; - } - - LOperand* left() { return inputs_[0]; } - LOperand* right() { return inputs_[1]; } - LOperand* temp() { return temps_[0]; } - - bool is_flooring() { return hydrogen_value()->IsMathFloorOfDiv(); } - - DECLARE_CONCRETE_INSTRUCTION(DivI, "div-i") - DECLARE_HYDROGEN_ACCESSOR(Div) -}; - - -class LDoubleToIntOrSmi V8_FINAL : public LTemplateInstruction<1, 1, 0> { - public: - explicit LDoubleToIntOrSmi(LOperand* value) { - inputs_[0] = value; - } - - LOperand* value() { return inputs_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(DoubleToIntOrSmi, "double-to-int-or-smi") - DECLARE_HYDROGEN_ACCESSOR(UnaryOperation) - - bool tag_result() { return hydrogen()->representation().IsSmi(); } -}; - - -class LForInCacheArray V8_FINAL : public LTemplateInstruction<1, 1, 0> { - public: - explicit LForInCacheArray(LOperand* map) { - inputs_[0] = map; - } - - LOperand* map() { return inputs_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(ForInCacheArray, "for-in-cache-array") - - int idx() { - return HForInCacheArray::cast(this->hydrogen_value())->idx(); - } -}; - - -class LForInPrepareMap V8_FINAL : public LTemplateInstruction<1, 2, 0> { - public: - LForInPrepareMap(LOperand* context, LOperand* object) { - inputs_[0] = context; - inputs_[1] = object; - } - - LOperand* context() { return inputs_[0]; } - LOperand* object() { return inputs_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(ForInPrepareMap, "for-in-prepare-map") -}; - - -class LGetCachedArrayIndex V8_FINAL : public LTemplateInstruction<1, 1, 0> { - public: - explicit LGetCachedArrayIndex(LOperand* value) { - inputs_[0] = value; - } - - LOperand* value() { return inputs_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(GetCachedArrayIndex, "get-cached-array-index") - DECLARE_HYDROGEN_ACCESSOR(GetCachedArrayIndex) -}; - - -class LHasCachedArrayIndexAndBranch V8_FINAL - : public LControlInstruction<1, 1> { - public: - LHasCachedArrayIndexAndBranch(LOperand* value, LOperand* temp) { - inputs_[0] = value; - temps_[0] = temp; - } - - LOperand* value() { return inputs_[0]; } - LOperand* temp() { return temps_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(HasCachedArrayIndexAndBranch, - "has-cached-array-index-and-branch") - DECLARE_HYDROGEN_ACCESSOR(HasCachedArrayIndexAndBranch) - - virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE; -}; - - -class LHasInstanceTypeAndBranch V8_FINAL : public LControlInstruction<1, 1> { - public: - LHasInstanceTypeAndBranch(LOperand* value, LOperand* temp) { - inputs_[0] = value; - temps_[0] = temp; - } - - LOperand* value() { return inputs_[0]; } - LOperand* temp() { return temps_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(HasInstanceTypeAndBranch, - "has-instance-type-and-branch") - DECLARE_HYDROGEN_ACCESSOR(HasInstanceTypeAndBranch) - - virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE; -}; - - -class LInnerAllocatedObject V8_FINAL : public LTemplateInstruction<1, 2, 0> { - public: - LInnerAllocatedObject(LOperand* base_object, LOperand* offset) { - inputs_[0] = base_object; - inputs_[1] = offset; - } - - LOperand* base_object() const { return inputs_[0]; } - LOperand* offset() const { return inputs_[1]; } - - virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE; - - DECLARE_CONCRETE_INSTRUCTION(InnerAllocatedObject, "inner-allocated-object") -}; - - -class LInstanceOf V8_FINAL : public LTemplateInstruction<1, 3, 0> { - public: - LInstanceOf(LOperand* context, LOperand* left, LOperand* right) { - inputs_[0] = context; - inputs_[1] = left; - inputs_[2] = right; - } - - LOperand* context() { return inputs_[0]; } - LOperand* left() { return inputs_[1]; } - LOperand* right() { return inputs_[2]; } - - DECLARE_CONCRETE_INSTRUCTION(InstanceOf, "instance-of") -}; - - -class LInstanceOfKnownGlobal V8_FINAL : public LTemplateInstruction<1, 2, 0> { - public: - LInstanceOfKnownGlobal(LOperand* context, LOperand* value) { - inputs_[0] = context; - inputs_[1] = value; - } - - LOperand* context() { return inputs_[0]; } - LOperand* value() { return inputs_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(InstanceOfKnownGlobal, - "instance-of-known-global") - DECLARE_HYDROGEN_ACCESSOR(InstanceOfKnownGlobal) - - Handle function() const { return hydrogen()->function(); } - LEnvironment* GetDeferredLazyDeoptimizationEnvironment() { - return lazy_deopt_env_; - } - virtual void SetDeferredLazyDeoptimizationEnvironment( - LEnvironment* env) V8_OVERRIDE { - lazy_deopt_env_ = env; - } - - private: - LEnvironment* lazy_deopt_env_; -}; - - -class LInteger32ToDouble V8_FINAL : public LTemplateInstruction<1, 1, 0> { - public: - explicit LInteger32ToDouble(LOperand* value) { - inputs_[0] = value; - } - - LOperand* value() { return inputs_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(Integer32ToDouble, "int32-to-double") -}; - - -class LInteger32ToSmi V8_FINAL : public LTemplateInstruction<1, 1, 0> { - public: - explicit LInteger32ToSmi(LOperand* value) { - inputs_[0] = value; - } - - LOperand* value() { return inputs_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(Integer32ToDouble, "int32-to-smi") - DECLARE_HYDROGEN_ACCESSOR(Change) -}; - - -class LCallWithDescriptor V8_FINAL : public LTemplateResultInstruction<1> { - public: - LCallWithDescriptor(const CallInterfaceDescriptor* descriptor, - ZoneList& operands, - Zone* zone) - : descriptor_(descriptor), - inputs_(descriptor->environment_length() + 1, zone) { - ASSERT(descriptor->environment_length() + 1 == operands.length()); - inputs_.AddAll(operands, zone); - } - - LOperand* target() const { return inputs_[0]; } - - const CallInterfaceDescriptor* descriptor() { return descriptor_; } - - private: - DECLARE_CONCRETE_INSTRUCTION(CallWithDescriptor, "call-with-descriptor") - DECLARE_HYDROGEN_ACCESSOR(CallWithDescriptor) - - virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE; - - int arity() const { return hydrogen()->argument_count() - 1; } - - const CallInterfaceDescriptor* descriptor_; - ZoneList inputs_; - - // Iterator support. - virtual int InputCount() V8_FINAL V8_OVERRIDE { return inputs_.length(); } - virtual LOperand* InputAt(int i) V8_FINAL V8_OVERRIDE { return inputs_[i]; } - - virtual int TempCount() V8_FINAL V8_OVERRIDE { return 0; } - virtual LOperand* TempAt(int i) V8_FINAL V8_OVERRIDE { return NULL; } -}; - - -class LInvokeFunction V8_FINAL : public LTemplateInstruction<1, 2, 0> { - public: - LInvokeFunction(LOperand* context, LOperand* function) { - inputs_[0] = context; - inputs_[1] = function; - } - - LOperand* context() { return inputs_[0]; } - LOperand* function() { return inputs_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(InvokeFunction, "invoke-function") - DECLARE_HYDROGEN_ACCESSOR(InvokeFunction) - - virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE; - - int arity() const { return hydrogen()->argument_count() - 1; } -}; - - -class LIsConstructCallAndBranch V8_FINAL : public LControlInstruction<0, 2> { - public: - LIsConstructCallAndBranch(LOperand* temp1, LOperand* temp2) { - temps_[0] = temp1; - temps_[1] = temp2; - } - - LOperand* temp1() { return temps_[0]; } - LOperand* temp2() { return temps_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(IsConstructCallAndBranch, - "is-construct-call-and-branch") -}; - - -class LIsObjectAndBranch V8_FINAL : public LControlInstruction<1, 2> { - public: - LIsObjectAndBranch(LOperand* value, LOperand* temp1, LOperand* temp2) { - inputs_[0] = value; - temps_[0] = temp1; - temps_[1] = temp2; - } - - LOperand* value() { return inputs_[0]; } - LOperand* temp1() { return temps_[0]; } - LOperand* temp2() { return temps_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(IsObjectAndBranch, "is-object-and-branch") - DECLARE_HYDROGEN_ACCESSOR(IsObjectAndBranch) - - virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE; -}; - - -class LIsStringAndBranch V8_FINAL : public LControlInstruction<1, 1> { - public: - LIsStringAndBranch(LOperand* value, LOperand* temp) { - inputs_[0] = value; - temps_[0] = temp; - } - - LOperand* value() { return inputs_[0]; } - LOperand* temp() { return temps_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(IsStringAndBranch, "is-string-and-branch") - DECLARE_HYDROGEN_ACCESSOR(IsStringAndBranch) - - virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE; -}; - - -class LIsSmiAndBranch V8_FINAL : public LControlInstruction<1, 0> { - public: - explicit LIsSmiAndBranch(LOperand* value) { - inputs_[0] = value; - } - - LOperand* value() { return inputs_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(IsSmiAndBranch, "is-smi-and-branch") - DECLARE_HYDROGEN_ACCESSOR(IsSmiAndBranch) - - virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE; -}; - - -class LIsUndetectableAndBranch V8_FINAL : public LControlInstruction<1, 1> { - public: - explicit LIsUndetectableAndBranch(LOperand* value, LOperand* temp) { - inputs_[0] = value; - temps_[0] = temp; - } - - LOperand* value() { return inputs_[0]; } - LOperand* temp() { return temps_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(IsUndetectableAndBranch, - "is-undetectable-and-branch") - DECLARE_HYDROGEN_ACCESSOR(IsUndetectableAndBranch) - - virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE; -}; - - -class LLoadContextSlot V8_FINAL : public LTemplateInstruction<1, 1, 0> { - public: - explicit LLoadContextSlot(LOperand* context) { - inputs_[0] = context; - } - - LOperand* context() { return inputs_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(LoadContextSlot, "load-context-slot") - DECLARE_HYDROGEN_ACCESSOR(LoadContextSlot) - - int slot_index() const { return hydrogen()->slot_index(); } - - virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE; -}; - - -class LLoadNamedField V8_FINAL : public LTemplateInstruction<1, 1, 0> { - public: - explicit LLoadNamedField(LOperand* object) { - inputs_[0] = object; - } - - LOperand* object() { return inputs_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(LoadNamedField, "load-named-field") - DECLARE_HYDROGEN_ACCESSOR(LoadNamedField) -}; - - -class LFunctionLiteral V8_FINAL : public LTemplateInstruction<1, 1, 0> { - public: - explicit LFunctionLiteral(LOperand* context) { - inputs_[0] = context; - } - - LOperand* context() { return inputs_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(FunctionLiteral, "function-literal") - DECLARE_HYDROGEN_ACCESSOR(FunctionLiteral) -}; - - -class LLoadFunctionPrototype V8_FINAL : public LTemplateInstruction<1, 1, 1> { - public: - LLoadFunctionPrototype(LOperand* function, LOperand* temp) { - inputs_[0] = function; - temps_[0] = temp; - } - - LOperand* function() { return inputs_[0]; } - LOperand* temp() { return temps_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(LoadFunctionPrototype, "load-function-prototype") - DECLARE_HYDROGEN_ACCESSOR(LoadFunctionPrototype) -}; - - -class LLoadGlobalCell V8_FINAL : public LTemplateInstruction<1, 0, 0> { - public: - DECLARE_CONCRETE_INSTRUCTION(LoadGlobalCell, "load-global-cell") - DECLARE_HYDROGEN_ACCESSOR(LoadGlobalCell) -}; - - -class LLoadGlobalGeneric V8_FINAL : public LTemplateInstruction<1, 2, 0> { - public: - LLoadGlobalGeneric(LOperand* context, LOperand* global_object) { - inputs_[0] = context; - inputs_[1] = global_object; - } - - LOperand* context() { return inputs_[0]; } - LOperand* global_object() { return inputs_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(LoadGlobalGeneric, "load-global-generic") - DECLARE_HYDROGEN_ACCESSOR(LoadGlobalGeneric) - - Handle name() const { return hydrogen()->name(); } - bool for_typeof() const { return hydrogen()->for_typeof(); } -}; - - -template -class LLoadKeyed : public LTemplateInstruction<1, 2, T> { - public: - LLoadKeyed(LOperand* elements, LOperand* key) { - this->inputs_[0] = elements; - this->inputs_[1] = key; - } - - LOperand* elements() { return this->inputs_[0]; } - LOperand* key() { return this->inputs_[1]; } - ElementsKind elements_kind() const { - return this->hydrogen()->elements_kind(); - } - bool is_external() const { - return this->hydrogen()->is_external(); - } - bool is_fixed_typed_array() const { - return hydrogen()->is_fixed_typed_array(); - } - bool is_typed_elements() const { - return is_external() || is_fixed_typed_array(); - } - uint32_t additional_index() const { - return this->hydrogen()->index_offset(); - } - void PrintDataTo(StringStream* stream) V8_OVERRIDE { - this->elements()->PrintTo(stream); - stream->Add("["); - this->key()->PrintTo(stream); - if (this->hydrogen()->IsDehoisted()) { - stream->Add(" + %d]", this->additional_index()); - } else { - stream->Add("]"); - } - } - - DECLARE_HYDROGEN_ACCESSOR(LoadKeyed) -}; - - -class LLoadKeyedExternal: public LLoadKeyed<1> { - public: - LLoadKeyedExternal(LOperand* elements, LOperand* key, LOperand* temp) : - LLoadKeyed<1>(elements, key) { - temps_[0] = temp; - } - - LOperand* temp() { return temps_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(LoadKeyedExternal, "load-keyed-external"); -}; - - -class LLoadKeyedFixed: public LLoadKeyed<1> { - public: - LLoadKeyedFixed(LOperand* elements, LOperand* key, LOperand* temp) : - LLoadKeyed<1>(elements, key) { - temps_[0] = temp; - } - - LOperand* temp() { return temps_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(LoadKeyedFixed, "load-keyed-fixed"); -}; - - -class LLoadKeyedFixedDouble: public LLoadKeyed<1> { - public: - LLoadKeyedFixedDouble(LOperand* elements, LOperand* key, LOperand* temp) : - LLoadKeyed<1>(elements, key) { - temps_[0] = temp; - } - - LOperand* temp() { return temps_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(LoadKeyedFixedDouble, "load-keyed-fixed-double"); -}; - - -class LLoadKeyedGeneric V8_FINAL : public LTemplateInstruction<1, 3, 0> { - public: - LLoadKeyedGeneric(LOperand* context, LOperand* object, LOperand* key) { - inputs_[0] = context; - inputs_[1] = object; - inputs_[2] = key; - } - - LOperand* context() { return inputs_[0]; } - LOperand* object() { return inputs_[1]; } - LOperand* key() { return inputs_[2]; } - - DECLARE_CONCRETE_INSTRUCTION(LoadKeyedGeneric, "load-keyed-generic") -}; - - -class LLoadNamedGeneric V8_FINAL : public LTemplateInstruction<1, 2, 0> { - public: - LLoadNamedGeneric(LOperand* context, LOperand* object) { - inputs_[0] = context; - inputs_[1] = object; - } - - LOperand* context() { return inputs_[0]; } - LOperand* object() { return inputs_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(LoadNamedGeneric, "load-named-generic") - DECLARE_HYDROGEN_ACCESSOR(LoadNamedGeneric) - - Handle name() const { return hydrogen()->name(); } -}; - - -class LLoadRoot V8_FINAL : public LTemplateInstruction<1, 0, 0> { - public: - DECLARE_CONCRETE_INSTRUCTION(LoadRoot, "load-root") - DECLARE_HYDROGEN_ACCESSOR(LoadRoot) - - Heap::RootListIndex index() const { return hydrogen()->index(); } -}; - - -class LMapEnumLength V8_FINAL : public LTemplateInstruction<1, 1, 0> { - public: - explicit LMapEnumLength(LOperand* value) { - inputs_[0] = value; - } - - LOperand* value() { return inputs_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(MapEnumLength, "map-enum-length") -}; - - -template -class LUnaryMathOperation : public LTemplateInstruction<1, 1, T> { - public: - explicit LUnaryMathOperation(LOperand* value) { - this->inputs_[0] = value; - } - - LOperand* value() { return this->inputs_[0]; } - BuiltinFunctionId op() const { return this->hydrogen()->op(); } - - void PrintDataTo(StringStream* stream) V8_OVERRIDE; - - DECLARE_HYDROGEN_ACCESSOR(UnaryMathOperation) -}; - - -class LMathAbs V8_FINAL : public LUnaryMathOperation<0> { - public: - explicit LMathAbs(LOperand* value) : LUnaryMathOperation<0>(value) {} - - DECLARE_CONCRETE_INSTRUCTION(MathAbs, "math-abs") -}; - - -class LMathAbsTagged: public LTemplateInstruction<1, 2, 3> { - public: - LMathAbsTagged(LOperand* context, LOperand* value, - LOperand* temp1, LOperand* temp2, LOperand* temp3) { - inputs_[0] = context; - inputs_[1] = value; - temps_[0] = temp1; - temps_[1] = temp2; - temps_[2] = temp3; - } - - LOperand* context() { return inputs_[0]; } - LOperand* value() { return inputs_[1]; } - LOperand* temp1() { return temps_[0]; } - LOperand* temp2() { return temps_[1]; } - LOperand* temp3() { return temps_[2]; } - - DECLARE_CONCRETE_INSTRUCTION(MathAbsTagged, "math-abs-tagged") - DECLARE_HYDROGEN_ACCESSOR(UnaryMathOperation) -}; - - -class LMathExp V8_FINAL : public LUnaryMathOperation<4> { - public: - LMathExp(LOperand* value, - LOperand* double_temp1, - LOperand* temp1, - LOperand* temp2, - LOperand* temp3) - : LUnaryMathOperation<4>(value) { - temps_[0] = double_temp1; - temps_[1] = temp1; - temps_[2] = temp2; - temps_[3] = temp3; - ExternalReference::InitializeMathExpData(); - } - - LOperand* double_temp1() { return temps_[0]; } - LOperand* temp1() { return temps_[1]; } - LOperand* temp2() { return temps_[2]; } - LOperand* temp3() { return temps_[3]; } - - DECLARE_CONCRETE_INSTRUCTION(MathExp, "math-exp") -}; - - -class LMathFloor V8_FINAL : public LUnaryMathOperation<0> { - public: - explicit LMathFloor(LOperand* value) : LUnaryMathOperation<0>(value) { } - DECLARE_CONCRETE_INSTRUCTION(MathFloor, "math-floor") -}; - - -class LMathFloorOfDiv V8_FINAL : public LTemplateInstruction<1, 2, 1> { - public: - LMathFloorOfDiv(LOperand* left, - LOperand* right, - LOperand* temp = NULL) { - inputs_[0] = left; - inputs_[1] = right; - temps_[0] = temp; - } - - LOperand* left() { return inputs_[0]; } - LOperand* right() { return inputs_[1]; } - LOperand* temp() { return temps_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(MathFloorOfDiv, "math-floor-of-div") - DECLARE_HYDROGEN_ACCESSOR(MathFloorOfDiv) -}; - - -class LMathLog V8_FINAL : public LUnaryMathOperation<0> { - public: - explicit LMathLog(LOperand* value) : LUnaryMathOperation<0>(value) { } - DECLARE_CONCRETE_INSTRUCTION(MathLog, "math-log") -}; - - -class LMathMinMax V8_FINAL : public LTemplateInstruction<1, 2, 0> { - public: - LMathMinMax(LOperand* left, LOperand* right) { - inputs_[0] = left; - inputs_[1] = right; - } - - LOperand* left() { return inputs_[0]; } - LOperand* right() { return inputs_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(MathMinMax, "math-min-max") - DECLARE_HYDROGEN_ACCESSOR(MathMinMax) -}; - - -class LMathPowHalf V8_FINAL : public LUnaryMathOperation<0> { - public: - explicit LMathPowHalf(LOperand* value) : LUnaryMathOperation<0>(value) { } - DECLARE_CONCRETE_INSTRUCTION(MathPowHalf, "math-pow-half") -}; - - -class LMathRound V8_FINAL : public LUnaryMathOperation<1> { - public: - LMathRound(LOperand* value, LOperand* temp1) - : LUnaryMathOperation<1>(value) { - temps_[0] = temp1; - } - - LOperand* temp1() { return temps_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(MathRound, "math-round") -}; - - -class LMathSqrt V8_FINAL : public LUnaryMathOperation<0> { - public: - explicit LMathSqrt(LOperand* value) : LUnaryMathOperation<0>(value) { } - DECLARE_CONCRETE_INSTRUCTION(MathSqrt, "math-sqrt") -}; - - -class LModI V8_FINAL : public LTemplateInstruction<1, 2, 0> { - public: - LModI(LOperand* left, LOperand* right) { - inputs_[0] = left; - inputs_[1] = right; - } - - LOperand* left() { return inputs_[0]; } - LOperand* right() { return inputs_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(ModI, "mod-i") - DECLARE_HYDROGEN_ACCESSOR(Mod) -}; - - -class LMulConstIS V8_FINAL : public LTemplateInstruction<1, 2, 0> { - public: - LMulConstIS(LOperand* left, LConstantOperand* right) { - inputs_[0] = left; - inputs_[1] = right; - } - - LOperand* left() { return inputs_[0]; } - LConstantOperand* right() { return LConstantOperand::cast(inputs_[1]); } - - DECLARE_CONCRETE_INSTRUCTION(MulConstIS, "mul-const-i-s") - DECLARE_HYDROGEN_ACCESSOR(Mul) -}; - - -class LMulI V8_FINAL : public LTemplateInstruction<1, 2, 0> { - public: - LMulI(LOperand* left, LOperand* right) { - inputs_[0] = left; - inputs_[1] = right; - } - - LOperand* left() { return inputs_[0]; } - LOperand* right() { return inputs_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(MulI, "mul-i") - DECLARE_HYDROGEN_ACCESSOR(Mul) -}; - - -class LMulS V8_FINAL : public LTemplateInstruction<1, 2, 0> { - public: - LMulS(LOperand* left, LOperand* right) { - inputs_[0] = left; - inputs_[1] = right; - } - - LOperand* left() { return inputs_[0]; } - LOperand* right() { return inputs_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(MulI, "mul-s") - DECLARE_HYDROGEN_ACCESSOR(Mul) -}; - - -class LNumberTagD V8_FINAL : public LTemplateInstruction<1, 1, 2> { - public: - LNumberTagD(LOperand* value, LOperand* temp1, LOperand* temp2) { - inputs_[0] = value; - temps_[0] = temp1; - temps_[1] = temp2; - } - - LOperand* value() { return inputs_[0]; } - LOperand* temp1() { return temps_[0]; } - LOperand* temp2() { return temps_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(NumberTagD, "number-tag-d") - DECLARE_HYDROGEN_ACCESSOR(Change) -}; - - -class LNumberTagU V8_FINAL : public LTemplateInstruction<1, 1, 2> { - public: - explicit LNumberTagU(LOperand* value, - LOperand* temp1, - LOperand* temp2) { - inputs_[0] = value; - temps_[0] = temp1; - temps_[1] = temp2; - } - - LOperand* value() { return inputs_[0]; } - LOperand* temp1() { return temps_[0]; } - LOperand* temp2() { return temps_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(NumberTagU, "number-tag-u") -}; - - -class LNumberUntagD V8_FINAL : public LTemplateInstruction<1, 1, 1> { - public: - LNumberUntagD(LOperand* value, LOperand* temp) { - inputs_[0] = value; - temps_[0] = temp; - } - - LOperand* value() { return inputs_[0]; } - - LOperand* temp() { return temps_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(NumberUntagD, "double-untag") - DECLARE_HYDROGEN_ACCESSOR(Change) -}; - - -class LParameter V8_FINAL : public LTemplateInstruction<1, 0, 0> { - public: - virtual bool HasInterestingComment(LCodeGen* gen) const { return false; } - DECLARE_CONCRETE_INSTRUCTION(Parameter, "parameter") -}; - - -class LPower V8_FINAL : public LTemplateInstruction<1, 2, 0> { - public: - LPower(LOperand* left, LOperand* right) { - inputs_[0] = left; - inputs_[1] = right; - } - - LOperand* left() { return inputs_[0]; } - LOperand* right() { return inputs_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(Power, "power") - DECLARE_HYDROGEN_ACCESSOR(Power) -}; - - -class LPushArgument V8_FINAL : public LTemplateInstruction<0, 1, 0> { - public: - explicit LPushArgument(LOperand* value) { - inputs_[0] = value; - } - - LOperand* value() { return inputs_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(PushArgument, "push-argument") -}; - - -class LRegExpLiteral V8_FINAL : public LTemplateInstruction<1, 1, 0> { - public: - explicit LRegExpLiteral(LOperand* context) { - inputs_[0] = context; - } - - LOperand* context() { return inputs_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(RegExpLiteral, "regexp-literal") - DECLARE_HYDROGEN_ACCESSOR(RegExpLiteral) -}; - - -class LReturn V8_FINAL : public LTemplateInstruction<0, 3, 0> { - public: - LReturn(LOperand* value, LOperand* context, LOperand* parameter_count) { - inputs_[0] = value; - inputs_[1] = context; - inputs_[2] = parameter_count; - } - - LOperand* value() { return inputs_[0]; } - LOperand* parameter_count() { return inputs_[2]; } - - bool has_constant_parameter_count() { - return parameter_count()->IsConstantOperand(); - } - LConstantOperand* constant_parameter_count() { - ASSERT(has_constant_parameter_count()); - return LConstantOperand::cast(parameter_count()); - } - - DECLARE_CONCRETE_INSTRUCTION(Return, "return") -}; - - -class LSeqStringGetChar V8_FINAL : public LTemplateInstruction<1, 2, 1> { - public: - LSeqStringGetChar(LOperand* string, - LOperand* index, - LOperand* temp) { - inputs_[0] = string; - inputs_[1] = index; - temps_[0] = temp; - } - - LOperand* string() { return inputs_[0]; } - LOperand* index() { return inputs_[1]; } - LOperand* temp() { return temps_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(SeqStringGetChar, "seq-string-get-char") - DECLARE_HYDROGEN_ACCESSOR(SeqStringGetChar) -}; - - -class LSeqStringSetChar V8_FINAL : public LTemplateInstruction<1, 4, 1> { - public: - LSeqStringSetChar(LOperand* context, - LOperand* string, - LOperand* index, - LOperand* value, - LOperand* temp) { - inputs_[0] = context; - inputs_[1] = string; - inputs_[2] = index; - inputs_[3] = value; - temps_[0] = temp; - } - - LOperand* context() { return inputs_[0]; } - LOperand* string() { return inputs_[1]; } - LOperand* index() { return inputs_[2]; } - LOperand* value() { return inputs_[3]; } - LOperand* temp() { return temps_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(SeqStringSetChar, "seq-string-set-char") - DECLARE_HYDROGEN_ACCESSOR(SeqStringSetChar) -}; - - -class LSmiTag V8_FINAL : public LTemplateInstruction<1, 1, 0> { - public: - explicit LSmiTag(LOperand* value) { - inputs_[0] = value; - } - - LOperand* value() { return inputs_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(SmiTag, "smi-tag") -}; - - -class LSmiUntag V8_FINAL : public LTemplateInstruction<1, 1, 0> { - public: - LSmiUntag(LOperand* value, bool needs_check) - : needs_check_(needs_check) { - inputs_[0] = value; - } - - LOperand* value() { return inputs_[0]; } - bool needs_check() const { return needs_check_; } - - DECLARE_CONCRETE_INSTRUCTION(SmiUntag, "smi-untag") - - private: - bool needs_check_; -}; - - -class LStackCheck V8_FINAL : public LTemplateInstruction<0, 1, 0> { - public: - explicit LStackCheck(LOperand* context) { - inputs_[0] = context; - } - - LOperand* context() { return inputs_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(StackCheck, "stack-check") - DECLARE_HYDROGEN_ACCESSOR(StackCheck) - - Label* done_label() { return &done_label_; } - - private: - Label done_label_; -}; - - -template -class LStoreKeyed : public LTemplateInstruction<0, 3, T> { - public: - LStoreKeyed(LOperand* elements, LOperand* key, LOperand* value) { - this->inputs_[0] = elements; - this->inputs_[1] = key; - this->inputs_[2] = value; - } - - bool is_external() const { return this->hydrogen()->is_external(); } - bool is_fixed_typed_array() const { - return hydrogen()->is_fixed_typed_array(); - } - bool is_typed_elements() const { - return is_external() || is_fixed_typed_array(); - } - LOperand* elements() { return this->inputs_[0]; } - LOperand* key() { return this->inputs_[1]; } - LOperand* value() { return this->inputs_[2]; } - ElementsKind elements_kind() const { - return this->hydrogen()->elements_kind(); - } - - bool NeedsCanonicalization() { - return this->hydrogen()->NeedsCanonicalization(); - } - uint32_t additional_index() const { return this->hydrogen()->index_offset(); } - - void PrintDataTo(StringStream* stream) V8_OVERRIDE { - this->elements()->PrintTo(stream); - stream->Add("["); - this->key()->PrintTo(stream); - if (this->hydrogen()->IsDehoisted()) { - stream->Add(" + %d] <-", this->additional_index()); - } else { - stream->Add("] <- "); - } - - if (this->value() == NULL) { - ASSERT(hydrogen()->IsConstantHoleStore() && - hydrogen()->value()->representation().IsDouble()); - stream->Add(""); - } else { - this->value()->PrintTo(stream); - } - } - - DECLARE_HYDROGEN_ACCESSOR(StoreKeyed) -}; - - -class LStoreKeyedExternal V8_FINAL : public LStoreKeyed<1> { - public: - LStoreKeyedExternal(LOperand* elements, LOperand* key, LOperand* value, - LOperand* temp) : - LStoreKeyed<1>(elements, key, value) { - temps_[0] = temp; - }; - - LOperand* temp() { return temps_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(StoreKeyedExternal, "store-keyed-external") -}; - - -class LStoreKeyedFixed V8_FINAL : public LStoreKeyed<1> { - public: - LStoreKeyedFixed(LOperand* elements, LOperand* key, LOperand* value, - LOperand* temp) : - LStoreKeyed<1>(elements, key, value) { - temps_[0] = temp; - }; - - LOperand* temp() { return temps_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(StoreKeyedFixed, "store-keyed-fixed") -}; - - -class LStoreKeyedFixedDouble V8_FINAL : public LStoreKeyed<1> { - public: - LStoreKeyedFixedDouble(LOperand* elements, LOperand* key, LOperand* value, - LOperand* temp) : - LStoreKeyed<1>(elements, key, value) { - temps_[0] = temp; - }; - - LOperand* temp() { return temps_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(StoreKeyedFixedDouble, - "store-keyed-fixed-double") -}; - - -class LStoreKeyedGeneric V8_FINAL : public LTemplateInstruction<0, 4, 0> { - public: - LStoreKeyedGeneric(LOperand* context, - LOperand* obj, - LOperand* key, - LOperand* value) { - inputs_[0] = context; - inputs_[1] = obj; - inputs_[2] = key; - inputs_[3] = value; - } - - LOperand* context() { return inputs_[0]; } - LOperand* object() { return inputs_[1]; } - LOperand* key() { return inputs_[2]; } - LOperand* value() { return inputs_[3]; } - - DECLARE_CONCRETE_INSTRUCTION(StoreKeyedGeneric, "store-keyed-generic") - DECLARE_HYDROGEN_ACCESSOR(StoreKeyedGeneric) - - virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE; - - StrictModeFlag strict_mode_flag() { return hydrogen()->strict_mode_flag(); } -}; - - -class LStoreNamedField V8_FINAL : public LTemplateInstruction<0, 2, 2> { - public: - LStoreNamedField(LOperand* object, LOperand* value, - LOperand* temp0, LOperand* temp1) { - inputs_[0] = object; - inputs_[1] = value; - temps_[0] = temp0; - temps_[1] = temp1; - } - - LOperand* object() { return inputs_[0]; } - LOperand* value() { return inputs_[1]; } - LOperand* temp0() { return temps_[0]; } - LOperand* temp1() { return temps_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(StoreNamedField, "store-named-field") - DECLARE_HYDROGEN_ACCESSOR(StoreNamedField) - - virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE; - - Handle transition() const { return hydrogen()->transition_map(); } - Representation representation() const { - return hydrogen()->field_representation(); - } -}; - - -class LStoreNamedGeneric V8_FINAL: public LTemplateInstruction<0, 3, 0> { - public: - LStoreNamedGeneric(LOperand* context, LOperand* object, LOperand* value) { - inputs_[0] = context; - inputs_[1] = object; - inputs_[2] = value; - } - - LOperand* context() { return inputs_[0]; } - LOperand* object() { return inputs_[1]; } - LOperand* value() { return inputs_[2]; } - - DECLARE_CONCRETE_INSTRUCTION(StoreNamedGeneric, "store-named-generic") - DECLARE_HYDROGEN_ACCESSOR(StoreNamedGeneric) - - virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE; - - Handle name() const { return hydrogen()->name(); } - StrictModeFlag strict_mode_flag() { return hydrogen()->strict_mode_flag(); } -}; - - -class LStringAdd V8_FINAL : public LTemplateInstruction<1, 3, 0> { - public: - LStringAdd(LOperand* context, LOperand* left, LOperand* right) { - inputs_[0] = context; - inputs_[1] = left; - inputs_[2] = right; - } - - LOperand* context() { return inputs_[0]; } - LOperand* left() { return inputs_[1]; } - LOperand* right() { return inputs_[2]; } - - DECLARE_CONCRETE_INSTRUCTION(StringAdd, "string-add") - DECLARE_HYDROGEN_ACCESSOR(StringAdd) -}; - - - -class LStringCharCodeAt V8_FINAL : public LTemplateInstruction<1, 3, 0> { - public: - LStringCharCodeAt(LOperand* context, LOperand* string, LOperand* index) { - inputs_[0] = context; - inputs_[1] = string; - inputs_[2] = index; - } - - LOperand* context() { return inputs_[0]; } - LOperand* string() { return inputs_[1]; } - LOperand* index() { return inputs_[2]; } - - DECLARE_CONCRETE_INSTRUCTION(StringCharCodeAt, "string-char-code-at") - DECLARE_HYDROGEN_ACCESSOR(StringCharCodeAt) -}; - - -class LStringCharFromCode V8_FINAL : public LTemplateInstruction<1, 2, 0> { - public: - LStringCharFromCode(LOperand* context, LOperand* char_code) { - inputs_[0] = context; - inputs_[1] = char_code; - } - - LOperand* context() { return inputs_[0]; } - LOperand* char_code() { return inputs_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(StringCharFromCode, "string-char-from-code") - DECLARE_HYDROGEN_ACCESSOR(StringCharFromCode) -}; - - -class LStringCompareAndBranch V8_FINAL : public LControlInstruction<3, 0> { - public: - LStringCompareAndBranch(LOperand* context, LOperand* left, LOperand* right) { - inputs_[0] = context; - inputs_[1] = left; - inputs_[2] = right; - } - - LOperand* context() { return inputs_[0]; } - LOperand* left() { return inputs_[1]; } - LOperand* right() { return inputs_[2]; } - - DECLARE_CONCRETE_INSTRUCTION(StringCompareAndBranch, - "string-compare-and-branch") - DECLARE_HYDROGEN_ACCESSOR(StringCompareAndBranch) - - Token::Value op() const { return hydrogen()->token(); } - - virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE; -}; - - -// Truncating conversion from a tagged value to an int32. -class LTaggedToI V8_FINAL : public LTemplateInstruction<1, 1, 2> { - public: - explicit LTaggedToI(LOperand* value, LOperand* temp1, LOperand* temp2) { - inputs_[0] = value; - temps_[0] = temp1; - temps_[1] = temp2; - } - - LOperand* value() { return inputs_[0]; } - LOperand* temp1() { return temps_[0]; } - LOperand* temp2() { return temps_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(TaggedToI, "tagged-to-i") - DECLARE_HYDROGEN_ACCESSOR(Change) - - bool truncating() { return hydrogen()->CanTruncateToInt32(); } -}; - - -class LShiftI V8_FINAL : public LTemplateInstruction<1, 2, 0> { - public: - LShiftI(Token::Value op, LOperand* left, LOperand* right, bool can_deopt) - : op_(op), can_deopt_(can_deopt) { - inputs_[0] = left; - inputs_[1] = right; - } - - Token::Value op() const { return op_; } - LOperand* left() { return inputs_[0]; } - LOperand* right() { return inputs_[1]; } - bool can_deopt() const { return can_deopt_; } - - DECLARE_CONCRETE_INSTRUCTION(ShiftI, "shift-i") - - private: - Token::Value op_; - bool can_deopt_; -}; - - -class LShiftS V8_FINAL : public LTemplateInstruction<1, 2, 1> { - public: - LShiftS(Token::Value op, LOperand* left, LOperand* right, LOperand* temp, - bool can_deopt) : op_(op), can_deopt_(can_deopt) { - inputs_[0] = left; - inputs_[1] = right; - temps_[0] = temp; - } - - Token::Value op() const { return op_; } - LOperand* left() { return inputs_[0]; } - LOperand* right() { return inputs_[1]; } - LOperand* temp() { return temps_[0]; } - bool can_deopt() const { return can_deopt_; } - - DECLARE_CONCRETE_INSTRUCTION(ShiftS, "shift-s") - - private: - Token::Value op_; - bool can_deopt_; -}; - - -class LStoreCodeEntry V8_FINAL: public LTemplateInstruction<0, 2, 1> { - public: - LStoreCodeEntry(LOperand* function, LOperand* code_object, - LOperand* temp) { - inputs_[0] = function; - inputs_[1] = code_object; - temps_[0] = temp; - } - - LOperand* function() { return inputs_[0]; } - LOperand* code_object() { return inputs_[1]; } - LOperand* temp() { return temps_[0]; } - - virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE; - - DECLARE_CONCRETE_INSTRUCTION(StoreCodeEntry, "store-code-entry") - DECLARE_HYDROGEN_ACCESSOR(StoreCodeEntry) -}; - - -class LStoreContextSlot V8_FINAL : public LTemplateInstruction<0, 2, 1> { - public: - LStoreContextSlot(LOperand* context, LOperand* value, LOperand* temp) { - inputs_[0] = context; - inputs_[1] = value; - temps_[0] = temp; - } - - LOperand* context() { return inputs_[0]; } - LOperand* value() { return inputs_[1]; } - LOperand* temp() { return temps_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(StoreContextSlot, "store-context-slot") - DECLARE_HYDROGEN_ACCESSOR(StoreContextSlot) - - int slot_index() { return hydrogen()->slot_index(); } - - virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE; -}; - - -class LStoreGlobalCell V8_FINAL : public LTemplateInstruction<0, 1, 2> { - public: - LStoreGlobalCell(LOperand* value, LOperand* temp1, LOperand* temp2) { - inputs_[0] = value; - temps_[0] = temp1; - temps_[1] = temp2; - } - - LOperand* value() { return inputs_[0]; } - LOperand* temp1() { return temps_[0]; } - LOperand* temp2() { return temps_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(StoreGlobalCell, "store-global-cell") - DECLARE_HYDROGEN_ACCESSOR(StoreGlobalCell) -}; - - -class LSubI V8_FINAL : public LTemplateInstruction<1, 2, 0> { - public: - LSubI(LOperand* left, LOperand* right) { - inputs_[0] = left; - inputs_[1] = right; - } - - LOperand* left() { return inputs_[0]; } - LOperand* right() { return inputs_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(SubI, "sub-i") - DECLARE_HYDROGEN_ACCESSOR(Sub) -}; - - -class LSubS: public LTemplateInstruction<1, 2, 0> { - public: - LSubS(LOperand* left, LOperand* right) { - inputs_[0] = left; - inputs_[1] = right; - } - - LOperand* left() { return inputs_[0]; } - LOperand* right() { return inputs_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(SubS, "sub-s") - DECLARE_HYDROGEN_ACCESSOR(Sub) -}; - - -class LThisFunction V8_FINAL : public LTemplateInstruction<1, 0, 0> { - public: - DECLARE_CONCRETE_INSTRUCTION(ThisFunction, "this-function") - DECLARE_HYDROGEN_ACCESSOR(ThisFunction) -}; - - -class LToFastProperties V8_FINAL : public LTemplateInstruction<1, 1, 0> { - public: - explicit LToFastProperties(LOperand* value) { - inputs_[0] = value; - } - - LOperand* value() { return inputs_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(ToFastProperties, "to-fast-properties") - DECLARE_HYDROGEN_ACCESSOR(ToFastProperties) -}; - - -class LTransitionElementsKind V8_FINAL : public LTemplateInstruction<0, 2, 2> { - public: - LTransitionElementsKind(LOperand* object, - LOperand* context, - LOperand* temp1, - LOperand* temp2 = NULL) { - inputs_[0] = object; - inputs_[1] = context; - temps_[0] = temp1; - temps_[1] = temp2; - } - - LOperand* object() { return inputs_[0]; } - LOperand* context() { return inputs_[1]; } - LOperand* temp1() { return temps_[0]; } - LOperand* temp2() { return temps_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(TransitionElementsKind, - "transition-elements-kind") - DECLARE_HYDROGEN_ACCESSOR(TransitionElementsKind) - - virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE; - - Handle original_map() { return hydrogen()->original_map().handle(); } - Handle transitioned_map() { - return hydrogen()->transitioned_map().handle(); - } - ElementsKind from_kind() const { return hydrogen()->from_kind(); } - ElementsKind to_kind() const { return hydrogen()->to_kind(); } -}; - - -class LTrapAllocationMemento V8_FINAL : public LTemplateInstruction<0, 1, 2> { - public: - LTrapAllocationMemento(LOperand* object, LOperand* temp1, LOperand* temp2) { - inputs_[0] = object; - temps_[0] = temp1; - temps_[1] = temp2; - } - - LOperand* object() { return inputs_[0]; } - LOperand* temp1() { return temps_[0]; } - LOperand* temp2() { return temps_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(TrapAllocationMemento, "trap-allocation-memento") -}; - - -class LTruncateDoubleToIntOrSmi V8_FINAL - : public LTemplateInstruction<1, 1, 0> { - public: - explicit LTruncateDoubleToIntOrSmi(LOperand* value) { - inputs_[0] = value; - } - - LOperand* value() { return inputs_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(TruncateDoubleToIntOrSmi, - "truncate-double-to-int-or-smi") - DECLARE_HYDROGEN_ACCESSOR(UnaryOperation) - - bool tag_result() { return hydrogen()->representation().IsSmi(); } -}; - - -class LTypeof V8_FINAL : public LTemplateInstruction<1, 2, 0> { - public: - LTypeof(LOperand* context, LOperand* value) { - inputs_[0] = context; - inputs_[1] = value; - } - - LOperand* context() { return inputs_[0]; } - LOperand* value() { return inputs_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(Typeof, "typeof") -}; - - -class LTypeofIsAndBranch V8_FINAL : public LControlInstruction<1, 2> { - public: - LTypeofIsAndBranch(LOperand* value, LOperand* temp1, LOperand* temp2) { - inputs_[0] = value; - temps_[0] = temp1; - temps_[1] = temp2; - } - - LOperand* value() { return inputs_[0]; } - LOperand* temp1() { return temps_[0]; } - LOperand* temp2() { return temps_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(TypeofIsAndBranch, "typeof-is-and-branch") - DECLARE_HYDROGEN_ACCESSOR(TypeofIsAndBranch) - - Handle type_literal() const { return hydrogen()->type_literal(); } - - virtual void PrintDataTo(StringStream* stream) V8_OVERRIDE; -}; - - -class LUint32ToDouble V8_FINAL : public LTemplateInstruction<1, 1, 0> { - public: - explicit LUint32ToDouble(LOperand* value) { - inputs_[0] = value; - } - - LOperand* value() { return inputs_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(Uint32ToDouble, "uint32-to-double") -}; - - -class LUint32ToSmi V8_FINAL : public LTemplateInstruction<1, 1, 0> { - public: - explicit LUint32ToSmi(LOperand* value) { - inputs_[0] = value; - } - - LOperand* value() { return inputs_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(Uint32ToSmi, "uint32-to-smi") - DECLARE_HYDROGEN_ACCESSOR(Change) -}; - - -class LCheckMapValue V8_FINAL : public LTemplateInstruction<0, 2, 1> { - public: - LCheckMapValue(LOperand* value, LOperand* map, LOperand* temp) { - inputs_[0] = value; - inputs_[1] = map; - temps_[0] = temp; - } - - LOperand* value() { return inputs_[0]; } - LOperand* map() { return inputs_[1]; } - LOperand* temp() { return temps_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(CheckMapValue, "check-map-value") -}; - - -class LLoadFieldByIndex V8_FINAL : public LTemplateInstruction<1, 2, 0> { - public: - LLoadFieldByIndex(LOperand* object, LOperand* index) { - inputs_[0] = object; - inputs_[1] = index; - } - - LOperand* object() { return inputs_[0]; } - LOperand* index() { return inputs_[1]; } - - DECLARE_CONCRETE_INSTRUCTION(LoadFieldByIndex, "load-field-by-index") -}; - - -class LWrapReceiver V8_FINAL : public LTemplateInstruction<1, 2, 0> { - public: - LWrapReceiver(LOperand* receiver, LOperand* function) { - inputs_[0] = receiver; - inputs_[1] = function; - } - - DECLARE_CONCRETE_INSTRUCTION(WrapReceiver, "wrap-receiver") - DECLARE_HYDROGEN_ACCESSOR(WrapReceiver) - - LOperand* receiver() { return inputs_[0]; } - LOperand* function() { return inputs_[1]; } -}; - - -class LChunkBuilder; -class LPlatformChunk V8_FINAL : public LChunk { - public: - LPlatformChunk(CompilationInfo* info, HGraph* graph) - : LChunk(info, graph) { } - - int GetNextSpillIndex(); - LOperand* GetNextSpillSlot(RegisterKind kind); -}; - - -class LChunkBuilder V8_FINAL : public LChunkBuilderBase { - public: - LChunkBuilder(CompilationInfo* info, HGraph* graph, LAllocator* allocator) - : LChunkBuilderBase(graph->zone()), - chunk_(NULL), - info_(info), - graph_(graph), - status_(UNUSED), - current_instruction_(NULL), - current_block_(NULL), - allocator_(allocator), - instruction_pending_deoptimization_environment_(NULL), - pending_deoptimization_ast_id_(BailoutId::None()) { } - - // Build the sequence for the graph. - LPlatformChunk* Build(); - - LInstruction* CheckElideControlInstruction(HControlInstruction* instr); - - // Declare methods that deal with the individual node types. -#define DECLARE_DO(type) LInstruction* Do##type(H##type* node); - HYDROGEN_CONCRETE_INSTRUCTION_LIST(DECLARE_DO) -#undef DECLARE_DO - - static bool HasMagicNumberForDivision(int32_t divisor); - - private: - enum Status { - UNUSED, - BUILDING, - DONE, - ABORTED - }; - - HGraph* graph() const { return graph_; } - Isolate* isolate() const { return info_->isolate(); } - - bool is_unused() const { return status_ == UNUSED; } - bool is_building() const { return status_ == BUILDING; } - bool is_done() const { return status_ == DONE; } - bool is_aborted() const { return status_ == ABORTED; } - - int argument_count() const { return argument_count_; } - CompilationInfo* info() const { return info_; } - Heap* heap() const { return isolate()->heap(); } - - void Abort(BailoutReason reason); - - // Methods for getting operands for Use / Define / Temp. - LUnallocated* ToUnallocated(Register reg); - LUnallocated* ToUnallocated(DoubleRegister reg); - - // Methods for setting up define-use relationships. - MUST_USE_RESULT LOperand* Use(HValue* value, LUnallocated* operand); - MUST_USE_RESULT LOperand* UseFixed(HValue* value, Register fixed_register); - MUST_USE_RESULT LOperand* UseFixedDouble(HValue* value, - DoubleRegister fixed_register); - - // A value that is guaranteed to be allocated to a register. - // The operand created by UseRegister is guaranteed to be live until the end - // of the instruction. This means that register allocator will not reuse its - // register for any other operand inside instruction. - MUST_USE_RESULT LOperand* UseRegister(HValue* value); - - // The operand created by UseRegisterAndClobber is guaranteed to be live until - // the end of the end of the instruction, and it may also be used as a scratch - // register by the instruction implementation. - // - // This behaves identically to ARM's UseTempRegister. However, it is renamed - // to discourage its use in A64, since in most cases it is better to allocate - // a temporary register for the Lithium instruction. - MUST_USE_RESULT LOperand* UseRegisterAndClobber(HValue* value); - - // The operand created by UseRegisterAtStart is guaranteed to be live only at - // instruction start. The register allocator is free to assign the same - // register to some other operand used inside instruction (i.e. temporary or - // output). - MUST_USE_RESULT LOperand* UseRegisterAtStart(HValue* value); - - // An input operand in a register or a constant operand. - MUST_USE_RESULT LOperand* UseRegisterOrConstant(HValue* value); - MUST_USE_RESULT LOperand* UseRegisterOrConstantAtStart(HValue* value); - - // A constant operand. - MUST_USE_RESULT LConstantOperand* UseConstant(HValue* value); - - // An input operand in register, stack slot or a constant operand. - // Will not be moved to a register even if one is freely available. - virtual MUST_USE_RESULT LOperand* UseAny(HValue* value); - - // Temporary operand that must be in a register. - MUST_USE_RESULT LUnallocated* TempRegister(); - - // Temporary operand that must be in a fixed double register. - MUST_USE_RESULT LOperand* FixedTemp(DoubleRegister reg); - - // Methods for setting up define-use relationships. - // Return the same instruction that they are passed. - LInstruction* Define(LTemplateResultInstruction<1>* instr, - LUnallocated* result); - LInstruction* DefineAsRegister(LTemplateResultInstruction<1>* instr); - LInstruction* DefineAsSpilled(LTemplateResultInstruction<1>* instr, - int index); - - LInstruction* DefineSameAsFirst(LTemplateResultInstruction<1>* instr); - LInstruction* DefineFixed(LTemplateResultInstruction<1>* instr, - Register reg); - LInstruction* DefineFixedDouble(LTemplateResultInstruction<1>* instr, - DoubleRegister reg); - - enum CanDeoptimize { CAN_DEOPTIMIZE_EAGERLY, CANNOT_DEOPTIMIZE_EAGERLY }; - - // By default we assume that instruction sequences generated for calls - // cannot deoptimize eagerly and we do not attach environment to this - // instruction. - LInstruction* MarkAsCall( - LInstruction* instr, - HInstruction* hinstr, - CanDeoptimize can_deoptimize = CANNOT_DEOPTIMIZE_EAGERLY); - - LInstruction* AssignPointerMap(LInstruction* instr); - LInstruction* AssignEnvironment(LInstruction* instr); - - void VisitInstruction(HInstruction* current); - void DoBasicBlock(HBasicBlock* block); - - LInstruction* DoShift(Token::Value op, HBitwiseBinaryOperation* instr); - LInstruction* DoArithmeticD(Token::Value op, - HArithmeticBinaryOperation* instr); - LInstruction* DoArithmeticT(Token::Value op, - HBinaryOperation* instr); - - LPlatformChunk* chunk_; - CompilationInfo* info_; - HGraph* const graph_; - Status status_; - HInstruction* current_instruction_; - HBasicBlock* current_block_; - LAllocator* allocator_; - LInstruction* instruction_pending_deoptimization_environment_; - BailoutId pending_deoptimization_ast_id_; - - DISALLOW_COPY_AND_ASSIGN(LChunkBuilder); -}; - -#undef DECLARE_HYDROGEN_ACCESSOR -#undef DECLARE_CONCRETE_INSTRUCTION - -} } // namespace v8::internal - -#endif // V8_A64_LITHIUM_A64_H_ diff --git a/deps/v8/src/a64/lithium-codegen-a64.cc b/deps/v8/src/a64/lithium-codegen-a64.cc deleted file mode 100644 index b4b875fb04..0000000000 --- a/deps/v8/src/a64/lithium-codegen-a64.cc +++ /dev/null @@ -1,5692 +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 "v8.h" - -#include "a64/lithium-codegen-a64.h" -#include "a64/lithium-gap-resolver-a64.h" -#include "code-stubs.h" -#include "stub-cache.h" -#include "hydrogen-osr.h" - -namespace v8 { -namespace internal { - - -class SafepointGenerator V8_FINAL : public CallWrapper { - public: - SafepointGenerator(LCodeGen* codegen, - LPointerMap* pointers, - Safepoint::DeoptMode mode) - : codegen_(codegen), - pointers_(pointers), - deopt_mode_(mode) { } - virtual ~SafepointGenerator() { } - - virtual void BeforeCall(int call_size) const { } - - virtual void AfterCall() const { - codegen_->RecordSafepoint(pointers_, deopt_mode_); - } - - private: - LCodeGen* codegen_; - LPointerMap* pointers_; - Safepoint::DeoptMode deopt_mode_; -}; - - -#define __ masm()-> - -// Emit code to branch if the given condition holds. -// The code generated here doesn't modify the flags and they must have -// been set by some prior instructions. -// -// The EmitInverted function simply inverts the condition. -class BranchOnCondition : public BranchGenerator { - public: - BranchOnCondition(LCodeGen* codegen, Condition cond) - : BranchGenerator(codegen), - cond_(cond) { } - - virtual void Emit(Label* label) const { - __ B(cond_, label); - } - - virtual void EmitInverted(Label* label) const { - if (cond_ != al) { - __ B(InvertCondition(cond_), label); - } - } - - private: - Condition cond_; -}; - - -// Emit code to compare lhs and rhs and branch if the condition holds. -// This uses MacroAssembler's CompareAndBranch function so it will handle -// converting the comparison to Cbz/Cbnz if the right-hand side is 0. -// -// EmitInverted still compares the two operands but inverts the condition. -class CompareAndBranch : public BranchGenerator { - public: - CompareAndBranch(LCodeGen* codegen, - Condition cond, - const Register& lhs, - const Operand& rhs) - : BranchGenerator(codegen), - cond_(cond), - lhs_(lhs), - rhs_(rhs) { } - - virtual void Emit(Label* label) const { - __ CompareAndBranch(lhs_, rhs_, cond_, label); - } - - virtual void EmitInverted(Label* label) const { - __ CompareAndBranch(lhs_, rhs_, InvertCondition(cond_), label); - } - - private: - Condition cond_; - const Register& lhs_; - const Operand& rhs_; -}; - - -// Test the input with the given mask and branch if the condition holds. -// If the condition is 'eq' or 'ne' this will use MacroAssembler's -// TestAndBranchIfAllClear and TestAndBranchIfAnySet so it will handle the -// conversion to Tbz/Tbnz when possible. -class TestAndBranch : public BranchGenerator { - public: - TestAndBranch(LCodeGen* codegen, - Condition cond, - const Register& value, - uint64_t mask) - : BranchGenerator(codegen), - cond_(cond), - value_(value), - mask_(mask) { } - - virtual void Emit(Label* label) const { - switch (cond_) { - case eq: - __ TestAndBranchIfAllClear(value_, mask_, label); - break; - case ne: - __ TestAndBranchIfAnySet(value_, mask_, label); - break; - default: - __ Tst(value_, mask_); - __ B(cond_, label); - } - } - - virtual void EmitInverted(Label* label) const { - // The inverse of "all clear" is "any set" and vice versa. - switch (cond_) { - case eq: - __ TestAndBranchIfAnySet(value_, mask_, label); - break; - case ne: - __ TestAndBranchIfAllClear(value_, mask_, label); - break; - default: - __ Tst(value_, mask_); - __ B(InvertCondition(cond_), label); - } - } - - private: - Condition cond_; - const Register& value_; - uint64_t mask_; -}; - - -// Test the input and branch if it is non-zero and not a NaN. -class BranchIfNonZeroNumber : public BranchGenerator { - public: - BranchIfNonZeroNumber(LCodeGen* codegen, const FPRegister& value, - const FPRegister& scratch) - : BranchGenerator(codegen), value_(value), scratch_(scratch) { } - - virtual void Emit(Label* label) const { - __ Fabs(scratch_, value_); - // Compare with 0.0. Because scratch_ is positive, the result can be one of - // nZCv (equal), nzCv (greater) or nzCV (unordered). - __ Fcmp(scratch_, 0.0); - __ B(gt, label); - } - - virtual void EmitInverted(Label* label) const { - __ Fabs(scratch_, value_); - __ Fcmp(scratch_, 0.0); - __ B(le, label); - } - - private: - const FPRegister& value_; - const FPRegister& scratch_; -}; - - -// Test the input and branch if it is a heap number. -class BranchIfHeapNumber : public BranchGenerator { - public: - BranchIfHeapNumber(LCodeGen* codegen, const Register& value) - : BranchGenerator(codegen), value_(value) { } - - virtual void Emit(Label* label) const { - __ JumpIfHeapNumber(value_, label); - } - - virtual void EmitInverted(Label* label) const { - __ JumpIfNotHeapNumber(value_, label); - } - - private: - const Register& value_; -}; - - -// Test the input and branch if it is the specified root value. -class BranchIfRoot : public BranchGenerator { - public: - BranchIfRoot(LCodeGen* codegen, const Register& value, - Heap::RootListIndex index) - : BranchGenerator(codegen), value_(value), index_(index) { } - - virtual void Emit(Label* label) const { - __ JumpIfRoot(value_, index_, label); - } - - virtual void EmitInverted(Label* label) const { - __ JumpIfNotRoot(value_, index_, label); - } - - private: - const Register& value_; - const Heap::RootListIndex index_; -}; - - -void LCodeGen::WriteTranslation(LEnvironment* environment, - Translation* translation) { - if (environment == NULL) return; - - // The translation includes one command per value in the environment. - int translation_size = environment->translation_size(); - // The output frame height does not include the parameters. - int height = translation_size - environment->parameter_count(); - - WriteTranslation(environment->outer(), translation); - bool has_closure_id = !info()->closure().is_null() && - !info()->closure().is_identical_to(environment->closure()); - int closure_id = has_closure_id - ? DefineDeoptimizationLiteral(environment->closure()) - : Translation::kSelfLiteralId; - - switch (environment->frame_type()) { - case JS_FUNCTION: - translation->BeginJSFrame(environment->ast_id(), closure_id, height); - break; - case JS_CONSTRUCT: - translation->BeginConstructStubFrame(closure_id, translation_size); - break; - case JS_GETTER: - ASSERT(translation_size == 1); - ASSERT(height == 0); - translation->BeginGetterStubFrame(closure_id); - break; - case JS_SETTER: - ASSERT(translation_size == 2); - ASSERT(height == 0); - translation->BeginSetterStubFrame(closure_id); - break; - case STUB: - translation->BeginCompiledStubFrame(); - break; - case ARGUMENTS_ADAPTOR: - translation->BeginArgumentsAdaptorFrame(closure_id, translation_size); - break; - default: - UNREACHABLE(); - } - - int object_index = 0; - int dematerialized_index = 0; - for (int i = 0; i < translation_size; ++i) { - LOperand* value = environment->values()->at(i); - - AddToTranslation(environment, - translation, - value, - environment->HasTaggedValueAt(i), - environment->HasUint32ValueAt(i), - &object_index, - &dematerialized_index); - } -} - - -void LCodeGen::AddToTranslation(LEnvironment* environment, - Translation* translation, - LOperand* op, - bool is_tagged, - bool is_uint32, - int* object_index_pointer, - int* dematerialized_index_pointer) { - if (op == LEnvironment::materialization_marker()) { - int object_index = (*object_index_pointer)++; - if (environment->ObjectIsDuplicateAt(object_index)) { - int dupe_of = environment->ObjectDuplicateOfAt(object_index); - translation->DuplicateObject(dupe_of); - return; - } - int object_length = environment->ObjectLengthAt(object_index); - if (environment->ObjectIsArgumentsAt(object_index)) { - translation->BeginArgumentsObject(object_length); - } else { - translation->BeginCapturedObject(object_length); - } - int dematerialized_index = *dematerialized_index_pointer; - int env_offset = environment->translation_size() + dematerialized_index; - *dematerialized_index_pointer += object_length; - for (int i = 0; i < object_length; ++i) { - LOperand* value = environment->values()->at(env_offset + i); - AddToTranslation(environment, - translation, - value, - environment->HasTaggedValueAt(env_offset + i), - environment->HasUint32ValueAt(env_offset + i), - object_index_pointer, - dematerialized_index_pointer); - } - return; - } - - if (op->IsStackSlot()) { - if (is_tagged) { - translation->StoreStackSlot(op->index()); - } else if (is_uint32) { - translation->StoreUint32StackSlot(op->index()); - } else { - translation->StoreInt32StackSlot(op->index()); - } - } else if (op->IsDoubleStackSlot()) { - translation->StoreDoubleStackSlot(op->index()); - } else if (op->IsArgument()) { - ASSERT(is_tagged); - int src_index = GetStackSlotCount() + op->index(); - translation->StoreStackSlot(src_index); - } else if (op->IsRegister()) { - Register reg = ToRegister(op); - if (is_tagged) { - translation->StoreRegister(reg); - } else if (is_uint32) { - translation->StoreUint32Register(reg); - } else { - translation->StoreInt32Register(reg); - } - } else if (op->IsDoubleRegister()) { - DoubleRegister reg = ToDoubleRegister(op); - translation->StoreDoubleRegister(reg); - } else if (op->IsConstantOperand()) { - HConstant* constant = chunk()->LookupConstant(LConstantOperand::cast(op)); - int src_index = DefineDeoptimizationLiteral(constant->handle(isolate())); - translation->StoreLiteral(src_index); - } else { - UNREACHABLE(); - } -} - - -int LCodeGen::DefineDeoptimizationLiteral(Handle literal) { - int result = deoptimization_literals_.length(); - for (int i = 0; i < deoptimization_literals_.length(); ++i) { - if (deoptimization_literals_[i].is_identical_to(literal)) return i; - } - deoptimization_literals_.Add(literal, zone()); - return result; -} - - -void LCodeGen::RegisterEnvironmentForDeoptimization(LEnvironment* environment, - Safepoint::DeoptMode mode) { - if (!environment->HasBeenRegistered()) { - int frame_count = 0; - int jsframe_count = 0; - for (LEnvironment* e = environment; e != NULL; e = e->outer()) { - ++frame_count; - if (e->frame_type() == JS_FUNCTION) { - ++jsframe_count; - } - } - Translation translation(&translations_, frame_count, jsframe_count, zone()); - WriteTranslation(environment, &translation); - int deoptimization_index = deoptimizations_.length(); - int pc_offset = masm()->pc_offset(); - environment->Register(deoptimization_index, - translation.index(), - (mode == Safepoint::kLazyDeopt) ? pc_offset : -1); - deoptimizations_.Add(environment, zone()); - } -} - - -void LCodeGen::CallCode(Handle code, - RelocInfo::Mode mode, - LInstruction* instr) { - CallCodeGeneric(code, mode, instr, RECORD_SIMPLE_SAFEPOINT); -} - - -void LCodeGen::CallCodeGeneric(Handle code, - RelocInfo::Mode mode, - LInstruction* instr, - SafepointMode safepoint_mode) { - ASSERT(instr != NULL); - - Assembler::BlockConstPoolScope scope(masm_); - __ Call(code, mode); - RecordSafepointWithLazyDeopt(instr, safepoint_mode); - - if ((code->kind() == Code::BINARY_OP_IC) || - (code->kind() == Code::COMPARE_IC)) { - // Signal that we don't inline smi code before these stubs in the - // optimizing code generator. - InlineSmiCheckInfo::EmitNotInlined(masm()); - } -} - - -void LCodeGen::DoCallFunction(LCallFunction* instr) { - ASSERT(ToRegister(instr->context()).is(cp)); - ASSERT(ToRegister(instr->function()).Is(x1)); - ASSERT(ToRegister(instr->result()).Is(x0)); - - int arity = instr->arity(); - CallFunctionStub stub(arity, instr->hydrogen()->function_flags()); - CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr); -} - - -void LCodeGen::DoCallNew(LCallNew* instr) { - ASSERT(ToRegister(instr->context()).is(cp)); - ASSERT(instr->IsMarkedAsCall()); - ASSERT(ToRegister(instr->constructor()).is(x1)); - - __ Mov(x0, instr->arity()); - // No cell in x2 for construct type feedback in optimized code. - Handle undefined_value(isolate()->factory()->undefined_value()); - __ Mov(x2, Operand(undefined_value)); - - CallConstructStub stub(NO_CALL_FUNCTION_FLAGS); - CallCode(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL, instr); - - ASSERT(ToRegister(instr->result()).is(x0)); -} - - -void LCodeGen::DoCallNewArray(LCallNewArray* instr) { - ASSERT(instr->IsMarkedAsCall()); - ASSERT(ToRegister(instr->context()).is(cp)); - ASSERT(ToRegister(instr->constructor()).is(x1)); - - __ Mov(x0, Operand(instr->arity())); - __ Mov(x2, Operand(factory()->undefined_value())); - - ElementsKind kind = instr->hydrogen()->elements_kind(); - AllocationSiteOverrideMode override_mode = - (AllocationSite::GetMode(kind) == TRACK_ALLOCATION_SITE) - ? DISABLE_ALLOCATION_SITES - : DONT_OVERRIDE; - - if (instr->arity() == 0) { - ArrayNoArgumentConstructorStub stub(kind, override_mode); - CallCode(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL, instr); - } else if (instr->arity() == 1) { - Label done; - if (IsFastPackedElementsKind(kind)) { - Label packed_case; - - // We might need to create a holey array; look at the first argument. - __ Peek(x10, 0); - __ Cbz(x10, &packed_case); - - ElementsKind holey_kind = GetHoleyElementsKind(kind); - ArraySingleArgumentConstructorStub stub(holey_kind, override_mode); - CallCode(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL, instr); - __ B(&done); - __ Bind(&packed_case); - } - - ArraySingleArgumentConstructorStub stub(kind, override_mode); - CallCode(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL, instr); - __ Bind(&done); - } else { - ArrayNArgumentsConstructorStub stub(kind, override_mode); - CallCode(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL, instr); - } - - ASSERT(ToRegister(instr->result()).is(x0)); -} - - -void LCodeGen::CallRuntime(const Runtime::Function* function, - int num_arguments, - LInstruction* instr, - SaveFPRegsMode save_doubles) { - ASSERT(instr != NULL); - - __ CallRuntime(function, num_arguments, save_doubles); - - RecordSafepointWithLazyDeopt(instr, RECORD_SIMPLE_SAFEPOINT); -} - - -void LCodeGen::LoadContextFromDeferred(LOperand* context) { - if (context->IsRegister()) { - __ Mov(cp, ToRegister(context)); - } else if (context->IsStackSlot()) { - __ Ldr(cp, ToMemOperand(context)); - } else if (context->IsConstantOperand()) { - HConstant* constant = - chunk_->LookupConstant(LConstantOperand::cast(context)); - __ LoadHeapObject(cp, - Handle::cast(constant->handle(isolate()))); - } else { - UNREACHABLE(); - } -} - - -void LCodeGen::CallRuntimeFromDeferred(Runtime::FunctionId id, - int argc, - LInstruction* instr, - LOperand* context) { - LoadContextFromDeferred(context); - __ CallRuntimeSaveDoubles(id); - RecordSafepointWithRegisters( - instr->pointer_map(), argc, Safepoint::kNoLazyDeopt); -} - - -void LCodeGen::RecordAndWritePosition(int position) { - if (position == RelocInfo::kNoPosition) return; - masm()->positions_recorder()->RecordPosition(position); - masm()->positions_recorder()->WriteRecordedPositions(); -} - - -void LCodeGen::RecordSafepointWithLazyDeopt(LInstruction* instr, - SafepointMode safepoint_mode) { - if (safepoint_mode == RECORD_SIMPLE_SAFEPOINT) { - RecordSafepoint(instr->pointer_map(), Safepoint::kLazyDeopt); - } else { - ASSERT(safepoint_mode == RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS); - RecordSafepointWithRegisters( - instr->pointer_map(), 0, Safepoint::kLazyDeopt); - } -} - - -void LCodeGen::RecordSafepoint(LPointerMap* pointers, - Safepoint::Kind kind, - int arguments, - Safepoint::DeoptMode deopt_mode) { - ASSERT(expected_safepoint_kind_ == kind); - - const ZoneList* operands = pointers->GetNormalizedOperands(); - Safepoint safepoint = safepoints_.DefineSafepoint( - masm(), kind, arguments, deopt_mode); - - for (int i = 0; i < operands->length(); i++) { - LOperand* pointer = operands->at(i); - if (pointer->IsStackSlot()) { - safepoint.DefinePointerSlot(pointer->index(), zone()); - } else if (pointer->IsRegister() && (kind & Safepoint::kWithRegisters)) { - safepoint.DefinePointerRegister(ToRegister(pointer), zone()); - } - } - - if (kind & Safepoint::kWithRegisters) { - // Register cp always contains a pointer to the context. - safepoint.DefinePointerRegister(cp, zone()); - } -} - -void LCodeGen::RecordSafepoint(LPointerMap* pointers, - Safepoint::DeoptMode deopt_mode) { - RecordSafepoint(pointers, Safepoint::kSimple, 0, deopt_mode); -} - - -void LCodeGen::RecordSafepoint(Safepoint::DeoptMode deopt_mode) { - LPointerMap empty_pointers(zone()); - RecordSafepoint(&empty_pointers, deopt_mode); -} - - -void LCodeGen::RecordSafepointWithRegisters(LPointerMap* pointers, - int arguments, - Safepoint::DeoptMode deopt_mode) { - RecordSafepoint(pointers, Safepoint::kWithRegisters, arguments, deopt_mode); -} - - -void LCodeGen::RecordSafepointWithRegistersAndDoubles( - LPointerMap* pointers, int arguments, Safepoint::DeoptMode deopt_mode) { - RecordSafepoint( - pointers, Safepoint::kWithRegistersAndDoubles, arguments, deopt_mode); -} - - -bool LCodeGen::GenerateCode() { - LPhase phase("Z_Code generation", chunk()); - ASSERT(is_unused()); - status_ = GENERATING; - - // Open a frame scope to indicate that there is a frame on the stack. The - // NONE indicates that the scope shouldn't actually generate code to set up - // the frame (that is done in GeneratePrologue). - FrameScope frame_scope(masm_, StackFrame::NONE); - - return GeneratePrologue() && - GenerateBody() && - GenerateDeferredCode() && - GenerateDeoptJumpTable() && - GenerateSafepointTable(); -} - - -void LCodeGen::SaveCallerDoubles() { - ASSERT(info()->saves_caller_doubles()); - ASSERT(NeedsEagerFrame()); - Comment(";;; Save clobbered callee double registers"); - BitVector* doubles = chunk()->allocated_double_registers(); - BitVector::Iterator iterator(doubles); - int count = 0; - while (!iterator.Done()) { - // TODO(all): Is this supposed to save just the callee-saved doubles? It - // looks like it's saving all of them. - FPRegister value = FPRegister::FromAllocationIndex(iterator.Current()); - __ Poke(value, count * kDoubleSize); - iterator.Advance(); - count++; - } -} - - -void LCodeGen::RestoreCallerDoubles() { - ASSERT(info()->saves_caller_doubles()); - ASSERT(NeedsEagerFrame()); - Comment(";;; Restore clobbered callee double registers"); - BitVector* doubles = chunk()->allocated_double_registers(); - BitVector::Iterator iterator(doubles); - int count = 0; - while (!iterator.Done()) { - // TODO(all): Is this supposed to restore just the callee-saved doubles? It - // looks like it's restoring all of them. - FPRegister value = FPRegister::FromAllocationIndex(iterator.Current()); - __ Peek(value, count * kDoubleSize); - iterator.Advance(); - count++; - } -} - - -bool LCodeGen::GeneratePrologue() { - ASSERT(is_generating()); - - if (info()->IsOptimizing()) { - ProfileEntryHookStub::MaybeCallEntryHook(masm_); - - // TODO(all): Add support for stop_t FLAG in DEBUG mode. - - // Classic mode functions and builtins need to replace the receiver with the - // global proxy when called as functions (without an explicit receiver - // object). - if (info_->this_has_uses() && - info_->is_classic_mode() && - !info_->is_native()) { - Label ok; - int receiver_offset = info_->scope()->num_parameters() * kXRegSizeInBytes; - __ Peek(x10, receiver_offset); - __ JumpIfNotRoot(x10, Heap::kUndefinedValueRootIndex, &ok); - - __ Ldr(x10, GlobalObjectMemOperand()); - __ Ldr(x10, FieldMemOperand(x10, GlobalObject::kGlobalReceiverOffset)); - __ Poke(x10, receiver_offset); - - __ Bind(&ok); - } - } - - ASSERT(__ StackPointer().Is(jssp)); - info()->set_prologue_offset(masm_->pc_offset()); - if (NeedsEagerFrame()) { - __ Prologue(info()->IsStub() ? BUILD_STUB_FRAME : BUILD_FUNCTION_FRAME); - frame_is_built_ = true; - info_->AddNoFrameRange(0, masm_->pc_offset()); - } - - // Reserve space for the stack slots needed by the code. - int slots = GetStackSlotCount(); - if (slots > 0) { - __ Claim(slots, kPointerSize); - } - - if (info()->saves_caller_doubles()) { - SaveCallerDoubles(); - } - - // Allocate a local context if needed. - int heap_slots = info()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS; - if (heap_slots > 0) { - Comment(";;; Allocate local context"); - // Argument to NewContext is the function, which is in x1. - if (heap_slots <= FastNewContextStub::kMaximumSlots) { - FastNewContextStub stub(heap_slots); - __ CallStub(&stub); - } else { - __ Push(x1); - __ CallRuntime(Runtime::kNewFunctionContext, 1); - } - RecordSafepoint(Safepoint::kNoLazyDeopt); - // Context is returned in x0. It replaces the context passed to us. It's - // saved in the stack and kept live in cp. - __ Mov(cp, x0); - __ Str(x0, MemOperand(fp, StandardFrameConstants::kContextOffset)); - // Copy any necessary parameters into the context. - int num_parameters = scope()->num_parameters(); - for (int i = 0; i < num_parameters; i++) { - Variable* var = scope()->parameter(i); - if (var->IsContextSlot()) { - Register value = x0; - Register scratch = x3; - - int parameter_offset = StandardFrameConstants::kCallerSPOffset + - (num_parameters - 1 - i) * kPointerSize; - // Load parameter from stack. - __ Ldr(value, MemOperand(fp, parameter_offset)); - // Store it in the context. - MemOperand target = ContextMemOperand(cp, var->index()); - __ Str(value, target); - // Update the write barrier. This clobbers value and scratch. - __ RecordWriteContextSlot(cp, target.offset(), value, scratch, - GetLinkRegisterState(), kSaveFPRegs); - } - } - Comment(";;; End allocate local context"); - } - - // Trace the call. - if (FLAG_trace && info()->IsOptimizing()) { - // We have not executed any compiled code yet, so cp still holds the - // incoming context. - __ CallRuntime(Runtime::kTraceEnter, 0); - } - - return !is_aborted(); -} - - -void LCodeGen::GenerateOsrPrologue() { - // Generate the OSR entry prologue at the first unknown OSR value, or if there - // are none, at the OSR entrypoint instruction. - if (osr_pc_offset_ >= 0) return; - - osr_pc_offset_ = masm()->pc_offset(); - - // Adjust the frame size, subsuming the unoptimized frame into the - // optimized frame. - int slots = GetStackSlotCount() - graph()->osr()->UnoptimizedFrameSlots(); - ASSERT(slots >= 0); - __ Claim(slots); -} - - -bool LCodeGen::GenerateDeferredCode() { - ASSERT(is_generating()); - if (deferred_.length() > 0) { - for (int i = 0; !is_aborted() && (i < deferred_.length()); i++) { - LDeferredCode* code = deferred_[i]; - - HValue* value = - instructions_->at(code->instruction_index())->hydrogen_value(); - RecordAndWritePosition( - chunk()->graph()->SourcePositionToScriptPosition(value->position())); - - Comment(";;; <@%d,#%d> " - "-------------------- Deferred %s --------------------", - code->instruction_index(), - code->instr()->hydrogen_value()->id(), - code->instr()->Mnemonic()); - - __ Bind(code->entry()); - - if (NeedsDeferredFrame()) { - Comment(";;; Build frame"); - ASSERT(!frame_is_built_); - ASSERT(info()->IsStub()); - frame_is_built_ = true; - __ Push(lr, fp, cp); - __ Mov(fp, Operand(Smi::FromInt(StackFrame::STUB))); - __ Push(fp); - __ Add(fp, __ StackPointer(), - StandardFrameConstants::kFixedFrameSizeFromFp); - Comment(";;; Deferred code"); - } - - code->Generate(); - - if (NeedsDeferredFrame()) { - Comment(";;; Destroy frame"); - ASSERT(frame_is_built_); - __ Pop(xzr, cp, fp, lr); - frame_is_built_ = false; - } - - __ B(code->exit()); - } - } - - // Force constant pool emission at the end of the deferred code to make - // sure that no constant pools are emitted after deferred code because - // deferred code generation is the last step which generates code. The two - // following steps will only output data used by crakshaft. - masm()->CheckConstPool(true, false); - - return !is_aborted(); -} - - -bool LCodeGen::GenerateDeoptJumpTable() { - if (deopt_jump_table_.length() > 0) { - Comment(";;; -------------------- Jump table --------------------"); - } - Label table_start; - __ bind(&table_start); - Label needs_frame; - for (int i = 0; i < deopt_jump_table_.length(); i++) { - __ Bind(&deopt_jump_table_[i].label); - Address entry = deopt_jump_table_[i].address; - Deoptimizer::BailoutType type = deopt_jump_table_[i].bailout_type; - int id = Deoptimizer::GetDeoptimizationId(isolate(), entry, type); - if (id == Deoptimizer::kNotDeoptimizationEntry) { - Comment(";;; jump table entry %d.", i); - } else { - Comment(";;; jump table entry %d: deoptimization bailout %d.", i, id); - } - if (deopt_jump_table_[i].needs_frame) { - ASSERT(!info()->saves_caller_doubles()); - __ Mov(__ Tmp0(), Operand(ExternalReference::ForDeoptEntry(entry))); - if (needs_frame.is_bound()) { - __ B(&needs_frame); - } else { - __ Bind(&needs_frame); - // This variant of deopt can only be used with stubs. Since we don't - // have a function pointer to install in the stack frame that we're - // building, install a special marker there instead. - // TODO(jochen): Revisit the use of TmpX(). - ASSERT(info()->IsStub()); - __ Mov(__ Tmp1(), Operand(Smi::FromInt(StackFrame::STUB))); - __ Push(lr, fp, cp, __ Tmp1()); - __ Add(fp, __ StackPointer(), 2 * kPointerSize); - __ Call(__ Tmp0()); - } - } else { - if (info()->saves_caller_doubles()) { - ASSERT(info()->IsStub()); - RestoreCallerDoubles(); - } - __ Call(entry, RelocInfo::RUNTIME_ENTRY); - } - masm()->CheckConstPool(false, false); - } - - // Force constant pool emission at the end of the deopt jump table to make - // sure that no constant pools are emitted after. - masm()->CheckConstPool(true, false); - - // The deoptimization jump table is the last part of the instruction - // sequence. Mark the generated code as done unless we bailed out. - if (!is_aborted()) status_ = DONE; - return !is_aborted(); -} - - -bool LCodeGen::GenerateSafepointTable() { - ASSERT(is_done()); - safepoints_.Emit(masm(), GetStackSlotCount()); - return !is_aborted(); -} - - -void LCodeGen::FinishCode(Handle code) { - ASSERT(is_done()); - code->set_stack_slots(GetStackSlotCount()); - code->set_safepoint_table_offset(safepoints_.GetCodeOffset()); - RegisterDependentCodeForEmbeddedMaps(code); - PopulateDeoptimizationData(code); - info()->CommitDependencies(code); -} - - -void LCodeGen::Abort(BailoutReason reason) { - info()->set_bailout_reason(reason); - status_ = ABORTED; -} - - -void LCodeGen::PopulateDeoptimizationData(Handle code) { - int length = deoptimizations_.length(); - if (length == 0) return; - - Handle data = - factory()->NewDeoptimizationInputData(length, TENURED); - - Handle translations = - translations_.CreateByteArray(isolate()->factory()); - data->SetTranslationByteArray(*translations); - data->SetInlinedFunctionCount(Smi::FromInt(inlined_function_count_)); - data->SetOptimizationId(Smi::FromInt(info_->optimization_id())); - - Handle literals = - factory()->NewFixedArray(deoptimization_literals_.length(), TENURED); - { AllowDeferredHandleDereference copy_handles; - for (int i = 0; i < deoptimization_literals_.length(); i++) { - literals->set(i, *deoptimization_literals_[i]); - } - data->SetLiteralArray(*literals); - } - - data->SetOsrAstId(Smi::FromInt(info_->osr_ast_id().ToInt())); - data->SetOsrPcOffset(Smi::FromInt(osr_pc_offset_)); - - // Populate the deoptimization entries. - for (int i = 0; i < length; i++) { - LEnvironment* env = deoptimizations_[i]; - data->SetAstId(i, env->ast_id()); - data->SetTranslationIndex(i, Smi::FromInt(env->translation_index())); - data->SetArgumentsStackHeight(i, - Smi::FromInt(env->arguments_stack_height())); - data->SetPc(i, Smi::FromInt(env->pc_offset())); - } - - code->set_deoptimization_data(*data); -} - - -void LCodeGen::PopulateDeoptimizationLiteralsWithInlinedFunctions() { - ASSERT(deoptimization_literals_.length() == 0); - - const ZoneList >* inlined_closures = - chunk()->inlined_closures(); - - for (int i = 0, length = inlined_closures->length(); i < length; i++) { - DefineDeoptimizationLiteral(inlined_closures->at(i)); - } - - inlined_function_count_ = deoptimization_literals_.length(); -} - - -Deoptimizer::BailoutType LCodeGen::DeoptimizeHeader( - LEnvironment* environment, - Deoptimizer::BailoutType* override_bailout_type) { - RegisterEnvironmentForDeoptimization(environment, Safepoint::kNoLazyDeopt); - ASSERT(environment->HasBeenRegistered()); - ASSERT(info()->IsOptimizing() || info()->IsStub()); - int id = environment->deoptimization_index(); - Deoptimizer::BailoutType bailout_type = - info()->IsStub() ? Deoptimizer::LAZY : Deoptimizer::EAGER; - if (override_bailout_type) bailout_type = *override_bailout_type; - Address entry = - Deoptimizer::GetDeoptimizationEntry(isolate(), id, bailout_type); - - if (entry == NULL) { - Abort(kBailoutWasNotPrepared); - return bailout_type; - } - - if (FLAG_deopt_every_n_times != 0 && !info()->IsStub()) { - Label not_zero; - ExternalReference count = ExternalReference::stress_deopt_count(isolate()); - - __ Push(x0, x1, x2); - __ Mrs(x2, NZCV); - __ Mov(x0, Operand(count)); - __ Ldr(w1, MemOperand(x0)); - __ Subs(x1, x1, 1); - __ B(gt, ¬_zero); - __ Mov(w1, FLAG_deopt_every_n_times); - __ Str(w1, MemOperand(x0)); - __ Pop(x0, x1, x2); - ASSERT(frame_is_built_); - __ Call(entry, RelocInfo::RUNTIME_ENTRY); - __ Unreachable(); - - __ Bind(¬_zero); - __ Str(w1, MemOperand(x0)); - __ Msr(NZCV, x2); - __ Pop(x0, x1, x2); - } - - return bailout_type; -} - - -void LCodeGen::Deoptimize(LEnvironment* environment, - Deoptimizer::BailoutType bailout_type) { - ASSERT(environment->HasBeenRegistered()); - ASSERT(info()->IsOptimizing() || info()->IsStub()); - int id = environment->deoptimization_index(); - Address entry = - Deoptimizer::GetDeoptimizationEntry(isolate(), id, bailout_type); - - if (info()->ShouldTrapOnDeopt()) { - __ Debug("trap_on_deopt", __LINE__, BREAK); - } - - ASSERT(info()->IsStub() || frame_is_built_); - // Go through jump table if we need to build frame, or restore caller doubles. - if (frame_is_built_ && !info()->saves_caller_doubles()) { - __ Call(entry, RelocInfo::RUNTIME_ENTRY); - } else { - // We often have several deopts to the same entry, reuse the last - // jump entry if this is the case. - if (deopt_jump_table_.is_empty() || - (deopt_jump_table_.last().address != entry) || - (deopt_jump_table_.last().bailout_type != bailout_type) || - (deopt_jump_table_.last().needs_frame != !frame_is_built_)) { - Deoptimizer::JumpTableEntry table_entry(entry, - bailout_type, - !frame_is_built_); - deopt_jump_table_.Add(table_entry, zone()); - } - __ B(&deopt_jump_table_.last().label); - } -} - - -void LCodeGen::Deoptimize(LEnvironment* environment) { - Deoptimizer::BailoutType bailout_type = DeoptimizeHeader(environment, NULL); - Deoptimize(environment, bailout_type); -} - - -void LCodeGen::DeoptimizeIf(Condition cond, LEnvironment* environment) { - Label dont_deopt; - Deoptimizer::BailoutType bailout_type = DeoptimizeHeader(environment, NULL); - __ B(InvertCondition(cond), &dont_deopt); - Deoptimize(environment, bailout_type); - __ Bind(&dont_deopt); -} - - -void LCodeGen::DeoptimizeIfZero(Register rt, LEnvironment* environment) { - Label dont_deopt; - Deoptimizer::BailoutType bailout_type = DeoptimizeHeader(environment, NULL); - __ Cbnz(rt, &dont_deopt); - Deoptimize(environment, bailout_type); - __ Bind(&dont_deopt); -} - - -void LCodeGen::DeoptimizeIfNegative(Register rt, LEnvironment* environment) { - Label dont_deopt; - Deoptimizer::BailoutType bailout_type = DeoptimizeHeader(environment, NULL); - __ Tbz(rt, rt.Is64Bits() ? kXSignBit : kWSignBit, &dont_deopt); - Deoptimize(environment, bailout_type); - __ Bind(&dont_deopt); -} - - -void LCodeGen::DeoptimizeIfSmi(Register rt, - LEnvironment* environment) { - Label dont_deopt; - Deoptimizer::BailoutType bailout_type = DeoptimizeHeader(environment, NULL); - __ JumpIfNotSmi(rt, &dont_deopt); - Deoptimize(environment, bailout_type); - __ Bind(&dont_deopt); -} - - -void LCodeGen::DeoptimizeIfNotSmi(Register rt, LEnvironment* environment) { - Label dont_deopt; - Deoptimizer::BailoutType bailout_type = DeoptimizeHeader(environment, NULL); - __ JumpIfSmi(rt, &dont_deopt); - Deoptimize(environment, bailout_type); - __ Bind(&dont_deopt); -} - - -void LCodeGen::DeoptimizeIfRoot(Register rt, - Heap::RootListIndex index, - LEnvironment* environment) { - Label dont_deopt; - Deoptimizer::BailoutType bailout_type = DeoptimizeHeader(environment, NULL); - __ JumpIfNotRoot(rt, index, &dont_deopt); - Deoptimize(environment, bailout_type); - __ Bind(&dont_deopt); -} - - -void LCodeGen::DeoptimizeIfNotRoot(Register rt, - Heap::RootListIndex index, - LEnvironment* environment) { - Label dont_deopt; - Deoptimizer::BailoutType bailout_type = DeoptimizeHeader(environment, NULL); - __ JumpIfRoot(rt, index, &dont_deopt); - Deoptimize(environment, bailout_type); - __ Bind(&dont_deopt); -} - - -void LCodeGen::EnsureSpaceForLazyDeopt(int space_needed) { - if (!info()->IsStub()) { - // Ensure that we have enough space after the previous lazy-bailout - // instruction for patching the code here. - intptr_t current_pc = masm()->pc_offset(); - - if (current_pc < (last_lazy_deopt_pc_ + space_needed)) { - ptrdiff_t padding_size = last_lazy_deopt_pc_ + space_needed - current_pc; - ASSERT((padding_size % kInstructionSize) == 0); - InstructionAccurateScope instruction_accurate( - masm(), padding_size / kInstructionSize); - - while (padding_size > 0) { - __ nop(); - padding_size -= kInstructionSize; - } - } - } - last_lazy_deopt_pc_ = masm()->pc_offset(); -} - - -Register LCodeGen::ToRegister(LOperand* op) const { - // TODO(all): support zero register results, as ToRegister32. - ASSERT((op != NULL) && op->IsRegister()); - return Register::FromAllocationIndex(op->index()); -} - - -Register LCodeGen::ToRegister32(LOperand* op) const { - ASSERT(op != NULL); - if (op->IsConstantOperand()) { - // If this is a constant operand, the result must be the zero register. - ASSERT(ToInteger32(LConstantOperand::cast(op)) == 0); - return wzr; - } else { - return ToRegister(op).W(); - } -} - - -Smi* LCodeGen::ToSmi(LConstantOperand* op) const { - HConstant* constant = chunk_->LookupConstant(op); - return Smi::FromInt(constant->Integer32Value()); -} - - -DoubleRegister LCodeGen::ToDoubleRegister(LOperand* op) const { - ASSERT((op != NULL) && op->IsDoubleRegister()); - return DoubleRegister::FromAllocationIndex(op->index()); -} - - -Operand LCodeGen::ToOperand(LOperand* op) { - ASSERT(op != NULL); - if (op->IsConstantOperand()) { - LConstantOperand* const_op = LConstantOperand::cast(op); - HConstant* constant = chunk()->LookupConstant(const_op); - Representation r = chunk_->LookupLiteralRepresentation(const_op); - if (r.IsSmi()) { - ASSERT(constant->HasSmiValue()); - return Operand(Smi::FromInt(constant->Integer32Value())); - } else if (r.IsInteger32()) { - ASSERT(constant->HasInteger32Value()); - return Operand(constant->Integer32Value()); - } else if (r.IsDouble()) { - Abort(kToOperandUnsupportedDoubleImmediate); - } - ASSERT(r.IsTagged()); - return Operand(constant->handle(isolate())); - } else if (op->IsRegister()) { - return Operand(ToRegister(op)); - } else if (op->IsDoubleRegister()) { - Abort(kToOperandIsDoubleRegisterUnimplemented); - return Operand(0); - } - // Stack slots not implemented, use ToMemOperand instead. - UNREACHABLE(); - return Operand(0); -} - - -Operand LCodeGen::ToOperand32I(LOperand* op) { - return ToOperand32(op, SIGNED_INT32); -} - - -Operand LCodeGen::ToOperand32U(LOperand* op) { - return ToOperand32(op, UNSIGNED_INT32); -} - - -Operand LCodeGen::ToOperand32(LOperand* op, IntegerSignedness signedness) { - ASSERT(op != NULL); - if (op->IsRegister()) { - return Operand(ToRegister32(op)); - } else if (op->IsConstantOperand()) { - LConstantOperand* const_op = LConstantOperand::cast(op); - HConstant* constant = chunk()->LookupConstant(const_op); - Representation r = chunk_->LookupLiteralRepresentation(const_op); - if (r.IsInteger32()) { - ASSERT(constant->HasInteger32Value()); - return Operand(signedness == SIGNED_INT32 - ? constant->Integer32Value() - : static_cast(constant->Integer32Value())); - } else { - // Other constants not implemented. - Abort(kToOperand32UnsupportedImmediate); - } - } - // Other cases are not implemented. - UNREACHABLE(); - return Operand(0); -} - - -static ptrdiff_t ArgumentsOffsetWithoutFrame(ptrdiff_t index) { - ASSERT(index < 0); - return -(index + 1) * kPointerSize; -} - - -MemOperand LCodeGen::ToMemOperand(LOperand* op) const { - ASSERT(op != NULL); - ASSERT(!op->IsRegister()); - ASSERT(!op->IsDoubleRegister()); - ASSERT(op->IsStackSlot() || op->IsDoubleStackSlot()); - if (NeedsEagerFrame()) { - return MemOperand(fp, StackSlotOffset(op->index())); - } else { - // Retrieve parameter without eager stack-frame relative to the - // stack-pointer. - return MemOperand(masm()->StackPointer(), - ArgumentsOffsetWithoutFrame(op->index())); - } -} - - -Handle LCodeGen::ToHandle(LConstantOperand* op) const { - HConstant* constant = chunk_->LookupConstant(op); - ASSERT(chunk_->LookupLiteralRepresentation(op).IsSmiOrTagged()); - return constant->handle(isolate()); -} - - -bool LCodeGen::IsSmi(LConstantOperand* op) const { - return chunk_->LookupLiteralRepresentation(op).IsSmi(); -} - - -bool LCodeGen::IsInteger32Constant(LConstantOperand* op) const { - return op->IsConstantOperand() && - chunk_->LookupLiteralRepresentation(op).IsSmiOrInteger32(); -} - - -int32_t LCodeGen::ToInteger32(LConstantOperand* op) const { - HConstant* constant = chunk_->LookupConstant(op); - return constant->Integer32Value(); -} - - -double LCodeGen::ToDouble(LConstantOperand* op) const { - HConstant* constant = chunk_->LookupConstant(op); - ASSERT(constant->HasDoubleValue()); - return constant->DoubleValue(); -} - - -Condition LCodeGen::TokenToCondition(Token::Value op, bool is_unsigned) { - Condition cond = nv; - switch (op) { - case Token::EQ: - case Token::EQ_STRICT: - cond = eq; - break; - case Token::NE: - case Token::NE_STRICT: - cond = ne; - break; - case Token::LT: - cond = is_unsigned ? lo : lt; - break; - case Token::GT: - cond = is_unsigned ? hi : gt; - break; - case Token::LTE: - cond = is_unsigned ? ls : le; - break; - case Token::GTE: - cond = is_unsigned ? hs : ge; - break; - case Token::IN: - case Token::INSTANCEOF: - default: - UNREACHABLE(); - } - return cond; -} - - -template -void LCodeGen::EmitBranchGeneric(InstrType instr, - const BranchGenerator& branch) { - int left_block = instr->TrueDestination(chunk_); - int right_block = instr->FalseDestination(chunk_); - - int next_block = GetNextEmittedBlock(); - - if (right_block == left_block) { - EmitGoto(left_block); - } else if (left_block == next_block) { - branch.EmitInverted(chunk_->GetAssemblyLabel(right_block)); - } else if (right_block == next_block) { - branch.Emit(chunk_->GetAssemblyLabel(left_block)); - } else { - branch.Emit(chunk_->GetAssemblyLabel(left_block)); - __ B(chunk_->GetAssemblyLabel(right_block)); - } -} - - -template -void LCodeGen::EmitBranch(InstrType instr, Condition condition) { - ASSERT((condition != al) && (condition != nv)); - BranchOnCondition branch(this, condition); - EmitBranchGeneric(instr, branch); -} - - -template -void LCodeGen::EmitCompareAndBranch(InstrType instr, - Condition condition, - const Register& lhs, - const Operand& rhs) { - ASSERT((condition != al) && (condition != nv)); - CompareAndBranch branch(this, condition, lhs, rhs); - EmitBranchGeneric(instr, branch); -} - - -template -void LCodeGen::EmitTestAndBranch(InstrType instr, - Condition condition, - const Register& value, - uint64_t mask) { - ASSERT((condition != al) && (condition != nv)); - TestAndBranch branch(this, condition, value, mask); - EmitBranchGeneric(instr, branch); -} - - -template -void LCodeGen::EmitBranchIfNonZeroNumber(InstrType instr, - const FPRegister& value, - const FPRegister& scratch) { - BranchIfNonZeroNumber branch(this, value, scratch); - EmitBranchGeneric(instr, branch); -} - - -template -void LCodeGen::EmitBranchIfHeapNumber(InstrType instr, - const Register& value) { - BranchIfHeapNumber branch(this, value); - EmitBranchGeneric(instr, branch); -} - - -template -void LCodeGen::EmitBranchIfRoot(InstrType instr, - const Register& value, - Heap::RootListIndex index) { - BranchIfRoot branch(this, value, index); - EmitBranchGeneric(instr, branch); -} - - -void LCodeGen::DoGap(LGap* gap) { - for (int i = LGap::FIRST_INNER_POSITION; - i <= LGap::LAST_INNER_POSITION; - i++) { - LGap::InnerPosition inner_pos = static_cast(i); - LParallelMove* move = gap->GetParallelMove(inner_pos); - if (move != NULL) { - resolver_.Resolve(move); - } - } -} - - -void LCodeGen::DoAccessArgumentsAt(LAccessArgumentsAt* instr) { - // TODO(all): Try to improve this, like ARM r17925. - Register arguments = ToRegister(instr->arguments()); - Register result = ToRegister(instr->result()); - - if (instr->length()->IsConstantOperand() && - instr->index()->IsConstantOperand()) { - ASSERT(instr->temp() == NULL); - int index = ToInteger32(LConstantOperand::cast(instr->index())); - int length = ToInteger32(LConstantOperand::cast(instr->length())); - int offset = ((length - index) + 1) * kPointerSize; - __ Ldr(result, MemOperand(arguments, offset)); - } else { - ASSERT(instr->temp() != NULL); - Register temp = ToRegister32(instr->temp()); - Register length = ToRegister32(instr->length()); - Operand index = ToOperand32I(instr->index()); - // There are two words between the frame pointer and the last arguments. - // Subtracting from length accounts for only one, so we add one more. - __ Sub(temp, length, index); - __ Add(temp, temp, 1); - __ Ldr(result, MemOperand(arguments, temp, UXTW, kPointerSizeLog2)); - } -} - - -void LCodeGen::DoAddE(LAddE* instr) { - Register result = ToRegister(instr->result()); - Register left = ToRegister(instr->left()); - Operand right = (instr->right()->IsConstantOperand()) - ? ToInteger32(LConstantOperand::cast(instr->right())) - : Operand(ToRegister32(instr->right()), SXTW); - - ASSERT(!instr->hydrogen()->CheckFlag(HValue::kCanOverflow)); - __ Add(result, left, right); -} - - -void LCodeGen::DoAddI(LAddI* instr) { - bool can_overflow = instr->hydrogen()->CheckFlag(HValue::kCanOverflow); - Register result = ToRegister32(instr->result()); - Register left = ToRegister32(instr->left()); - Operand right = ToOperand32I(instr->right()); - if (can_overflow) { - __ Adds(result, left, right); - DeoptimizeIf(vs, instr->environment()); - } else { - __ Add(result, left, right); - } -} - - -void LCodeGen::DoAddS(LAddS* instr) { - bool can_overflow = instr->hydrogen()->CheckFlag(HValue::kCanOverflow); - Register result = ToRegister(instr->result()); - Register left = ToRegister(instr->left()); - Operand right = ToOperand(instr->right()); - if (can_overflow) { - __ Adds(result, left, right); - DeoptimizeIf(vs, instr->environment()); - } else { - __ Add(result, left, right); - } -} - - -void LCodeGen::DoAllocate(LAllocate* instr) { - class DeferredAllocate: public LDeferredCode { - public: - DeferredAllocate(LCodeGen* codegen, LAllocate* instr) - : LDeferredCode(codegen), instr_(instr) { } - virtual void Generate() { codegen()->DoDeferredAllocate(instr_); } - virtual LInstruction* instr() { return instr_; } - private: - LAllocate* instr_; - }; - - DeferredAllocate* deferred = new(zone()) DeferredAllocate(this, instr); - - Register result = ToRegister(instr->result()); - Register temp1 = ToRegister(instr->temp1()); - Register temp2 = ToRegister(instr->temp2()); - - // Allocate memory for the object. - AllocationFlags flags = TAG_OBJECT; - if (instr->hydrogen()->MustAllocateDoubleAligned()) { - flags = static_cast(flags | DOUBLE_ALIGNMENT); - } - - if (instr->hydrogen()->IsOldPointerSpaceAllocation()) { - ASSERT(!instr->hydrogen()->IsOldDataSpaceAllocation()); - ASSERT(!instr->hydrogen()->IsNewSpaceAllocation()); - flags = static_cast(flags | PRETENURE_OLD_POINTER_SPACE); - } else if (instr->hydrogen()->IsOldDataSpaceAllocation()) { - ASSERT(!instr->hydrogen()->IsNewSpaceAllocation()); - flags = static_cast(flags | PRETENURE_OLD_DATA_SPACE); - } - - if (instr->size()->IsConstantOperand()) { - int32_t size = ToInteger32(LConstantOperand::cast(instr->size())); - __ Allocate(size, result, temp1, temp2, deferred->entry(), flags); - } else { - Register size = ToRegister32(instr->size()); - __ Sxtw(size.X(), size); - __ Allocate(size.X(), result, temp1, temp2, deferred->entry(), flags); - } - - __ Bind(deferred->exit()); - - if (instr->hydrogen()->MustPrefillWithFiller()) { - if (instr->size()->IsConstantOperand()) { - int32_t size = ToInteger32(LConstantOperand::cast(instr->size())); - __ Mov(temp1, size - kPointerSize); - } else { - __ Sub(temp1.W(), ToRegister32(instr->size()), kPointerSize); - } - __ Sub(result, result, kHeapObjectTag); - - // TODO(jbramley): Optimize this loop using stp. - Label loop; - __ Bind(&loop); - __ Mov(temp2, Operand(isolate()->factory()->one_pointer_filler_map())); - __ Str(temp2, MemOperand(result, temp1)); - __ Subs(temp1, temp1, kPointerSize); - __ B(ge, &loop); - - __ Add(result, result, kHeapObjectTag); - } -} - - -void LCodeGen::DoDeferredAllocate(LAllocate* instr) { - // TODO(3095996): Get rid of this. For now, we need to make the - // result register contain a valid pointer because it is already - // contained in the register pointer map. - __ Mov(ToRegister(instr->result()), Operand(Smi::FromInt(0))); - - PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters); - // We're in a SafepointRegistersScope so we can use any scratch registers. - Register size = x0; - if (instr->size()->IsConstantOperand()) { - __ Mov(size, Operand(ToSmi(LConstantOperand::cast(instr->size())))); - } else { - __ SmiTag(size, ToRegister32(instr->size()).X()); - } - int flags = AllocateDoubleAlignFlag::encode( - instr->hydrogen()->MustAllocateDoubleAligned()); - if (instr->hydrogen()->IsOldPointerSpaceAllocation()) { - ASSERT(!instr->hydrogen()->IsOldDataSpaceAllocation()); - ASSERT(!instr->hydrogen()->IsNewSpaceAllocation()); - flags = AllocateTargetSpace::update(flags, OLD_POINTER_SPACE); - } else if (instr->hydrogen()->IsOldDataSpaceAllocation()) { - ASSERT(!instr->hydrogen()->IsNewSpaceAllocation()); - flags = AllocateTargetSpace::update(flags, OLD_DATA_SPACE); - } else { - flags = AllocateTargetSpace::update(flags, NEW_SPACE); - } - __ Mov(x10, Operand(Smi::FromInt(flags))); - __ Push(size, x10); - - CallRuntimeFromDeferred( - Runtime::kAllocateInTargetSpace, 2, instr, instr->context()); - __ StoreToSafepointRegisterSlot(x0, ToRegister(instr->result())); -} - - -void LCodeGen::DoApplyArguments(LApplyArguments* instr) { - Register receiver = ToRegister(instr->receiver()); - Register function = ToRegister(instr->function()); - Register length = ToRegister32(instr->length()); - - Register elements = ToRegister(instr->elements()); - Register scratch = x5; - ASSERT(receiver.Is(x0)); // Used for parameter count. - ASSERT(function.Is(x1)); // Required by InvokeFunction. - ASSERT(ToRegister(instr->result()).Is(x0)); - ASSERT(instr->IsMarkedAsCall()); - - // Copy the arguments to this function possibly from the - // adaptor frame below it. - const uint32_t kArgumentsLimit = 1 * KB; - __ Cmp(length, kArgumentsLimit); - DeoptimizeIf(hi, instr->environment()); - - // Push the receiver and use the register to keep the original - // number of arguments. - __ Push(receiver); - Register argc = receiver; - receiver = NoReg; - __ Sxtw(argc, length); - // The arguments are at a one pointer size offset from elements. - __ Add(elements, elements, 1 * kPointerSize); - - // Loop through the arguments pushing them onto the execution - // stack. - Label invoke, loop; - // length is a small non-negative integer, due to the test above. - __ Cbz(length, &invoke); - __ Bind(&loop); - __ Ldr(scratch, MemOperand(elements, length, SXTW, kPointerSizeLog2)); - __ Push(scratch); - __ Subs(length, length, 1); - __ B(ne, &loop); - - __ Bind(&invoke); - ASSERT(instr->HasPointerMap()); - LPointerMap* pointers = instr->pointer_map(); - SafepointGenerator safepoint_generator(this, pointers, Safepoint::kLazyDeopt); - // The number of arguments is stored in argc (receiver) which is x0, as - // expected by InvokeFunction. - ParameterCount actual(argc); - __ InvokeFunction(function, actual, CALL_FUNCTION, safepoint_generator); -} - - -void LCodeGen::DoArgumentsElements(LArgumentsElements* instr) { - Register result = ToRegister(instr->result()); - - if (instr->hydrogen()->from_inlined()) { - // When we are inside an inlined function, the arguments are the last things - // that have been pushed on the stack. Therefore the arguments array can be - // accessed directly from jssp. - // However in the normal case, it is accessed via fp but there are two words - // on the stack between fp and the arguments (the saved lr and fp) and the - // LAccessArgumentsAt implementation take that into account. - // In the inlined case we need to subtract the size of 2 words to jssp to - // get a pointer which will work well with LAccessArgumentsAt. - ASSERT(masm()->StackPointer().Is(jssp)); - __ Sub(result, jssp, 2 * kPointerSize); - } else { - ASSERT(instr->temp() != NULL); - Register previous_fp = ToRegister(instr->temp()); - - __ Ldr(previous_fp, - MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); - __ Ldr(result, - MemOperand(previous_fp, StandardFrameConstants::kContextOffset)); - __ Cmp(result, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); - __ Csel(result, fp, previous_fp, ne); - } -} - - -void LCodeGen::DoArgumentsLength(LArgumentsLength* instr) { - Register elements = ToRegister(instr->elements()); - Register result = ToRegister32(instr->result()); - Label done; - - // If no arguments adaptor frame the number of arguments is fixed. - __ Cmp(fp, elements); - __ Mov(result, scope()->num_parameters()); - __ B(eq, &done); - - // Arguments adaptor frame present. Get argument length from there. - __ Ldr(result.X(), MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); - __ Ldr(result, - UntagSmiMemOperand(result.X(), - ArgumentsAdaptorFrameConstants::kLengthOffset)); - - // Argument length is in result register. - __ Bind(&done); -} - - -void LCodeGen::DoArithmeticD(LArithmeticD* instr) { - DoubleRegister left = ToDoubleRegister(instr->left()); - DoubleRegister right = ToDoubleRegister(instr->right()); - DoubleRegister result = ToDoubleRegister(instr->result()); - - switch (instr->op()) { - case Token::ADD: __ Fadd(result, left, right); break; - case Token::SUB: __ Fsub(result, left, right); break; - case Token::MUL: __ Fmul(result, left, right); break; - case Token::DIV: __ Fdiv(result, left, right); break; - case Token::MOD: { - // The ECMA-262 remainder operator is the remainder from a truncating - // (round-towards-zero) division. Note that this differs from IEEE-754. - // - // TODO(jbramley): See if it's possible to do this inline, rather than by - // calling a helper function. With frintz (to produce the intermediate - // quotient) and fmsub (to calculate the remainder without loss of - // precision), it should be possible. However, we would need support for - // fdiv in round-towards-zero mode, and the A64 simulator doesn't support - // that yet. - ASSERT(left.Is(d0)); - ASSERT(right.Is(d1)); - __ CallCFunction( - ExternalReference::mod_two_doubles_operation(isolate()), - 0, 2); - ASSERT(result.Is(d0)); - break; - } - default: - UNREACHABLE(); - break; - } -} - - -void LCodeGen::DoArithmeticT(LArithmeticT* instr) { - ASSERT(ToRegister(instr->context()).is(cp)); - ASSERT(ToRegister(instr->left()).is(x1)); - ASSERT(ToRegister(instr->right()).is(x0)); - ASSERT(ToRegister(instr->result()).is(x0)); - - BinaryOpICStub stub(instr->op(), NO_OVERWRITE); - CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr); -} - - -void LCodeGen::DoBitI(LBitI* instr) { - Register result = ToRegister32(instr->result()); - Register left = ToRegister32(instr->left()); - Operand right = ToOperand32U(instr->right()); - - switch (instr->op()) { - case Token::BIT_AND: __ And(result, left, right); break; - case Token::BIT_OR: __ Orr(result, left, right); break; - case Token::BIT_XOR: __ Eor(result, left, right); break; - default: - UNREACHABLE(); - break; - } -} - - -void LCodeGen::DoBitS(LBitS* instr) { - Register result = ToRegister(instr->result()); - Register left = ToRegister(instr->left()); - Operand right = ToOperand(instr->right()); - - switch (instr->op()) { - case Token::BIT_AND: __ And(result, left, right); break; - case Token::BIT_OR: __ Orr(result, left, right); break; - case Token::BIT_XOR: __ Eor(result, left, right); break; - default: - UNREACHABLE(); - break; - } -} - - -void LCodeGen::ApplyCheckIf(Condition cc, LBoundsCheck* check) { - if (FLAG_debug_code && check->hydrogen()->skip_check()) { - __ Assert(InvertCondition(cc), kEliminatedBoundsCheckFailed); - } else { - DeoptimizeIf(cc, check->environment()); - } -} - - -void LCodeGen::DoBoundsCheck(LBoundsCheck *instr) { - if (instr->hydrogen()->skip_check()) return; - - ASSERT(instr->hydrogen()->length()->representation().IsInteger32()); - Register length = ToRegister32(instr->length()); - - if (instr->index()->IsConstantOperand()) { - int constant_index = - ToInteger32(LConstantOperand::cast(instr->index())); - - if (instr->hydrogen()->length()->representation().IsSmi()) { - __ Cmp(length, Operand(Smi::FromInt(constant_index))); - } else { - __ Cmp(length, Operand(constant_index)); - } - } else { - ASSERT(instr->hydrogen()->index()->representation().IsInteger32()); - __ Cmp(length, ToRegister32(instr->index())); - } - Condition condition = instr->hydrogen()->allow_equality() ? lo : ls; - ApplyCheckIf(condition, instr); -} - - -void LCodeGen::DoBranch(LBranch* instr) { - Representation r = instr->hydrogen()->value()->representation(); - Label* true_label = instr->TrueLabel(chunk_); - Label* false_label = instr->FalseLabel(chunk_); - - if (r.IsInteger32()) { - ASSERT(!info()->IsStub()); - EmitCompareAndBranch(instr, ne, ToRegister32(instr->value()), 0); - } else if (r.IsSmi()) { - ASSERT(!info()->IsStub()); - STATIC_ASSERT(kSmiTag == 0); - EmitCompareAndBranch(instr, ne, ToRegister(instr->value()), 0); - } else if (r.IsDouble()) { - DoubleRegister value = ToDoubleRegister(instr->value()); - // Test the double value. Zero and NaN are false. - EmitBranchIfNonZeroNumber(instr, value, double_scratch()); - } else { - ASSERT(r.IsTagged()); - Register value = ToRegister(instr->value()); - HType type = instr->hydrogen()->value()->type(); - - if (type.IsBoolean()) { - ASSERT(!info()->IsStub()); - __ CompareRoot(value, Heap::kTrueValueRootIndex); - EmitBranch(instr, eq); - } else if (type.IsSmi()) { - ASSERT(!info()->IsStub()); - EmitCompareAndBranch(instr, ne, value, Operand(Smi::FromInt(0))); - } else if (type.IsJSArray()) { - ASSERT(!info()->IsStub()); - EmitGoto(instr->TrueDestination(chunk())); - } else if (type.IsHeapNumber()) { - ASSERT(!info()->IsStub()); - __ Ldr(double_scratch(), FieldMemOperand(value, - HeapNumber::kValueOffset)); - // Test the double value. Zero and NaN are false. - EmitBranchIfNonZeroNumber(instr, double_scratch(), double_scratch()); - } else if (type.IsString()) { - ASSERT(!info()->IsStub()); - Register temp = ToRegister(instr->temp1()); - __ Ldr(temp, FieldMemOperand(value, String::kLengthOffset)); - EmitCompareAndBranch(instr, ne, temp, 0); - } else { - ToBooleanStub::Types expected = instr->hydrogen()->expected_input_types(); - // Avoid deopts in the case where we've never executed this path before. - if (expected.IsEmpty()) expected = ToBooleanStub::Types::Generic(); - - if (expected.Contains(ToBooleanStub::UNDEFINED)) { - // undefined -> false. - __ JumpIfRoot( - value, Heap::kUndefinedValueRootIndex, false_label); - } - - if (expected.Contains(ToBooleanStub::BOOLEAN)) { - // Boolean -> its value. - __ JumpIfRoot( - value, Heap::kTrueValueRootIndex, true_label); - __ JumpIfRoot( - value, Heap::kFalseValueRootIndex, false_label); - } - - if (expected.Contains(ToBooleanStub::NULL_TYPE)) { - // 'null' -> false. - __ JumpIfRoot( - value, Heap::kNullValueRootIndex, false_label); - } - - if (expected.Contains(ToBooleanStub::SMI)) { - // Smis: 0 -> false, all other -> true. - ASSERT(Smi::FromInt(0) == 0); - __ Cbz(value, false_label); - __ JumpIfSmi(value, true_label); - } else if (expected.NeedsMap()) { - // If we need a map later and have a smi, deopt. - DeoptimizeIfSmi(value, instr->environment()); - } - - Register map = NoReg; - Register scratch = NoReg; - - if (expected.NeedsMap()) { - ASSERT((instr->temp1() != NULL) && (instr->temp2() != NULL)); - map = ToRegister(instr->temp1()); - scratch = ToRegister(instr->temp2()); - - __ Ldr(map, FieldMemOperand(value, HeapObject::kMapOffset)); - - if (expected.CanBeUndetectable()) { - // Undetectable -> false. - __ Ldrb(scratch, FieldMemOperand(map, Map::kBitFieldOffset)); - __ TestAndBranchIfAnySet( - scratch, 1 << Map::kIsUndetectable, false_label); - } - } - - if (expected.Contains(ToBooleanStub::SPEC_OBJECT)) { - // spec object -> true. - __ CompareInstanceType(map, scratch, FIRST_SPEC_OBJECT_TYPE); - __ B(ge, true_label); - } - - if (expected.Contains(ToBooleanStub::STRING)) { - // String value -> false iff empty. - Label not_string; - __ CompareInstanceType(map, scratch, FIRST_NONSTRING_TYPE); - __ B(ge, ¬_string); - __ Ldr(scratch, FieldMemOperand(value, String::kLengthOffset)); - __ Cbz(scratch, false_label); - __ B(true_label); - __ Bind(¬_string); - } - - if (expected.Contains(ToBooleanStub::SYMBOL)) { - // Symbol value -> true. - __ CompareInstanceType(map, scratch, SYMBOL_TYPE); - __ B(eq, true_label); - } - - if (expected.Contains(ToBooleanStub::HEAP_NUMBER)) { - Label not_heap_number; - __ JumpIfNotRoot(map, Heap::kHeapNumberMapRootIndex, ¬_heap_number); - - __ Ldr(double_scratch(), - FieldMemOperand(value, HeapNumber::kValueOffset)); - __ Fcmp(double_scratch(), 0.0); - // If we got a NaN (overflow bit is set), jump to the false branch. - __ B(vs, false_label); - __ B(eq, false_label); - __ B(true_label); - __ Bind(¬_heap_number); - } - - if (!expected.IsGeneric()) { - // We've seen something for the first time -> deopt. - // This can only happen if we are not generic already. - Deoptimize(instr->environment()); - } - } - } -} - - -void LCodeGen::CallKnownFunction(Handle function, - int formal_parameter_count, - int arity, - LInstruction* instr, - Register function_reg) { - bool dont_adapt_arguments = - formal_parameter_count == SharedFunctionInfo::kDontAdaptArgumentsSentinel; - bool can_invoke_directly = - dont_adapt_arguments || formal_parameter_count == arity; - - // The function interface relies on the following register assignments. - ASSERT(function_reg.Is(x1) || function_reg.IsNone()); - Register arity_reg = x0; - - LPointerMap* pointers = instr->pointer_map(); - - // If necessary, load the function object. - if (function_reg.IsNone()) { - function_reg = x1; - __ LoadObject(function_reg, function); - } - - if (FLAG_debug_code) { - Label is_not_smi; - // Try to confirm that function_reg (x1) is a tagged pointer. - __ JumpIfNotSmi(function_reg, &is_not_smi); - __ Abort(kExpectedFunctionObject); - __ Bind(&is_not_smi); - } - - if (can_invoke_directly) { - // Change context. - __ Ldr(cp, FieldMemOperand(function_reg, JSFunction::kContextOffset)); - - // Set the arguments count if adaption is not needed. Assumes that x0 is - // available to write to at this point. - if (dont_adapt_arguments) { - __ Mov(arity_reg, arity); - } - - // Invoke function. - __ Ldr(x10, FieldMemOperand(function_reg, JSFunction::kCodeEntryOffset)); - __ Call(x10); - - // Set up deoptimization. - RecordSafepointWithLazyDeopt(instr, RECORD_SIMPLE_SAFEPOINT); - } else { - SafepointGenerator generator(this, pointers, Safepoint::kLazyDeopt); - ParameterCount count(arity); - ParameterCount expected(formal_parameter_count); - __ InvokeFunction(function_reg, expected, count, CALL_FUNCTION, generator); - } -} - - -void LCodeGen::DoCallWithDescriptor(LCallWithDescriptor* instr) { - ASSERT(instr->IsMarkedAsCall()); - ASSERT(ToRegister(instr->result()).Is(x0)); - - LPointerMap* pointers = instr->pointer_map(); - SafepointGenerator generator(this, pointers, Safepoint::kLazyDeopt); - - if (instr->target()->IsConstantOperand()) { - LConstantOperand* target = LConstantOperand::cast(instr->target()); - Handle code = Handle::cast(ToHandle(target)); - generator.BeforeCall(__ CallSize(code, RelocInfo::CODE_TARGET)); - // TODO(all): on ARM we use a call descriptor to specify a storage mode - // but on A64 we only have one storage mode so it isn't necessary. Check - // this understanding is correct. - __ Call(code, RelocInfo::CODE_TARGET, TypeFeedbackId::None()); - } else { - ASSERT(instr->target()->IsRegister()); - Register target = ToRegister(instr->target()); - generator.BeforeCall(__ CallSize(target)); - __ Add(target, target, Code::kHeaderSize - kHeapObjectTag); - __ Call(target); - } - generator.AfterCall(); -} - - -void LCodeGen::DoCallJSFunction(LCallJSFunction* instr) { - ASSERT(instr->IsMarkedAsCall()); - ASSERT(ToRegister(instr->function()).is(x1)); - - if (instr->hydrogen()->pass_argument_count()) { - __ Mov(x0, Operand(instr->arity())); - } - - // Change context. - __ Ldr(cp, FieldMemOperand(x1, JSFunction::kContextOffset)); - - // Load the code entry address - __ Ldr(x10, FieldMemOperand(x1, JSFunction::kCodeEntryOffset)); - __ Call(x10); - - RecordSafepointWithLazyDeopt(instr, RECORD_SIMPLE_SAFEPOINT); -} - - -void LCodeGen::DoCallRuntime(LCallRuntime* instr) { - CallRuntime(instr->function(), instr->arity(), instr); -} - - -void LCodeGen::DoCallStub(LCallStub* instr) { - ASSERT(ToRegister(instr->context()).is(cp)); - ASSERT(ToRegister(instr->result()).is(x0)); - switch (instr->hydrogen()->major_key()) { - case CodeStub::RegExpExec: { - RegExpExecStub stub; - CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr); - break; - } - case CodeStub::SubString: { - SubStringStub stub; - CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr); - break; - } - case CodeStub::StringCompare: { - StringCompareStub stub; - CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr); - break; - } - default: - UNREACHABLE(); - } -} - - -void LCodeGen::DoUnknownOSRValue(LUnknownOSRValue* instr) { - GenerateOsrPrologue(); -} - - -void LCodeGen::DoDeferredInstanceMigration(LCheckMaps* instr, Register object) { - Register temp = ToRegister(instr->temp()); - { - PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters); - __ Push(object); - __ Mov(cp, 0); - __ CallRuntimeSaveDoubles(Runtime::kTryMigrateInstance); - RecordSafepointWithRegisters( - instr->pointer_map(), 1, Safepoint::kNoLazyDeopt); - __ StoreToSafepointRegisterSlot(x0, temp); - } - DeoptimizeIfSmi(temp, instr->environment()); -} - - -void LCodeGen::DoCheckMaps(LCheckMaps* instr) { - class DeferredCheckMaps: public LDeferredCode { - public: - DeferredCheckMaps(LCodeGen* codegen, LCheckMaps* instr, Register object) - : LDeferredCode(codegen), instr_(instr), object_(object) { - SetExit(check_maps()); - } - virtual void Generate() { - codegen()->DoDeferredInstanceMigration(instr_, object_); - } - Label* check_maps() { return &check_maps_; } - virtual LInstruction* instr() { return instr_; } - private: - LCheckMaps* instr_; - Label check_maps_; - Register object_; - }; - - if (instr->hydrogen()->CanOmitMapChecks()) { - ASSERT(instr->value() == NULL); - ASSERT(instr->temp() == NULL); - return; - } - - Register object = ToRegister(instr->value()); - Register map_reg = ToRegister(instr->temp()); - - __ Ldr(map_reg, FieldMemOperand(object, HeapObject::kMapOffset)); - - DeferredCheckMaps* deferred = NULL; - if (instr->hydrogen()->has_migration_target()) { - deferred = new(zone()) DeferredCheckMaps(this, instr, object); - __ Bind(deferred->check_maps()); - } - - UniqueSet map_set = instr->hydrogen()->map_set(); - Label success; - for (int i = 0; i < map_set.size(); i++) { - Handle map = map_set.at(i).handle(); - __ CompareMap(map_reg, map, &success); - __ B(eq, &success); - } - - // We didn't match a map. - if (instr->hydrogen()->has_migration_target()) { - __ B(deferred->entry()); - } else { - Deoptimize(instr->environment()); - } - - __ Bind(&success); -} - - -void LCodeGen::DoCheckNonSmi(LCheckNonSmi* instr) { - if (!instr->hydrogen()->value()->IsHeapObject()) { - // TODO(all): Depending of how we chose to implement the deopt, if we could - // guarantee that we have a deopt handler reachable by a tbz instruction, - // we could use tbz here and produce less code to support this instruction. - DeoptimizeIfSmi(ToRegister(instr->value()), instr->environment()); - } -} - - -void LCodeGen::DoCheckSmi(LCheckSmi* instr) { - Register value = ToRegister(instr->value()); - ASSERT(!instr->result() || ToRegister(instr->result()).Is(value)); - // TODO(all): See DoCheckNonSmi for comments on use of tbz. - DeoptimizeIfNotSmi(value, instr->environment()); -} - - -void LCodeGen::DoCheckInstanceType(LCheckInstanceType* instr) { - Register input = ToRegister(instr->value()); - Register scratch = ToRegister(instr->temp()); - - __ Ldr(scratch, FieldMemOperand(input, HeapObject::kMapOffset)); - __ Ldrb(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset)); - - if (instr->hydrogen()->is_interval_check()) { - InstanceType first, last; - instr->hydrogen()->GetCheckInterval(&first, &last); - - __ Cmp(scratch, first); - if (first == last) { - // If there is only one type in the interval check for equality. - DeoptimizeIf(ne, instr->environment()); - } else if (last == LAST_TYPE) { - // We don't need to compare with the higher bound of the interval. - DeoptimizeIf(lo, instr->environment()); - } else { - // If we are below the lower bound, set the C flag and clear the Z flag - // to force a deopt. - __ Ccmp(scratch, last, CFlag, hs); - DeoptimizeIf(hi, instr->environment()); - } - } else { - uint8_t mask; - uint8_t tag; - instr->hydrogen()->GetCheckMaskAndTag(&mask, &tag); - - if (IsPowerOf2(mask)) { - ASSERT((tag == 0) || (tag == mask)); - // TODO(all): We might be able to use tbz/tbnz if we can guarantee that - // the deopt handler is reachable by a tbz instruction. - __ Tst(scratch, mask); - DeoptimizeIf(tag == 0 ? ne : eq, instr->environment()); - } else { - if (tag == 0) { - __ Tst(scratch, mask); - } else { - __ And(scratch, scratch, mask); - __ Cmp(scratch, tag); - } - DeoptimizeIf(ne, instr->environment()); - } - } -} - - -void LCodeGen::DoClampDToUint8(LClampDToUint8* instr) { - DoubleRegister input = ToDoubleRegister(instr->unclamped()); - Register result = ToRegister32(instr->result()); - __ ClampDoubleToUint8(result, input, double_scratch()); -} - - -void LCodeGen::DoClampIToUint8(LClampIToUint8* instr) { - Register input = ToRegister32(instr->unclamped()); - Register result = ToRegister32(instr->result()); - __ ClampInt32ToUint8(result, input); -} - - -void LCodeGen::DoClampTToUint8(LClampTToUint8* instr) { - Register input = ToRegister(instr->unclamped()); - Register result = ToRegister32(instr->result()); - Register scratch = ToRegister(instr->temp1()); - Label done; - - // Both smi and heap number cases are handled. - Label is_not_smi; - __ JumpIfNotSmi(input, &is_not_smi); - __ SmiUntag(result.X(), input); - __ ClampInt32ToUint8(result); - __ B(&done); - - __ Bind(&is_not_smi); - - // Check for heap number. - Label is_heap_number; - __ Ldr(scratch, FieldMemOperand(input, HeapObject::kMapOffset)); - __ JumpIfRoot(scratch, Heap::kHeapNumberMapRootIndex, &is_heap_number); - - // Check for undefined. Undefined is coverted to zero for clamping conversion. - DeoptimizeIfNotRoot(input, Heap::kUndefinedValueRootIndex, - instr->environment()); - __ Mov(result, 0); - __ B(&done); - - // Heap number case. - __ Bind(&is_heap_number); - DoubleRegister dbl_scratch = double_scratch(); - DoubleRegister dbl_scratch2 = ToDoubleRegister(instr->temp2()); - __ Ldr(dbl_scratch, FieldMemOperand(input, HeapNumber::kValueOffset)); - __ ClampDoubleToUint8(result, dbl_scratch, dbl_scratch2); - - __ Bind(&done); -} - - -void LCodeGen::DoClassOfTestAndBranch(LClassOfTestAndBranch* instr) { - Handle class_name = instr->hydrogen()->class_name(); - Label* true_label = instr->TrueLabel(chunk_); - Label* false_label = instr->FalseLabel(chunk_); - Register input = ToRegister(instr->value()); - Register scratch1 = ToRegister(instr->temp1()); - Register scratch2 = ToRegister(instr->temp2()); - - __ JumpIfSmi(input, false_label); - - Register map = scratch2; - if (class_name->IsUtf8EqualTo(CStrVector("Function"))) { - // Assuming the following assertions, we can use the same compares to test - // for both being a function type and being in the object type range. - STATIC_ASSERT(NUM_OF_CALLABLE_SPEC_OBJECT_TYPES == 2); - STATIC_ASSERT(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE == - FIRST_SPEC_OBJECT_TYPE + 1); - STATIC_ASSERT(LAST_NONCALLABLE_SPEC_OBJECT_TYPE == - LAST_SPEC_OBJECT_TYPE - 1); - STATIC_ASSERT(LAST_SPEC_OBJECT_TYPE == LAST_TYPE); - - // We expect CompareObjectType to load the object instance type in scratch1. - __ CompareObjectType(input, map, scratch1, FIRST_SPEC_OBJECT_TYPE); - __ B(lt, false_label); - __ B(eq, true_label); - __ Cmp(scratch1, LAST_SPEC_OBJECT_TYPE); - __ B(eq, true_label); - } else { - __ IsObjectJSObjectType(input, map, scratch1, false_label); - } - - // Now we are in the FIRST-LAST_NONCALLABLE_SPEC_OBJECT_TYPE range. - // Check if the constructor in the map is a function. - __ Ldr(scratch1, FieldMemOperand(map, Map::kConstructorOffset)); - - // Objects with a non-function constructor have class 'Object'. - if (class_name->IsUtf8EqualTo(CStrVector("Object"))) { - __ JumpIfNotObjectType( - scratch1, scratch2, scratch2, JS_FUNCTION_TYPE, true_label); - } else { - __ JumpIfNotObjectType( - scratch1, scratch2, scratch2, JS_FUNCTION_TYPE, false_label); - } - - // The constructor function is in scratch1. Get its instance class name. - __ Ldr(scratch1, - FieldMemOperand(scratch1, JSFunction::kSharedFunctionInfoOffset)); - __ Ldr(scratch1, - FieldMemOperand(scratch1, - SharedFunctionInfo::kInstanceClassNameOffset)); - - // The class name we are testing against is internalized since it's a literal. - // The name in the constructor is internalized because of the way the context - // is booted. This routine isn't expected to work for random API-created - // classes and it doesn't have to because you can't access it with natives - // syntax. Since both sides are internalized it is sufficient to use an - // identity comparison. - EmitCompareAndBranch(instr, eq, scratch1, Operand(class_name)); -} - - -void LCodeGen::DoCmpHoleAndBranchD(LCmpHoleAndBranchD* instr) { - ASSERT(instr->hydrogen()->representation().IsDouble()); - FPRegister object = ToDoubleRegister(instr->object()); - Register temp = ToRegister(instr->temp()); - - // If we don't have a NaN, we don't have the hole, so branch now to avoid the - // (relatively expensive) hole-NaN check. - __ Fcmp(object, object); - __ B(vc, instr->FalseLabel(chunk_)); - - // We have a NaN, but is it the hole? - __ Fmov(temp, object); - EmitCompareAndBranch(instr, eq, temp, kHoleNanInt64); -} - - -void LCodeGen::DoCmpHoleAndBranchT(LCmpHoleAndBranchT* instr) { - ASSERT(instr->hydrogen()->representation().IsTagged()); - Register object = ToRegister(instr->object()); - - EmitBranchIfRoot(instr, object, Heap::kTheHoleValueRootIndex); -} - - -void LCodeGen::DoCmpMapAndBranch(LCmpMapAndBranch* instr) { - Register value = ToRegister(instr->value()); - Register map = ToRegister(instr->temp()); - - __ Ldr(map, FieldMemOperand(value, HeapObject::kMapOffset)); - EmitCompareAndBranch(instr, eq, map, Operand(instr->map())); -} - - -void LCodeGen::DoCompareMinusZeroAndBranch(LCompareMinusZeroAndBranch* instr) { - Representation rep = instr->hydrogen()->value()->representation(); - ASSERT(!rep.IsInteger32()); - Register scratch = ToRegister(instr->temp()); - - if (rep.IsDouble()) { - __ JumpIfMinusZero(ToDoubleRegister(instr->value()), - instr->TrueLabel(chunk())); - } else { - Register value = ToRegister(instr->value()); - __ CheckMap(value, scratch, Heap::kHeapNumberMapRootIndex, - instr->FalseLabel(chunk()), DO_SMI_CHECK); - __ Ldr(double_scratch(), FieldMemOperand(value, HeapNumber::kValueOffset)); - __ JumpIfMinusZero(double_scratch(), instr->TrueLabel(chunk())); - } - EmitGoto(instr->FalseDestination(chunk())); -} - - -void LCodeGen::DoCompareNumericAndBranch(LCompareNumericAndBranch* instr) { - LOperand* left = instr->left(); - LOperand* right = instr->right(); - Condition cond = TokenToCondition(instr->op(), false); - - if (left->IsConstantOperand() && right->IsConstantOperand()) { - // We can statically evaluate the comparison. - double left_val = ToDouble(LConstantOperand::cast(left)); - double right_val = ToDouble(LConstantOperand::cast(right)); - int next_block = EvalComparison(instr->op(), left_val, right_val) ? - instr->TrueDestination(chunk_) : instr->FalseDestination(chunk_); - EmitGoto(next_block); - } else { - if (instr->is_double()) { - if (right->IsConstantOperand()) { - __ Fcmp(ToDoubleRegister(left), - ToDouble(LConstantOperand::cast(right))); - } else if (left->IsConstantOperand()) { - // Transpose the operands and reverse the condition. - __ Fcmp(ToDoubleRegister(right), - ToDouble(LConstantOperand::cast(left))); - cond = ReverseConditionForCmp(cond); - } else { - __ Fcmp(ToDoubleRegister(left), ToDoubleRegister(right)); - } - - // If a NaN is involved, i.e. the result is unordered (V set), - // jump to false block label. - __ B(vs, instr->FalseLabel(chunk_)); - EmitBranch(instr, cond); - } else { - if (instr->hydrogen_value()->representation().IsInteger32()) { - if (right->IsConstantOperand()) { - EmitCompareAndBranch(instr, - cond, - ToRegister32(left), - ToOperand32I(right)); - } else { - // Transpose the operands and reverse the condition. - EmitCompareAndBranch(instr, - ReverseConditionForCmp(cond), - ToRegister32(right), - ToOperand32I(left)); - } - } else { - ASSERT(instr->hydrogen_value()->representation().IsSmi()); - if (right->IsConstantOperand()) { - int32_t value = ToInteger32(LConstantOperand::cast(right)); - EmitCompareAndBranch(instr, - cond, - ToRegister(left), - Operand(Smi::FromInt(value))); - } else if (left->IsConstantOperand()) { - // Transpose the operands and reverse the condition. - int32_t value = ToInteger32(LConstantOperand::cast(left)); - EmitCompareAndBranch(instr, - ReverseConditionForCmp(cond), - ToRegister(right), - Operand(Smi::FromInt(value))); - } else { - EmitCompareAndBranch(instr, - cond, - ToRegister(left), - ToRegister(right)); - } - } - } - } -} - - -void LCodeGen::DoCmpObjectEqAndBranch(LCmpObjectEqAndBranch* instr) { - Register left = ToRegister(instr->left()); - Register right = ToRegister(instr->right()); - EmitCompareAndBranch(instr, eq, left, right); -} - - -void LCodeGen::DoCmpT(LCmpT* instr) { - ASSERT(ToRegister(instr->context()).is(cp)); - Token::Value op = instr->op(); - Condition cond = TokenToCondition(op, false); - - ASSERT(ToRegister(instr->left()).Is(x1)); - ASSERT(ToRegister(instr->right()).Is(x0)); - Handle ic = CompareIC::GetUninitialized(isolate(), op); - CallCode(ic, RelocInfo::CODE_TARGET, instr); - // Signal that we don't inline smi code before this stub. - InlineSmiCheckInfo::EmitNotInlined(masm()); - - // Return true or false depending on CompareIC result. - // This instruction is marked as call. We can clobber any register. - ASSERT(instr->IsMarkedAsCall()); - __ LoadTrueFalseRoots(x1, x2); - __ Cmp(x0, 0); - __ Csel(ToRegister(instr->result()), x1, x2, cond); -} - - -void LCodeGen::DoConstantD(LConstantD* instr) { - ASSERT(instr->result()->IsDoubleRegister()); - DoubleRegister result = ToDoubleRegister(instr->result()); - __ Fmov(result, instr->value()); -} - - -void LCodeGen::DoConstantE(LConstantE* instr) { - __ Mov(ToRegister(instr->result()), Operand(instr->value())); -} - - -void LCodeGen::DoConstantI(LConstantI* instr) { - ASSERT(is_int32(instr->value())); - // Cast the value here to ensure that the value isn't sign extended by the - // implicit Operand constructor. - __ Mov(ToRegister32(instr->result()), static_cast(instr->value())); -} - - -void LCodeGen::DoConstantS(LConstantS* instr) { - __ Mov(ToRegister(instr->result()), Operand(instr->value())); -} - - -void LCodeGen::DoConstantT(LConstantT* instr) { - Handle value = instr->value(isolate()); - AllowDeferredHandleDereference smi_check; - __ LoadObject(ToRegister(instr->result()), value); -} - - -void LCodeGen::DoContext(LContext* instr) { - // If there is a non-return use, the context must be moved to a register. - Register result = ToRegister(instr->result()); - if (info()->IsOptimizing()) { - __ Ldr(result, MemOperand(fp, StandardFrameConstants::kContextOffset)); - } else { - // If there is no frame, the context must be in cp. - ASSERT(result.is(cp)); - } -} - - -void LCodeGen::DoCheckValue(LCheckValue* instr) { - Register reg = ToRegister(instr->value()); - Handle object = instr->hydrogen()->object().handle(); - AllowDeferredHandleDereference smi_check; - if (isolate()->heap()->InNewSpace(*object)) { - Register temp = ToRegister(instr->temp()); - Handle cell = isolate()->factory()->NewCell(object); - __ Mov(temp, Operand(Handle(cell))); - __ Ldr(temp, FieldMemOperand(temp, Cell::kValueOffset)); - __ Cmp(reg, temp); - } else { - __ Cmp(reg, Operand(object)); - } - DeoptimizeIf(ne, instr->environment()); -} - - -void LCodeGen::DoLazyBailout(LLazyBailout* instr) { - EnsureSpaceForLazyDeopt(Deoptimizer::patch_size()); - ASSERT(instr->HasEnvironment()); - LEnvironment* env = instr->environment(); - RegisterEnvironmentForDeoptimization(env, Safepoint::kLazyDeopt); - safepoints_.RecordLazyDeoptimizationIndex(env->deoptimization_index()); -} - - -void LCodeGen::DoDateField(LDateField* instr) { - Register object = ToRegister(instr->date()); - Register result = ToRegister(instr->result()); - Register temp1 = x10; - Register temp2 = x11; - Smi* index = instr->index(); - Label runtime, done, deopt, obj_ok; - - ASSERT(object.is(result) && object.Is(x0)); - ASSERT(instr->IsMarkedAsCall()); - - __ JumpIfSmi(object, &deopt); - __ CompareObjectType(object, temp1, temp1, JS_DATE_TYPE); - __ B(eq, &obj_ok); - - __ Bind(&deopt); - Deoptimize(instr->environment()); - - __ Bind(&obj_ok); - if (index->value() == 0) { - __ Ldr(result, FieldMemOperand(object, JSDate::kValueOffset)); - } else { - if (index->value() < JSDate::kFirstUncachedField) { - ExternalReference stamp = ExternalReference::date_cache_stamp(isolate()); - __ Mov(temp1, Operand(stamp)); - __ Ldr(temp1, MemOperand(temp1)); - __ Ldr(temp2, FieldMemOperand(object, JSDate::kCacheStampOffset)); - __ Cmp(temp1, temp2); - __ B(ne, &runtime); - __ Ldr(result, FieldMemOperand(object, JSDate::kValueOffset + - kPointerSize * index->value())); - __ B(&done); - } - - __ Bind(&runtime); - __ Mov(x1, Operand(index)); - __ CallCFunction(ExternalReference::get_date_field_function(isolate()), 2); - } - - __ Bind(&done); -} - - -void LCodeGen::DoDeoptimize(LDeoptimize* instr) { - Deoptimizer::BailoutType type = instr->hydrogen()->type(); - // TODO(danno): Stubs expect all deopts to be lazy for historical reasons (the - // needed return address), even though the implementation of LAZY and EAGER is - // now identical. When LAZY is eventually completely folded into EAGER, remove - // the special case below. - if (info()->IsStub() && (type == Deoptimizer::EAGER)) { - type = Deoptimizer::LAZY; - } - - Comment(";;; deoptimize: %s", instr->hydrogen()->reason()); - DeoptimizeHeader(instr->environment(), &type); - Deoptimize(instr->environment(), type); -} - - -void LCodeGen::DoDivI(LDivI* instr) { - if (!instr->is_flooring() && instr->hydrogen()->RightIsPowerOf2()) { - HDiv* hdiv = instr->hydrogen(); - Register dividend = ToRegister32(instr->left()); - int32_t divisor = hdiv->right()->GetInteger32Constant(); - Register result = ToRegister32(instr->result()); - ASSERT(!result.is(dividend)); - - // Check for (0 / -x) that will produce negative zero. - if (hdiv->left()->RangeCanInclude(0) && divisor < 0 && - hdiv->CheckFlag(HValue::kBailoutOnMinusZero)) { - __ Cmp(dividend, 0); - DeoptimizeIf(eq, instr->environment()); - } - // Check for (kMinInt / -1). - if (hdiv->left()->RangeCanInclude(kMinInt) && divisor == -1 && - hdiv->CheckFlag(HValue::kCanOverflow)) { - __ Cmp(dividend, kMinInt); - DeoptimizeIf(eq, instr->environment()); - } - // Deoptimize if remainder will not be 0. - if (!hdiv->CheckFlag(HInstruction::kAllUsesTruncatingToInt32) && - Abs(divisor) != 1) { - __ Tst(dividend, Abs(divisor) - 1); - DeoptimizeIf(ne, instr->environment()); - } - if (divisor == -1) { // Nice shortcut, not needed for correctness. - __ Neg(result, dividend); - return; - } - int32_t shift = WhichPowerOf2(Abs(divisor)); - if (shift == 0) { - __ Mov(result, dividend); - } else if (shift == 1) { - __ Add(result, dividend, Operand(dividend, LSR, 31)); - } else { - __ Mov(result, Operand(dividend, ASR, 31)); - __ Add(result, dividend, Operand(result, LSR, 32 - shift)); - } - if (shift > 0) __ Mov(result, Operand(result, ASR, shift)); - if (divisor < 0) __ Neg(result, result); - return; - } - - Register dividend = ToRegister32(instr->left()); - Register divisor = ToRegister32(instr->right()); - Register result = ToRegister32(instr->result()); - HValue* hdiv = instr->hydrogen_value(); - - // Issue the division first, and then check for any deopt cases whilst the - // result is computed. - __ Sdiv(result, dividend, divisor); - - if (hdiv->CheckFlag(HInstruction::kAllUsesTruncatingToInt32)) { - ASSERT_EQ(NULL, instr->temp()); - return; - } - - Label deopt; - // Check for x / 0. - if (hdiv->CheckFlag(HValue::kCanBeDivByZero)) { - __ Cbz(divisor, &deopt); - } - - // Check for (0 / -x) as that will produce negative zero. - if (hdiv->CheckFlag(HValue::kBailoutOnMinusZero)) { - __ Cmp(divisor, 0); - - // If the divisor < 0 (mi), compare the dividend, and deopt if it is - // zero, ie. zero dividend with negative divisor deopts. - // If the divisor >= 0 (pl, the opposite of mi) set the flags to - // condition ne, so we don't deopt, ie. positive divisor doesn't deopt. - __ Ccmp(dividend, 0, NoFlag, mi); - __ B(eq, &deopt); - } - - // Check for (kMinInt / -1). - if (hdiv->CheckFlag(HValue::kCanOverflow)) { - // Test dividend for kMinInt by subtracting one (cmp) and checking for - // overflow. - __ Cmp(dividend, 1); - // If overflow is set, ie. dividend = kMinInt, compare the divisor with - // -1. If overflow is clear, set the flags for condition ne, as the - // dividend isn't -1, and thus we shouldn't deopt. - __ Ccmp(divisor, -1, NoFlag, vs); - __ B(eq, &deopt); - } - - // Compute remainder and deopt if it's not zero. - Register remainder = ToRegister32(instr->temp()); - __ Msub(remainder, result, divisor, dividend); - __ Cbnz(remainder, &deopt); - - Label div_ok; - __ B(&div_ok); - __ Bind(&deopt); - Deoptimize(instr->environment()); - __ Bind(&div_ok); -} - - -void LCodeGen::DoDoubleToIntOrSmi(LDoubleToIntOrSmi* instr) { - DoubleRegister input = ToDoubleRegister(instr->value()); - Register result = ToRegister32(instr->result()); - Label done, deopt; - - if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { - __ JumpIfMinusZero(input, &deopt); - } - - __ TryConvertDoubleToInt32(result, input, double_scratch(), &done); - __ Bind(&deopt); - Deoptimize(instr->environment()); - __ Bind(&done); - - if (instr->tag_result()) { - __ SmiTag(result.X()); - } -} - - -void LCodeGen::DoDrop(LDrop* instr) { - __ Drop(instr->count()); -} - - -void LCodeGen::DoDummy(LDummy* instr) { - // Nothing to see here, move on! -} - - -void LCodeGen::DoDummyUse(LDummyUse* instr) { - // Nothing to see here, move on! -} - - -void LCodeGen::DoFunctionLiteral(LFunctionLiteral* instr) { - ASSERT(ToRegister(instr->context()).is(cp)); - // FunctionLiteral instruction is marked as call, we can trash any register. - ASSERT(instr->IsMarkedAsCall()); - - // Use the fast case closure allocation code that allocates in new - // space for nested functions that don't need literals cloning. - bool pretenure = instr->hydrogen()->pretenure(); - if (!pretenure && instr->hydrogen()->has_no_literals()) { - FastNewClosureStub stub(instr->hydrogen()->language_mode(), - instr->hydrogen()->is_generator()); - __ Mov(x2, Operand(instr->hydrogen()->shared_info())); - CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr); - } else { - __ Mov(x2, Operand(instr->hydrogen()->shared_info())); - __ Mov(x1, Operand(pretenure ? factory()->true_value() - : factory()->false_value())); - __ Push(cp, x2, x1); - CallRuntime(Runtime::kNewClosure, 3, instr); - } -} - - -void LCodeGen::DoForInCacheArray(LForInCacheArray* instr) { - Register map = ToRegister(instr->map()); - Register result = ToRegister(instr->result()); - Label load_cache, done; - - __ EnumLengthUntagged(result, map); - __ Cbnz(result, &load_cache); - - __ Mov(result, Operand(isolate()->factory()->empty_fixed_array())); - __ B(&done); - - __ Bind(&load_cache); - __ LoadInstanceDescriptors(map, result); - __ Ldr(result, FieldMemOperand(result, DescriptorArray::kEnumCacheOffset)); - __ Ldr(result, FieldMemOperand(result, FixedArray::SizeFor(instr->idx()))); - DeoptimizeIfZero(result, instr->environment()); - - __ Bind(&done); -} - - -void LCodeGen::DoForInPrepareMap(LForInPrepareMap* instr) { - Register object = ToRegister(instr->object()); - Register null_value = x5; - - ASSERT(instr->IsMarkedAsCall()); - ASSERT(object.Is(x0)); - - Label deopt; - - __ JumpIfRoot(object, Heap::kUndefinedValueRootIndex, &deopt); - - __ LoadRoot(null_value, Heap::kNullValueRootIndex); - __ Cmp(object, null_value); - __ B(eq, &deopt); - - __ JumpIfSmi(object, &deopt); - - STATIC_ASSERT(FIRST_JS_PROXY_TYPE == FIRST_SPEC_OBJECT_TYPE); - __ CompareObjectType(object, x1, x1, LAST_JS_PROXY_TYPE); - __ B(le, &deopt); - - Label use_cache, call_runtime; - __ CheckEnumCache(object, null_value, x1, x2, x3, x4, &call_runtime); - - __ Ldr(object, FieldMemOperand(object, HeapObject::kMapOffset)); - __ B(&use_cache); - - __ Bind(&deopt); - Deoptimize(instr->environment()); - - // Get the set of properties to enumerate. - __ Bind(&call_runtime); - __ Push(object); - CallRuntime(Runtime::kGetPropertyNamesFast, 1, instr); - - __ Ldr(x1, FieldMemOperand(object, HeapObject::kMapOffset)); - __ JumpIfNotRoot(x1, Heap::kMetaMapRootIndex, &deopt); - - __ Bind(&use_cache); -} - - -void LCodeGen::DoGetCachedArrayIndex(LGetCachedArrayIndex* instr) { - Register input = ToRegister(instr->value()); - Register result = ToRegister(instr->result()); - - __ AssertString(input); - - // Assert that we can use a W register load to get the hash. - ASSERT((String::kHashShift + String::kArrayIndexValueBits) < kWRegSize); - __ Ldr(result.W(), FieldMemOperand(input, String::kHashFieldOffset)); - __ IndexFromHash(result, result); -} - - -void LCodeGen::EmitGoto(int block) { - // Do not emit jump if we are emitting a goto to the next block. - if (!IsNextEmittedBlock(block)) { - __ B(chunk_->GetAssemblyLabel(LookupDestination(block))); - } -} - - -void LCodeGen::DoGoto(LGoto* instr) { - EmitGoto(instr->block_id()); -} - - -void LCodeGen::DoHasCachedArrayIndexAndBranch( - LHasCachedArrayIndexAndBranch* instr) { - Register input = ToRegister(instr->value()); - Register temp = ToRegister32(instr->temp()); - - // Assert that the cache status bits fit in a W register. - ASSERT(is_uint32(String::kContainsCachedArrayIndexMask)); - __ Ldr(temp, FieldMemOperand(input, String::kHashFieldOffset)); - __ Tst(temp, String::kContainsCachedArrayIndexMask); - EmitBranch(instr, eq); -} - - -// HHasInstanceTypeAndBranch instruction is built with an interval of type -// to test but is only used in very restricted ways. The only possible kinds -// of intervals are: -// - [ FIRST_TYPE, instr->to() ] -// - [ instr->form(), LAST_TYPE ] -// - instr->from() == instr->to() -// -// These kinds of intervals can be check with only one compare instruction -// providing the correct value and test condition are used. -// -// TestType() will return the value to use in the compare instruction and -// BranchCondition() will return the condition to use depending on the kind -// of interval actually specified in the instruction. -static InstanceType TestType(HHasInstanceTypeAndBranch* instr) { - InstanceType from = instr->from(); - InstanceType to = instr->to(); - if (from == FIRST_TYPE) return to; - ASSERT((from == to) || (to == LAST_TYPE)); - return from; -} - - -// See comment above TestType function for what this function does. -static Condition BranchCondition(HHasInstanceTypeAndBranch* instr) { - InstanceType from = instr->from(); - InstanceType to = instr->to(); - if (from == to) return eq; - if (to == LAST_TYPE) return hs; - if (from == FIRST_TYPE) return ls; - UNREACHABLE(); - return eq; -} - - -void LCodeGen::DoHasInstanceTypeAndBranch(LHasInstanceTypeAndBranch* instr) { - Register input = ToRegister(instr->value()); - Register scratch = ToRegister(instr->temp()); - - if (!instr->hydrogen()->value()->IsHeapObject()) { - __ JumpIfSmi(input, instr->FalseLabel(chunk_)); - } - __ CompareObjectType(input, scratch, scratch, TestType(instr->hydrogen())); - EmitBranch(instr, BranchCondition(instr->hydrogen())); -} - - -void LCodeGen::DoInnerAllocatedObject(LInnerAllocatedObject* instr) { - Register result = ToRegister(instr->result()); - Register base = ToRegister(instr->base_object()); - if (instr->offset()->IsConstantOperand()) { - __ Add(result, base, ToOperand32I(instr->offset())); - } else { - __ Add(result, base, Operand(ToRegister32(instr->offset()), SXTW)); - } -} - - -void LCodeGen::DoInstanceOf(LInstanceOf* instr) { - ASSERT(ToRegister(instr->context()).is(cp)); - // Assert that the arguments are in the registers expected by InstanceofStub. - ASSERT(ToRegister(instr->left()).Is(InstanceofStub::left())); - ASSERT(ToRegister(instr->right()).Is(InstanceofStub::right())); - - InstanceofStub stub(InstanceofStub::kArgsInRegisters); - CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr); - - // InstanceofStub returns a result in x0: - // 0 => not an instance - // smi 1 => instance. - __ Cmp(x0, 0); - __ LoadTrueFalseRoots(x0, x1); - __ Csel(x0, x0, x1, eq); -} - - -void LCodeGen::DoInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr) { - class DeferredInstanceOfKnownGlobal: public LDeferredCode { - public: - DeferredInstanceOfKnownGlobal(LCodeGen* codegen, - LInstanceOfKnownGlobal* instr) - : LDeferredCode(codegen), instr_(instr) { } - virtual void Generate() { - codegen()->DoDeferredInstanceOfKnownGlobal(instr_); - } - virtual LInstruction* instr() { return instr_; } - private: - LInstanceOfKnownGlobal* instr_; - }; - - DeferredInstanceOfKnownGlobal* deferred = - new(zone()) DeferredInstanceOfKnownGlobal(this, instr); - - Label map_check, return_false, cache_miss, done; - Register object = ToRegister(instr->value()); - Register result = ToRegister(instr->result()); - // x4 is expected in the associated deferred code and stub. - Register map_check_site = x4; - Register map = x5; - - // This instruction is marked as call. We can clobber any register. - ASSERT(instr->IsMarkedAsCall()); - - // We must take into account that object is in x11. - ASSERT(object.Is(x11)); - Register scratch = x10; - - // A Smi is not instance of anything. - __ JumpIfSmi(object, &return_false); - - // This is the inlined call site instanceof cache. The two occurences of the - // hole value will be patched to the last map/result pair generated by the - // instanceof stub. - __ Ldr(map, FieldMemOperand(object, HeapObject::kMapOffset)); - { - // Below we use Factory::the_hole_value() on purpose instead of loading from - // the root array to force relocation and later be able to patch with a - // custom value. - InstructionAccurateScope scope(masm(), 5); - __ bind(&map_check); - // Will be patched with the cached map. - Handle cell = factory()->NewCell(factory()->the_hole_value()); - __ LoadRelocated(scratch, Operand(Handle(cell))); - __ ldr(scratch, FieldMemOperand(scratch, PropertyCell::kValueOffset)); - __ cmp(map, Operand(scratch)); - __ b(&cache_miss, ne); - // The address of this instruction is computed relative to the map check - // above, so check the size of the code generated. - ASSERT(masm()->InstructionsGeneratedSince(&map_check) == 4); - // Will be patched with the cached result. - __ LoadRelocated(result, Operand(factory()->the_hole_value())); - } - __ B(&done); - - // The inlined call site cache did not match. - // Check null and string before calling the deferred code. - __ Bind(&cache_miss); - // Compute the address of the map check. It must not be clobbered until the - // InstanceOfStub has used it. - __ Adr(map_check_site, &map_check); - // Null is not instance of anything. - __ JumpIfRoot(object, Heap::kNullValueRootIndex, &return_false); - - // String values are not instances of anything. - // Return false if the object is a string. Otherwise, jump to the deferred - // code. - // Note that we can't jump directly to deferred code from - // IsObjectJSStringType, because it uses tbz for the jump and the deferred - // code can be out of range. - __ IsObjectJSStringType(object, scratch, NULL, &return_false); - __ B(deferred->entry()); - - __ Bind(&return_false); - __ LoadRoot(result, Heap::kFalseValueRootIndex); - - // Here result is either true or false. - __ Bind(deferred->exit()); - __ Bind(&done); -} - - -void LCodeGen::DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr) { - Register result = ToRegister(instr->result()); - ASSERT(result.Is(x0)); // InstanceofStub returns its result in x0. - InstanceofStub::Flags flags = InstanceofStub::kNoFlags; - flags = static_cast( - flags | InstanceofStub::kArgsInRegisters); - flags = static_cast( - flags | InstanceofStub::kReturnTrueFalseObject); - flags = static_cast( - flags | InstanceofStub::kCallSiteInlineCheck); - - PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters); - LoadContextFromDeferred(instr->context()); - - // Prepare InstanceofStub arguments. - ASSERT(ToRegister(instr->value()).Is(InstanceofStub::left())); - __ LoadObject(InstanceofStub::right(), instr->function()); - - InstanceofStub stub(flags); - CallCodeGeneric(stub.GetCode(isolate()), - RelocInfo::CODE_TARGET, - instr, - RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS); - LEnvironment* env = instr->GetDeferredLazyDeoptimizationEnvironment(); - safepoints_.RecordLazyDeoptimizationIndex(env->deoptimization_index()); - - // Put the result value into the result register slot. - __ StoreToSafepointRegisterSlot(result, result); -} - - -void LCodeGen::DoInstructionGap(LInstructionGap* instr) { - DoGap(instr); -} - - -void LCodeGen::DoInteger32ToDouble(LInteger32ToDouble* instr) { - Register value = ToRegister32(instr->value()); - DoubleRegister result = ToDoubleRegister(instr->result()); - __ Scvtf(result, value); -} - - -void LCodeGen::DoInteger32ToSmi(LInteger32ToSmi* instr) { - // A64 smis can represent all Integer32 values, so this cannot deoptimize. - ASSERT(!instr->hydrogen()->value()->HasRange() || - instr->hydrogen()->value()->range()->IsInSmiRange()); - - Register value = ToRegister32(instr->value()); - Register result = ToRegister(instr->result()); - __ SmiTag(result, value.X()); -} - - -void LCodeGen::DoInvokeFunction(LInvokeFunction* instr) { - ASSERT(ToRegister(instr->context()).is(cp)); - // The function is required to be in x1. - ASSERT(ToRegister(instr->function()).is(x1)); - ASSERT(instr->HasPointerMap()); - - Handle known_function = instr->hydrogen()->known_function(); - if (known_function.is_null()) { - LPointerMap* pointers = instr->pointer_map(); - SafepointGenerator generator(this, pointers, Safepoint::kLazyDeopt); - ParameterCount count(instr->arity()); - __ InvokeFunction(x1, count, CALL_FUNCTION, generator); - } else { - CallKnownFunction(known_function, - instr->hydrogen()->formal_parameter_count(), - instr->arity(), - instr, - x1); - } -} - - -void LCodeGen::DoIsConstructCallAndBranch(LIsConstructCallAndBranch* instr) { - Register temp1 = ToRegister(instr->temp1()); - Register temp2 = ToRegister(instr->temp2()); - - // Get the frame pointer for the calling frame. - __ Ldr(temp1, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); - - // Skip the arguments adaptor frame if it exists. - Label check_frame_marker; - __ Ldr(temp2, MemOperand(temp1, StandardFrameConstants::kContextOffset)); - __ Cmp(temp2, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); - __ B(ne, &check_frame_marker); - __ Ldr(temp1, MemOperand(temp1, StandardFrameConstants::kCallerFPOffset)); - - // Check the marker in the calling frame. - __ Bind(&check_frame_marker); - __ Ldr(temp1, MemOperand(temp1, StandardFrameConstants::kMarkerOffset)); - - EmitCompareAndBranch( - instr, eq, temp1, Operand(Smi::FromInt(StackFrame::CONSTRUCT))); -} - - -void LCodeGen::DoIsObjectAndBranch(LIsObjectAndBranch* instr) { - Label* is_object = instr->TrueLabel(chunk_); - Label* is_not_object = instr->FalseLabel(chunk_); - Register value = ToRegister(instr->value()); - Register map = ToRegister(instr->temp1()); - Register scratch = ToRegister(instr->temp2()); - - __ JumpIfSmi(value, is_not_object); - __ JumpIfRoot(value, Heap::kNullValueRootIndex, is_object); - - __ Ldr(map, FieldMemOperand(value, HeapObject::kMapOffset)); - - // Check for undetectable objects. - __ Ldrb(scratch, FieldMemOperand(map, Map::kBitFieldOffset)); - __ TestAndBranchIfAnySet(scratch, 1 << Map::kIsUndetectable, is_not_object); - - // Check that instance type is in object type range. - __ IsInstanceJSObjectType(map, scratch, NULL); - // Flags have been updated by IsInstanceJSObjectType. We can now test the - // flags for "le" condition to check if the object's type is a valid - // JS object type. - EmitBranch(instr, le); -} - - -Condition LCodeGen::EmitIsString(Register input, - Register temp1, - Label* is_not_string, - SmiCheck check_needed = INLINE_SMI_CHECK) { - if (check_needed == INLINE_SMI_CHECK) { - __ JumpIfSmi(input, is_not_string); - } - __ CompareObjectType(input, temp1, temp1, FIRST_NONSTRING_TYPE); - - return lt; -} - - -void LCodeGen::DoIsStringAndBranch(LIsStringAndBranch* instr) { - Register val = ToRegister(instr->value()); - Register scratch = ToRegister(instr->temp()); - - SmiCheck check_needed = - instr->hydrogen()->value()->IsHeapObject() - ? OMIT_SMI_CHECK : INLINE_SMI_CHECK; - Condition true_cond = - EmitIsString(val, scratch, instr->FalseLabel(chunk_), check_needed); - - EmitBranch(instr, true_cond); -} - - -void LCodeGen::DoIsSmiAndBranch(LIsSmiAndBranch* instr) { - Register value = ToRegister(instr->value()); - STATIC_ASSERT(kSmiTag == 0); - EmitTestAndBranch(instr, eq, value, kSmiTagMask); -} - - -void LCodeGen::DoIsUndetectableAndBranch(LIsUndetectableAndBranch* instr) { - Register input = ToRegister(instr->value()); - Register temp = ToRegister(instr->temp()); - - if (!instr->hydrogen()->value()->IsHeapObject()) { - __ JumpIfSmi(input, instr->FalseLabel(chunk_)); - } - __ Ldr(temp, FieldMemOperand(input, HeapObject::kMapOffset)); - __ Ldrb(temp, FieldMemOperand(temp, Map::kBitFieldOffset)); - - EmitTestAndBranch(instr, ne, temp, 1 << Map::kIsUndetectable); -} - - -static const char* LabelType(LLabel* label) { - if (label->is_loop_header()) return " (loop header)"; - if (label->is_osr_entry()) return " (OSR entry)"; - return ""; -} - - -void LCodeGen::DoLabel(LLabel* label) { - Comment(";;; <@%d,#%d> -------------------- B%d%s --------------------", - current_instruction_, - label->hydrogen_value()->id(), - label->block_id(), - LabelType(label)); - - __ Bind(label->label()); - current_block_ = label->block_id(); - DoGap(label); -} - - -void LCodeGen::DoLoadContextSlot(LLoadContextSlot* instr) { - Register context = ToRegister(instr->context()); - Register result = ToRegister(instr->result()); - __ Ldr(result, ContextMemOperand(context, instr->slot_index())); - if (instr->hydrogen()->RequiresHoleCheck()) { - if (instr->hydrogen()->DeoptimizesOnHole()) { - DeoptimizeIfRoot(result, Heap::kTheHoleValueRootIndex, - instr->environment()); - } else { - Label not_the_hole; - __ JumpIfNotRoot(result, Heap::kTheHoleValueRootIndex, ¬_the_hole); - __ LoadRoot(result, Heap::kUndefinedValueRootIndex); - __ Bind(¬_the_hole); - } - } -} - - -void LCodeGen::DoLoadFunctionPrototype(LLoadFunctionPrototype* instr) { - Register function = ToRegister(instr->function()); - Register result = ToRegister(instr->result()); - Register temp = ToRegister(instr->temp()); - Label deopt; - - // Check that the function really is a function. Leaves map in the result - // register. - __ JumpIfNotObjectType(function, result, temp, JS_FUNCTION_TYPE, &deopt); - - // Make sure that the function has an instance prototype. - Label non_instance; - __ Ldrb(temp, FieldMemOperand(result, Map::kBitFieldOffset)); - __ Tbnz(temp, Map::kHasNonInstancePrototype, &non_instance); - - // Get the prototype or initial map from the function. - __ Ldr(result, FieldMemOperand(function, - JSFunction::kPrototypeOrInitialMapOffset)); - - // Check that the function has a prototype or an initial map. - __ JumpIfRoot(result, Heap::kTheHoleValueRootIndex, &deopt); - - // If the function does not have an initial map, we're done. - Label done; - __ CompareObjectType(result, temp, temp, MAP_TYPE); - __ B(ne, &done); - - // Get the prototype from the initial map. - __ Ldr(result, FieldMemOperand(result, Map::kPrototypeOffset)); - __ B(&done); - - // Non-instance prototype: fetch prototype from constructor field in initial - // map. - __ Bind(&non_instance); - __ Ldr(result, FieldMemOperand(result, Map::kConstructorOffset)); - __ B(&done); - - // Deoptimize case. - __ Bind(&deopt); - Deoptimize(instr->environment()); - - // All done. - __ Bind(&done); -} - - -void LCodeGen::DoLoadGlobalCell(LLoadGlobalCell* instr) { - Register result = ToRegister(instr->result()); - __ Mov(result, Operand(Handle(instr->hydrogen()->cell().handle()))); - __ Ldr(result, FieldMemOperand(result, Cell::kValueOffset)); - if (instr->hydrogen()->RequiresHoleCheck()) { - DeoptimizeIfRoot( - result, Heap::kTheHoleValueRootIndex, instr->environment()); - } -} - - -void LCodeGen::DoLoadGlobalGeneric(LLoadGlobalGeneric* instr) { - ASSERT(ToRegister(instr->context()).is(cp)); - ASSERT(ToRegister(instr->global_object()).Is(x0)); - ASSERT(ToRegister(instr->result()).Is(x0)); - __ Mov(x2, Operand(instr->name())); - ContextualMode mode = instr->for_typeof() ? NOT_CONTEXTUAL : CONTEXTUAL; - Handle ic = LoadIC::initialize_stub(isolate(), mode); - CallCode(ic, RelocInfo::CODE_TARGET, instr); -} - - -MemOperand LCodeGen::PrepareKeyedExternalArrayOperand( - Register key, - Register base, - Register scratch, - bool key_is_smi, - bool key_is_constant, - int constant_key, - ElementsKind elements_kind, - int additional_index) { - int element_size_shift = ElementsKindToShiftSize(elements_kind); - int additional_offset = IsFixedTypedArrayElementsKind(elements_kind) - ? FixedTypedArrayBase::kDataOffset - kHeapObjectTag - : 0; - - if (key_is_constant) { - int base_offset = ((constant_key + additional_index) << element_size_shift); - return MemOperand(base, base_offset + additional_offset); - } - - if (additional_index == 0) { - if (key_is_smi) { - // Key is smi: untag, and scale by element size. - __ Add(scratch, base, Operand::UntagSmiAndScale(key, element_size_shift)); - return MemOperand(scratch, additional_offset); - } else { - // Key is not smi, and element size is not byte: scale by element size. - if (additional_offset == 0) { - return MemOperand(base, key, SXTW, element_size_shift); - } else { - __ Add(scratch, base, Operand(key, SXTW, element_size_shift)); - return MemOperand(scratch, additional_offset); - } - } - } else { - // TODO(all): Try to combine these cases a bit more intelligently. - if (additional_offset == 0) { - if (key_is_smi) { - __ SmiUntag(scratch, key); - __ Add(scratch.W(), scratch.W(), additional_index); - } else { - __ Add(scratch.W(), key.W(), additional_index); - } - return MemOperand(base, scratch, LSL, element_size_shift); - } else { - if (key_is_smi) { - __ Add(scratch, base, - Operand::UntagSmiAndScale(key, element_size_shift)); - } else { - __ Add(scratch, base, Operand(key, SXTW, element_size_shift)); - } - return MemOperand( - scratch, - (additional_index << element_size_shift) + additional_offset); - } - } -} - - -void LCodeGen::DoLoadKeyedExternal(LLoadKeyedExternal* instr) { - Register ext_ptr = ToRegister(instr->elements()); - Register scratch; - ElementsKind elements_kind = instr->elements_kind(); - - bool key_is_smi = instr->hydrogen()->key()->representation().IsSmi(); - bool key_is_constant = instr->key()->IsConstantOperand(); - Register key = no_reg; - int constant_key = 0; - if (key_is_constant) { - ASSERT(instr->temp() == NULL); - constant_key = ToInteger32(LConstantOperand::cast(instr->key())); - if (constant_key & 0xf0000000) { - Abort(kArrayIndexConstantValueTooBig); - } - } else { - scratch = ToRegister(instr->temp()); - key = ToRegister(instr->key()); - } - - MemOperand mem_op = - PrepareKeyedExternalArrayOperand(key, ext_ptr, scratch, key_is_smi, - key_is_constant, constant_key, - elements_kind, - instr->additional_index()); - - if ((elements_kind == EXTERNAL_FLOAT32_ELEMENTS) || - (elements_kind == FLOAT32_ELEMENTS)) { - DoubleRegister result = ToDoubleRegister(instr->result()); - __ Ldr(result.S(), mem_op); - __ Fcvt(result, result.S()); - } else if ((elements_kind == EXTERNAL_FLOAT64_ELEMENTS) || - (elements_kind == FLOAT64_ELEMENTS)) { - DoubleRegister result = ToDoubleRegister(instr->result()); - __ Ldr(result, mem_op); - } else { - Register result = ToRegister(instr->result()); - - switch (elements_kind) { - case EXTERNAL_INT8_ELEMENTS: - case INT8_ELEMENTS: - __ Ldrsb(result, mem_op); - break; - case EXTERNAL_UINT8_CLAMPED_ELEMENTS: - case EXTERNAL_UINT8_ELEMENTS: - case UINT8_ELEMENTS: - case UINT8_CLAMPED_ELEMENTS: - __ Ldrb(result, mem_op); - break; - case EXTERNAL_INT16_ELEMENTS: - case INT16_ELEMENTS: - __ Ldrsh(result, mem_op); - break; - case EXTERNAL_UINT16_ELEMENTS: - case UINT16_ELEMENTS: - __ Ldrh(result, mem_op); - break; - case EXTERNAL_INT32_ELEMENTS: - case INT32_ELEMENTS: - __ Ldrsw(result, mem_op); - break; - case EXTERNAL_UINT32_ELEMENTS: - case UINT32_ELEMENTS: - __ Ldr(result.W(), mem_op); - if (!instr->hydrogen()->CheckFlag(HInstruction::kUint32)) { - // Deopt if value > 0x80000000. - __ Tst(result, 0xFFFFFFFF80000000); - DeoptimizeIf(ne, instr->environment()); - } - break; - case FLOAT32_ELEMENTS: - case FLOAT64_ELEMENTS: - case EXTERNAL_FLOAT32_ELEMENTS: - case EXTERNAL_FLOAT64_ELEMENTS: - case FAST_HOLEY_DOUBLE_ELEMENTS: - case FAST_HOLEY_ELEMENTS: - case FAST_HOLEY_SMI_ELEMENTS: - case FAST_DOUBLE_ELEMENTS: - case FAST_ELEMENTS: - case FAST_SMI_ELEMENTS: - case DICTIONARY_ELEMENTS: - case NON_STRICT_ARGUMENTS_ELEMENTS: - UNREACHABLE(); - break; - } - } -} - - -void LCodeGen::CalcKeyedArrayBaseRegister(Register base, - Register elements, - Register key, - bool key_is_tagged, - ElementsKind elements_kind) { - int element_size_shift = ElementsKindToShiftSize(elements_kind); - - // Even though the HLoad/StoreKeyed instructions force the input - // representation for the key to be an integer, the input gets replaced during - // bounds check elimination with the index argument to the bounds check, which - // can be tagged, so that case must be handled here, too. - if (key_is_tagged) { - __ Add(base, elements, Operand::UntagSmiAndScale(key, element_size_shift)); - } else { - // Sign extend key because it could be a 32-bit negative value or contain - // garbage in the top 32-bits. The address computation happens in 64-bit. - ASSERT((element_size_shift >= 0) && (element_size_shift <= 4)); - __ Add(base, elements, Operand(key, SXTW, element_size_shift)); - } -} - - -void LCodeGen::DoLoadKeyedFixedDouble(LLoadKeyedFixedDouble* instr) { - Register elements = ToRegister(instr->elements()); - DoubleRegister result = ToDoubleRegister(instr->result()); - Register load_base; - int offset = 0; - - if (instr->key()->IsConstantOperand()) { - ASSERT(instr->hydrogen()->RequiresHoleCheck() || - (instr->temp() == NULL)); - - int constant_key = ToInteger32(LConstantOperand::cast(instr->key())); - if (constant_key & 0xf0000000) { - Abort(kArrayIndexConstantValueTooBig); - } - offset = FixedDoubleArray::OffsetOfElementAt(constant_key + - instr->additional_index()); - load_base = elements; - } else { - load_base = ToRegister(instr->temp()); - Register key = ToRegister(instr->key()); - bool key_is_tagged = instr->hydrogen()->key()->representation().IsSmi(); - CalcKeyedArrayBaseRegister(load_base, elements, key, key_is_tagged, - instr->hydrogen()->elements_kind()); - offset = FixedDoubleArray::OffsetOfElementAt(instr->additional_index()); - } - __ Ldr(result, FieldMemOperand(load_base, offset)); - - if (instr->hydrogen()->RequiresHoleCheck()) { - Register scratch = ToRegister(instr->temp()); - - // TODO(all): Is it faster to reload this value to an integer register, or - // move from fp to integer? - __ Fmov(scratch, result); - __ Cmp(scratch, kHoleNanInt64); - DeoptimizeIf(eq, instr->environment()); - } -} - - -void LCodeGen::DoLoadKeyedFixed(LLoadKeyedFixed* instr) { - Register elements = ToRegister(instr->elements()); - Register result = ToRegister(instr->result()); - Register load_base; - int offset = 0; - - if (instr->key()->IsConstantOperand()) { - ASSERT(instr->temp() == NULL); - LConstantOperand* const_operand = LConstantOperand::cast(instr->key()); - offset = FixedArray::OffsetOfElementAt(ToInteger32(const_operand) + - instr->additional_index()); - load_base = elements; - } else { - load_base = ToRegister(instr->temp()); - Register key = ToRegister(instr->key()); - bool key_is_tagged = instr->hydrogen()->key()->representation().IsSmi(); - CalcKeyedArrayBaseRegister(load_base, elements, key, key_is_tagged, - instr->hydrogen()->elements_kind()); - offset = FixedArray::OffsetOfElementAt(instr->additional_index()); - } - Representation representation = instr->hydrogen()->representation(); - - if (representation.IsInteger32() && - instr->hydrogen()->elements_kind() == FAST_SMI_ELEMENTS) { - STATIC_ASSERT(kSmiValueSize == 32 && kSmiShift == 32 && kSmiTag == 0); - __ Load(result, UntagSmiFieldMemOperand(load_base, offset), - Representation::Integer32()); - } else { - __ Load(result, FieldMemOperand(load_base, offset), - representation); - } - - if (instr->hydrogen()->RequiresHoleCheck()) { - if (IsFastSmiElementsKind(instr->hydrogen()->elements_kind())) { - DeoptimizeIfNotSmi(result, instr->environment()); - } else { - DeoptimizeIfRoot(result, Heap::kTheHoleValueRootIndex, - instr->environment()); - } - } -} - - -void LCodeGen::DoLoadKeyedGeneric(LLoadKeyedGeneric* instr) { - ASSERT(ToRegister(instr->context()).is(cp)); - ASSERT(ToRegister(instr->object()).Is(x1)); - ASSERT(ToRegister(instr->key()).Is(x0)); - - Handle ic = isolate()->builtins()->KeyedLoadIC_Initialize(); - CallCode(ic, RelocInfo::CODE_TARGET, instr); - - ASSERT(ToRegister(instr->result()).Is(x0)); -} - - -void LCodeGen::DoLoadNamedField(LLoadNamedField* instr) { - HObjectAccess access = instr->hydrogen()->access(); - int offset = access.offset(); - Register object = ToRegister(instr->object()); - - if (access.IsExternalMemory()) { - Register result = ToRegister(instr->result()); - __ Load(result, MemOperand(object, offset), access.representation()); - return; - } - - if (instr->hydrogen()->representation().IsDouble()) { - FPRegister result = ToDoubleRegister(instr->result()); - __ Ldr(result, FieldMemOperand(object, offset)); - return; - } - - Register result = ToRegister(instr->result()); - Register source; - if (access.IsInobject()) { - source = object; - } else { - // Load the properties array, using result as a scratch register. - __ Ldr(result, FieldMemOperand(object, JSObject::kPropertiesOffset)); - source = result; - } - - if (access.representation().IsSmi() && - instr->hydrogen()->representation().IsInteger32()) { - // Read int value directly from upper half of the smi. - STATIC_ASSERT(kSmiValueSize == 32 && kSmiShift == 32 && kSmiTag == 0); - __ Load(result, UntagSmiFieldMemOperand(source, offset), - Representation::Integer32()); - } else { - __ Load(result, FieldMemOperand(source, offset), access.representation()); - } -} - - -void LCodeGen::DoLoadNamedGeneric(LLoadNamedGeneric* instr) { - ASSERT(ToRegister(instr->context()).is(cp)); - // LoadIC expects x2 to hold the name, and x0 to hold the receiver. - ASSERT(ToRegister(instr->object()).is(x0)); - __ Mov(x2, Operand(instr->name())); - - Handle ic = LoadIC::initialize_stub(isolate(), NOT_CONTEXTUAL); - CallCode(ic, RelocInfo::CODE_TARGET, instr); - - ASSERT(ToRegister(instr->result()).is(x0)); -} - - -void LCodeGen::DoLoadRoot(LLoadRoot* instr) { - Register result = ToRegister(instr->result()); - __ LoadRoot(result, instr->index()); -} - - -void LCodeGen::DoMapEnumLength(LMapEnumLength* instr) { - Register result = ToRegister(instr->result()); - Register map = ToRegister(instr->value()); - __ EnumLengthSmi(result, map); -} - - -void LCodeGen::DoMathAbs(LMathAbs* instr) { - Representation r = instr->hydrogen()->value()->representation(); - if (r.IsDouble()) { - DoubleRegister input = ToDoubleRegister(instr->value()); - DoubleRegister result = ToDoubleRegister(instr->result()); - __ Fabs(result, input); - } else if (r.IsSmi() || r.IsInteger32()) { - Register input = r.IsSmi() ? ToRegister(instr->value()) - : ToRegister32(instr->value()); - Register result = r.IsSmi() ? ToRegister(instr->result()) - : ToRegister32(instr->result()); - Label done; - __ Abs(result, input, NULL, &done); - Deoptimize(instr->environment()); - __ Bind(&done); - } -} - - -void LCodeGen::DoDeferredMathAbsTagged(LMathAbsTagged* instr, - Label* exit, - Label* allocation_entry) { - // Handle the tricky cases of MathAbsTagged: - // - HeapNumber inputs. - // - Negative inputs produce a positive result, so a new HeapNumber is - // allocated to hold it. - // - Positive inputs are returned as-is, since there is no need to allocate - // a new HeapNumber for the result. - // - The (smi) input -0x80000000, produces +0x80000000, which does not fit - // a smi. In this case, the inline code sets the result and jumps directly - // to the allocation_entry label. - ASSERT(instr->context() != NULL); - ASSERT(ToRegister(instr->context()).is(cp)); - Register input = ToRegister(instr->value()); - Register temp1 = ToRegister(instr->temp1()); - Register temp2 = ToRegister(instr->temp2()); - Register result_bits = ToRegister(instr->temp3()); - Register result = ToRegister(instr->result()); - - Label runtime_allocation; - - // Deoptimize if the input is not a HeapNumber. - __ Ldr(temp1, FieldMemOperand(input, HeapObject::kMapOffset)); - DeoptimizeIfNotRoot(temp1, Heap::kHeapNumberMapRootIndex, - instr->environment()); - - // If the argument is positive, we can return it as-is, without any need to - // allocate a new HeapNumber for the result. We have to do this in integer - // registers (rather than with fabs) because we need to be able to distinguish - // the two zeroes. - __ Ldr(result_bits, FieldMemOperand(input, HeapNumber::kValueOffset)); - __ Mov(result, input); - __ Tbz(result_bits, kXSignBit, exit); - - // Calculate abs(input) by clearing the sign bit. - __ Bic(result_bits, result_bits, kXSignMask); - - // Allocate a new HeapNumber to hold the result. - // result_bits The bit representation of the (double) result. - __ Bind(allocation_entry); - __ AllocateHeapNumber(result, &runtime_allocation, temp1, temp2); - // The inline (non-deferred) code will store result_bits into result. - __ B(exit); - - __ Bind(&runtime_allocation); - if (FLAG_debug_code) { - // Because result is in the pointer map, we need to make sure it has a valid - // tagged value before we call the runtime. We speculatively set it to the - // input (for abs(+x)) or to a smi (for abs(-SMI_MIN)), so it should already - // be valid. - Label result_ok; - Register input = ToRegister(instr->value()); - __ JumpIfSmi(result, &result_ok); - __ Cmp(input, result); - // TODO(all): Shouldn't we assert here? - DeoptimizeIf(ne, instr->environment()); - __ Bind(&result_ok); - } - - { PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters); - CallRuntimeFromDeferred(Runtime::kAllocateHeapNumber, 0, instr, - instr->context()); - __ StoreToSafepointRegisterSlot(x0, result); - } - // The inline (non-deferred) code will store result_bits into result. -} - - -void LCodeGen::DoMathAbsTagged(LMathAbsTagged* instr) { - // Class for deferred case. - class DeferredMathAbsTagged: public LDeferredCode { - public: - DeferredMathAbsTagged(LCodeGen* codegen, LMathAbsTagged* instr) - : LDeferredCode(codegen), instr_(instr) { } - virtual void Generate() { - codegen()->DoDeferredMathAbsTagged(instr_, exit(), - allocation_entry()); - } - virtual LInstruction* instr() { return instr_; } - Label* allocation_entry() { return &allocation; } - private: - LMathAbsTagged* instr_; - Label allocation; - }; - - // TODO(jbramley): The early-exit mechanism would skip the new frame handling - // in GenerateDeferredCode. Tidy this up. - ASSERT(!NeedsDeferredFrame()); - - DeferredMathAbsTagged* deferred = - new(zone()) DeferredMathAbsTagged(this, instr); - - ASSERT(instr->hydrogen()->value()->representation().IsTagged() || - instr->hydrogen()->value()->representation().IsSmi()); - Register input = ToRegister(instr->value()); - Register result_bits = ToRegister(instr->temp3()); - Register result = ToRegister(instr->result()); - Label done; - - // Handle smis inline. - // We can treat smis as 64-bit integers, since the (low-order) tag bits will - // never get set by the negation. This is therefore the same as the Integer32 - // case in DoMathAbs, except that it operates on 64-bit values. - STATIC_ASSERT((kSmiValueSize == 32) && (kSmiShift == 32) && (kSmiTag == 0)); - - // TODO(jbramley): We can't use JumpIfNotSmi here because the tbz it uses - // doesn't always have enough range. Consider making a variant of it, or a - // TestIsSmi helper. - STATIC_ASSERT(kSmiTag == 0); - __ Tst(input, kSmiTagMask); - __ B(ne, deferred->entry()); - - __ Abs(result, input, NULL, &done); - - // The result is the magnitude (abs) of the smallest value a smi can - // represent, encoded as a double. - __ Mov(result_bits, double_to_rawbits(0x80000000)); - __ B(deferred->allocation_entry()); - - __ Bind(deferred->exit()); - __ Str(result_bits, FieldMemOperand(result, HeapNumber::kValueOffset)); - - __ Bind(&done); -} - - -void LCodeGen::DoMathExp(LMathExp* instr) { - DoubleRegister input = ToDoubleRegister(instr->value()); - DoubleRegister result = ToDoubleRegister(instr->result()); - DoubleRegister double_temp1 = ToDoubleRegister(instr->double_temp1()); - DoubleRegister double_temp2 = double_scratch(); - Register temp1 = ToRegister(instr->temp1()); - Register temp2 = ToRegister(instr->temp2()); - Register temp3 = ToRegister(instr->temp3()); - - MathExpGenerator::EmitMathExp(masm(), input, result, - double_temp1, double_temp2, - temp1, temp2, temp3); -} - - -void LCodeGen::DoMathFloor(LMathFloor* instr) { - // TODO(jbramley): If we could provide a double result, we could use frintm - // and produce a valid double result in a single instruction. - DoubleRegister input = ToDoubleRegister(instr->value()); - Register result = ToRegister(instr->result()); - Label deopt; - Label done; - - if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { - __ JumpIfMinusZero(input, &deopt); - } - - __ Fcvtms(result, input); - - // Check that the result fits into a 32-bit integer. - // - The result did not overflow. - __ Cmp(result, Operand(result, SXTW)); - // - The input was not NaN. - __ Fccmp(input, input, NoFlag, eq); - __ B(&done, eq); - - __ Bind(&deopt); - Deoptimize(instr->environment()); - - __ Bind(&done); -} - - -void LCodeGen::DoMathFloorOfDiv(LMathFloorOfDiv* instr) { - Register result = ToRegister32(instr->result()); - Register left = ToRegister32(instr->left()); - Register right = ToRegister32(instr->right()); - Register remainder = ToRegister32(instr->temp()); - - // This can't cause an exception on ARM, so we can speculatively - // execute it already now. - __ Sdiv(result, left, right); - - // Check for x / 0. - DeoptimizeIfZero(right, instr->environment()); - - // Check for (kMinInt / -1). - if (instr->hydrogen()->CheckFlag(HValue::kCanOverflow)) { - // The V flag will be set iff left == kMinInt. - __ Cmp(left, 1); - __ Ccmp(right, -1, NoFlag, vs); - DeoptimizeIf(eq, instr->environment()); - } - - // Check for (0 / -x) that will produce negative zero. - if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { - __ Cmp(right, 0); - __ Ccmp(left, 0, ZFlag, mi); - // "right" can't be null because the code would have already been - // deoptimized. The Z flag is set only if (right < 0) and (left == 0). - // In this case we need to deoptimize to produce a -0. - DeoptimizeIf(eq, instr->environment()); - } - - Label done; - // If both operands have the same sign then we are done. - __ Eor(remainder, left, right); - __ Tbz(remainder, kWSignBit, &done); - - // Check if the result needs to be corrected. - __ Msub(remainder, result, right, left); - __ Cbz(remainder, &done); - __ Sub(result, result, 1); - - __ Bind(&done); -} - - -void LCodeGen::DoMathLog(LMathLog* instr) { - ASSERT(instr->IsMarkedAsCall()); - ASSERT(ToDoubleRegister(instr->value()).is(d0)); - __ CallCFunction(ExternalReference::math_log_double_function(isolate()), - 0, 1); - ASSERT(ToDoubleRegister(instr->result()).Is(d0)); -} - - -void LCodeGen::DoMathPowHalf(LMathPowHalf* instr) { - DoubleRegister input = ToDoubleRegister(instr->value()); - DoubleRegister result = ToDoubleRegister(instr->result()); - Label done; - - // Math.pow(x, 0.5) differs from fsqrt(x) in the following cases: - // Math.pow(-Infinity, 0.5) == +Infinity - // Math.pow(-0.0, 0.5) == +0.0 - - // Catch -infinity inputs first. - // TODO(jbramley): A constant infinity register would be helpful here. - __ Fmov(double_scratch(), kFP64NegativeInfinity); - __ Fcmp(double_scratch(), input); - __ Fabs(result, input); - __ B(&done, eq); - - // Add +0.0 to convert -0.0 to +0.0. - // TODO(jbramley): A constant zero register would be helpful here. - __ Fmov(double_scratch(), 0.0); - __ Fadd(double_scratch(), input, double_scratch()); - __ Fsqrt(result, double_scratch()); - - __ Bind(&done); -} - - -void LCodeGen::DoPower(LPower* instr) { - Representation exponent_type = instr->hydrogen()->right()->representation(); - // Having marked this as a call, we can use any registers. - // Just make sure that the input/output registers are the expected ones. - ASSERT(!instr->right()->IsDoubleRegister() || - ToDoubleRegister(instr->right()).is(d1)); - ASSERT(exponent_type.IsInteger32() || !instr->right()->IsRegister() || - ToRegister(instr->right()).is(x11)); - ASSERT(!exponent_type.IsInteger32() || ToRegister(instr->right()).is(x12)); - ASSERT(ToDoubleRegister(instr->left()).is(d0)); - ASSERT(ToDoubleRegister(instr->result()).is(d0)); - - if (exponent_type.IsSmi()) { - MathPowStub stub(MathPowStub::TAGGED); - __ CallStub(&stub); - } else if (exponent_type.IsTagged()) { - Label no_deopt; - __ JumpIfSmi(x11, &no_deopt); - __ Ldr(x0, FieldMemOperand(x11, HeapObject::kMapOffset)); - DeoptimizeIfNotRoot(x0, Heap::kHeapNumberMapRootIndex, - instr->environment()); - __ Bind(&no_deopt); - MathPowStub stub(MathPowStub::TAGGED); - __ CallStub(&stub); - } else if (exponent_type.IsInteger32()) { - // Ensure integer exponent has no garbage in top 32-bits, as MathPowStub - // supports large integer exponents. - Register exponent = ToRegister(instr->right()); - __ Sxtw(exponent, exponent); - MathPowStub stub(MathPowStub::INTEGER); - __ CallStub(&stub); - } else { - ASSERT(exponent_type.IsDouble()); - MathPowStub stub(MathPowStub::DOUBLE); - __ CallStub(&stub); - } -} - - -void LCodeGen::DoMathRound(LMathRound* instr) { - // TODO(jbramley): We could provide a double result here using frint. - DoubleRegister input = ToDoubleRegister(instr->value()); - DoubleRegister temp1 = ToDoubleRegister(instr->temp1()); - Register result = ToRegister(instr->result()); - Label try_rounding; - Label deopt; - Label done; - - // Math.round() rounds to the nearest integer, with ties going towards - // +infinity. This does not match any IEEE-754 rounding mode. - // - Infinities and NaNs are propagated unchanged, but cause deopts because - // they can't be represented as integers. - // - The sign of the result is the same as the sign of the input. This means - // that -0.0 rounds to itself, and values -0.5 <= input < 0 also produce a - // result of -0.0. - - DoubleRegister dot_five = double_scratch(); - __ Fmov(dot_five, 0.5); - __ Fabs(temp1, input); - __ Fcmp(temp1, dot_five); - // If input is in [-0.5, -0], the result is -0. - // If input is in [+0, +0.5[, the result is +0. - // If the input is +0.5, the result is 1. - __ B(hi, &try_rounding); // hi so NaN will also branch. - - if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { - __ Fmov(result, input); - __ Cmp(result, 0); - DeoptimizeIf(mi, instr->environment()); // [-0.5, -0.0]. - } - __ Fcmp(input, dot_five); - __ Mov(result, 1); // +0.5. - // Remaining cases: [+0, +0.5[ or [-0.5, +0.5[, depending on - // flag kBailoutOnMinusZero, will return 0 (xzr). - __ Csel(result, result, xzr, eq); - __ B(&done); - - __ Bind(&deopt); - Deoptimize(instr->environment()); - - __ Bind(&try_rounding); - // Since we're providing a 32-bit result, we can implement ties-to-infinity by - // adding 0.5 to the input, then taking the floor of the result. This does not - // work for very large positive doubles because adding 0.5 would cause an - // intermediate rounding stage, so a different approach will be necessary if a - // double result is needed. - __ Fadd(temp1, input, dot_five); - __ Fcvtms(result, temp1); - - // Deopt if - // * the input was NaN - // * the result is not representable using a 32-bit integer. - __ Fcmp(input, 0.0); - __ Ccmp(result, Operand(result.W(), SXTW), NoFlag, vc); - __ B(ne, &deopt); - - __ Bind(&done); -} - - -void LCodeGen::DoMathSqrt(LMathSqrt* instr) { - DoubleRegister input = ToDoubleRegister(instr->value()); - DoubleRegister result = ToDoubleRegister(instr->result()); - __ Fsqrt(result, input); -} - - -void LCodeGen::DoMathMinMax(LMathMinMax* instr) { - HMathMinMax::Operation op = instr->hydrogen()->operation(); - if (instr->hydrogen()->representation().IsInteger32()) { - Register result = ToRegister32(instr->result()); - Register left = ToRegister32(instr->left()); - Operand right = ToOperand32I(instr->right()); - - __ Cmp(left, right); - __ Csel(result, left, right, (op == HMathMinMax::kMathMax) ? ge : le); - } else if (instr->hydrogen()->representation().IsSmi()) { - Register result = ToRegister(instr->result()); - Register left = ToRegister(instr->left()); - Operand right = ToOperand(instr->right()); - - __ Cmp(left, right); - __ Csel(result, left, right, (op == HMathMinMax::kMathMax) ? ge : le); - } else { - ASSERT(instr->hydrogen()->representation().IsDouble()); - DoubleRegister result = ToDoubleRegister(instr->result()); - DoubleRegister left = ToDoubleRegister(instr->left()); - DoubleRegister right = ToDoubleRegister(instr->right()); - - if (op == HMathMinMax::kMathMax) { - __ Fmax(result, left, right); - } else { - ASSERT(op == HMathMinMax::kMathMin); - __ Fmin(result, left, right); - } - } -} - - -void LCodeGen::DoModI(LModI* instr) { - HMod* hmod = instr->hydrogen(); - HValue* hleft = hmod->left(); - HValue* hright = hmod->right(); - - Label done; - Register result = ToRegister32(instr->result()); - Register dividend = ToRegister32(instr->left()); - - bool need_minus_zero_check = (hmod->CheckFlag(HValue::kBailoutOnMinusZero) && - hleft->CanBeNegative() && hmod->CanBeZero()); - - if (hmod->RightIsPowerOf2()) { - // Note: The code below even works when right contains kMinInt. - int32_t divisor = Abs(hright->GetInteger32Constant()); - - if (hleft->CanBeNegative()) { - __ Cmp(dividend, 0); - __ Cneg(result, dividend, mi); - __ And(result, result, divisor - 1); - __ Cneg(result, result, mi); - if (need_minus_zero_check) { - __ Cbnz(result, &done); - // The result is 0. Deoptimize if the dividend was negative. - DeoptimizeIf(mi, instr->environment()); - } - } else { - __ And(result, dividend, divisor - 1); - } - - } else { - Label deopt; - Register divisor = ToRegister32(instr->right()); - // Compute: - // modulo = dividend - quotient * divisor - __ Sdiv(result, dividend, divisor); - if (hright->CanBeZero()) { - // Combine the deoptimization sites. - Label ok; - __ Cbnz(divisor, &ok); - __ Bind(&deopt); - Deoptimize(instr->environment()); - __ Bind(&ok); - } - __ Msub(result, result, divisor, dividend); - if (need_minus_zero_check) { - __ Cbnz(result, &done); - if (deopt.is_bound()) { - __ Tbnz(dividend, kWSignBit, &deopt); - } else { - DeoptimizeIfNegative(dividend, instr->environment()); - } - } - } - __ Bind(&done); -} - - -void LCodeGen::DoMulConstIS(LMulConstIS* instr) { - ASSERT(instr->hydrogen()->representation().IsSmiOrInteger32()); - bool is_smi = instr->hydrogen()->representation().IsSmi(); - Register result = - is_smi ? ToRegister(instr->result()) : ToRegister32(instr->result()); - Register left = - is_smi ? ToRegister(instr->left()) : ToRegister32(instr->left()) ; - int32_t right = ToInteger32(instr->right()); - - bool can_overflow = instr->hydrogen()->CheckFlag(HValue::kCanOverflow); - bool bailout_on_minus_zero = - instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero); - - if (bailout_on_minus_zero) { - if (right < 0) { - // The result is -0 if right is negative and left is zero. - DeoptimizeIfZero(left, instr->environment()); - } else if (right == 0) { - // The result is -0 if the right is zero and the left is negative. - DeoptimizeIfNegative(left, instr->environment()); - } - } - - switch (right) { - // Cases which can detect overflow. - case -1: - if (can_overflow) { - // Only 0x80000000 can overflow here. - __ Negs(result, left); - DeoptimizeIf(vs, instr->environment()); - } else { - __ Neg(result, left); - } - break; - case 0: - // This case can never overflow. - __ Mov(result, 0); - break; - case 1: - // This case can never overflow. - __ Mov(result, left, kDiscardForSameWReg); - break; - case 2: - if (can_overflow) { - __ Adds(result, left, left); - DeoptimizeIf(vs, instr->environment()); - } else { - __ Add(result, left, left); - } - break; - - // All other cases cannot detect overflow, because it would probably be no - // faster than using the smull method in LMulI. - // TODO(jbramley): Investigate this, and add overflow support if it would - // be useful. - default: - ASSERT(!can_overflow); - - // Multiplication by constant powers of two (and some related values) - // can be done efficiently with shifted operands. - if (right >= 0) { - if (IsPowerOf2(right)) { - // result = left << log2(right) - __ Lsl(result, left, WhichPowerOf2(right)); - } else if (IsPowerOf2(right - 1)) { - // result = left + left << log2(right - 1) - __ Add(result, left, Operand(left, LSL, WhichPowerOf2(right - 1))); - } else if (IsPowerOf2(right + 1)) { - // result = -left + left << log2(right + 1) - __ Sub(result, left, Operand(left, LSL, WhichPowerOf2(right + 1))); - __ Neg(result, result); - } else { - UNREACHABLE(); - } - } else { - if (IsPowerOf2(-right)) { - // result = -left << log2(-right) - __ Neg(result, Operand(left, LSL, WhichPowerOf2(-right))); - } else if (IsPowerOf2(-right + 1)) { - // result = left - left << log2(-right + 1) - __ Sub(result, left, Operand(left, LSL, WhichPowerOf2(-right + 1))); - } else if (IsPowerOf2(-right - 1)) { - // result = -left - left << log2(-right - 1) - __ Add(result, left, Operand(left, LSL, WhichPowerOf2(-right - 1))); - __ Neg(result, result); - } else { - UNREACHABLE(); - } - } - break; - } -} - - -void LCodeGen::DoMulI(LMulI* instr) { - Register result = ToRegister32(instr->result()); - Register left = ToRegister32(instr->left()); - Register right = ToRegister32(instr->right()); - - bool can_overflow = instr->hydrogen()->CheckFlag(HValue::kCanOverflow); - bool bailout_on_minus_zero = - instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero); - - if (bailout_on_minus_zero) { - // If one operand is zero and the other is negative, the result is -0. - // - Set Z (eq) if either left or right, or both, are 0. - __ Cmp(left, 0); - __ Ccmp(right, 0, ZFlag, ne); - // - If so (eq), set N (mi) if left + right is negative. - // - Otherwise, clear N. - __ Ccmn(left, right, NoFlag, eq); - DeoptimizeIf(mi, instr->environment()); - } - - if (can_overflow) { - __ Smull(result.X(), left, right); - __ Cmp(result.X(), Operand(result, SXTW)); - DeoptimizeIf(ne, instr->environment()); - } else { - __ Mul(result, left, right); - } -} - - -void LCodeGen::DoMulS(LMulS* instr) { - Register result = ToRegister(instr->result()); - Register left = ToRegister(instr->left()); - Register right = ToRegister(instr->right()); - - bool can_overflow = instr->hydrogen()->CheckFlag(HValue::kCanOverflow); - bool bailout_on_minus_zero = - instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero); - - if (bailout_on_minus_zero) { - // If one operand is zero and the other is negative, the result is -0. - // - Set Z (eq) if either left or right, or both, are 0. - __ Cmp(left, 0); - __ Ccmp(right, 0, ZFlag, ne); - // - If so (eq), set N (mi) if left + right is negative. - // - Otherwise, clear N. - __ Ccmn(left, right, NoFlag, eq); - DeoptimizeIf(mi, instr->environment()); - } - - STATIC_ASSERT((kSmiShift == 32) && (kSmiTag == 0)); - if (can_overflow) { - __ Smulh(result, left, right); - __ Cmp(result, Operand(result.W(), SXTW)); - __ SmiTag(result); - DeoptimizeIf(ne, instr->environment()); - } else { - // TODO(jbramley): This could be rewritten to support UseRegisterAtStart. - ASSERT(!AreAliased(result, right)); - __ SmiUntag(result, left); - __ Mul(result, result, right); - } -} - - -void LCodeGen::DoDeferredNumberTagD(LNumberTagD* instr) { - // TODO(3095996): Get rid of this. For now, we need to make the - // result register contain a valid pointer because it is already - // contained in the register pointer map. - Register result = ToRegister(instr->result()); - __ Mov(result, 0); - - PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters); - // NumberTagU and NumberTagD use the context from the frame, rather than - // the environment's HContext or HInlinedContext value. - // They only call Runtime::kAllocateHeapNumber. - // The corresponding HChange instructions are added in a phase that does - // not have easy access to the local context. - __ Ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); - __ CallRuntimeSaveDoubles(Runtime::kAllocateHeapNumber); - RecordSafepointWithRegisters( - instr->pointer_map(), 0, Safepoint::kNoLazyDeopt); - __ StoreToSafepointRegisterSlot(x0, result); -} - - -void LCodeGen::DoNumberTagD(LNumberTagD* instr) { - class DeferredNumberTagD: public LDeferredCode { - public: - DeferredNumberTagD(LCodeGen* codegen, LNumberTagD* instr) - : LDeferredCode(codegen), instr_(instr) { } - virtual void Generate() { codegen()->DoDeferredNumberTagD(instr_); } - virtual LInstruction* instr() { return instr_; } - private: - LNumberTagD* instr_; - }; - - DoubleRegister input = ToDoubleRegister(instr->value()); - Register result = ToRegister(instr->result()); - Register temp1 = ToRegister(instr->temp1()); - Register temp2 = ToRegister(instr->temp2()); - - DeferredNumberTagD* deferred = new(zone()) DeferredNumberTagD(this, instr); - if (FLAG_inline_new) { - __ AllocateHeapNumber(result, deferred->entry(), temp1, temp2); - } else { - __ B(deferred->entry()); - } - - __ Bind(deferred->exit()); - __ Str(input, FieldMemOperand(result, HeapNumber::kValueOffset)); -} - - -void LCodeGen::DoDeferredNumberTagU(LInstruction* instr, - LOperand* value, - LOperand* temp1, - LOperand* temp2) { - Label slow, convert_and_store; - Register src = ToRegister32(value); - Register dst = ToRegister(instr->result()); - Register scratch1 = ToRegister(temp1); - - if (FLAG_inline_new) { - Register scratch2 = ToRegister(temp2); - __ AllocateHeapNumber(dst, &slow, scratch1, scratch2); - __ B(&convert_and_store); - } - - // Slow case: call the runtime system to do the number allocation. - __ Bind(&slow); - // TODO(3095996): Put a valid pointer value in the stack slot where the result - // register is stored, as this register is in the pointer map, but contains an - // integer value. - __ Mov(dst, 0); - { - // Preserve the value of all registers. - PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters); - - // NumberTagU and NumberTagD use the context from the frame, rather than - // the environment's HContext or HInlinedContext value. - // They only call Runtime::kAllocateHeapNumber. - // The corresponding HChange instructions are added in a phase that does - // not have easy access to the local context. - __ Ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); - __ CallRuntimeSaveDoubles(Runtime::kAllocateHeapNumber); - RecordSafepointWithRegisters( - instr->pointer_map(), 0, Safepoint::kNoLazyDeopt); - __ StoreToSafepointRegisterSlot(x0, dst); - } - - // Convert number to floating point and store in the newly allocated heap - // number. - __ Bind(&convert_and_store); - DoubleRegister dbl_scratch = double_scratch(); - __ Ucvtf(dbl_scratch, src); - __ Str(dbl_scratch, FieldMemOperand(dst, HeapNumber::kValueOffset)); -} - - -void LCodeGen::DoNumberTagU(LNumberTagU* instr) { - class DeferredNumberTagU: public LDeferredCode { - public: - DeferredNumberTagU(LCodeGen* codegen, LNumberTagU* instr) - : LDeferredCode(codegen), instr_(instr) { } - virtual void Generate() { - codegen()->DoDeferredNumberTagU(instr_, - instr_->value(), - instr_->temp1(), - instr_->temp2()); - } - virtual LInstruction* instr() { return instr_; } - private: - LNumberTagU* instr_; - }; - - Register value = ToRegister32(instr->value()); - Register result = ToRegister(instr->result()); - - DeferredNumberTagU* deferred = new(zone()) DeferredNumberTagU(this, instr); - __ Cmp(value, Smi::kMaxValue); - __ B(hi, deferred->entry()); - __ SmiTag(result, value.X()); - __ Bind(deferred->exit()); -} - - -void LCodeGen::DoNumberUntagD(LNumberUntagD* instr) { - Register input = ToRegister(instr->value()); - Register scratch = ToRegister(instr->temp()); - DoubleRegister result = ToDoubleRegister(instr->result()); - bool can_convert_undefined_to_nan = - instr->hydrogen()->can_convert_undefined_to_nan(); - - Label done, load_smi; - - // Work out what untag mode we're working with. - HValue* value = instr->hydrogen()->value(); - NumberUntagDMode mode = value->representation().IsSmi() - ? NUMBER_CANDIDATE_IS_SMI : NUMBER_CANDIDATE_IS_ANY_TAGGED; - - if (mode == NUMBER_CANDIDATE_IS_ANY_TAGGED) { - __ JumpIfSmi(input, &load_smi); - - Label convert_undefined, deopt; - - // Heap number map check. - Label* not_heap_number = can_convert_undefined_to_nan ? &convert_undefined - : &deopt; - __ Ldr(scratch, FieldMemOperand(input, HeapObject::kMapOffset)); - __ JumpIfNotRoot(scratch, Heap::kHeapNumberMapRootIndex, not_heap_number); - - // Load heap number. - __ Ldr(result, FieldMemOperand(input, HeapNumber::kValueOffset)); - if (instr->hydrogen()->deoptimize_on_minus_zero()) { - __ JumpIfMinusZero(result, &deopt); - } - __ B(&done); - - if (can_convert_undefined_to_nan) { - __ Bind(&convert_undefined); - __ JumpIfNotRoot(input, Heap::kUndefinedValueRootIndex, &deopt); - - __ LoadRoot(scratch, Heap::kNanValueRootIndex); - __ Ldr(result, FieldMemOperand(scratch, HeapNumber::kValueOffset)); - __ B(&done); - } - - __ Bind(&deopt); - Deoptimize(instr->environment()); - } else { - ASSERT(mode == NUMBER_CANDIDATE_IS_SMI); - // Fall through to load_smi. - } - - // Smi to double register conversion. - __ Bind(&load_smi); - __ SmiUntagToDouble(result, input); - - __ Bind(&done); -} - - -void LCodeGen::DoOsrEntry(LOsrEntry* instr) { - // This is a pseudo-instruction that ensures that the environment here is - // properly registered for deoptimization and records the assembler's PC - // offset. - LEnvironment* environment = instr->environment(); - - // If the environment were already registered, we would have no way of - // backpatching it with the spill slot operands. - ASSERT(!environment->HasBeenRegistered()); - RegisterEnvironmentForDeoptimization(environment, Safepoint::kNoLazyDeopt); - - GenerateOsrPrologue(); -} - - -void LCodeGen::DoParameter(LParameter* instr) { - // Nothing to do. -} - - -void LCodeGen::DoPushArgument(LPushArgument* instr) { - LOperand* argument = instr->value(); - if (argument->IsDoubleRegister() || argument->IsDoubleStackSlot()) { - Abort(kDoPushArgumentNotImplementedForDoubleType); - } else { - __ Push(ToRegister(argument)); - } -} - - -void LCodeGen::DoReturn(LReturn* instr) { - if (FLAG_trace && info()->IsOptimizing()) { - // Push the return value on the stack as the parameter. - // Runtime::TraceExit returns its parameter in x0. We're leaving the code - // managed by the register allocator and tearing down the frame, it's - // safe to write to the context register. - __ Push(x0); - __ Ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); - __ CallRuntime(Runtime::kTraceExit, 1); - } - - if (info()->saves_caller_doubles()) { - RestoreCallerDoubles(); - } - - int no_frame_start = -1; - if (NeedsEagerFrame()) { - Register stack_pointer = masm()->StackPointer(); - __ Mov(stack_pointer, fp); - no_frame_start = masm_->pc_offset(); - __ Pop(fp, lr); - } - - if (instr->has_constant_parameter_count()) { - int parameter_count = ToInteger32(instr->constant_parameter_count()); - __ Drop(parameter_count + 1); - } else { - Register parameter_count = ToRegister(instr->parameter_count()); - __ DropBySMI(parameter_count); - } - __ Ret(); - - if (no_frame_start != -1) { - info_->AddNoFrameRange(no_frame_start, masm_->pc_offset()); - } -} - - -MemOperand LCodeGen::BuildSeqStringOperand(Register string, - Register temp, - LOperand* index, - String::Encoding encoding) { - if (index->IsConstantOperand()) { - int offset = ToInteger32(LConstantOperand::cast(index)); - if (encoding == String::TWO_BYTE_ENCODING) { - offset *= kUC16Size; - } - STATIC_ASSERT(kCharSize == 1); - return FieldMemOperand(string, SeqString::kHeaderSize + offset); - } - ASSERT(!temp.is(string)); - ASSERT(!temp.is(ToRegister(index))); - if (encoding == String::ONE_BYTE_ENCODING) { - __ Add(temp, string, Operand(ToRegister32(index), SXTW)); - } else { - STATIC_ASSERT(kUC16Size == 2); - __ Add(temp, string, Operand(ToRegister32(index), SXTW, 1)); - } - return FieldMemOperand(temp, SeqString::kHeaderSize); -} - - -void LCodeGen::DoSeqStringGetChar(LSeqStringGetChar* instr) { - String::Encoding encoding = instr->hydrogen()->encoding(); - Register string = ToRegister(instr->string()); - Register result = ToRegister(instr->result()); - Register temp = ToRegister(instr->temp()); - - if (FLAG_debug_code) { - __ Ldr(temp, FieldMemOperand(string, HeapObject::kMapOffset)); - __ Ldrb(temp, FieldMemOperand(temp, Map::kInstanceTypeOffset)); - - __ And(temp, temp, - Operand(kStringRepresentationMask | kStringEncodingMask)); - static const uint32_t one_byte_seq_type = kSeqStringTag | kOneByteStringTag; - static const uint32_t two_byte_seq_type = kSeqStringTag | kTwoByteStringTag; - __ Cmp(temp, Operand(encoding == String::ONE_BYTE_ENCODING - ? one_byte_seq_type : two_byte_seq_type)); - __ Check(eq, kUnexpectedStringType); - } - - MemOperand operand = - BuildSeqStringOperand(string, temp, instr->index(), encoding); - if (encoding == String::ONE_BYTE_ENCODING) { - __ Ldrb(result, operand); - } else { - __ Ldrh(result, operand); - } -} - - -void LCodeGen::DoSeqStringSetChar(LSeqStringSetChar* instr) { - String::Encoding encoding = instr->hydrogen()->encoding(); - Register string = ToRegister(instr->string()); - Register value = ToRegister(instr->value()); - Register temp = ToRegister(instr->temp()); - - if (FLAG_debug_code) { - ASSERT(ToRegister(instr->context()).is(cp)); - Register index = ToRegister(instr->index()); - static const uint32_t one_byte_seq_type = kSeqStringTag | kOneByteStringTag; - static const uint32_t two_byte_seq_type = kSeqStringTag | kTwoByteStringTag; - int encoding_mask = - instr->hydrogen()->encoding() == String::ONE_BYTE_ENCODING - ? one_byte_seq_type : two_byte_seq_type; - __ EmitSeqStringSetCharCheck(string, index, kIndexIsInteger32, temp, - encoding_mask); - } - MemOperand operand = - BuildSeqStringOperand(string, temp, instr->index(), encoding); - if (encoding == String::ONE_BYTE_ENCODING) { - __ Strb(value, operand); - } else { - __ Strh(value, operand); - } -} - - -void LCodeGen::DoSmiTag(LSmiTag* instr) { - ASSERT(!instr->hydrogen_value()->CheckFlag(HValue::kCanOverflow)); - __ SmiTag(ToRegister(instr->result()), ToRegister(instr->value())); -} - - -void LCodeGen::DoSmiUntag(LSmiUntag* instr) { - Register input = ToRegister(instr->value()); - Register result = ToRegister(instr->result()); - Label done, untag; - - if (instr->needs_check()) { - DeoptimizeIfNotSmi(input, instr->environment()); - } - - __ Bind(&untag); - __ SmiUntag(result, input); - __ Bind(&done); -} - - -void LCodeGen::DoShiftI(LShiftI* instr) { - LOperand* right_op = instr->right(); - Register left = ToRegister32(instr->left()); - Register result = ToRegister32(instr->result()); - - if (right_op->IsRegister()) { - Register right = ToRegister32(instr->right()); - switch (instr->op()) { - case Token::ROR: __ Ror(result, left, right); break; - case Token::SAR: __ Asr(result, left, right); break; - case Token::SHL: __ Lsl(result, left, right); break; - case Token::SHR: - if (instr->can_deopt()) { - Label right_not_zero; - __ Cbnz(right, &right_not_zero); - DeoptimizeIfNegative(left, instr->environment()); - __ Bind(&right_not_zero); - } - __ Lsr(result, left, right); - break; - default: UNREACHABLE(); - } - } else { - ASSERT(right_op->IsConstantOperand()); - int shift_count = ToInteger32(LConstantOperand::cast(right_op)) & 0x1f; - if (shift_count == 0) { - if ((instr->op() == Token::SHR) && instr->can_deopt()) { - DeoptimizeIfNegative(left, instr->environment()); - } - __ Mov(result, left, kDiscardForSameWReg); - } else { - switch (instr->op()) { - case Token::ROR: __ Ror(result, left, shift_count); break; - case Token::SAR: __ Asr(result, left, shift_count); break; - case Token::SHL: __ Lsl(result, left, shift_count); break; - case Token::SHR: __ Lsr(result, left, shift_count); break; - default: UNREACHABLE(); - } - } - } -} - - -void LCodeGen::DoShiftS(LShiftS* instr) { - LOperand* right_op = instr->right(); - Register left = ToRegister(instr->left()); - Register result = ToRegister(instr->result()); - - // Only ROR by register needs a temp. - ASSERT(((instr->op() == Token::ROR) && right_op->IsRegister()) || - (instr->temp() == NULL)); - - if (right_op->IsRegister()) { - Register right = ToRegister(instr->right()); - switch (instr->op()) { - case Token::ROR: { - Register temp = ToRegister(instr->temp()); - __ Ubfx(temp, right, kSmiShift, 5); - __ SmiUntag(result, left); - __ Ror(result.W(), result.W(), temp.W()); - __ SmiTag(result); - break; - } - case Token::SAR: - __ Ubfx(result, right, kSmiShift, 5); - __ Asr(result, left, result); - __ Bic(result, result, kSmiShiftMask); - break; - case Token::SHL: - __ Ubfx(result, right, kSmiShift, 5); - __ Lsl(result, left, result); - break; - case Token::SHR: - if (instr->can_deopt()) { - Label right_not_zero; - __ Cbnz(right, &right_not_zero); - DeoptimizeIfNegative(left, instr->environment()); - __ Bind(&right_not_zero); - } - __ Ubfx(result, right, kSmiShift, 5); - __ Lsr(result, left, result); - __ Bic(result, result, kSmiShiftMask); - break; - default: UNREACHABLE(); - } - } else { - ASSERT(right_op->IsConstantOperand()); - int shift_count = ToInteger32(LConstantOperand::cast(right_op)) & 0x1f; - if (shift_count == 0) { - if ((instr->op() == Token::SHR) && instr->can_deopt()) { - DeoptimizeIfNegative(left, instr->environment()); - } - __ Mov(result, left); - } else { - switch (instr->op()) { - case Token::ROR: - __ SmiUntag(result, left); - __ Ror(result.W(), result.W(), shift_count); - __ SmiTag(result); - break; - case Token::SAR: - __ Asr(result, left, shift_count); - __ Bic(result, result, kSmiShiftMask); - break; - case Token::SHL: - __ Lsl(result, left, shift_count); - break; - case Token::SHR: - __ Lsr(result, left, shift_count); - __ Bic(result, result, kSmiShiftMask); - break; - default: UNREACHABLE(); - } - } - } -} - - -void LCodeGen::DoDebugBreak(LDebugBreak* instr) { - __ Debug("LDebugBreak", 0, BREAK); -} - - -void LCodeGen::DoDeclareGlobals(LDeclareGlobals* instr) { - ASSERT(ToRegister(instr->context()).is(cp)); - Register scratch1 = x5; - Register scratch2 = x6; - ASSERT(instr->IsMarkedAsCall()); - - ASM_UNIMPLEMENTED_BREAK("DoDeclareGlobals"); - // TODO(all): if Mov could handle object in new space then it could be used - // here. - __ LoadHeapObject(scratch1, instr->hydrogen()->pairs()); - __ Mov(scratch2, Operand(Smi::FromInt(instr->hydrogen()->flags()))); - __ Push(cp, scratch1, scratch2); // The context is the first argument. - CallRuntime(Runtime::kDeclareGlobals, 3, instr); -} - - -void LCodeGen::DoDeferredStackCheck(LStackCheck* instr) { - PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters); - LoadContextFromDeferred(instr->context()); - __ CallRuntimeSaveDoubles(Runtime::kStackGuard); - RecordSafepointWithLazyDeopt( - instr, RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS); - ASSERT(instr->HasEnvironment()); - LEnvironment* env = instr->environment(); - safepoints_.RecordLazyDeoptimizationIndex(env->deoptimization_index()); -} - - -void LCodeGen::DoStackCheck(LStackCheck* instr) { - class DeferredStackCheck: public LDeferredCode { - public: - DeferredStackCheck(LCodeGen* codegen, LStackCheck* instr) - : LDeferredCode(codegen), instr_(instr) { } - virtual void Generate() { codegen()->DoDeferredStackCheck(instr_); } - virtual LInstruction* instr() { return instr_; } - private: - LStackCheck* instr_; - }; - - ASSERT(instr->HasEnvironment()); - LEnvironment* env = instr->environment(); - // There is no LLazyBailout instruction for stack-checks. We have to - // prepare for lazy deoptimization explicitly here. - if (instr->hydrogen()->is_function_entry()) { - // Perform stack overflow check. - Label done; - __ CompareRoot(masm()->StackPointer(), Heap::kStackLimitRootIndex); - __ B(hs, &done); - - PredictableCodeSizeScope predictable(masm_, - Assembler::kCallSizeWithRelocation); - ASSERT(instr->context()->IsRegister()); - ASSERT(ToRegister(instr->context()).is(cp)); - CallCode(isolate()->builtins()->StackCheck(), - RelocInfo::CODE_TARGET, - instr); - EnsureSpaceForLazyDeopt(Deoptimizer::patch_size()); - - __ Bind(&done); - RegisterEnvironmentForDeoptimization(env, Safepoint::kLazyDeopt); - safepoints_.RecordLazyDeoptimizationIndex(env->deoptimization_index()); - } else { - ASSERT(instr->hydrogen()->is_backwards_branch()); - // Perform stack overflow check if this goto needs it before jumping. - DeferredStackCheck* deferred_stack_check = - new(zone()) DeferredStackCheck(this, instr); - __ CompareRoot(masm()->StackPointer(), Heap::kStackLimitRootIndex); - __ B(lo, deferred_stack_check->entry()); - - EnsureSpaceForLazyDeopt(Deoptimizer::patch_size()); - __ Bind(instr->done_label()); - deferred_stack_check->SetExit(instr->done_label()); - RegisterEnvironmentForDeoptimization(env, Safepoint::kLazyDeopt); - // Don't record a deoptimization index for the safepoint here. - // This will be done explicitly when emitting call and the safepoint in - // the deferred code. - } -} - - -void LCodeGen::DoStoreCodeEntry(LStoreCodeEntry* instr) { - Register function = ToRegister(instr->function()); - Register code_object = ToRegister(instr->code_object()); - Register temp = ToRegister(instr->temp()); - __ Add(temp, code_object, Code::kHeaderSize - kHeapObjectTag); - __ Str(temp, FieldMemOperand(function, JSFunction::kCodeEntryOffset)); -} - - -void LCodeGen::DoStoreContextSlot(LStoreContextSlot* instr) { - Register context = ToRegister(instr->context()); - Register value = ToRegister(instr->value()); - Register scratch = ToRegister(instr->temp()); - MemOperand target = ContextMemOperand(context, instr->slot_index()); - - Label skip_assignment; - - if (instr->hydrogen()->RequiresHoleCheck()) { - __ Ldr(scratch, target); - if (instr->hydrogen()->DeoptimizesOnHole()) { - DeoptimizeIfRoot(scratch, Heap::kTheHoleValueRootIndex, - instr->environment()); - } else { - __ JumpIfNotRoot(scratch, Heap::kTheHoleValueRootIndex, &skip_assignment); - } - } - - __ Str(value, target); - if (instr->hydrogen()->NeedsWriteBarrier()) { - SmiCheck check_needed = - instr->hydrogen()->value()->IsHeapObject() - ? OMIT_SMI_CHECK : INLINE_SMI_CHECK; - __ RecordWriteContextSlot(context, - target.offset(), - value, - scratch, - GetLinkRegisterState(), - kSaveFPRegs, - EMIT_REMEMBERED_SET, - check_needed); - } - __ Bind(&skip_assignment); -} - - -void LCodeGen::DoStoreGlobalCell(LStoreGlobalCell* instr) { - Register value = ToRegister(instr->value()); - Register cell = ToRegister(instr->temp1()); - - // Load the cell. - __ Mov(cell, Operand(instr->hydrogen()->cell().handle())); - - // If the cell we are storing to contains the hole it could have - // been deleted from the property dictionary. In that case, we need - // to update the property details in the property dictionary to mark - // it as no longer deleted. We deoptimize in that case. - if (instr->hydrogen()->RequiresHoleCheck()) { - Register payload = ToRegister(instr->temp2()); - __ Ldr(payload, FieldMemOperand(cell, Cell::kValueOffset)); - DeoptimizeIfRoot( - payload, Heap::kTheHoleValueRootIndex, instr->environment()); - } - - // Store the value. - __ Str(value, FieldMemOperand(cell, Cell::kValueOffset)); - // Cells are always rescanned, so no write barrier here. -} - - -void LCodeGen::DoStoreKeyedExternal(LStoreKeyedExternal* instr) { - Register ext_ptr = ToRegister(instr->elements()); - Register key = no_reg; - Register scratch; - ElementsKind elements_kind = instr->elements_kind(); - - bool key_is_smi = instr->hydrogen()->key()->representation().IsSmi(); - bool key_is_constant = instr->key()->IsConstantOperand(); - int constant_key = 0; - if (key_is_constant) { - ASSERT(instr->temp() == NULL); - constant_key = ToInteger32(LConstantOperand::cast(instr->key())); - if (constant_key & 0xf0000000) { - Abort(kArrayIndexConstantValueTooBig); - } - } else { - key = ToRegister(instr->key()); - scratch = ToRegister(instr->temp()); - } - - MemOperand dst = - PrepareKeyedExternalArrayOperand(key, ext_ptr, scratch, key_is_smi, - key_is_constant, constant_key, - elements_kind, - instr->additional_index()); - - if ((elements_kind == EXTERNAL_FLOAT32_ELEMENTS) || - (elements_kind == FLOAT32_ELEMENTS)) { - DoubleRegister value = ToDoubleRegister(instr->value()); - DoubleRegister dbl_scratch = double_scratch(); - __ Fcvt(dbl_scratch.S(), value); - __ Str(dbl_scratch.S(), dst); - } else if ((elements_kind == EXTERNAL_FLOAT64_ELEMENTS) || - (elements_kind == FLOAT64_ELEMENTS)) { - DoubleRegister value = ToDoubleRegister(instr->value()); - __ Str(value, dst); - } else { - Register value = ToRegister(instr->value()); - - switch (elements_kind) { - case EXTERNAL_UINT8_CLAMPED_ELEMENTS: - case EXTERNAL_INT8_ELEMENTS: - case EXTERNAL_UINT8_ELEMENTS: - case UINT8_ELEMENTS: - case UINT8_CLAMPED_ELEMENTS: - case INT8_ELEMENTS: - __ Strb(value, dst); - break; - case EXTERNAL_INT16_ELEMENTS: - case EXTERNAL_UINT16_ELEMENTS: - case INT16_ELEMENTS: - case UINT16_ELEMENTS: - __ Strh(value, dst); - break; - case EXTERNAL_INT32_ELEMENTS: - case EXTERNAL_UINT32_ELEMENTS: - case INT32_ELEMENTS: - case UINT32_ELEMENTS: - __ Str(value.W(), dst); - break; - case FLOAT32_ELEMENTS: - case FLOAT64_ELEMENTS: - case EXTERNAL_FLOAT32_ELEMENTS: - case EXTERNAL_FLOAT64_ELEMENTS: - case FAST_DOUBLE_ELEMENTS: - case FAST_ELEMENTS: - case FAST_SMI_ELEMENTS: - case FAST_HOLEY_DOUBLE_ELEMENTS: - case FAST_HOLEY_ELEMENTS: - case FAST_HOLEY_SMI_ELEMENTS: - case DICTIONARY_ELEMENTS: - case NON_STRICT_ARGUMENTS_ELEMENTS: - UNREACHABLE(); - break; - } - } -} - - -void LCodeGen::DoStoreKeyedFixedDouble(LStoreKeyedFixedDouble* instr) { - Register elements = ToRegister(instr->elements()); - DoubleRegister value = ToDoubleRegister(instr->value()); - Register store_base = ToRegister(instr->temp()); - int offset = 0; - - if (instr->key()->IsConstantOperand()) { - int constant_key = ToInteger32(LConstantOperand::cast(instr->key())); - if (constant_key & 0xf0000000) { - Abort(kArrayIndexConstantValueTooBig); - } - offset = FixedDoubleArray::OffsetOfElementAt(constant_key + - instr->additional_index()); - store_base = elements; - } else { - Register key = ToRegister(instr->key()); - bool key_is_tagged = instr->hydrogen()->key()->representation().IsSmi(); - CalcKeyedArrayBaseRegister(store_base, elements, key, key_is_tagged, - instr->hydrogen()->elements_kind()); - offset = FixedDoubleArray::OffsetOfElementAt(instr->additional_index()); - } - - if (instr->NeedsCanonicalization()) { - DoubleRegister dbl_scratch = double_scratch(); - __ Fmov(dbl_scratch, - FixedDoubleArray::canonical_not_the_hole_nan_as_double()); - __ Fmaxnm(dbl_scratch, dbl_scratch, value); - __ Str(dbl_scratch, FieldMemOperand(store_base, offset)); - } else { - __ Str(value, FieldMemOperand(store_base, offset)); - } -} - - -void LCodeGen::DoStoreKeyedFixed(LStoreKeyedFixed* instr) { - Register value = ToRegister(instr->value()); - Register elements = ToRegister(instr->elements()); - Register store_base = ToRegister(instr->temp()); - Register key = no_reg; - int offset = 0; - - if (instr->key()->IsConstantOperand()) { - ASSERT(!instr->hydrogen()->NeedsWriteBarrier()); - LConstantOperand* const_operand = LConstantOperand::cast(instr->key()); - offset = FixedArray::OffsetOfElementAt(ToInteger32(const_operand) + - instr->additional_index()); - store_base = elements; - } else { - key = ToRegister(instr->key()); - bool key_is_tagged = instr->hydrogen()->key()->representation().IsSmi(); - CalcKeyedArrayBaseRegister(store_base, elements, key, key_is_tagged, - instr->hydrogen()->elements_kind()); - offset = FixedArray::OffsetOfElementAt(instr->additional_index()); - } - Representation representation = instr->hydrogen()->value()->representation(); - if (representation.IsInteger32()) { - ASSERT(instr->hydrogen()->store_mode() == STORE_TO_INITIALIZED_ENTRY); - ASSERT(instr->hydrogen()->elements_kind() == FAST_SMI_ELEMENTS); - STATIC_ASSERT(kSmiValueSize == 32 && kSmiShift == 32 && kSmiTag == 0); - __ Store(value, UntagSmiFieldMemOperand(store_base, offset), - Representation::Integer32()); - } else { - __ Store(value, FieldMemOperand(store_base, offset), representation); - } - - if (instr->hydrogen()->NeedsWriteBarrier()) { - SmiCheck check_needed = - instr->hydrogen()->value()->IsHeapObject() - ? OMIT_SMI_CHECK : INLINE_SMI_CHECK; - // Compute address of modified element and store it into key register. - __ Add(key, store_base, offset - kHeapObjectTag); - __ RecordWrite(elements, key, value, GetLinkRegisterState(), kSaveFPRegs, - EMIT_REMEMBERED_SET, check_needed); - } -} - - -void LCodeGen::DoStoreKeyedGeneric(LStoreKeyedGeneric* instr) { - ASSERT(ToRegister(instr->context()).is(cp)); - ASSERT(ToRegister(instr->object()).Is(x2)); - ASSERT(ToRegister(instr->key()).Is(x1)); - ASSERT(ToRegister(instr->value()).Is(x0)); - - Handle ic = (instr->strict_mode_flag() == kStrictMode) - ? isolate()->builtins()->KeyedStoreIC_Initialize_Strict() - : isolate()->builtins()->KeyedStoreIC_Initialize(); - CallCode(ic, RelocInfo::CODE_TARGET, instr); -} - - -// TODO(jbramley): Once the merge is done and we're tracking bleeding_edge, try -// to tidy up this function. -void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) { - Representation representation = instr->representation(); - - Register object = ToRegister(instr->object()); - Register temp0 = ToRegister(instr->temp0()); - Register temp1 = ToRegister(instr->temp1()); - HObjectAccess access = instr->hydrogen()->access(); - int offset = access.offset(); - - if (access.IsExternalMemory()) { - Register value = ToRegister(instr->value()); - __ Store(value, MemOperand(object, offset), representation); - return; - } - - Handle transition = instr->transition(); - SmiCheck check_needed = - instr->hydrogen()->value()->IsHeapObject() - ? OMIT_SMI_CHECK : INLINE_SMI_CHECK; - - if (FLAG_track_heap_object_fields && representation.IsHeapObject()) { - Register value = ToRegister(instr->value()); - if (!instr->hydrogen()->value()->type().IsHeapObject()) { - DeoptimizeIfSmi(value, instr->environment()); - - // We know that value is a smi now, so we can omit the check below. - check_needed = OMIT_SMI_CHECK; - } - } else if (representation.IsDouble()) { - ASSERT(transition.is_null()); - ASSERT(access.IsInobject()); - ASSERT(!instr->hydrogen()->NeedsWriteBarrier()); - FPRegister value = ToDoubleRegister(instr->value()); - __ Str(value, FieldMemOperand(object, offset)); - return; - } - - if (!transition.is_null()) { - // Store the new map value. - Register new_map_value = temp0; - __ Mov(new_map_value, Operand(transition)); - __ Str(new_map_value, FieldMemOperand(object, HeapObject::kMapOffset)); - if (instr->hydrogen()->NeedsWriteBarrierForMap()) { - // Update the write barrier for the map field. - __ RecordWriteField(object, - HeapObject::kMapOffset, - new_map_value, - temp1, - GetLinkRegisterState(), - kSaveFPRegs, - OMIT_REMEMBERED_SET, - OMIT_SMI_CHECK); - } - } - - // Do the store. - Register value = ToRegister(instr->value()); - Register destination; - if (access.IsInobject()) { - destination = object; - } else { - __ Ldr(temp0, FieldMemOperand(object, JSObject::kPropertiesOffset)); - destination = temp0; - } - - if (representation.IsSmi() && - instr->hydrogen()->value()->representation().IsInteger32()) { - ASSERT(instr->hydrogen()->store_mode() == STORE_TO_INITIALIZED_ENTRY); -#ifdef DEBUG - __ Ldr(temp1, FieldMemOperand(destination, offset)); - __ AssertSmi(temp1); -#endif - STATIC_ASSERT(kSmiValueSize == 32 && kSmiShift == 32 && kSmiTag == 0); - __ Store(value, UntagSmiFieldMemOperand(destination, offset), - Representation::Integer32()); - } else { - __ Store(value, FieldMemOperand(destination, offset), representation); - } - if (instr->hydrogen()->NeedsWriteBarrier()) { - __ RecordWriteField(destination, - offset, - value, // Clobbered. - temp1, // Clobbered. - GetLinkRegisterState(), - kSaveFPRegs, - EMIT_REMEMBERED_SET, - check_needed); - } -} - - -void LCodeGen::DoStoreNamedGeneric(LStoreNamedGeneric* instr) { - ASSERT(ToRegister(instr->context()).is(cp)); - ASSERT(ToRegister(instr->value()).is(x0)); - ASSERT(ToRegister(instr->object()).is(x1)); - - // Name must be in x2. - __ Mov(x2, Operand(instr->name())); - Handle ic = StoreIC::initialize_stub(isolate(), - instr->strict_mode_flag()); - CallCode(ic, RelocInfo::CODE_TARGET, instr); -} - - -void LCodeGen::DoStringAdd(LStringAdd* instr) { - ASSERT(ToRegister(instr->context()).is(cp)); - ASSERT(ToRegister(instr->left()).Is(x1)); - ASSERT(ToRegister(instr->right()).Is(x0)); - StringAddStub stub(instr->hydrogen()->flags(), - instr->hydrogen()->pretenure_flag()); - CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr); -} - - -void LCodeGen::DoStringCharCodeAt(LStringCharCodeAt* instr) { - class DeferredStringCharCodeAt: public LDeferredCode { - public: - DeferredStringCharCodeAt(LCodeGen* codegen, LStringCharCodeAt* instr) - : LDeferredCode(codegen), instr_(instr) { } - virtual void Generate() { codegen()->DoDeferredStringCharCodeAt(instr_); } - virtual LInstruction* instr() { return instr_; } - private: - LStringCharCodeAt* instr_; - }; - - DeferredStringCharCodeAt* deferred = - new(zone()) DeferredStringCharCodeAt(this, instr); - - StringCharLoadGenerator::Generate(masm(), - ToRegister(instr->string()), - ToRegister(instr->index()), - ToRegister(instr->result()), - deferred->entry()); - __ Bind(deferred->exit()); -} - - -void LCodeGen::DoDeferredStringCharCodeAt(LStringCharCodeAt* instr) { - Register string = ToRegister(instr->string()); - Register result = ToRegister(instr->result()); - - // TODO(3095996): Get rid of this. For now, we need to make the - // result register contain a valid pointer because it is already - // contained in the register pointer map. - __ Mov(result, 0); - - PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters); - __ Push(string); - // Push the index as a smi. This is safe because of the checks in - // DoStringCharCodeAt above. - Register index = ToRegister(instr->index()); - __ SmiTag(index); - __ Push(index); - - CallRuntimeFromDeferred(Runtime::kStringCharCodeAt, 2, instr, - instr->context()); - __ AssertSmi(x0); - __ SmiUntag(x0); - __ StoreToSafepointRegisterSlot(x0, result); -} - - -void LCodeGen::DoStringCharFromCode(LStringCharFromCode* instr) { - class DeferredStringCharFromCode: public LDeferredCode { - public: - DeferredStringCharFromCode(LCodeGen* codegen, LStringCharFromCode* instr) - : LDeferredCode(codegen), instr_(instr) { } - virtual void Generate() { codegen()->DoDeferredStringCharFromCode(instr_); } - virtual LInstruction* instr() { return instr_; } - private: - LStringCharFromCode* instr_; - }; - - DeferredStringCharFromCode* deferred = - new(zone()) DeferredStringCharFromCode(this, instr); - - ASSERT(instr->hydrogen()->value()->representation().IsInteger32()); - Register char_code = ToRegister(instr->char_code()); - Register result = ToRegister(instr->result()); - - __ Cmp(char_code, Operand(String::kMaxOneByteCharCode)); - __ B(hi, deferred->entry()); - __ LoadRoot(result, Heap::kSingleCharacterStringCacheRootIndex); - __ Add(result, result, Operand(char_code, LSL, kPointerSizeLog2)); - __ Ldr(result, FieldMemOperand(result, FixedArray::kHeaderSize)); - __ CompareRoot(result, Heap::kUndefinedValueRootIndex); - __ B(eq, deferred->entry()); - __ Bind(deferred->exit()); -} - - -void LCodeGen::DoDeferredStringCharFromCode(LStringCharFromCode* instr) { - Register char_code = ToRegister(instr->char_code()); - Register result = ToRegister(instr->result()); - - // TODO(3095996): Get rid of this. For now, we need to make the - // result register contain a valid pointer because it is already - // contained in the register pointer map. - __ Mov(result, 0); - - PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters); - __ SmiTag(char_code); - __ Push(char_code); - CallRuntimeFromDeferred(Runtime::kCharFromCode, 1, instr, instr->context()); - __ StoreToSafepointRegisterSlot(x0, result); -} - - -void LCodeGen::DoStringCompareAndBranch(LStringCompareAndBranch* instr) { - ASSERT(ToRegister(instr->context()).is(cp)); - Token::Value op = instr->op(); - - Handle ic = CompareIC::GetUninitialized(isolate(), op); - CallCode(ic, RelocInfo::CODE_TARGET, instr); - InlineSmiCheckInfo::EmitNotInlined(masm()); - - Condition condition = TokenToCondition(op, false); - - EmitCompareAndBranch(instr, condition, x0, 0); -} - - -void LCodeGen::DoSubI(LSubI* instr) { - bool can_overflow = instr->hydrogen()->CheckFlag(HValue::kCanOverflow); - Register result = ToRegister32(instr->result()); - Register left = ToRegister32(instr->left()); - Operand right = ToOperand32I(instr->right()); - if (can_overflow) { - __ Subs(result, left, right); - DeoptimizeIf(vs, instr->environment()); - } else { - __ Sub(result, left, right); - } -} - - -void LCodeGen::DoSubS(LSubS* instr) { - bool can_overflow = instr->hydrogen()->CheckFlag(HValue::kCanOverflow); - Register result = ToRegister(instr->result()); - Register left = ToRegister(instr->left()); - Operand right = ToOperand(instr->right()); - if (can_overflow) { - __ Subs(result, left, right); - DeoptimizeIf(vs, instr->environment()); - } else { - __ Sub(result, left, right); - } -} - - -void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr, - LOperand* value, - LOperand* temp1, - LOperand* temp2) { - Register input = ToRegister(value); - Register scratch1 = ToRegister(temp1); - DoubleRegister dbl_scratch1 = double_scratch(); - - Label done; - - // Load heap object map. - __ Ldr(scratch1, FieldMemOperand(input, HeapObject::kMapOffset)); - - if (instr->truncating()) { - Register output = ToRegister(instr->result()); - Register scratch2 = ToRegister(temp2); - Label check_bools; - - // If it's not a heap number, jump to undefined check. - __ JumpIfNotRoot(scratch1, Heap::kHeapNumberMapRootIndex, &check_bools); - - // A heap number: load value and convert to int32 using truncating function. - __ TruncateHeapNumberToI(output, input); - __ B(&done); - - __ Bind(&check_bools); - - Register true_root = output; - Register false_root = scratch2; - __ LoadTrueFalseRoots(true_root, false_root); - __ Cmp(scratch1, true_root); - __ Cset(output, eq); - __ Ccmp(scratch1, false_root, ZFlag, ne); - __ B(eq, &done); - - // Output contains zero, undefined is converted to zero for truncating - // conversions. - DeoptimizeIfNotRoot(input, Heap::kUndefinedValueRootIndex, - instr->environment()); - } else { - Register output = ToRegister32(instr->result()); - - DoubleRegister dbl_scratch2 = ToDoubleRegister(temp2); - Label converted; - - // Deoptimized if it's not a heap number. - DeoptimizeIfNotRoot(scratch1, Heap::kHeapNumberMapRootIndex, - instr->environment()); - - // A heap number: load value and convert to int32 using non-truncating - // function. If the result is out of range, branch to deoptimize. - __ Ldr(dbl_scratch1, FieldMemOperand(input, HeapNumber::kValueOffset)); - __ TryConvertDoubleToInt32(output, dbl_scratch1, dbl_scratch2, &converted); - Deoptimize(instr->environment()); - - __ Bind(&converted); - - if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { - __ Cmp(output, 0); - __ B(ne, &done); - __ Fmov(scratch1, dbl_scratch1); - DeoptimizeIfNegative(scratch1, instr->environment()); - } - } - __ Bind(&done); -} - - -void LCodeGen::DoTaggedToI(LTaggedToI* instr) { - class DeferredTaggedToI: public LDeferredCode { - public: - DeferredTaggedToI(LCodeGen* codegen, LTaggedToI* instr) - : LDeferredCode(codegen), instr_(instr) { } - virtual void Generate() { - codegen()->DoDeferredTaggedToI(instr_, instr_->value(), instr_->temp1(), - instr_->temp2()); - } - - virtual LInstruction* instr() { return instr_; } - private: - LTaggedToI* instr_; - }; - - Register input = ToRegister(instr->value()); - Register output = ToRegister(instr->result()); - - if (instr->hydrogen()->value()->representation().IsSmi()) { - __ SmiUntag(input); - } else { - DeferredTaggedToI* deferred = new(zone()) DeferredTaggedToI(this, instr); - - // TODO(jbramley): We can't use JumpIfNotSmi here because the tbz it uses - // doesn't always have enough range. Consider making a variant of it, or a - // TestIsSmi helper. - STATIC_ASSERT(kSmiTag == 0); - __ Tst(input, kSmiTagMask); - __ B(ne, deferred->entry()); - - __ SmiUntag(output, input); - __ Bind(deferred->exit()); - } -} - - -void LCodeGen::DoThisFunction(LThisFunction* instr) { - Register result = ToRegister(instr->result()); - __ Ldr(result, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); -} - - -void LCodeGen::DoToFastProperties(LToFastProperties* instr) { - ASSERT(ToRegister(instr->value()).Is(x0)); - ASSERT(ToRegister(instr->result()).Is(x0)); - __ Push(x0); - CallRuntime(Runtime::kToFastProperties, 1, instr); -} - - -void LCodeGen::DoRegExpLiteral(LRegExpLiteral* instr) { - ASSERT(ToRegister(instr->context()).is(cp)); - Label materialized; - // Registers will be used as follows: - // x7 = literals array. - // x1 = regexp literal. - // x0 = regexp literal clone. - // x10-x12 are used as temporaries. - int literal_offset = - FixedArray::OffsetOfElementAt(instr->hydrogen()->literal_index()); - __ LoadObject(x7, instr->hydrogen()->literals()); - __ Ldr(x1, FieldMemOperand(x7, literal_offset)); - __ JumpIfNotRoot(x1, Heap::kUndefinedValueRootIndex, &materialized); - - // Create regexp literal using runtime function - // Result will be in x0. - __ Mov(x12, Operand(Smi::FromInt(instr->hydrogen()->literal_index()))); - __ Mov(x11, Operand(instr->hydrogen()->pattern())); - __ Mov(x10, Operand(instr->hydrogen()->flags())); - __ Push(x7, x12, x11, x10); - CallRuntime(Runtime::kMaterializeRegExpLiteral, 4, instr); - __ Mov(x1, x0); - - __ Bind(&materialized); - int size = JSRegExp::kSize + JSRegExp::kInObjectFieldCount * kPointerSize; - Label allocated, runtime_allocate; - - __ Allocate(size, x0, x10, x11, &runtime_allocate, TAG_OBJECT); - __ B(&allocated); - - __ Bind(&runtime_allocate); - __ Mov(x0, Operand(Smi::FromInt(size))); - __ Push(x1, x0); - CallRuntime(Runtime::kAllocateInNewSpace, 1, instr); - __ Pop(x1); - - __ Bind(&allocated); - // Copy the content into the newly allocated memory. - __ CopyFields(x0, x1, CPURegList(x10, x11, x12), size / kPointerSize); -} - - -void LCodeGen::DoTransitionElementsKind(LTransitionElementsKind* instr) { - Register object = ToRegister(instr->object()); - Register temp1 = ToRegister(instr->temp1()); - - Handle from_map = instr->original_map(); - Handle to_map = instr->transitioned_map(); - ElementsKind from_kind = instr->from_kind(); - ElementsKind to_kind = instr->to_kind(); - - Label not_applicable; - __ CheckMap(object, temp1, from_map, ¬_applicable, DONT_DO_SMI_CHECK); - - if (IsSimpleMapChangeTransition(from_kind, to_kind)) { - Register new_map = ToRegister(instr->temp2()); - __ Mov(new_map, Operand(to_map)); - __ Str(new_map, FieldMemOperand(object, HeapObject::kMapOffset)); - // Write barrier. - __ RecordWriteField(object, HeapObject::kMapOffset, new_map, temp1, - GetLinkRegisterState(), kDontSaveFPRegs); - } else { - ASSERT(ToRegister(instr->context()).is(cp)); - PushSafepointRegistersScope scope( - this, Safepoint::kWithRegistersAndDoubles); - __ Mov(x0, object); - __ Mov(x1, Operand(to_map)); - TransitionElementsKindStub stub(from_kind, to_kind); - __ CallStub(&stub); - RecordSafepointWithRegistersAndDoubles( - instr->pointer_map(), 0, Safepoint::kNoLazyDeopt); - } - __ Bind(¬_applicable); -} - - -void LCodeGen::DoTrapAllocationMemento(LTrapAllocationMemento* instr) { - Register object = ToRegister(instr->object()); - Register temp1 = ToRegister(instr->temp1()); - Register temp2 = ToRegister(instr->temp2()); - - Label no_memento_found; - __ JumpIfJSArrayHasAllocationMemento(object, temp1, temp2, &no_memento_found); - Deoptimize(instr->environment()); - __ Bind(&no_memento_found); -} - - -void LCodeGen::DoTruncateDoubleToIntOrSmi(LTruncateDoubleToIntOrSmi* instr) { - DoubleRegister input = ToDoubleRegister(instr->value()); - Register result = ToRegister(instr->result()); - __ TruncateDoubleToI(result, input); - if (instr->tag_result()) { - __ SmiTag(result, result); - } -} - - -void LCodeGen::DoTypeof(LTypeof* instr) { - Register input = ToRegister(instr->value()); - __ Push(input); - CallRuntime(Runtime::kTypeof, 1, instr); -} - - -void LCodeGen::DoTypeofIsAndBranch(LTypeofIsAndBranch* instr) { - Handle type_name = instr->type_literal(); - Label* true_label = instr->TrueLabel(chunk_); - Label* false_label = instr->FalseLabel(chunk_); - Register value = ToRegister(instr->value()); - - if (type_name->Equals(heap()->number_string())) { - ASSERT(instr->temp1() != NULL); - Register map = ToRegister(instr->temp1()); - - __ JumpIfSmi(value, true_label); - __ Ldr(map, FieldMemOperand(value, HeapObject::kMapOffset)); - __ CompareRoot(map, Heap::kHeapNumberMapRootIndex); - EmitBranch(instr, eq); - - } else if (type_name->Equals(heap()->string_string())) { - ASSERT((instr->temp1() != NULL) && (instr->temp2() != NULL)); - Register map = ToRegister(instr->temp1()); - Register scratch = ToRegister(instr->temp2()); - - __ JumpIfSmi(value, false_label); - __ JumpIfObjectType( - value, map, scratch, FIRST_NONSTRING_TYPE, false_label, ge); - __ Ldrb(scratch, FieldMemOperand(map, Map::kBitFieldOffset)); - EmitTestAndBranch(instr, eq, scratch, 1 << Map::kIsUndetectable); - - } else if (type_name->Equals(heap()->symbol_string())) { - ASSERT((instr->temp1() != NULL) && (instr->temp2() != NULL)); - Register map = ToRegister(instr->temp1()); - Register scratch = ToRegister(instr->temp2()); - - __ JumpIfSmi(value, false_label); - __ CompareObjectType(value, map, scratch, SYMBOL_TYPE); - EmitBranch(instr, eq); - - } else if (type_name->Equals(heap()->boolean_string())) { - __ JumpIfRoot(value, Heap::kTrueValueRootIndex, true_label); - __ CompareRoot(value, Heap::kFalseValueRootIndex); - EmitBranch(instr, eq); - - } else if (FLAG_harmony_typeof && type_name->Equals(heap()->null_string())) { - __ CompareRoot(value, Heap::kNullValueRootIndex); - EmitBranch(instr, eq); - - } else if (type_name->Equals(heap()->undefined_string())) { - ASSERT(instr->temp1() != NULL); - Register scratch = ToRegister(instr->temp1()); - - __ JumpIfRoot(value, Heap::kUndefinedValueRootIndex, true_label); - __ JumpIfSmi(value, false_label); - // Check for undetectable objects and jump to the true branch in this case. - __ Ldr(scratch, FieldMemOperand(value, HeapObject::kMapOffset)); - __ Ldrb(scratch, FieldMemOperand(scratch, Map::kBitFieldOffset)); - EmitTestAndBranch(instr, ne, scratch, 1 << Map::kIsUndetectable); - - } else if (type_name->Equals(heap()->function_string())) { - STATIC_ASSERT(NUM_OF_CALLABLE_SPEC_OBJECT_TYPES == 2); - ASSERT(instr->temp1() != NULL); - Register type = ToRegister(instr->temp1()); - - __ JumpIfSmi(value, false_label); - __ JumpIfObjectType(value, type, type, JS_FUNCTION_TYPE, true_label); - // HeapObject's type has been loaded into type register by JumpIfObjectType. - EmitCompareAndBranch(instr, eq, type, JS_FUNCTION_PROXY_TYPE); - - } else if (type_name->Equals(heap()->object_string())) { - ASSERT((instr->temp1() != NULL) && (instr->temp2() != NULL)); - Register map = ToRegister(instr->temp1()); - Register scratch = ToRegister(instr->temp2()); - - __ JumpIfSmi(value, false_label); - if (!FLAG_harmony_typeof) { - __ JumpIfRoot(value, Heap::kNullValueRootIndex, true_label); - } - __ JumpIfObjectType(value, map, scratch, - FIRST_NONCALLABLE_SPEC_OBJECT_TYPE, false_label, lt); - __ CompareInstanceType(map, scratch, LAST_NONCALLABLE_SPEC_OBJECT_TYPE); - __ B(gt, false_label); - // Check for undetectable objects => false. - __ Ldrb(scratch, FieldMemOperand(value, Map::kBitFieldOffset)); - EmitTestAndBranch(instr, eq, scratch, 1 << Map::kIsUndetectable); - - } else { - __ B(false_label); - } -} - - -void LCodeGen::DoUint32ToDouble(LUint32ToDouble* instr) { - __ Ucvtf(ToDoubleRegister(instr->result()), ToRegister32(instr->value())); -} - - -void LCodeGen::DoUint32ToSmi(LUint32ToSmi* instr) { - Register value = ToRegister(instr->value()); - Register result = ToRegister(instr->result()); - - if (!instr->hydrogen()->value()->HasRange() || - !instr->hydrogen()->value()->range()->IsInSmiRange() || - instr->hydrogen()->value()->range()->upper() == kMaxInt) { - // The Range class can't express upper bounds in the (kMaxInt, kMaxUint32] - // interval, so we treat kMaxInt as a sentinel for this entire interval. - DeoptimizeIfNegative(value.W(), instr->environment()); - } - __ SmiTag(result, value); -} - - -void LCodeGen::DoCheckMapValue(LCheckMapValue* instr) { - Register object = ToRegister(instr->value()); - Register map = ToRegister(instr->map()); - Register temp = ToRegister(instr->temp()); - __ Ldr(temp, FieldMemOperand(object, HeapObject::kMapOffset)); - __ Cmp(map, temp); - DeoptimizeIf(ne, instr->environment()); -} - - -void LCodeGen::DoWrapReceiver(LWrapReceiver* instr) { - Register receiver = ToRegister(instr->receiver()); - Register function = ToRegister(instr->function()); - Register result = ToRegister(instr->result()); - - // If the receiver is null or undefined, we have to pass the global object as - // a receiver to normal functions. Values have to be passed unchanged to - // builtins and strict-mode functions. - Label global_object, done, deopt; - - if (!instr->hydrogen()->known_function()) { - __ Ldr(result, FieldMemOperand(function, - JSFunction::kSharedFunctionInfoOffset)); - - // CompilerHints is an int32 field. See objects.h. - __ Ldr(result.W(), - FieldMemOperand(result, SharedFunctionInfo::kCompilerHintsOffset)); - - // Do not transform the receiver to object for strict mode functions. - __ Tbnz(result, SharedFunctionInfo::kStrictModeFunction, &done); - - // Do not transform the receiver to object for builtins. - __ Tbnz(result, SharedFunctionInfo::kNative, &done); - } - - // Normal function. Replace undefined or null with global receiver. - __ JumpIfRoot(receiver, Heap::kNullValueRootIndex, &global_object); - __ JumpIfRoot(receiver, Heap::kUndefinedValueRootIndex, &global_object); - - // Deoptimize if the receiver is not a JS object. - __ JumpIfSmi(receiver, &deopt); - __ CompareObjectType(receiver, result, result, FIRST_SPEC_OBJECT_TYPE); - __ Mov(result, receiver); - __ B(ge, &done); - // Otherwise, fall through to deopt. - - __ Bind(&deopt); - Deoptimize(instr->environment()); - - __ Bind(&global_object); - __ Ldr(result, FieldMemOperand(function, JSFunction::kContextOffset)); - __ Ldr(result, ContextMemOperand(result, Context::GLOBAL_OBJECT_INDEX)); - __ Ldr(result, FieldMemOperand(result, GlobalObject::kGlobalReceiverOffset)); - - __ Bind(&done); -} - - -void LCodeGen::DoLoadFieldByIndex(LLoadFieldByIndex* instr) { - Register object = ToRegister(instr->object()); - Register index = ToRegister(instr->index()); - Register result = ToRegister(instr->result()); - - __ AssertSmi(index); - - Label out_of_object, done; - __ Cmp(index, Operand(Smi::FromInt(0))); - __ B(lt, &out_of_object); - - STATIC_ASSERT(kPointerSizeLog2 > kSmiTagSize); - __ Add(result, object, Operand::UntagSmiAndScale(index, kPointerSizeLog2)); - __ Ldr(result, FieldMemOperand(result, JSObject::kHeaderSize)); - - __ B(&done); - - __ Bind(&out_of_object); - __ Ldr(result, FieldMemOperand(object, JSObject::kPropertiesOffset)); - // Index is equal to negated out of object property index plus 1. - __ Sub(result, result, Operand::UntagSmiAndScale(index, kPointerSizeLog2)); - __ Ldr(result, FieldMemOperand(result, - FixedArray::kHeaderSize - kPointerSize)); - __ Bind(&done); -} - -} } // namespace v8::internal diff --git a/deps/v8/src/a64/lithium-codegen-a64.h b/deps/v8/src/a64/lithium-codegen-a64.h deleted file mode 100644 index 006165157f..0000000000 --- a/deps/v8/src/a64/lithium-codegen-a64.h +++ /dev/null @@ -1,473 +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. - -#ifndef V8_A64_LITHIUM_CODEGEN_A64_H_ -#define V8_A64_LITHIUM_CODEGEN_A64_H_ - -#include "a64/lithium-a64.h" - -#include "a64/lithium-gap-resolver-a64.h" -#include "deoptimizer.h" -#include "lithium-codegen.h" -#include "safepoint-table.h" -#include "scopes.h" -#include "v8utils.h" - -namespace v8 { -namespace internal { - -// Forward declarations. -class LDeferredCode; -class SafepointGenerator; -class BranchGenerator; - -class LCodeGen: public LCodeGenBase { - public: - LCodeGen(LChunk* chunk, MacroAssembler* assembler, CompilationInfo* info) - : LCodeGenBase(chunk, assembler, info), - deoptimizations_(4, info->zone()), - deopt_jump_table_(4, info->zone()), - deoptimization_literals_(8, info->zone()), - inlined_function_count_(0), - scope_(info->scope()), - translations_(info->zone()), - deferred_(8, info->zone()), - osr_pc_offset_(-1), - frame_is_built_(false), - safepoints_(info->zone()), - resolver_(this), - expected_safepoint_kind_(Safepoint::kSimple) { - PopulateDeoptimizationLiteralsWithInlinedFunctions(); - } - - // Simple accessors. - Scope* scope() const { return scope_; } - - int LookupDestination(int block_id) const { - return chunk()->LookupDestination(block_id); - } - - bool IsNextEmittedBlock(int block_id) const { - return LookupDestination(block_id) == GetNextEmittedBlock(); - } - - bool NeedsEagerFrame() const { - return GetStackSlotCount() > 0 || - info()->is_non_deferred_calling() || - !info()->IsStub() || - info()->requires_frame(); - } - bool NeedsDeferredFrame() const { - return !NeedsEagerFrame() && info()->is_deferred_calling(); - } - - LinkRegisterStatus GetLinkRegisterState() const { - return frame_is_built_ ? kLRHasBeenSaved : kLRHasNotBeenSaved; - } - - // Try to generate code for the entire chunk, but it may fail if the - // chunk contains constructs we cannot handle. Returns true if the - // code generation attempt succeeded. - bool GenerateCode(); - - // Finish the code by setting stack height, safepoint, and bailout - // information on it. - void FinishCode(Handle code); - - // Support for converting LOperands to assembler types. - // LOperand must be a register. - Register ToRegister(LOperand* op) const; - Register ToRegister32(LOperand* op) const; - Operand ToOperand(LOperand* op); - Operand ToOperand32I(LOperand* op); - Operand ToOperand32U(LOperand* op); - MemOperand ToMemOperand(LOperand* op) const; - Handle ToHandle(LConstantOperand* op) const; - - // TODO(jbramley): Examine these helpers and check that they make sense. - // IsInteger32Constant returns true for smi constants, for example. - bool IsInteger32Constant(LConstantOperand* op) const; - bool IsSmi(LConstantOperand* op) const; - - int32_t ToInteger32(LConstantOperand* op) const; - Smi* ToSmi(LConstantOperand* op) const; - double ToDouble(LConstantOperand* op) const; - DoubleRegister ToDoubleRegister(LOperand* op) const; - - // Declare methods that deal with the individual node types. -#define DECLARE_DO(type) void Do##type(L##type* node); - LITHIUM_CONCRETE_INSTRUCTION_LIST(DECLARE_DO) -#undef DECLARE_DO - - private: - // Return a double scratch register which can be used locally - // when generating code for a lithium instruction. - DoubleRegister double_scratch() { return crankshaft_fp_scratch; } - - // Deferred code support. - void DoDeferredNumberTagD(LNumberTagD* instr); - void DoDeferredStackCheck(LStackCheck* instr); - void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr); - void DoDeferredStringCharFromCode(LStringCharFromCode* instr); - void DoDeferredMathAbsTagged(LMathAbsTagged* instr, - Label* exit, - Label* allocation_entry); - - enum IntegerSignedness { SIGNED_INT32, UNSIGNED_INT32 }; - void DoDeferredNumberTagU(LInstruction* instr, - LOperand* value, - LOperand* temp1, - LOperand* temp2); - void DoDeferredTaggedToI(LTaggedToI* instr, - LOperand* value, - LOperand* temp1, - LOperand* temp2); - void DoDeferredAllocate(LAllocate* instr); - void DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr); - void DoDeferredInstanceMigration(LCheckMaps* instr, Register object); - - Operand ToOperand32(LOperand* op, IntegerSignedness signedness); - - static Condition TokenToCondition(Token::Value op, bool is_unsigned); - void EmitGoto(int block); - void DoGap(LGap* instr); - - // Generic version of EmitBranch. It contains some code to avoid emitting a - // branch on the next emitted basic block where we could just fall-through. - // You shouldn't use that directly but rather consider one of the helper like - // LCodeGen::EmitBranch, LCodeGen::EmitCompareAndBranch... - template - void EmitBranchGeneric(InstrType instr, - const BranchGenerator& branch); - - template - void EmitBranch(InstrType instr, Condition condition); - - template - void EmitCompareAndBranch(InstrType instr, - Condition condition, - const Register& lhs, - const Operand& rhs); - - template - void EmitTestAndBranch(InstrType instr, - Condition condition, - const Register& value, - uint64_t mask); - - template - void EmitBranchIfNonZeroNumber(InstrType instr, - const FPRegister& value, - const FPRegister& scratch); - - template - void EmitBranchIfHeapNumber(InstrType instr, - const Register& value); - - template - void EmitBranchIfRoot(InstrType instr, - const Register& value, - Heap::RootListIndex index); - - // Emits optimized code to deep-copy the contents of statically known object - // graphs (e.g. object literal boilerplate). Expects a pointer to the - // allocated destination object in the result register, and a pointer to the - // source object in the source register. - void EmitDeepCopy(Handle object, - Register result, - Register source, - Register scratch, - int* offset, - AllocationSiteMode mode); - - // Emits optimized code for %_IsString(x). Preserves input register. - // Returns the condition on which a final split to - // true and false label should be made, to optimize fallthrough. - Condition EmitIsString(Register input, Register temp1, Label* is_not_string, - SmiCheck check_needed); - - int DefineDeoptimizationLiteral(Handle literal); - void PopulateDeoptimizationData(Handle code); - void PopulateDeoptimizationLiteralsWithInlinedFunctions(); - - MemOperand BuildSeqStringOperand(Register string, - Register temp, - LOperand* index, - String::Encoding encoding); - Deoptimizer::BailoutType DeoptimizeHeader( - LEnvironment* environment, - Deoptimizer::BailoutType* override_bailout_type); - void Deoptimize(LEnvironment* environment); - void Deoptimize(LEnvironment* environment, - Deoptimizer::BailoutType bailout_type); - void DeoptimizeIf(Condition cc, LEnvironment* environment); - void DeoptimizeIfZero(Register rt, LEnvironment* environment); - void DeoptimizeIfNegative(Register rt, LEnvironment* environment); - void DeoptimizeIfSmi(Register rt, LEnvironment* environment); - void DeoptimizeIfNotSmi(Register rt, LEnvironment* environment); - void DeoptimizeIfRoot(Register rt, - Heap::RootListIndex index, - LEnvironment* environment); - void DeoptimizeIfNotRoot(Register rt, - Heap::RootListIndex index, - LEnvironment* environment); - void ApplyCheckIf(Condition cc, LBoundsCheck* check); - - MemOperand PrepareKeyedExternalArrayOperand(Register key, - Register base, - Register scratch, - bool key_is_smi, - bool key_is_constant, - int constant_key, - ElementsKind elements_kind, - int additional_index); - void CalcKeyedArrayBaseRegister(Register base, - Register elements, - Register key, - bool key_is_tagged, - ElementsKind elements_kind); - - void RegisterEnvironmentForDeoptimization(LEnvironment* environment, - Safepoint::DeoptMode mode); - - int GetStackSlotCount() const { return chunk()->spill_slot_count(); } - - void Abort(BailoutReason reason); - - void AddDeferredCode(LDeferredCode* code) { deferred_.Add(code, zone()); } - - // Emit frame translation commands for an environment. - void WriteTranslation(LEnvironment* environment, Translation* translation); - - void AddToTranslation(LEnvironment* environment, - Translation* translation, - LOperand* op, - bool is_tagged, - bool is_uint32, - int* object_index_pointer, - int* dematerialized_index_pointer); - - void SaveCallerDoubles(); - void RestoreCallerDoubles(); - - // Code generation steps. Returns true if code generation should continue. - bool GeneratePrologue(); - bool GenerateDeferredCode(); - bool GenerateDeoptJumpTable(); - bool GenerateSafepointTable(); - - // Generates the custom OSR entrypoint and sets the osr_pc_offset. - void GenerateOsrPrologue(); - - enum SafepointMode { - RECORD_SIMPLE_SAFEPOINT, - RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS - }; - - void CallCode(Handle code, - RelocInfo::Mode mode, - LInstruction* instr); - - void CallCodeGeneric(Handle code, - RelocInfo::Mode mode, - LInstruction* instr, - SafepointMode safepoint_mode); - - void CallRuntime(const Runtime::Function* function, - int num_arguments, - LInstruction* instr, - SaveFPRegsMode save_doubles = kDontSaveFPRegs); - - void CallRuntime(Runtime::FunctionId id, - int num_arguments, - LInstruction* instr) { - const Runtime::Function* function = Runtime::FunctionForId(id); - CallRuntime(function, num_arguments, instr); - } - - void LoadContextFromDeferred(LOperand* context); - void CallRuntimeFromDeferred(Runtime::FunctionId id, - int argc, - LInstruction* instr, - LOperand* context); - - // Generate a direct call to a known function. - // If the function is already loaded into x1 by the caller, function_reg may - // be set to x1. Otherwise, it must be NoReg, and CallKnownFunction will - // automatically load it. - void CallKnownFunction(Handle function, - int formal_parameter_count, - int arity, - LInstruction* instr, - Register function_reg = NoReg); - - // Support for recording safepoint and position information. - void RecordAndWritePosition(int position) V8_OVERRIDE; - void RecordSafepoint(LPointerMap* pointers, - Safepoint::Kind kind, - int arguments, - Safepoint::DeoptMode mode); - void RecordSafepoint(LPointerMap* pointers, Safepoint::DeoptMode mode); - void RecordSafepoint(Safepoint::DeoptMode mode); - void RecordSafepointWithRegisters(LPointerMap* pointers, - int arguments, - Safepoint::DeoptMode mode); - void RecordSafepointWithRegistersAndDoubles(LPointerMap* pointers, - int arguments, - Safepoint::DeoptMode mode); - void RecordSafepointWithLazyDeopt(LInstruction* instr, - SafepointMode safepoint_mode); - - void EnsureSpaceForLazyDeopt(int space_needed) V8_OVERRIDE; - - ZoneList deoptimizations_; - ZoneList deopt_jump_table_; - ZoneList > deoptimization_literals_; - int inlined_function_count_; - Scope* const scope_; - TranslationBuffer translations_; - ZoneList deferred_; - int osr_pc_offset_; - bool frame_is_built_; - - // Builder that keeps track of safepoints in the code. The table itself is - // emitted at the end of the generated code. - SafepointTableBuilder safepoints_; - - // Compiler from a set of parallel moves to a sequential list of moves. - LGapResolver resolver_; - - Safepoint::Kind expected_safepoint_kind_; - - int old_position_; - - class PushSafepointRegistersScope BASE_EMBEDDED { - public: - PushSafepointRegistersScope(LCodeGen* codegen, - Safepoint::Kind kind) - : codegen_(codegen) { - ASSERT(codegen_->info()->is_calling()); - ASSERT(codegen_->expected_safepoint_kind_ == Safepoint::kSimple); - codegen_->expected_safepoint_kind_ = kind; - - switch (codegen_->expected_safepoint_kind_) { - case Safepoint::kWithRegisters: - codegen_->masm_->PushSafepointRegisters(); - break; - case Safepoint::kWithRegistersAndDoubles: - codegen_->masm_->PushSafepointRegisters(); - codegen_->masm_->PushSafepointFPRegisters(); - break; - default: - UNREACHABLE(); - } - } - - ~PushSafepointRegistersScope() { - Safepoint::Kind kind = codegen_->expected_safepoint_kind_; - ASSERT((kind & Safepoint::kWithRegisters) != 0); - switch (kind) { - case Safepoint::kWithRegisters: - codegen_->masm_->PopSafepointRegisters(); - break; - case Safepoint::kWithRegistersAndDoubles: - codegen_->masm_->PopSafepointFPRegisters(); - codegen_->masm_->PopSafepointRegisters(); - break; - default: - UNREACHABLE(); - } - codegen_->expected_safepoint_kind_ = Safepoint::kSimple; - } - - private: - LCodeGen* codegen_; - }; - - friend class LDeferredCode; - friend class SafepointGenerator; - DISALLOW_COPY_AND_ASSIGN(LCodeGen); -}; - - -class LDeferredCode: public ZoneObject { - public: - explicit LDeferredCode(LCodeGen* codegen) - : codegen_(codegen), - external_exit_(NULL), - instruction_index_(codegen->current_instruction_) { - codegen->AddDeferredCode(this); - } - - virtual ~LDeferredCode() { } - virtual void Generate() = 0; - virtual LInstruction* instr() = 0; - - void SetExit(Label* exit) { external_exit_ = exit; } - Label* entry() { return &entry_; } - Label* exit() { return (external_exit_ != NULL) ? external_exit_ : &exit_; } - int instruction_index() const { return instruction_index_; } - - protected: - LCodeGen* codegen() const { return codegen_; } - MacroAssembler* masm() const { return codegen_->masm(); } - - private: - LCodeGen* codegen_; - Label entry_; - Label exit_; - Label* external_exit_; - int instruction_index_; -}; - - -// This is the abstract class used by EmitBranchGeneric. -// It is used to emit code for conditional branching. The Emit() function -// emits code to branch when the condition holds and EmitInverted() emits -// the branch when the inverted condition is verified. -// -// For actual examples of condition see the concrete implementation in -// lithium-codegen-a64.cc (e.g. BranchOnCondition, CompareAndBranch). -class BranchGenerator BASE_EMBEDDED { - public: - explicit BranchGenerator(LCodeGen* codegen) - : codegen_(codegen) { } - - virtual ~BranchGenerator() { } - - virtual void Emit(Label* label) const = 0; - virtual void EmitInverted(Label* label) const = 0; - - protected: - MacroAssembler* masm() const { return codegen_->masm(); } - - LCodeGen* codegen_; -}; - -} } // namespace v8::internal - -#endif // V8_A64_LITHIUM_CODEGEN_A64_H_ diff --git a/deps/v8/src/a64/lithium-gap-resolver-a64.cc b/deps/v8/src/a64/lithium-gap-resolver-a64.cc deleted file mode 100644 index 3087a3e930..0000000000 --- a/deps/v8/src/a64/lithium-gap-resolver-a64.cc +++ /dev/null @@ -1,326 +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 "v8.h" - -#include "a64/lithium-gap-resolver-a64.h" -#include "a64/lithium-codegen-a64.h" - -namespace v8 { -namespace internal { - -// We use the root register to spill a value while breaking a cycle in parallel -// moves. We don't need access to roots while resolving the move list and using -// the root register has two advantages: -// - It is not in crankshaft allocatable registers list, so it can't interfere -// with any of the moves we are resolving. -// - We don't need to push it on the stack, as we can reload it with its value -// once we have resolved a cycle. -#define kSavedValue root - -LGapResolver::LGapResolver(LCodeGen* owner) - : cgen_(owner), moves_(32, owner->zone()), root_index_(0), in_cycle_(false), - saved_destination_(NULL), need_to_restore_root_(false) { } - - -#define __ ACCESS_MASM(cgen_->masm()) - -void LGapResolver::Resolve(LParallelMove* parallel_move) { - ASSERT(moves_.is_empty()); - - // Build up a worklist of moves. - BuildInitialMoveList(parallel_move); - - for (int i = 0; i < moves_.length(); ++i) { - LMoveOperands move = moves_[i]; - - // Skip constants to perform them last. They don't block other moves - // and skipping such moves with register destinations keeps those - // registers free for the whole algorithm. - if (!move.IsEliminated() && !move.source()->IsConstantOperand()) { - root_index_ = i; // Any cycle is found when we reach this move again. - PerformMove(i); - if (in_cycle_) RestoreValue(); - } - } - - // Perform the moves with constant sources. - for (int i = 0; i < moves_.length(); ++i) { - LMoveOperands move = moves_[i]; - - if (!move.IsEliminated()) { - ASSERT(move.source()->IsConstantOperand()); - EmitMove(i); - } - } - - if (need_to_restore_root_) { - ASSERT(kSavedValue.Is(root)); - __ InitializeRootRegister(); - need_to_restore_root_ = false; - } - - moves_.Rewind(0); -} - - -void LGapResolver::BuildInitialMoveList(LParallelMove* parallel_move) { - // Perform a linear sweep of the moves to add them to the initial list of - // moves to perform, ignoring any move that is redundant (the source is - // the same as the destination, the destination is ignored and - // unallocated, or the move was already eliminated). - const ZoneList* moves = parallel_move->move_operands(); - for (int i = 0; i < moves->length(); ++i) { - LMoveOperands move = moves->at(i); - if (!move.IsRedundant()) moves_.Add(move, cgen_->zone()); - } - Verify(); -} - - -void LGapResolver::PerformMove(int index) { - // Each call to this function performs a move and deletes it from the move - // graph. We first recursively perform any move blocking this one. We - // mark a move as "pending" on entry to PerformMove in order to detect - // cycles in the move graph. - LMoveOperands& current_move = moves_[index]; - - ASSERT(!current_move.IsPending()); - ASSERT(!current_move.IsRedundant()); - - // Clear this move's destination to indicate a pending move. The actual - // destination is saved in a stack allocated local. Multiple moves can - // be pending because this function is recursive. - ASSERT(current_move.source() != NULL); // Otherwise it will look eliminated. - LOperand* destination = current_move.destination(); - current_move.set_destination(NULL); - - // Perform a depth-first traversal of the move graph to resolve - // dependencies. Any unperformed, unpending move with a source the same - // as this one's destination blocks this one so recursively perform all - // such moves. - for (int i = 0; i < moves_.length(); ++i) { - LMoveOperands other_move = moves_[i]; - if (other_move.Blocks(destination) && !other_move.IsPending()) { - PerformMove(i); - // If there is a blocking, pending move it must be moves_[root_index_] - // and all other moves with the same source as moves_[root_index_] are - // sucessfully executed (because they are cycle-free) by this loop. - } - } - - // We are about to resolve this move and don't need it marked as - // pending, so restore its destination. - current_move.set_destination(destination); - - // The move may be blocked on a pending move, which must be the starting move. - // In this case, we have a cycle, and we save the source of this move to - // a scratch register to break it. - LMoveOperands other_move = moves_[root_index_]; - if (other_move.Blocks(destination)) { - ASSERT(other_move.IsPending()); - BreakCycle(index); - return; - } - - // This move is no longer blocked. - EmitMove(index); -} - - -void LGapResolver::Verify() { -#ifdef ENABLE_SLOW_ASSERTS - // No operand should be the destination for more than one move. - for (int i = 0; i < moves_.length(); ++i) { - LOperand* destination = moves_[i].destination(); - for (int j = i + 1; j < moves_.length(); ++j) { - SLOW_ASSERT(!destination->Equals(moves_[j].destination())); - } - } -#endif -} - - -void LGapResolver::BreakCycle(int index) { - ASSERT(moves_[index].destination()->Equals(moves_[root_index_].source())); - ASSERT(!in_cycle_); - - // We use a register which is not allocatable by crankshaft to break the cycle - // to be sure it doesn't interfere with the moves we are resolving. - ASSERT(!kSavedValue.IsAllocatable()); - need_to_restore_root_ = true; - - // We save in a register the source of that move and we remember its - // destination. Then we mark this move as resolved so the cycle is - // broken and we can perform the other moves. - in_cycle_ = true; - LOperand* source = moves_[index].source(); - saved_destination_ = moves_[index].destination(); - - if (source->IsRegister()) { - __ Mov(kSavedValue, cgen_->ToRegister(source)); - } else if (source->IsStackSlot()) { - __ Ldr(kSavedValue, cgen_->ToMemOperand(source)); - } else if (source->IsDoubleRegister()) { - // TODO(all): We should use a double register to store the value to avoid - // the penalty of the mov across register banks. We are going to reserve - // d31 to hold 0.0 value. We could clobber this register while breaking the - // cycle and restore it after like we do with the root register. - // LGapResolver::RestoreValue() will need to be updated as well when we'll - // do that. - __ Fmov(kSavedValue, cgen_->ToDoubleRegister(source)); - } else if (source->IsDoubleStackSlot()) { - __ Ldr(kSavedValue, cgen_->ToMemOperand(source)); - } else { - UNREACHABLE(); - } - - // Mark this move as resolved. - // This move will be actually performed by moving the saved value to this - // move's destination in LGapResolver::RestoreValue(). - moves_[index].Eliminate(); -} - - -void LGapResolver::RestoreValue() { - ASSERT(in_cycle_); - ASSERT(saved_destination_ != NULL); - - if (saved_destination_->IsRegister()) { - __ Mov(cgen_->ToRegister(saved_destination_), kSavedValue); - } else if (saved_destination_->IsStackSlot()) { - __ Str(kSavedValue, cgen_->ToMemOperand(saved_destination_)); - } else if (saved_destination_->IsDoubleRegister()) { - __ Fmov(cgen_->ToDoubleRegister(saved_destination_), kSavedValue); - } else if (saved_destination_->IsDoubleStackSlot()) { - __ Str(kSavedValue, cgen_->ToMemOperand(saved_destination_)); - } else { - UNREACHABLE(); - } - - in_cycle_ = false; - saved_destination_ = NULL; -} - - -void LGapResolver::EmitMove(int index) { - LOperand* source = moves_[index].source(); - LOperand* destination = moves_[index].destination(); - - // Dispatch on the source and destination operand kinds. Not all - // combinations are possible. - - if (source->IsRegister()) { - Register source_register = cgen_->ToRegister(source); - if (destination->IsRegister()) { - __ Mov(cgen_->ToRegister(destination), source_register); - } else { - ASSERT(destination->IsStackSlot()); - __ Str(source_register, cgen_->ToMemOperand(destination)); - } - - } else if (source->IsStackSlot()) { - MemOperand source_operand = cgen_->ToMemOperand(source); - if (destination->IsRegister()) { - __ Ldr(cgen_->ToRegister(destination), source_operand); - } else { - ASSERT(destination->IsStackSlot()); - EmitStackSlotMove(index); - } - - } else if (source->IsConstantOperand()) { - LConstantOperand* constant_source = LConstantOperand::cast(source); - if (destination->IsRegister()) { - Register dst = cgen_->ToRegister(destination); - if (cgen_->IsSmi(constant_source)) { - __ Mov(dst, Operand(cgen_->ToSmi(constant_source))); - } else if (cgen_->IsInteger32Constant(constant_source)) { - __ Mov(dst, cgen_->ToInteger32(constant_source)); - } else { - __ LoadObject(dst, cgen_->ToHandle(constant_source)); - } - } else if (destination->IsDoubleRegister()) { - DoubleRegister result = cgen_->ToDoubleRegister(destination); - __ Fmov(result, cgen_->ToDouble(constant_source)); - } else { - ASSERT(destination->IsStackSlot()); - ASSERT(!in_cycle_); // Constant moves happen after all cycles are gone. - need_to_restore_root_ = true; - if (cgen_->IsSmi(constant_source)) { - __ Mov(kSavedValue, Operand(cgen_->ToSmi(constant_source))); - } else if (cgen_->IsInteger32Constant(constant_source)) { - __ Mov(kSavedValue, cgen_->ToInteger32(constant_source)); - } else { - __ LoadObject(kSavedValue, cgen_->ToHandle(constant_source)); - } - __ Str(kSavedValue, cgen_->ToMemOperand(destination)); - } - - } else if (source->IsDoubleRegister()) { - DoubleRegister src = cgen_->ToDoubleRegister(source); - if (destination->IsDoubleRegister()) { - __ Fmov(cgen_->ToDoubleRegister(destination), src); - } else { - ASSERT(destination->IsDoubleStackSlot()); - __ Str(src, cgen_->ToMemOperand(destination)); - } - - } else if (source->IsDoubleStackSlot()) { - MemOperand src = cgen_->ToMemOperand(source); - if (destination->IsDoubleRegister()) { - __ Ldr(cgen_->ToDoubleRegister(destination), src); - } else { - ASSERT(destination->IsDoubleStackSlot()); - EmitStackSlotMove(index); - } - - } else { - UNREACHABLE(); - } - - // The move has been emitted, we can eliminate it. - moves_[index].Eliminate(); -} - - -void LGapResolver::EmitStackSlotMove(int index) { - // We need a temp register to perform a stack slot to stack slot move, and - // the register must not be involved in breaking cycles. - - // Use the Crankshaft double scratch register as the temporary. - DoubleRegister temp = crankshaft_fp_scratch; - - LOperand* src = moves_[index].source(); - LOperand* dst = moves_[index].destination(); - - ASSERT(src->IsStackSlot()); - ASSERT(dst->IsStackSlot()); - __ Ldr(temp, cgen_->ToMemOperand(src)); - __ Str(temp, cgen_->ToMemOperand(dst)); -} - -} } // namespace v8::internal diff --git a/deps/v8/src/a64/lithium-gap-resolver-a64.h b/deps/v8/src/a64/lithium-gap-resolver-a64.h deleted file mode 100644 index 427065933e..0000000000 --- a/deps/v8/src/a64/lithium-gap-resolver-a64.h +++ /dev/null @@ -1,90 +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. - -#ifndef V8_A64_LITHIUM_GAP_RESOLVER_A64_H_ -#define V8_A64_LITHIUM_GAP_RESOLVER_A64_H_ - -#include "v8.h" - -#include "lithium.h" - -namespace v8 { -namespace internal { - -class LCodeGen; -class LGapResolver; - -class LGapResolver BASE_EMBEDDED { - public: - explicit LGapResolver(LCodeGen* owner); - - // Resolve a set of parallel moves, emitting assembler instructions. - void Resolve(LParallelMove* parallel_move); - - private: - // Build the initial list of moves. - void BuildInitialMoveList(LParallelMove* parallel_move); - - // Perform the move at the moves_ index in question (possibly requiring - // other moves to satisfy dependencies). - void PerformMove(int index); - - // If a cycle is found in the series of moves, save the blocking value to - // a scratch register. The cycle must be found by hitting the root of the - // depth-first search. - void BreakCycle(int index); - - // After a cycle has been resolved, restore the value from the scratch - // register to its proper destination. - void RestoreValue(); - - // Emit a move and remove it from the move graph. - void EmitMove(int index); - - // Emit a move from one stack slot to another. - void EmitStackSlotMove(int index); - - // Verify the move list before performing moves. - void Verify(); - - LCodeGen* cgen_; - - // List of moves not yet resolved. - ZoneList moves_; - - int root_index_; - bool in_cycle_; - LOperand* saved_destination_; - - // We use the root register as a scratch in a few places. When that happens, - // this flag is set to indicate that it needs to be restored. - bool need_to_restore_root_; -}; - -} } // namespace v8::internal - -#endif // V8_A64_LITHIUM_GAP_RESOLVER_A64_H_ diff --git a/deps/v8/src/a64/macro-assembler-a64-inl.h b/deps/v8/src/a64/macro-assembler-a64-inl.h deleted file mode 100644 index 0c62a8b62e..0000000000 --- a/deps/v8/src/a64/macro-assembler-a64-inl.h +++ /dev/null @@ -1,1647 +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. - -#ifndef V8_A64_MACRO_ASSEMBLER_A64_INL_H_ -#define V8_A64_MACRO_ASSEMBLER_A64_INL_H_ - -#include - -#include "v8globals.h" -#include "globals.h" - -#include "a64/assembler-a64.h" -#include "a64/assembler-a64-inl.h" -#include "a64/macro-assembler-a64.h" -#include "a64/instrument-a64.h" - - -namespace v8 { -namespace internal { - - -MemOperand FieldMemOperand(Register object, int offset) { - return MemOperand(object, offset - kHeapObjectTag); -} - - -MemOperand UntagSmiFieldMemOperand(Register object, int offset) { - return UntagSmiMemOperand(object, offset - kHeapObjectTag); -} - - -MemOperand UntagSmiMemOperand(Register object, int offset) { - // Assumes that Smis are shifted by 32 bits and little endianness. - STATIC_ASSERT(kSmiShift == 32); - return MemOperand(object, offset + (kSmiShift / kBitsPerByte)); -} - - -Handle MacroAssembler::CodeObject() { - ASSERT(!code_object_.is_null()); - return code_object_; -} - - -void MacroAssembler::And(const Register& rd, - const Register& rn, - const Operand& operand) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - LogicalMacro(rd, rn, operand, AND); -} - - -void MacroAssembler::Ands(const Register& rd, - const Register& rn, - const Operand& operand) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - LogicalMacro(rd, rn, operand, ANDS); -} - - -void MacroAssembler::Tst(const Register& rn, - const Operand& operand) { - ASSERT(allow_macro_instructions_); - LogicalMacro(AppropriateZeroRegFor(rn), rn, operand, ANDS); -} - - -void MacroAssembler::Bic(const Register& rd, - const Register& rn, - const Operand& operand) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - LogicalMacro(rd, rn, operand, BIC); -} - - -void MacroAssembler::Bics(const Register& rd, - const Register& rn, - const Operand& operand) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - LogicalMacro(rd, rn, operand, BICS); -} - - -void MacroAssembler::Orr(const Register& rd, - const Register& rn, - const Operand& operand) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - LogicalMacro(rd, rn, operand, ORR); -} - - -void MacroAssembler::Orn(const Register& rd, - const Register& rn, - const Operand& operand) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - LogicalMacro(rd, rn, operand, ORN); -} - - -void MacroAssembler::Eor(const Register& rd, - const Register& rn, - const Operand& operand) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - LogicalMacro(rd, rn, operand, EOR); -} - - -void MacroAssembler::Eon(const Register& rd, - const Register& rn, - const Operand& operand) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - LogicalMacro(rd, rn, operand, EON); -} - - -void MacroAssembler::Ccmp(const Register& rn, - const Operand& operand, - StatusFlags nzcv, - Condition cond) { - ASSERT(allow_macro_instructions_); - if (operand.IsImmediate() && (operand.immediate() < 0)) { - ConditionalCompareMacro(rn, -operand.immediate(), nzcv, cond, CCMN); - } else { - ConditionalCompareMacro(rn, operand, nzcv, cond, CCMP); - } -} - - -void MacroAssembler::Ccmn(const Register& rn, - const Operand& operand, - StatusFlags nzcv, - Condition cond) { - ASSERT(allow_macro_instructions_); - if (operand.IsImmediate() && (operand.immediate() < 0)) { - ConditionalCompareMacro(rn, -operand.immediate(), nzcv, cond, CCMP); - } else { - ConditionalCompareMacro(rn, operand, nzcv, cond, CCMN); - } -} - - -void MacroAssembler::Add(const Register& rd, - const Register& rn, - const Operand& operand) { - ASSERT(allow_macro_instructions_); - if (operand.IsImmediate() && (operand.immediate() < 0)) { - AddSubMacro(rd, rn, -operand.immediate(), LeaveFlags, SUB); - } else { - AddSubMacro(rd, rn, operand, LeaveFlags, ADD); - } -} - -void MacroAssembler::Adds(const Register& rd, - const Register& rn, - const Operand& operand) { - ASSERT(allow_macro_instructions_); - if (operand.IsImmediate() && (operand.immediate() < 0)) { - AddSubMacro(rd, rn, -operand.immediate(), SetFlags, SUB); - } else { - AddSubMacro(rd, rn, operand, SetFlags, ADD); - } -} - - -void MacroAssembler::Sub(const Register& rd, - const Register& rn, - const Operand& operand) { - ASSERT(allow_macro_instructions_); - if (operand.IsImmediate() && (operand.immediate() < 0)) { - AddSubMacro(rd, rn, -operand.immediate(), LeaveFlags, ADD); - } else { - AddSubMacro(rd, rn, operand, LeaveFlags, SUB); - } -} - - -void MacroAssembler::Subs(const Register& rd, - const Register& rn, - const Operand& operand) { - ASSERT(allow_macro_instructions_); - if (operand.IsImmediate() && (operand.immediate() < 0)) { - AddSubMacro(rd, rn, -operand.immediate(), SetFlags, ADD); - } else { - AddSubMacro(rd, rn, operand, SetFlags, SUB); - } -} - - -void MacroAssembler::Cmn(const Register& rn, const Operand& operand) { - ASSERT(allow_macro_instructions_); - Adds(AppropriateZeroRegFor(rn), rn, operand); -} - - -void MacroAssembler::Cmp(const Register& rn, const Operand& operand) { - ASSERT(allow_macro_instructions_); - Subs(AppropriateZeroRegFor(rn), rn, operand); -} - - -void MacroAssembler::Neg(const Register& rd, - const Operand& operand) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - if (operand.IsImmediate()) { - Mov(rd, -operand.immediate()); - } else { - Sub(rd, AppropriateZeroRegFor(rd), operand); - } -} - - -void MacroAssembler::Negs(const Register& rd, - const Operand& operand) { - ASSERT(allow_macro_instructions_); - Subs(rd, AppropriateZeroRegFor(rd), operand); -} - - -void MacroAssembler::Adc(const Register& rd, - const Register& rn, - const Operand& operand) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - AddSubWithCarryMacro(rd, rn, operand, LeaveFlags, ADC); -} - - -void MacroAssembler::Adcs(const Register& rd, - const Register& rn, - const Operand& operand) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - AddSubWithCarryMacro(rd, rn, operand, SetFlags, ADC); -} - - -void MacroAssembler::Sbc(const Register& rd, - const Register& rn, - const Operand& operand) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - AddSubWithCarryMacro(rd, rn, operand, LeaveFlags, SBC); -} - - -void MacroAssembler::Sbcs(const Register& rd, - const Register& rn, - const Operand& operand) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - AddSubWithCarryMacro(rd, rn, operand, SetFlags, SBC); -} - - -void MacroAssembler::Ngc(const Register& rd, - const Operand& operand) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - Register zr = AppropriateZeroRegFor(rd); - Sbc(rd, zr, operand); -} - - -void MacroAssembler::Ngcs(const Register& rd, - const Operand& operand) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - Register zr = AppropriateZeroRegFor(rd); - Sbcs(rd, zr, operand); -} - - -void MacroAssembler::Mvn(const Register& rd, uint64_t imm) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - Mov(rd, ~imm); -} - - -#define DEFINE_FUNCTION(FN, REGTYPE, REG, OP) \ -void MacroAssembler::FN(const REGTYPE REG, const MemOperand& addr) { \ - ASSERT(allow_macro_instructions_); \ - LoadStoreMacro(REG, addr, OP); \ -} -LS_MACRO_LIST(DEFINE_FUNCTION) -#undef DEFINE_FUNCTION - - -void MacroAssembler::Adr(const Register& rd, Label* label) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - adr(rd, label); -} - - -void MacroAssembler::Asr(const Register& rd, - const Register& rn, - unsigned shift) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - asr(rd, rn, shift); -} - - -void MacroAssembler::Asr(const Register& rd, - const Register& rn, - const Register& rm) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - asrv(rd, rn, rm); -} - - -void MacroAssembler::B(Label* label) { - b(label); - CheckVeneers(false); -} - - -void MacroAssembler::B(Condition cond, Label* label) { - ASSERT(allow_macro_instructions_); - B(label, cond); -} - - -void MacroAssembler::Bfi(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - bfi(rd, rn, lsb, width); -} - - -void MacroAssembler::Bfxil(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - bfxil(rd, rn, lsb, width); -} - - -void MacroAssembler::Bind(Label* label) { - ASSERT(allow_macro_instructions_); - bind(label); -} - - -void MacroAssembler::Bl(Label* label) { - ASSERT(allow_macro_instructions_); - bl(label); -} - - -void MacroAssembler::Blr(const Register& xn) { - ASSERT(allow_macro_instructions_); - ASSERT(!xn.IsZero()); - blr(xn); -} - - -void MacroAssembler::Br(const Register& xn) { - ASSERT(allow_macro_instructions_); - ASSERT(!xn.IsZero()); - br(xn); -} - - -void MacroAssembler::Brk(int code) { - ASSERT(allow_macro_instructions_); - brk(code); -} - - -void MacroAssembler::Cinc(const Register& rd, - const Register& rn, - Condition cond) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - ASSERT((cond != al) && (cond != nv)); - cinc(rd, rn, cond); -} - - -void MacroAssembler::Cinv(const Register& rd, - const Register& rn, - Condition cond) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - ASSERT((cond != al) && (cond != nv)); - cinv(rd, rn, cond); -} - - -void MacroAssembler::Cls(const Register& rd, const Register& rn) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - cls(rd, rn); -} - - -void MacroAssembler::Clz(const Register& rd, const Register& rn) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - clz(rd, rn); -} - - -void MacroAssembler::Cneg(const Register& rd, - const Register& rn, - Condition cond) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - ASSERT((cond != al) && (cond != nv)); - cneg(rd, rn, cond); -} - - -// Conditionally zero the destination register. Only X registers are supported -// due to the truncation side-effect when used on W registers. -void MacroAssembler::CzeroX(const Register& rd, - Condition cond) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsSP() && rd.Is64Bits()); - ASSERT((cond != al) && (cond != nv)); - csel(rd, xzr, rd, cond); -} - - -// Conditionally move a value into the destination register. Only X registers -// are supported due to the truncation side-effect when used on W registers. -void MacroAssembler::CmovX(const Register& rd, - const Register& rn, - Condition cond) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsSP()); - ASSERT(rd.Is64Bits() && rn.Is64Bits()); - ASSERT((cond != al) && (cond != nv)); - if (!rd.is(rn)) { - csel(rd, rn, rd, cond); - } -} - - -void MacroAssembler::Cset(const Register& rd, Condition cond) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - ASSERT((cond != al) && (cond != nv)); - cset(rd, cond); -} - - -void MacroAssembler::Csetm(const Register& rd, Condition cond) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - ASSERT((cond != al) && (cond != nv)); - csetm(rd, cond); -} - - -void MacroAssembler::Csinc(const Register& rd, - const Register& rn, - const Register& rm, - Condition cond) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - ASSERT((cond != al) && (cond != nv)); - csinc(rd, rn, rm, cond); -} - - -void MacroAssembler::Csinv(const Register& rd, - const Register& rn, - const Register& rm, - Condition cond) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - ASSERT((cond != al) && (cond != nv)); - csinv(rd, rn, rm, cond); -} - - -void MacroAssembler::Csneg(const Register& rd, - const Register& rn, - const Register& rm, - Condition cond) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - ASSERT((cond != al) && (cond != nv)); - csneg(rd, rn, rm, cond); -} - - -void MacroAssembler::Dmb(BarrierDomain domain, BarrierType type) { - ASSERT(allow_macro_instructions_); - dmb(domain, type); -} - - -void MacroAssembler::Dsb(BarrierDomain domain, BarrierType type) { - ASSERT(allow_macro_instructions_); - dsb(domain, type); -} - - -void MacroAssembler::Debug(const char* message, uint32_t code, Instr params) { - ASSERT(allow_macro_instructions_); - debug(message, code, params); -} - - -void MacroAssembler::Extr(const Register& rd, - const Register& rn, - const Register& rm, - unsigned lsb) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - extr(rd, rn, rm, lsb); -} - - -void MacroAssembler::Fabs(const FPRegister& fd, const FPRegister& fn) { - ASSERT(allow_macro_instructions_); - fabs(fd, fn); -} - - -void MacroAssembler::Fadd(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm) { - ASSERT(allow_macro_instructions_); - fadd(fd, fn, fm); -} - - -void MacroAssembler::Fccmp(const FPRegister& fn, - const FPRegister& fm, - StatusFlags nzcv, - Condition cond) { - ASSERT(allow_macro_instructions_); - ASSERT((cond != al) && (cond != nv)); - fccmp(fn, fm, nzcv, cond); -} - - -void MacroAssembler::Fcmp(const FPRegister& fn, const FPRegister& fm) { - ASSERT(allow_macro_instructions_); - fcmp(fn, fm); -} - - -void MacroAssembler::Fcmp(const FPRegister& fn, double value) { - ASSERT(allow_macro_instructions_); - if (value != 0.0) { - FPRegister tmp = AppropriateTempFor(fn); - Fmov(tmp, value); - fcmp(fn, tmp); - } else { - fcmp(fn, value); - } -} - - -void MacroAssembler::Fcsel(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm, - Condition cond) { - ASSERT(allow_macro_instructions_); - ASSERT((cond != al) && (cond != nv)); - fcsel(fd, fn, fm, cond); -} - - -void MacroAssembler::Fcvt(const FPRegister& fd, const FPRegister& fn) { - ASSERT(allow_macro_instructions_); - fcvt(fd, fn); -} - - -void MacroAssembler::Fcvtas(const Register& rd, const FPRegister& fn) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - fcvtas(rd, fn); -} - - -void MacroAssembler::Fcvtau(const Register& rd, const FPRegister& fn) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - fcvtau(rd, fn); -} - - -void MacroAssembler::Fcvtms(const Register& rd, const FPRegister& fn) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - fcvtms(rd, fn); -} - - -void MacroAssembler::Fcvtmu(const Register& rd, const FPRegister& fn) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - fcvtmu(rd, fn); -} - - -void MacroAssembler::Fcvtns(const Register& rd, const FPRegister& fn) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - fcvtns(rd, fn); -} - - -void MacroAssembler::Fcvtnu(const Register& rd, const FPRegister& fn) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - fcvtnu(rd, fn); -} - - -void MacroAssembler::Fcvtzs(const Register& rd, const FPRegister& fn) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - fcvtzs(rd, fn); -} -void MacroAssembler::Fcvtzu(const Register& rd, const FPRegister& fn) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - fcvtzu(rd, fn); -} - - -void MacroAssembler::Fdiv(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm) { - ASSERT(allow_macro_instructions_); - fdiv(fd, fn, fm); -} - - -void MacroAssembler::Fmadd(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm, - const FPRegister& fa) { - ASSERT(allow_macro_instructions_); - fmadd(fd, fn, fm, fa); -} - - -void MacroAssembler::Fmax(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm) { - ASSERT(allow_macro_instructions_); - fmax(fd, fn, fm); -} - - -void MacroAssembler::Fmaxnm(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm) { - ASSERT(allow_macro_instructions_); - fmaxnm(fd, fn, fm); -} - - -void MacroAssembler::Fmin(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm) { - ASSERT(allow_macro_instructions_); - fmin(fd, fn, fm); -} - - -void MacroAssembler::Fminnm(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm) { - ASSERT(allow_macro_instructions_); - fminnm(fd, fn, fm); -} - - -void MacroAssembler::Fmov(FPRegister fd, FPRegister fn) { - ASSERT(allow_macro_instructions_); - // Only emit an instruction if fd and fn are different, and they are both D - // registers. fmov(s0, s0) is not a no-op because it clears the top word of - // d0. Technically, fmov(d0, d0) is not a no-op either because it clears the - // top of q0, but FPRegister does not currently support Q registers. - if (!fd.Is(fn) || !fd.Is64Bits()) { - fmov(fd, fn); - } -} - - -void MacroAssembler::Fmov(FPRegister fd, Register rn) { - ASSERT(allow_macro_instructions_); - fmov(fd, rn); -} - - -void MacroAssembler::Fmov(FPRegister fd, double imm) { - ASSERT(allow_macro_instructions_); - if ((fd.Is64Bits() && IsImmFP64(imm)) || - (fd.Is32Bits() && IsImmFP32(imm)) || - ((imm == 0.0) && (copysign(1.0, imm) == 1.0))) { - // These cases can be handled by the Assembler. - fmov(fd, imm); - } else { - // TODO(all): The Assembler would try to relocate the immediate with - // Assembler::ldr(const FPRegister& ft, double imm) but it is not - // implemented yet. - if (fd.SizeInBits() == kDRegSize) { - Mov(Tmp0(), double_to_rawbits(imm)); - Fmov(fd, Tmp0()); - } else { - ASSERT(fd.SizeInBits() == kSRegSize); - Mov(WTmp0(), float_to_rawbits(static_cast(imm))); - Fmov(fd, WTmp0()); - } - } -} - - -void MacroAssembler::Fmov(Register rd, FPRegister fn) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - fmov(rd, fn); -} - - -void MacroAssembler::Fmsub(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm, - const FPRegister& fa) { - ASSERT(allow_macro_instructions_); - fmsub(fd, fn, fm, fa); -} - - -void MacroAssembler::Fmul(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm) { - ASSERT(allow_macro_instructions_); - fmul(fd, fn, fm); -} - - -void MacroAssembler::Fneg(const FPRegister& fd, const FPRegister& fn) { - ASSERT(allow_macro_instructions_); - fneg(fd, fn); -} - - -void MacroAssembler::Fnmadd(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm, - const FPRegister& fa) { - ASSERT(allow_macro_instructions_); - fnmadd(fd, fn, fm, fa); -} - - -void MacroAssembler::Fnmsub(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm, - const FPRegister& fa) { - ASSERT(allow_macro_instructions_); - fnmsub(fd, fn, fm, fa); -} - - -void MacroAssembler::Frinta(const FPRegister& fd, const FPRegister& fn) { - ASSERT(allow_macro_instructions_); - frinta(fd, fn); -} - - -void MacroAssembler::Frintn(const FPRegister& fd, const FPRegister& fn) { - ASSERT(allow_macro_instructions_); - frintn(fd, fn); -} - - -void MacroAssembler::Frintz(const FPRegister& fd, const FPRegister& fn) { - ASSERT(allow_macro_instructions_); - frintz(fd, fn); -} - - -void MacroAssembler::Fsqrt(const FPRegister& fd, const FPRegister& fn) { - ASSERT(allow_macro_instructions_); - fsqrt(fd, fn); -} - - -void MacroAssembler::Fsub(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm) { - ASSERT(allow_macro_instructions_); - fsub(fd, fn, fm); -} - - -void MacroAssembler::Hint(SystemHint code) { - ASSERT(allow_macro_instructions_); - hint(code); -} - - -void MacroAssembler::Hlt(int code) { - ASSERT(allow_macro_instructions_); - hlt(code); -} - - -void MacroAssembler::Isb() { - ASSERT(allow_macro_instructions_); - isb(); -} - - -void MacroAssembler::Ldnp(const CPURegister& rt, - const CPURegister& rt2, - const MemOperand& src) { - ASSERT(allow_macro_instructions_); - ASSERT(!AreAliased(rt, rt2)); - ldnp(rt, rt2, src); -} - - -void MacroAssembler::Ldp(const CPURegister& rt, - const CPURegister& rt2, - const MemOperand& src) { - ASSERT(allow_macro_instructions_); - ASSERT(!AreAliased(rt, rt2)); - ldp(rt, rt2, src); -} - - -void MacroAssembler::Ldpsw(const Register& rt, - const Register& rt2, - const MemOperand& src) { - ASSERT(allow_macro_instructions_); - ASSERT(!rt.IsZero()); - ASSERT(!rt2.IsZero()); - ldpsw(rt, rt2, src); -} - - -void MacroAssembler::Ldr(const FPRegister& ft, double imm) { - ASSERT(allow_macro_instructions_); - ldr(ft, imm); -} - - -void MacroAssembler::Ldr(const Register& rt, uint64_t imm) { - ASSERT(allow_macro_instructions_); - ASSERT(!rt.IsZero()); - ldr(rt, imm); -} - - -void MacroAssembler::Lsl(const Register& rd, - const Register& rn, - unsigned shift) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - lsl(rd, rn, shift); -} - - -void MacroAssembler::Lsl(const Register& rd, - const Register& rn, - const Register& rm) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - lslv(rd, rn, rm); -} - - -void MacroAssembler::Lsr(const Register& rd, - const Register& rn, - unsigned shift) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - lsr(rd, rn, shift); -} - - -void MacroAssembler::Lsr(const Register& rd, - const Register& rn, - const Register& rm) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - lsrv(rd, rn, rm); -} - - -void MacroAssembler::Madd(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - madd(rd, rn, rm, ra); -} - - -void MacroAssembler::Mneg(const Register& rd, - const Register& rn, - const Register& rm) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - mneg(rd, rn, rm); -} - - -void MacroAssembler::Mov(const Register& rd, const Register& rn) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - // Emit a register move only if the registers are distinct, or if they are - // not X registers. Note that mov(w0, w0) is not a no-op because it clears - // the top word of x0. - if (!rd.Is(rn) || !rd.Is64Bits()) { - Assembler::mov(rd, rn); - } -} - - -void MacroAssembler::Movk(const Register& rd, uint64_t imm, int shift) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - movk(rd, imm, shift); -} - - -void MacroAssembler::Mrs(const Register& rt, SystemRegister sysreg) { - ASSERT(allow_macro_instructions_); - ASSERT(!rt.IsZero()); - mrs(rt, sysreg); -} - - -void MacroAssembler::Msr(SystemRegister sysreg, const Register& rt) { - ASSERT(allow_macro_instructions_); - ASSERT(!rt.IsZero()); - msr(sysreg, rt); -} - - -void MacroAssembler::Msub(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - msub(rd, rn, rm, ra); -} - - -void MacroAssembler::Mul(const Register& rd, - const Register& rn, - const Register& rm) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - mul(rd, rn, rm); -} - - -void MacroAssembler::Rbit(const Register& rd, const Register& rn) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - rbit(rd, rn); -} - - -void MacroAssembler::Ret(const Register& xn) { - ASSERT(allow_macro_instructions_); - ASSERT(!xn.IsZero()); - ret(xn); - CheckVeneers(false); -} - - -void MacroAssembler::Rev(const Register& rd, const Register& rn) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - rev(rd, rn); -} - - -void MacroAssembler::Rev16(const Register& rd, const Register& rn) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - rev16(rd, rn); -} - - -void MacroAssembler::Rev32(const Register& rd, const Register& rn) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - rev32(rd, rn); -} - - -void MacroAssembler::Ror(const Register& rd, - const Register& rs, - unsigned shift) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - ror(rd, rs, shift); -} - - -void MacroAssembler::Ror(const Register& rd, - const Register& rn, - const Register& rm) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - rorv(rd, rn, rm); -} - - -void MacroAssembler::Sbfiz(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - sbfiz(rd, rn, lsb, width); -} - - -void MacroAssembler::Sbfx(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - sbfx(rd, rn, lsb, width); -} - - -void MacroAssembler::Scvtf(const FPRegister& fd, - const Register& rn, - unsigned fbits) { - ASSERT(allow_macro_instructions_); - scvtf(fd, rn, fbits); -} - - -void MacroAssembler::Sdiv(const Register& rd, - const Register& rn, - const Register& rm) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - sdiv(rd, rn, rm); -} - - -void MacroAssembler::Smaddl(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - smaddl(rd, rn, rm, ra); -} - - -void MacroAssembler::Smsubl(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - smsubl(rd, rn, rm, ra); -} - - -void MacroAssembler::Smull(const Register& rd, - const Register& rn, - const Register& rm) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - smull(rd, rn, rm); -} - - -void MacroAssembler::Smulh(const Register& rd, - const Register& rn, - const Register& rm) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - smulh(rd, rn, rm); -} - - -void MacroAssembler::Stnp(const CPURegister& rt, - const CPURegister& rt2, - const MemOperand& dst) { - ASSERT(allow_macro_instructions_); - stnp(rt, rt2, dst); -} - - -void MacroAssembler::Stp(const CPURegister& rt, - const CPURegister& rt2, - const MemOperand& dst) { - ASSERT(allow_macro_instructions_); - stp(rt, rt2, dst); -} - - -void MacroAssembler::Sxtb(const Register& rd, const Register& rn) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - sxtb(rd, rn); -} - - -void MacroAssembler::Sxth(const Register& rd, const Register& rn) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - sxth(rd, rn); -} - - -void MacroAssembler::Sxtw(const Register& rd, const Register& rn) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - sxtw(rd, rn); -} - - -void MacroAssembler::Ubfiz(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - ubfiz(rd, rn, lsb, width); -} - - -void MacroAssembler::Ubfx(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - ubfx(rd, rn, lsb, width); -} - - -void MacroAssembler::Ucvtf(const FPRegister& fd, - const Register& rn, - unsigned fbits) { - ASSERT(allow_macro_instructions_); - ucvtf(fd, rn, fbits); -} - - -void MacroAssembler::Udiv(const Register& rd, - const Register& rn, - const Register& rm) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - udiv(rd, rn, rm); -} - - -void MacroAssembler::Umaddl(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - umaddl(rd, rn, rm, ra); -} - - -void MacroAssembler::Umsubl(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - umsubl(rd, rn, rm, ra); -} - - -void MacroAssembler::Uxtb(const Register& rd, const Register& rn) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - uxtb(rd, rn); -} - - -void MacroAssembler::Uxth(const Register& rd, const Register& rn) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - uxth(rd, rn); -} - - -void MacroAssembler::Uxtw(const Register& rd, const Register& rn) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - uxtw(rd, rn); -} - - -void MacroAssembler::BumpSystemStackPointer(const Operand& space) { - ASSERT(!csp.Is(sp_)); - // TODO(jbramley): Several callers rely on this not using scratch registers, - // so we use the assembler directly here. However, this means that large - // immediate values of 'space' cannot be handled. Once we merge with V8, we - // should try to use the new scope that controls scratch register usage. - InstructionAccurateScope scope(this); - if ((space.IsImmediate()) && !is_uint12(space.immediate())) { - // The subtract instruction supports a 12-bit immediate, shifted left by - // zero or 12 bits. So, in two instructions, we can subtract any immediate - // between zero and (1 << 24) - 1. - int64_t imm = space.immediate(); - ASSERT(is_uint24(imm)); - - int64_t imm_top_12_bits = imm >> 12; - sub(csp, StackPointer(), imm_top_12_bits << 12); - imm -= imm_top_12_bits << 12; - if (imm > 0) { - sub(csp, csp, imm); - } - } else { - sub(csp, StackPointer(), space); - } -} - - -void MacroAssembler::InitializeRootRegister() { - ExternalReference roots_array_start = - ExternalReference::roots_array_start(isolate()); - Mov(root, Operand(roots_array_start)); -} - - -void MacroAssembler::SmiTag(Register dst, Register src) { - ASSERT(dst.Is64Bits() && src.Is64Bits()); - Lsl(dst, src, kSmiShift); -} - - -void MacroAssembler::SmiTag(Register smi) { SmiTag(smi, smi); } - - -void MacroAssembler::SmiUntag(Register dst, Register src) { - ASSERT(dst.Is64Bits() && src.Is64Bits()); - if (FLAG_enable_slow_asserts) { - AssertSmi(src); - } - Asr(dst, src, kSmiShift); -} - - -void MacroAssembler::SmiUntag(Register smi) { SmiUntag(smi, smi); } - - -void MacroAssembler::SmiUntagToDouble(FPRegister dst, - Register src, - UntagMode mode) { - ASSERT(dst.Is64Bits() && src.Is64Bits()); - if (FLAG_enable_slow_asserts && (mode == kNotSpeculativeUntag)) { - AssertSmi(src); - } - Scvtf(dst, src, kSmiShift); -} - - -void MacroAssembler::SmiUntagToFloat(FPRegister dst, - Register src, - UntagMode mode) { - ASSERT(dst.Is32Bits() && src.Is64Bits()); - if (FLAG_enable_slow_asserts && (mode == kNotSpeculativeUntag)) { - AssertSmi(src); - } - Scvtf(dst, src, kSmiShift); -} - - -void MacroAssembler::JumpIfSmi(Register value, - Label* smi_label, - Label* not_smi_label) { - STATIC_ASSERT((kSmiTagSize == 1) && (kSmiTag == 0)); - // Check if the tag bit is set. - if (smi_label) { - Tbz(value, 0, smi_label); - if (not_smi_label) { - B(not_smi_label); - } - } else { - ASSERT(not_smi_label); - Tbnz(value, 0, not_smi_label); - } -} - - -void MacroAssembler::JumpIfNotSmi(Register value, Label* not_smi_label) { - JumpIfSmi(value, NULL, not_smi_label); -} - - -void MacroAssembler::JumpIfBothSmi(Register value1, - Register value2, - Label* both_smi_label, - Label* not_smi_label) { - STATIC_ASSERT((kSmiTagSize == 1) && (kSmiTag == 0)); - // Check if both tag bits are clear. - Orr(Tmp0(), value1, value2); - JumpIfSmi(Tmp0(), both_smi_label, not_smi_label); -} - - -void MacroAssembler::JumpIfEitherSmi(Register value1, - Register value2, - Label* either_smi_label, - Label* not_smi_label) { - STATIC_ASSERT((kSmiTagSize == 1) && (kSmiTag == 0)); - // Check if either tag bit is clear. - And(Tmp0(), value1, value2); - JumpIfSmi(Tmp0(), either_smi_label, not_smi_label); -} - - -void MacroAssembler::JumpIfEitherNotSmi(Register value1, - Register value2, - Label* not_smi_label) { - JumpIfBothSmi(value1, value2, NULL, not_smi_label); -} - - -void MacroAssembler::JumpIfBothNotSmi(Register value1, - Register value2, - Label* not_smi_label) { - JumpIfEitherSmi(value1, value2, NULL, not_smi_label); -} - - -void MacroAssembler::IsObjectNameType(Register object, - Register type, - Label* fail) { - CompareObjectType(object, type, type, LAST_NAME_TYPE); - B(hi, fail); -} - - -void MacroAssembler::IsObjectJSObjectType(Register heap_object, - Register map, - Register scratch, - Label* fail) { - Ldr(map, FieldMemOperand(heap_object, HeapObject::kMapOffset)); - IsInstanceJSObjectType(map, scratch, fail); -} - - -void MacroAssembler::IsInstanceJSObjectType(Register map, - Register scratch, - Label* fail) { - Ldrb(scratch, FieldMemOperand(map, Map::kInstanceTypeOffset)); - // If cmp result is lt, the following ccmp will clear all flags. - // Z == 0, N == V implies gt condition. - Cmp(scratch, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE); - Ccmp(scratch, LAST_NONCALLABLE_SPEC_OBJECT_TYPE, NoFlag, ge); - - // If we didn't get a valid label object just fall through and leave the - // flags updated. - if (fail != NULL) { - B(gt, fail); - } -} - - -void MacroAssembler::IsObjectJSStringType(Register object, - Register type, - Label* not_string, - Label* string) { - Ldr(type, FieldMemOperand(object, HeapObject::kMapOffset)); - Ldrb(type.W(), FieldMemOperand(type, Map::kInstanceTypeOffset)); - - STATIC_ASSERT(kStringTag == 0); - ASSERT((string != NULL) || (not_string != NULL)); - if (string == NULL) { - TestAndBranchIfAnySet(type.W(), kIsNotStringMask, not_string); - } else if (not_string == NULL) { - TestAndBranchIfAllClear(type.W(), kIsNotStringMask, string); - } else { - TestAndBranchIfAnySet(type.W(), kIsNotStringMask, not_string); - B(string); - } -} - - -void MacroAssembler::Push(Handle handle) { - Mov(Tmp0(), Operand(handle)); - Push(Tmp0()); -} - - -void MacroAssembler::Claim(uint64_t count, uint64_t unit_size) { - uint64_t size = count * unit_size; - - if (size == 0) { - return; - } - - if (csp.Is(StackPointer())) { - ASSERT(size % 16 == 0); - } else { - BumpSystemStackPointer(size); - } - - Sub(StackPointer(), StackPointer(), size); -} - - -void MacroAssembler::Claim(const Register& count, uint64_t unit_size) { - ASSERT(IsPowerOf2(unit_size)); - - if (unit_size == 0) { - return; - } - - const int shift = CountTrailingZeros(unit_size, kXRegSize); - const Operand size(count, LSL, shift); - - if (size.IsZero()) { - return; - } - - if (!csp.Is(StackPointer())) { - BumpSystemStackPointer(size); - } - - Sub(StackPointer(), StackPointer(), size); -} - - -void MacroAssembler::ClaimBySMI(const Register& count_smi, uint64_t unit_size) { - ASSERT(IsPowerOf2(unit_size)); - const int shift = CountTrailingZeros(unit_size, kXRegSize) - kSmiShift; - const Operand size(count_smi, - (shift >= 0) ? (LSL) : (LSR), - (shift >= 0) ? (shift) : (-shift)); - - if (size.IsZero()) { - return; - } - - if (!csp.Is(StackPointer())) { - BumpSystemStackPointer(size); - } - - Sub(StackPointer(), StackPointer(), size); -} - - -void MacroAssembler::Drop(uint64_t count, uint64_t unit_size) { - uint64_t size = count * unit_size; - - if (size == 0) { - return; - } - - Add(StackPointer(), StackPointer(), size); - - if (csp.Is(StackPointer())) { - ASSERT(size % 16 == 0); - } else if (emit_debug_code()) { - // It is safe to leave csp where it is when unwinding the JavaScript stack, - // but if we keep it matching StackPointer, the simulator can detect memory - // accesses in the now-free part of the stack. - Mov(csp, StackPointer()); - } -} - - -void MacroAssembler::Drop(const Register& count, uint64_t unit_size) { - ASSERT(IsPowerOf2(unit_size)); - - if (unit_size == 0) { - return; - } - - const int shift = CountTrailingZeros(unit_size, kXRegSize); - const Operand size(count, LSL, shift); - - if (size.IsZero()) { - return; - } - - Add(StackPointer(), StackPointer(), size); - - if (!csp.Is(StackPointer()) && emit_debug_code()) { - // It is safe to leave csp where it is when unwinding the JavaScript stack, - // but if we keep it matching StackPointer, the simulator can detect memory - // accesses in the now-free part of the stack. - Mov(csp, StackPointer()); - } -} - - -void MacroAssembler::DropBySMI(const Register& count_smi, uint64_t unit_size) { - ASSERT(IsPowerOf2(unit_size)); - const int shift = CountTrailingZeros(unit_size, kXRegSize) - kSmiShift; - const Operand size(count_smi, - (shift >= 0) ? (LSL) : (LSR), - (shift >= 0) ? (shift) : (-shift)); - - if (size.IsZero()) { - return; - } - - Add(StackPointer(), StackPointer(), size); - - if (!csp.Is(StackPointer()) && emit_debug_code()) { - // It is safe to leave csp where it is when unwinding the JavaScript stack, - // but if we keep it matching StackPointer, the simulator can detect memory - // accesses in the now-free part of the stack. - Mov(csp, StackPointer()); - } -} - - -void MacroAssembler::CompareAndBranch(const Register& lhs, - const Operand& rhs, - Condition cond, - Label* label) { - if (rhs.IsImmediate() && (rhs.immediate() == 0) && - ((cond == eq) || (cond == ne))) { - if (cond == eq) { - Cbz(lhs, label); - } else { - Cbnz(lhs, label); - } - } else { - Cmp(lhs, rhs); - B(cond, label); - } -} - - -void MacroAssembler::TestAndBranchIfAnySet(const Register& reg, - const uint64_t bit_pattern, - Label* label) { - int bits = reg.SizeInBits(); - ASSERT(CountSetBits(bit_pattern, bits) > 0); - if (CountSetBits(bit_pattern, bits) == 1) { - Tbnz(reg, MaskToBit(bit_pattern), label); - } else { - Tst(reg, bit_pattern); - B(ne, label); - } -} - - -void MacroAssembler::TestAndBranchIfAllClear(const Register& reg, - const uint64_t bit_pattern, - Label* label) { - int bits = reg.SizeInBits(); - ASSERT(CountSetBits(bit_pattern, bits) > 0); - if (CountSetBits(bit_pattern, bits) == 1) { - Tbz(reg, MaskToBit(bit_pattern), label); - } else { - Tst(reg, bit_pattern); - B(eq, label); - } -} - - -void MacroAssembler::InlineData(uint64_t data) { - ASSERT(is_uint16(data)); - InstructionAccurateScope scope(this, 1); - movz(xzr, data); -} - - -void MacroAssembler::EnableInstrumentation() { - InstructionAccurateScope scope(this, 1); - movn(xzr, InstrumentStateEnable); -} - - -void MacroAssembler::DisableInstrumentation() { - InstructionAccurateScope scope(this, 1); - movn(xzr, InstrumentStateDisable); -} - - -void MacroAssembler::AnnotateInstrumentation(const char* marker_name) { - ASSERT(strlen(marker_name) == 2); - - // We allow only printable characters in the marker names. Unprintable - // characters are reserved for controlling features of the instrumentation. - ASSERT(isprint(marker_name[0]) && isprint(marker_name[1])); - - InstructionAccurateScope scope(this, 1); - movn(xzr, (marker_name[1] << 8) | marker_name[0]); -} - -} } // namespace v8::internal - -#endif // V8_A64_MACRO_ASSEMBLER_A64_INL_H_ diff --git a/deps/v8/src/a64/macro-assembler-a64.cc b/deps/v8/src/a64/macro-assembler-a64.cc deleted file mode 100644 index 14fb2fda63..0000000000 --- a/deps/v8/src/a64/macro-assembler-a64.cc +++ /dev/null @@ -1,4975 +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 "v8.h" - -#if V8_TARGET_ARCH_A64 - -#include "bootstrapper.h" -#include "codegen.h" -#include "cpu-profiler.h" -#include "debug.h" -#include "isolate-inl.h" -#include "runtime.h" - -namespace v8 { -namespace internal { - -// Define a fake double underscore to use with the ASM_UNIMPLEMENTED macros. -#define __ - - -MacroAssembler::MacroAssembler(Isolate* arg_isolate, - byte * buffer, - unsigned buffer_size) - : Assembler(arg_isolate, buffer, buffer_size), - generating_stub_(false), -#if DEBUG - allow_macro_instructions_(true), -#endif - has_frame_(false), - use_real_aborts_(true), - sp_(jssp), tmp0_(ip0), tmp1_(ip1), fptmp0_(fp_scratch) { - if (isolate() != NULL) { - code_object_ = Handle(isolate()->heap()->undefined_value(), - isolate()); - } -} - - -void MacroAssembler::LogicalMacro(const Register& rd, - const Register& rn, - const Operand& operand, - LogicalOp op) { - if (operand.NeedsRelocation()) { - LoadRelocated(Tmp0(), operand); - Logical(rd, rn, Tmp0(), op); - - } else if (operand.IsImmediate()) { - int64_t immediate = operand.immediate(); - unsigned reg_size = rd.SizeInBits(); - ASSERT(rd.Is64Bits() || is_uint32(immediate)); - - // If the operation is NOT, invert the operation and immediate. - if ((op & NOT) == NOT) { - op = static_cast(op & ~NOT); - immediate = ~immediate; - if (rd.Is32Bits()) { - immediate &= kWRegMask; - } - } - - // Special cases for all set or all clear immediates. - if (immediate == 0) { - switch (op) { - case AND: - Mov(rd, 0); - return; - case ORR: // Fall through. - case EOR: - Mov(rd, rn); - return; - case ANDS: // Fall through. - case BICS: - break; - default: - UNREACHABLE(); - } - } else if ((rd.Is64Bits() && (immediate == -1L)) || - (rd.Is32Bits() && (immediate == 0xffffffffL))) { - switch (op) { - case AND: - Mov(rd, rn); - return; - case ORR: - Mov(rd, immediate); - return; - case EOR: - Mvn(rd, rn); - return; - case ANDS: // Fall through. - case BICS: - break; - default: - UNREACHABLE(); - } - } - - unsigned n, imm_s, imm_r; - if (IsImmLogical(immediate, reg_size, &n, &imm_s, &imm_r)) { - // Immediate can be encoded in the instruction. - LogicalImmediate(rd, rn, n, imm_s, imm_r, op); - } else { - // Immediate can't be encoded: synthesize using move immediate. - Register temp = AppropriateTempFor(rn); - Mov(temp, immediate); - if (rd.Is(csp)) { - // If rd is the stack pointer we cannot use it as the destination - // register so we use the temp register as an intermediate again. - Logical(temp, rn, temp, op); - Mov(csp, temp); - } else { - Logical(rd, rn, temp, op); - } - } - - } else if (operand.IsExtendedRegister()) { - ASSERT(operand.reg().SizeInBits() <= rd.SizeInBits()); - // Add/sub extended supports shift <= 4. We want to support exactly the - // same modes here. - ASSERT(operand.shift_amount() <= 4); - ASSERT(operand.reg().Is64Bits() || - ((operand.extend() != UXTX) && (operand.extend() != SXTX))); - Register temp = AppropriateTempFor(rn, operand.reg()); - EmitExtendShift(temp, operand.reg(), operand.extend(), - operand.shift_amount()); - Logical(rd, rn, temp, op); - - } else { - // The operand can be encoded in the instruction. - ASSERT(operand.IsShiftedRegister()); - Logical(rd, rn, operand, op); - } -} - - -void MacroAssembler::Mov(const Register& rd, uint64_t imm) { - ASSERT(allow_macro_instructions_); - ASSERT(is_uint32(imm) || is_int32(imm) || rd.Is64Bits()); - ASSERT(!rd.IsZero()); - - // TODO(all) extend to support more immediates. - // - // Immediates on Aarch64 can be produced using an initial value, and zero to - // three move keep operations. - // - // Initial values can be generated with: - // 1. 64-bit move zero (movz). - // 2. 32-bit move inverted (movn). - // 3. 64-bit move inverted. - // 4. 32-bit orr immediate. - // 5. 64-bit orr immediate. - // Move-keep may then be used to modify each of the 16-bit half-words. - // - // The code below supports all five initial value generators, and - // applying move-keep operations to move-zero and move-inverted initial - // values. - - unsigned reg_size = rd.SizeInBits(); - unsigned n, imm_s, imm_r; - if (IsImmMovz(imm, reg_size) && !rd.IsSP()) { - // Immediate can be represented in a move zero instruction. Movz can't - // write to the stack pointer. - movz(rd, imm); - } else if (IsImmMovn(imm, reg_size) && !rd.IsSP()) { - // Immediate can be represented in a move inverted instruction. Movn can't - // write to the stack pointer. - movn(rd, rd.Is64Bits() ? ~imm : (~imm & kWRegMask)); - } else if (IsImmLogical(imm, reg_size, &n, &imm_s, &imm_r)) { - // Immediate can be represented in a logical orr instruction. - LogicalImmediate(rd, AppropriateZeroRegFor(rd), n, imm_s, imm_r, ORR); - } else { - // Generic immediate case. Imm will be represented by - // [imm3, imm2, imm1, imm0], where each imm is 16 bits. - // A move-zero or move-inverted is generated for the first non-zero or - // non-0xffff immX, and a move-keep for subsequent non-zero immX. - - uint64_t ignored_halfword = 0; - bool invert_move = false; - // If the number of 0xffff halfwords is greater than the number of 0x0000 - // halfwords, it's more efficient to use move-inverted. - if (CountClearHalfWords(~imm, reg_size) > - CountClearHalfWords(imm, reg_size)) { - ignored_halfword = 0xffffL; - invert_move = true; - } - - // Mov instructions can't move value into the stack pointer, so set up a - // temporary register, if needed. - Register temp = rd.IsSP() ? AppropriateTempFor(rd) : rd; - - // Iterate through the halfwords. Use movn/movz for the first non-ignored - // halfword, and movk for subsequent halfwords. - ASSERT((reg_size % 16) == 0); - bool first_mov_done = false; - for (unsigned i = 0; i < (rd.SizeInBits() / 16); i++) { - uint64_t imm16 = (imm >> (16 * i)) & 0xffffL; - if (imm16 != ignored_halfword) { - if (!first_mov_done) { - if (invert_move) { - movn(temp, (~imm16) & 0xffffL, 16 * i); - } else { - movz(temp, imm16, 16 * i); - } - first_mov_done = true; - } else { - // Construct a wider constant. - movk(temp, imm16, 16 * i); - } - } - } - ASSERT(first_mov_done); - - // Move the temporary if the original destination register was the stack - // pointer. - if (rd.IsSP()) { - mov(rd, temp); - } - } -} - - -void MacroAssembler::Mov(const Register& rd, - const Operand& operand, - DiscardMoveMode discard_mode) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - // Provide a swap register for instructions that need to write into the - // system stack pointer (and can't do this inherently). - Register dst = (rd.Is(csp)) ? (Tmp1()) : (rd); - - if (operand.NeedsRelocation()) { - LoadRelocated(dst, operand); - - } else if (operand.IsImmediate()) { - // Call the macro assembler for generic immediates. - Mov(dst, operand.immediate()); - - } else if (operand.IsShiftedRegister() && (operand.shift_amount() != 0)) { - // Emit a shift instruction if moving a shifted register. This operation - // could also be achieved using an orr instruction (like orn used by Mvn), - // but using a shift instruction makes the disassembly clearer. - EmitShift(dst, operand.reg(), operand.shift(), operand.shift_amount()); - - } else if (operand.IsExtendedRegister()) { - // Emit an extend instruction if moving an extended register. This handles - // extend with post-shift operations, too. - EmitExtendShift(dst, operand.reg(), operand.extend(), - operand.shift_amount()); - - } else { - // Otherwise, emit a register move only if the registers are distinct, or - // if they are not X registers. - // - // Note that mov(w0, w0) is not a no-op because it clears the top word of - // x0. A flag is provided (kDiscardForSameWReg) if a move between the same W - // registers is not required to clear the top word of the X register. In - // this case, the instruction is discarded. - // - // If csp is an operand, add #0 is emitted, otherwise, orr #0. - if (!rd.Is(operand.reg()) || (rd.Is32Bits() && - (discard_mode == kDontDiscardForSameWReg))) { - Assembler::mov(rd, operand.reg()); - } - // This case can handle writes into the system stack pointer directly. - dst = rd; - } - - // Copy the result to the system stack pointer. - if (!dst.Is(rd)) { - ASSERT(rd.IsZero()); - ASSERT(dst.Is(Tmp1())); - Assembler::mov(rd, dst); - } -} - - -void MacroAssembler::Mvn(const Register& rd, const Operand& operand) { - ASSERT(allow_macro_instructions_); - - if (operand.NeedsRelocation()) { - LoadRelocated(Tmp0(), operand); - Mvn(rd, Tmp0()); - - } else if (operand.IsImmediate()) { - // Call the macro assembler for generic immediates. - Mov(rd, ~operand.immediate()); - - } else if (operand.IsExtendedRegister()) { - // Emit two instructions for the extend case. This differs from Mov, as - // the extend and invert can't be achieved in one instruction. - Register temp = AppropriateTempFor(rd, operand.reg()); - EmitExtendShift(temp, operand.reg(), operand.extend(), - operand.shift_amount()); - mvn(rd, temp); - - } else { - // Otherwise, emit a register move only if the registers are distinct. - // If the jssp is an operand, add #0 is emitted, otherwise, orr #0. - mvn(rd, operand); - } -} - - -unsigned MacroAssembler::CountClearHalfWords(uint64_t imm, unsigned reg_size) { - ASSERT((reg_size % 8) == 0); - int count = 0; - for (unsigned i = 0; i < (reg_size / 16); i++) { - if ((imm & 0xffff) == 0) { - count++; - } - imm >>= 16; - } - return count; -} - - -// The movz instruction can generate immediates containing an arbitrary 16-bit -// half-word, with remaining bits clear, eg. 0x00001234, 0x0000123400000000. -bool MacroAssembler::IsImmMovz(uint64_t imm, unsigned reg_size) { - ASSERT((reg_size == kXRegSize) || (reg_size == kWRegSize)); - return CountClearHalfWords(imm, reg_size) >= ((reg_size / 16) - 1); -} - - -// The movn instruction can generate immediates containing an arbitrary 16-bit -// half-word, with remaining bits set, eg. 0xffff1234, 0xffff1234ffffffff. -bool MacroAssembler::IsImmMovn(uint64_t imm, unsigned reg_size) { - return IsImmMovz(~imm, reg_size); -} - - -void MacroAssembler::ConditionalCompareMacro(const Register& rn, - const Operand& operand, - StatusFlags nzcv, - Condition cond, - ConditionalCompareOp op) { - ASSERT((cond != al) && (cond != nv)); - if (operand.NeedsRelocation()) { - LoadRelocated(Tmp0(), operand); - ConditionalCompareMacro(rn, Tmp0(), nzcv, cond, op); - - } else if ((operand.IsShiftedRegister() && (operand.shift_amount() == 0)) || - (operand.IsImmediate() && IsImmConditionalCompare(operand.immediate()))) { - // The immediate can be encoded in the instruction, or the operand is an - // unshifted register: call the assembler. - ConditionalCompare(rn, operand, nzcv, cond, op); - - } else { - // The operand isn't directly supported by the instruction: perform the - // operation on a temporary register. - Register temp = AppropriateTempFor(rn); - Mov(temp, operand); - ConditionalCompare(rn, temp, nzcv, cond, op); - } -} - - -void MacroAssembler::Csel(const Register& rd, - const Register& rn, - const Operand& operand, - Condition cond) { - ASSERT(allow_macro_instructions_); - ASSERT(!rd.IsZero()); - ASSERT((cond != al) && (cond != nv)); - if (operand.IsImmediate()) { - // Immediate argument. Handle special cases of 0, 1 and -1 using zero - // register. - int64_t imm = operand.immediate(); - Register zr = AppropriateZeroRegFor(rn); - if (imm == 0) { - csel(rd, rn, zr, cond); - } else if (imm == 1) { - csinc(rd, rn, zr, cond); - } else if (imm == -1) { - csinv(rd, rn, zr, cond); - } else { - Register temp = AppropriateTempFor(rn); - Mov(temp, operand.immediate()); - csel(rd, rn, temp, cond); - } - } else if (operand.IsShiftedRegister() && (operand.shift_amount() == 0)) { - // Unshifted register argument. - csel(rd, rn, operand.reg(), cond); - } else { - // All other arguments. - Register temp = AppropriateTempFor(rn); - Mov(temp, operand); - csel(rd, rn, temp, cond); - } -} - - -void MacroAssembler::AddSubMacro(const Register& rd, - const Register& rn, - const Operand& operand, - FlagsUpdate S, - AddSubOp op) { - if (operand.IsZero() && rd.Is(rn) && rd.Is64Bits() && rn.Is64Bits() && - !operand.NeedsRelocation() && (S == LeaveFlags)) { - // The instruction would be a nop. Avoid generating useless code. - return; - } - - if (operand.NeedsRelocation()) { - LoadRelocated(Tmp0(), operand); - AddSubMacro(rd, rn, Tmp0(), S, op); - } else if ((operand.IsImmediate() && !IsImmAddSub(operand.immediate())) || - (rn.IsZero() && !operand.IsShiftedRegister()) || - (operand.IsShiftedRegister() && (operand.shift() == ROR))) { - Register temp = AppropriateTempFor(rn); - Mov(temp, operand); - AddSub(rd, rn, temp, S, op); - } else { - AddSub(rd, rn, operand, S, op); - } -} - - -void MacroAssembler::AddSubWithCarryMacro(const Register& rd, - const Register& rn, - const Operand& operand, - FlagsUpdate S, - AddSubWithCarryOp op) { - ASSERT(rd.SizeInBits() == rn.SizeInBits()); - - if (operand.NeedsRelocation()) { - LoadRelocated(Tmp0(), operand); - AddSubWithCarryMacro(rd, rn, Tmp0(), S, op); - - } else if (operand.IsImmediate() || - (operand.IsShiftedRegister() && (operand.shift() == ROR))) { - // Add/sub with carry (immediate or ROR shifted register.) - Register temp = AppropriateTempFor(rn); - Mov(temp, operand); - AddSubWithCarry(rd, rn, temp, S, op); - } else if (operand.IsShiftedRegister() && (operand.shift_amount() != 0)) { - // Add/sub with carry (shifted register). - ASSERT(operand.reg().SizeInBits() == rd.SizeInBits()); - ASSERT(operand.shift() != ROR); - ASSERT(is_uintn(operand.shift_amount(), - rd.SizeInBits() == kXRegSize ? kXRegSizeLog2 : kWRegSizeLog2)); - Register temp = AppropriateTempFor(rn, operand.reg()); - EmitShift(temp, operand.reg(), operand.shift(), operand.shift_amount()); - AddSubWithCarry(rd, rn, temp, S, op); - - } else if (operand.IsExtendedRegister()) { - // Add/sub with carry (extended register). - ASSERT(operand.reg().SizeInBits() <= rd.SizeInBits()); - // Add/sub extended supports a shift <= 4. We want to support exactly the - // same modes. - ASSERT(operand.shift_amount() <= 4); - ASSERT(operand.reg().Is64Bits() || - ((operand.extend() != UXTX) && (operand.extend() != SXTX))); - Register temp = AppropriateTempFor(rn, operand.reg()); - EmitExtendShift(temp, operand.reg(), operand.extend(), - operand.shift_amount()); - AddSubWithCarry(rd, rn, temp, S, op); - - } else { - // The addressing mode is directly supported by the instruction. - AddSubWithCarry(rd, rn, operand, S, op); - } -} - - -void MacroAssembler::LoadStoreMacro(const CPURegister& rt, - const MemOperand& addr, - LoadStoreOp op) { - int64_t offset = addr.offset(); - LSDataSize size = CalcLSDataSize(op); - - // Check if an immediate offset fits in the immediate field of the - // appropriate instruction. If not, emit two instructions to perform - // the operation. - if (addr.IsImmediateOffset() && !IsImmLSScaled(offset, size) && - !IsImmLSUnscaled(offset)) { - // Immediate offset that can't be encoded using unsigned or unscaled - // addressing modes. - Register temp = AppropriateTempFor(addr.base()); - Mov(temp, addr.offset()); - LoadStore(rt, MemOperand(addr.base(), temp), op); - } else if (addr.IsPostIndex() && !IsImmLSUnscaled(offset)) { - // Post-index beyond unscaled addressing range. - LoadStore(rt, MemOperand(addr.base()), op); - add(addr.base(), addr.base(), offset); - } else if (addr.IsPreIndex() && !IsImmLSUnscaled(offset)) { - // Pre-index beyond unscaled addressing range. - add(addr.base(), addr.base(), offset); - LoadStore(rt, MemOperand(addr.base()), op); - } else { - // Encodable in one load/store instruction. - LoadStore(rt, addr, op); - } -} - - -void MacroAssembler::Load(const Register& rt, - const MemOperand& addr, - Representation r) { - ASSERT(!r.IsDouble()); - - if (r.IsInteger8()) { - Ldrsb(rt, addr); - } else if (r.IsUInteger8()) { - Ldrb(rt, addr); - } else if (r.IsInteger16()) { - Ldrsh(rt, addr); - } else if (r.IsUInteger16()) { - Ldrh(rt, addr); - } else if (r.IsInteger32()) { - Ldr(rt.W(), addr); - } else { - ASSERT(rt.Is64Bits()); - Ldr(rt, addr); - } -} - - -void MacroAssembler::Store(const Register& rt, - const MemOperand& addr, - Representation r) { - ASSERT(!r.IsDouble()); - - if (r.IsInteger8() || r.IsUInteger8()) { - Strb(rt, addr); - } else if (r.IsInteger16() || r.IsUInteger16()) { - Strh(rt, addr); - } else if (r.IsInteger32()) { - Str(rt.W(), addr); - } else { - ASSERT(rt.Is64Bits()); - Str(rt, addr); - } -} - - -bool MacroAssembler::ShouldEmitVeneer(int max_reachable_pc, int margin) { - // Account for the branch around the veneers and the guard. - int protection_offset = 2 * kInstructionSize; - return pc_offset() > max_reachable_pc - margin - protection_offset - - static_cast(unresolved_branches_.size() * kMaxVeneerCodeSize); -} - - -void MacroAssembler::EmitVeneers(bool need_protection) { - RecordComment("[ Veneers"); - - Label end; - if (need_protection) { - B(&end); - } - - EmitVeneersGuard(); - - { - InstructionAccurateScope scope(this); - Label size_check; - - std::multimap::iterator it, it_to_delete; - - it = unresolved_branches_.begin(); - while (it != unresolved_branches_.end()) { - if (ShouldEmitVeneer(it->first)) { - Instruction* branch = InstructionAt(it->second.pc_offset_); - Label* label = it->second.label_; - -#ifdef DEBUG - __ bind(&size_check); -#endif - // Patch the branch to point to the current position, and emit a branch - // to the label. - Instruction* veneer = reinterpret_cast(pc_); - RemoveBranchFromLabelLinkChain(branch, label, veneer); - branch->SetImmPCOffsetTarget(veneer); - b(label); -#ifdef DEBUG - ASSERT(SizeOfCodeGeneratedSince(&size_check) <= - static_cast(kMaxVeneerCodeSize)); - size_check.Unuse(); -#endif - - it_to_delete = it++; - unresolved_branches_.erase(it_to_delete); - } else { - ++it; - } - } - } - - Bind(&end); - - RecordComment("]"); -} - - -void MacroAssembler::EmitVeneersGuard() { - if (emit_debug_code()) { - Unreachable(); - } -} - - -void MacroAssembler::CheckVeneers(bool need_protection) { - if (unresolved_branches_.empty()) { - return; - } - - CHECK(pc_offset() < unresolved_branches_first_limit()); - int margin = kVeneerDistanceMargin; - if (!need_protection) { - // Prefer emitting veneers protected by an existing instruction. - // The 4 divisor is a finger in the air guess. With a default margin of 2KB, - // that leaves 512B = 128 instructions of extra margin to avoid requiring a - // protective branch. - margin += margin / 4; - } - if (ShouldEmitVeneer(unresolved_branches_first_limit(), margin)) { - EmitVeneers(need_protection); - } -} - - -bool MacroAssembler::NeedExtraInstructionsOrRegisterBranch( - Label *label, ImmBranchType b_type) { - bool need_longer_range = false; - // There are two situations in which we care about the offset being out of - // range: - // - The label is bound but too far away. - // - The label is not bound but linked, and the previous branch - // instruction in the chain is too far away. - if (label->is_bound() || label->is_linked()) { - need_longer_range = - !Instruction::IsValidImmPCOffset(b_type, label->pos() - pc_offset()); - } - if (!need_longer_range && !label->is_bound()) { - int max_reachable_pc = pc_offset() + Instruction::ImmBranchRange(b_type); - unresolved_branches_.insert( - std::pair(max_reachable_pc, - FarBranchInfo(pc_offset(), label))); - } - return need_longer_range; -} - - -void MacroAssembler::B(Label* label, Condition cond) { - ASSERT(allow_macro_instructions_); - ASSERT((cond != al) && (cond != nv)); - - Label done; - bool need_extra_instructions = - NeedExtraInstructionsOrRegisterBranch(label, CondBranchType); - - if (need_extra_instructions) { - b(&done, InvertCondition(cond)); - b(label); - } else { - b(label, cond); - } - CheckVeneers(!need_extra_instructions); - bind(&done); -} - - -void MacroAssembler::Tbnz(const Register& rt, unsigned bit_pos, Label* label) { - ASSERT(allow_macro_instructions_); - - Label done; - bool need_extra_instructions = - NeedExtraInstructionsOrRegisterBranch(label, TestBranchType); - - if (need_extra_instructions) { - tbz(rt, bit_pos, &done); - b(label); - } else { - tbnz(rt, bit_pos, label); - } - CheckVeneers(!need_extra_instructions); - bind(&done); -} - - -void MacroAssembler::Tbz(const Register& rt, unsigned bit_pos, Label* label) { - ASSERT(allow_macro_instructions_); - - Label done; - bool need_extra_instructions = - NeedExtraInstructionsOrRegisterBranch(label, TestBranchType); - - if (need_extra_instructions) { - tbnz(rt, bit_pos, &done); - b(label); - } else { - tbz(rt, bit_pos, label); - } - CheckVeneers(!need_extra_instructions); - bind(&done); -} - - -void MacroAssembler::Cbnz(const Register& rt, Label* label) { - ASSERT(allow_macro_instructions_); - - Label done; - bool need_extra_instructions = - NeedExtraInstructionsOrRegisterBranch(label, CompareBranchType); - - if (need_extra_instructions) { - cbz(rt, &done); - b(label); - } else { - cbnz(rt, label); - } - CheckVeneers(!need_extra_instructions); - bind(&done); -} - - -void MacroAssembler::Cbz(const Register& rt, Label* label) { - ASSERT(allow_macro_instructions_); - - Label done; - bool need_extra_instructions = - NeedExtraInstructionsOrRegisterBranch(label, CompareBranchType); - - if (need_extra_instructions) { - cbnz(rt, &done); - b(label); - } else { - cbz(rt, label); - } - CheckVeneers(!need_extra_instructions); - bind(&done); -} - - -// Pseudo-instructions. - - -void MacroAssembler::Abs(const Register& rd, const Register& rm, - Label* is_not_representable, - Label* is_representable) { - ASSERT(allow_macro_instructions_); - ASSERT(AreSameSizeAndType(rd, rm)); - - Cmp(rm, 1); - Cneg(rd, rm, lt); - - // If the comparison sets the v flag, the input was the smallest value - // representable by rm, and the mathematical result of abs(rm) is not - // representable using two's complement. - if ((is_not_representable != NULL) && (is_representable != NULL)) { - B(is_not_representable, vs); - B(is_representable); - } else if (is_not_representable != NULL) { - B(is_not_representable, vs); - } else if (is_representable != NULL) { - B(is_representable, vc); - } -} - - -// Abstracted stack operations. - - -void MacroAssembler::Push(const CPURegister& src0, const CPURegister& src1, - const CPURegister& src2, const CPURegister& src3) { - ASSERT(AreSameSizeAndType(src0, src1, src2, src3)); - ASSERT(src0.IsValid()); - - int count = 1 + src1.IsValid() + src2.IsValid() + src3.IsValid(); - int size = src0.SizeInBytes(); - - PrepareForPush(count, size); - PushHelper(count, size, src0, src1, src2, src3); -} - - -void MacroAssembler::Pop(const CPURegister& dst0, const CPURegister& dst1, - const CPURegister& dst2, const CPURegister& dst3) { - // It is not valid to pop into the same register more than once in one - // instruction, not even into the zero register. - ASSERT(!AreAliased(dst0, dst1, dst2, dst3)); - ASSERT(AreSameSizeAndType(dst0, dst1, dst2, dst3)); - ASSERT(dst0.IsValid()); - - int count = 1 + dst1.IsValid() + dst2.IsValid() + dst3.IsValid(); - int size = dst0.SizeInBytes(); - - PrepareForPop(count, size); - PopHelper(count, size, dst0, dst1, dst2, dst3); - - if (!csp.Is(StackPointer()) && emit_debug_code()) { - // It is safe to leave csp where it is when unwinding the JavaScript stack, - // but if we keep it matching StackPointer, the simulator can detect memory - // accesses in the now-free part of the stack. - Mov(csp, StackPointer()); - } -} - - -void MacroAssembler::PushCPURegList(CPURegList registers) { - int size = registers.RegisterSizeInBytes(); - - PrepareForPush(registers.Count(), size); - // Push up to four registers at a time because if the current stack pointer is - // csp and reg_size is 32, registers must be pushed in blocks of four in order - // to maintain the 16-byte alignment for csp. - while (!registers.IsEmpty()) { - int count_before = registers.Count(); - const CPURegister& src0 = registers.PopHighestIndex(); - const CPURegister& src1 = registers.PopHighestIndex(); - const CPURegister& src2 = registers.PopHighestIndex(); - const CPURegister& src3 = registers.PopHighestIndex(); - int count = count_before - registers.Count(); - PushHelper(count, size, src0, src1, src2, src3); - } -} - - -void MacroAssembler::PopCPURegList(CPURegList registers) { - int size = registers.RegisterSizeInBytes(); - - PrepareForPop(registers.Count(), size); - // Pop up to four registers at a time because if the current stack pointer is - // csp and reg_size is 32, registers must be pushed in blocks of four in - // order to maintain the 16-byte alignment for csp. - while (!registers.IsEmpty()) { - int count_before = registers.Count(); - const CPURegister& dst0 = registers.PopLowestIndex(); - const CPURegister& dst1 = registers.PopLowestIndex(); - const CPURegister& dst2 = registers.PopLowestIndex(); - const CPURegister& dst3 = registers.PopLowestIndex(); - int count = count_before - registers.Count(); - PopHelper(count, size, dst0, dst1, dst2, dst3); - } - - if (!csp.Is(StackPointer()) && emit_debug_code()) { - // It is safe to leave csp where it is when unwinding the JavaScript stack, - // but if we keep it matching StackPointer, the simulator can detect memory - // accesses in the now-free part of the stack. - Mov(csp, StackPointer()); - } -} - - -void MacroAssembler::PushMultipleTimes(int count, Register src) { - int size = src.SizeInBytes(); - - PrepareForPush(count, size); - - if (FLAG_optimize_for_size && count > 8) { - Label loop; - __ Mov(Tmp0(), count / 2); - __ Bind(&loop); - PushHelper(2, size, src, src, NoReg, NoReg); - __ Subs(Tmp0(), Tmp0(), 1); - __ B(ne, &loop); - - count %= 2; - } - - // Push up to four registers at a time if possible because if the current - // stack pointer is csp and the register size is 32, registers must be pushed - // in blocks of four in order to maintain the 16-byte alignment for csp. - while (count >= 4) { - PushHelper(4, size, src, src, src, src); - count -= 4; - } - if (count >= 2) { - PushHelper(2, size, src, src, NoReg, NoReg); - count -= 2; - } - if (count == 1) { - PushHelper(1, size, src, NoReg, NoReg, NoReg); - count -= 1; - } - ASSERT(count == 0); -} - - -void MacroAssembler::PushHelper(int count, int size, - const CPURegister& src0, - const CPURegister& src1, - const CPURegister& src2, - const CPURegister& src3) { - // Ensure that we don't unintentially modify scratch or debug registers. - InstructionAccurateScope scope(this); - - ASSERT(AreSameSizeAndType(src0, src1, src2, src3)); - ASSERT(size == src0.SizeInBytes()); - - // When pushing multiple registers, the store order is chosen such that - // Push(a, b) is equivalent to Push(a) followed by Push(b). - switch (count) { - case 1: - ASSERT(src1.IsNone() && src2.IsNone() && src3.IsNone()); - str(src0, MemOperand(StackPointer(), -1 * size, PreIndex)); - break; - case 2: - ASSERT(src2.IsNone() && src3.IsNone()); - stp(src1, src0, MemOperand(StackPointer(), -2 * size, PreIndex)); - break; - case 3: - ASSERT(src3.IsNone()); - stp(src2, src1, MemOperand(StackPointer(), -3 * size, PreIndex)); - str(src0, MemOperand(StackPointer(), 2 * size)); - break; - case 4: - // Skip over 4 * size, then fill in the gap. This allows four W registers - // to be pushed using csp, whilst maintaining 16-byte alignment for csp - // at all times. - stp(src3, src2, MemOperand(StackPointer(), -4 * size, PreIndex)); - stp(src1, src0, MemOperand(StackPointer(), 2 * size)); - break; - default: - UNREACHABLE(); - } -} - - -void MacroAssembler::PopHelper(int count, int size, - const CPURegister& dst0, - const CPURegister& dst1, - const CPURegister& dst2, - const CPURegister& dst3) { - // Ensure that we don't unintentially modify scratch or debug registers. - InstructionAccurateScope scope(this); - - ASSERT(AreSameSizeAndType(dst0, dst1, dst2, dst3)); - ASSERT(size == dst0.SizeInBytes()); - - // When popping multiple registers, the load order is chosen such that - // Pop(a, b) is equivalent to Pop(a) followed by Pop(b). - switch (count) { - case 1: - ASSERT(dst1.IsNone() && dst2.IsNone() && dst3.IsNone()); - ldr(dst0, MemOperand(StackPointer(), 1 * size, PostIndex)); - break; - case 2: - ASSERT(dst2.IsNone() && dst3.IsNone()); - ldp(dst0, dst1, MemOperand(StackPointer(), 2 * size, PostIndex)); - break; - case 3: - ASSERT(dst3.IsNone()); - ldr(dst2, MemOperand(StackPointer(), 2 * size)); - ldp(dst0, dst1, MemOperand(StackPointer(), 3 * size, PostIndex)); - break; - case 4: - // Load the higher addresses first, then load the lower addresses and - // skip the whole block in the second instruction. This allows four W - // registers to be popped using csp, whilst maintaining 16-byte alignment - // for csp at all times. - ldp(dst2, dst3, MemOperand(StackPointer(), 2 * size)); - ldp(dst0, dst1, MemOperand(StackPointer(), 4 * size, PostIndex)); - break; - default: - UNREACHABLE(); - } -} - - -void MacroAssembler::PrepareForPush(int count, int size) { - // TODO(jbramley): Use AssertStackConsistency here, if possible. See the - // AssertStackConsistency for details of why we can't at the moment. - if (csp.Is(StackPointer())) { - // If the current stack pointer is csp, then it must be aligned to 16 bytes - // on entry and the total size of the specified registers must also be a - // multiple of 16 bytes. - ASSERT((count * size) % 16 == 0); - } else { - // Even if the current stack pointer is not the system stack pointer (csp), - // the system stack pointer will still be modified in order to comply with - // ABI rules about accessing memory below the system stack pointer. - BumpSystemStackPointer(count * size); - } -} - - -void MacroAssembler::PrepareForPop(int count, int size) { - AssertStackConsistency(); - if (csp.Is(StackPointer())) { - // If the current stack pointer is csp, then it must be aligned to 16 bytes - // on entry and the total size of the specified registers must also be a - // multiple of 16 bytes. - ASSERT((count * size) % 16 == 0); - } -} - - -void MacroAssembler::Poke(const CPURegister& src, const Operand& offset) { - if (offset.IsImmediate()) { - ASSERT(offset.immediate() >= 0); - } else if (emit_debug_code()) { - Cmp(xzr, offset); - Check(le, kStackAccessBelowStackPointer); - } - - Str(src, MemOperand(StackPointer(), offset)); -} - - -void MacroAssembler::Peek(const CPURegister& dst, const Operand& offset) { - if (offset.IsImmediate()) { - ASSERT(offset.immediate() >= 0); - } else if (emit_debug_code()) { - Cmp(xzr, offset); - Check(le, kStackAccessBelowStackPointer); - } - - Ldr(dst, MemOperand(StackPointer(), offset)); -} - - -void MacroAssembler::PokePair(const CPURegister& src1, - const CPURegister& src2, - int offset) { - ASSERT(AreSameSizeAndType(src1, src2)); - ASSERT((offset >= 0) && ((offset % src1.SizeInBytes()) == 0)); - Stp(src1, src2, MemOperand(StackPointer(), offset)); -} - - -void MacroAssembler::PeekPair(const CPURegister& dst1, - const CPURegister& dst2, - int offset) { - ASSERT(AreSameSizeAndType(dst1, dst2)); - ASSERT((offset >= 0) && ((offset % dst1.SizeInBytes()) == 0)); - Ldp(dst1, dst2, MemOperand(StackPointer(), offset)); -} - - -void MacroAssembler::PushCalleeSavedRegisters() { - // Ensure that the macro-assembler doesn't use any scratch registers. - InstructionAccurateScope scope(this); - - // This method must not be called unless the current stack pointer is the - // system stack pointer (csp). - ASSERT(csp.Is(StackPointer())); - - MemOperand tos(csp, -2 * kXRegSizeInBytes, PreIndex); - - stp(d14, d15, tos); - stp(d12, d13, tos); - stp(d10, d11, tos); - stp(d8, d9, tos); - - stp(x29, x30, tos); - stp(x27, x28, tos); // x28 = jssp - stp(x25, x26, tos); - stp(x23, x24, tos); - stp(x21, x22, tos); - stp(x19, x20, tos); -} - - -void MacroAssembler::PopCalleeSavedRegisters() { - // Ensure that the macro-assembler doesn't use any scratch registers. - InstructionAccurateScope scope(this); - - // This method must not be called unless the current stack pointer is the - // system stack pointer (csp). - ASSERT(csp.Is(StackPointer())); - - MemOperand tos(csp, 2 * kXRegSizeInBytes, PostIndex); - - ldp(x19, x20, tos); - ldp(x21, x22, tos); - ldp(x23, x24, tos); - ldp(x25, x26, tos); - ldp(x27, x28, tos); // x28 = jssp - ldp(x29, x30, tos); - - ldp(d8, d9, tos); - ldp(d10, d11, tos); - ldp(d12, d13, tos); - ldp(d14, d15, tos); -} - - -void MacroAssembler::AssertStackConsistency() { - if (emit_debug_code() && !csp.Is(StackPointer())) { - if (csp.Is(StackPointer())) { - // TODO(jbramley): Check for csp alignment if it is the stack pointer. - } else { - // TODO(jbramley): Currently we cannot use this assertion in Push because - // some calling code assumes that the flags are preserved. For an example, - // look at Builtins::Generate_ArgumentsAdaptorTrampoline. - Cmp(csp, StackPointer()); - Check(ls, kTheCurrentStackPointerIsBelowCsp); - } - } -} - - -void MacroAssembler::LoadRoot(Register destination, - Heap::RootListIndex index) { - // TODO(jbramley): Most root values are constants, and can be synthesized - // without a load. Refer to the ARM back end for details. - Ldr(destination, MemOperand(root, index << kPointerSizeLog2)); -} - - -void MacroAssembler::StoreRoot(Register source, - Heap::RootListIndex index) { - Str(source, MemOperand(root, index << kPointerSizeLog2)); -} - - -void MacroAssembler::LoadTrueFalseRoots(Register true_root, - Register false_root) { - STATIC_ASSERT((Heap::kTrueValueRootIndex + 1) == Heap::kFalseValueRootIndex); - Ldp(true_root, false_root, - MemOperand(root, Heap::kTrueValueRootIndex << kPointerSizeLog2)); -} - - -void MacroAssembler::LoadHeapObject(Register result, - Handle object) { - AllowDeferredHandleDereference using_raw_address; - if (isolate()->heap()->InNewSpace(*object)) { - Handle cell = isolate()->factory()->NewCell(object); - Mov(result, Operand(cell)); - Ldr(result, FieldMemOperand(result, Cell::kValueOffset)); - } else { - Mov(result, Operand(object)); - } -} - - -void MacroAssembler::LoadInstanceDescriptors(Register map, - Register descriptors) { - Ldr(descriptors, FieldMemOperand(map, Map::kDescriptorsOffset)); -} - - -void MacroAssembler::NumberOfOwnDescriptors(Register dst, Register map) { - Ldr(dst, FieldMemOperand(map, Map::kBitField3Offset)); - DecodeField(dst); -} - - -void MacroAssembler::EnumLengthUntagged(Register dst, Register map) { - STATIC_ASSERT(Map::EnumLengthBits::kShift == 0); - Ldrsw(dst, UntagSmiFieldMemOperand(map, Map::kBitField3Offset)); - And(dst, dst, Map::EnumLengthBits::kMask); -} - - -void MacroAssembler::EnumLengthSmi(Register dst, Register map) { - STATIC_ASSERT(Map::EnumLengthBits::kShift == 0); - Ldr(dst, FieldMemOperand(map, Map::kBitField3Offset)); - And(dst, dst, Operand(Smi::FromInt(Map::EnumLengthBits::kMask))); -} - - -void MacroAssembler::CheckEnumCache(Register object, - Register null_value, - Register scratch0, - Register scratch1, - Register scratch2, - Register scratch3, - Label* call_runtime) { - ASSERT(!AreAliased(object, null_value, scratch0, scratch1, scratch2, - scratch3)); - - Register empty_fixed_array_value = scratch0; - Register current_object = scratch1; - - LoadRoot(empty_fixed_array_value, Heap::kEmptyFixedArrayRootIndex); - Label next, start; - - Mov(current_object, object); - - // Check if the enum length field is properly initialized, indicating that - // there is an enum cache. - Register map = scratch2; - Register enum_length = scratch3; - Ldr(map, FieldMemOperand(current_object, HeapObject::kMapOffset)); - - EnumLengthUntagged(enum_length, map); - Cmp(enum_length, kInvalidEnumCacheSentinel); - B(eq, call_runtime); - - B(&start); - - Bind(&next); - Ldr(map, FieldMemOperand(current_object, HeapObject::kMapOffset)); - - // For all objects but the receiver, check that the cache is empty. - EnumLengthUntagged(enum_length, map); - Cbnz(enum_length, call_runtime); - - Bind(&start); - - // Check that there are no elements. Register current_object contains the - // current JS object we've reached through the prototype chain. - Label no_elements; - Ldr(current_object, FieldMemOperand(current_object, - JSObject::kElementsOffset)); - Cmp(current_object, empty_fixed_array_value); - B(eq, &no_elements); - - // Second chance, the object may be using the empty slow element dictionary. - CompareRoot(current_object, Heap::kEmptySlowElementDictionaryRootIndex); - B(ne, call_runtime); - - Bind(&no_elements); - Ldr(current_object, FieldMemOperand(map, Map::kPrototypeOffset)); - Cmp(current_object, null_value); - B(ne, &next); -} - - -void MacroAssembler::TestJSArrayForAllocationMemento(Register receiver, - Register scratch1, - Register scratch2, - Label* no_memento_found) { - ExternalReference new_space_start = - ExternalReference::new_space_start(isolate()); - ExternalReference new_space_allocation_top = - ExternalReference::new_space_allocation_top_address(isolate()); - - Add(scratch1, receiver, - JSArray::kSize + AllocationMemento::kSize - kHeapObjectTag); - Cmp(scratch1, Operand(new_space_start)); - B(lt, no_memento_found); - - Mov(scratch2, Operand(new_space_allocation_top)); - Ldr(scratch2, MemOperand(scratch2)); - Cmp(scratch1, scratch2); - B(gt, no_memento_found); - - Ldr(scratch1, MemOperand(scratch1, -AllocationMemento::kSize)); - Cmp(scratch1, - Operand(isolate()->factory()->allocation_memento_map())); -} - - -void MacroAssembler::JumpToHandlerEntry(Register exception, - Register object, - Register state, - Register scratch1, - Register scratch2) { - // Handler expects argument in x0. - ASSERT(exception.Is(x0)); - - // Compute the handler entry address and jump to it. The handler table is - // a fixed array of (smi-tagged) code offsets. - Ldr(scratch1, FieldMemOperand(object, Code::kHandlerTableOffset)); - Add(scratch1, scratch1, FixedArray::kHeaderSize - kHeapObjectTag); - STATIC_ASSERT(StackHandler::kKindWidth < kPointerSizeLog2); - Lsr(scratch2, state, StackHandler::kKindWidth); - Ldr(scratch2, MemOperand(scratch1, scratch2, LSL, kPointerSizeLog2)); - Add(scratch1, object, Code::kHeaderSize - kHeapObjectTag); - Add(scratch1, scratch1, Operand::UntagSmi(scratch2)); - Br(scratch1); -} - - -void MacroAssembler::InNewSpace(Register object, - Condition cond, - Label* branch) { - ASSERT(cond == eq || cond == ne); - // Use Tmp1() to have a different destination register, as Tmp0() will be used - // for relocation. - And(Tmp1(), object, Operand(ExternalReference::new_space_mask(isolate()))); - Cmp(Tmp1(), Operand(ExternalReference::new_space_start(isolate()))); - B(cond, branch); -} - - -void MacroAssembler::Throw(Register value, - Register scratch1, - Register scratch2, - Register scratch3, - Register scratch4) { - // Adjust this code if not the case. - STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); - STATIC_ASSERT(StackHandlerConstants::kCodeOffset == 1 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kStateOffset == 2 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kContextOffset == 3 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kFPOffset == 4 * kPointerSize); - - // The handler expects the exception in x0. - ASSERT(value.Is(x0)); - - // Drop the stack pointer to the top of the top handler. - ASSERT(jssp.Is(StackPointer())); - Mov(scratch1, Operand(ExternalReference(Isolate::kHandlerAddress, - isolate()))); - Ldr(jssp, MemOperand(scratch1)); - // Restore the next handler. - Pop(scratch2); - Str(scratch2, MemOperand(scratch1)); - - // Get the code object and state. Restore the context and frame pointer. - Register object = scratch1; - Register state = scratch2; - Pop(object, state, cp, fp); - - // If the handler is a JS frame, restore the context to the frame. - // (kind == ENTRY) == (fp == 0) == (cp == 0), so we could test either fp - // or cp. - Label not_js_frame; - Cbz(cp, ¬_js_frame); - Str(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); - Bind(¬_js_frame); - - JumpToHandlerEntry(value, object, state, scratch3, scratch4); -} - - -void MacroAssembler::ThrowUncatchable(Register value, - Register scratch1, - Register scratch2, - Register scratch3, - Register scratch4) { - // Adjust this code if not the case. - STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kCodeOffset == 1 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kStateOffset == 2 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kContextOffset == 3 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kFPOffset == 4 * kPointerSize); - - // The handler expects the exception in x0. - ASSERT(value.Is(x0)); - - // Drop the stack pointer to the top of the top stack handler. - ASSERT(jssp.Is(StackPointer())); - Mov(scratch1, Operand(ExternalReference(Isolate::kHandlerAddress, - isolate()))); - Ldr(jssp, MemOperand(scratch1)); - - // Unwind the handlers until the ENTRY handler is found. - Label fetch_next, check_kind; - B(&check_kind); - Bind(&fetch_next); - Peek(jssp, StackHandlerConstants::kNextOffset); - - Bind(&check_kind); - STATIC_ASSERT(StackHandler::JS_ENTRY == 0); - Peek(scratch2, StackHandlerConstants::kStateOffset); - TestAndBranchIfAnySet(scratch2, StackHandler::KindField::kMask, &fetch_next); - - // Set the top handler address to next handler past the top ENTRY handler. - Pop(scratch2); - Str(scratch2, MemOperand(scratch1)); - - // Get the code object and state. Clear the context and frame pointer (0 was - // saved in the handler). - Register object = scratch1; - Register state = scratch2; - Pop(object, state, cp, fp); - - JumpToHandlerEntry(value, object, state, scratch3, scratch4); -} - - -void MacroAssembler::Throw(BailoutReason reason) { - Label throw_start; - Bind(&throw_start); -#ifdef DEBUG - const char* msg = GetBailoutReason(reason); - RecordComment("Throw message: "); - RecordComment((msg != NULL) ? msg : "UNKNOWN"); -#endif - - Mov(x0, Operand(Smi::FromInt(reason))); - Push(x0); - - // Disable stub call restrictions to always allow calls to throw. - 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); - CallRuntime(Runtime::kThrowMessage, 1); - } else { - CallRuntime(Runtime::kThrowMessage, 1); - } - // ThrowMessage should not return here. - Unreachable(); -} - - -void MacroAssembler::ThrowIf(Condition cc, BailoutReason reason) { - Label ok; - B(InvertCondition(cc), &ok); - Throw(reason); - Bind(&ok); -} - - -void MacroAssembler::ThrowIfSmi(const Register& value, BailoutReason reason) { - Label ok; - JumpIfNotSmi(value, &ok); - Throw(reason); - Bind(&ok); -} - - -void MacroAssembler::SmiAbs(const Register& smi, Label* slow) { - ASSERT(smi.Is64Bits()); - Abs(smi, smi, slow); -} - - -void MacroAssembler::AssertSmi(Register object, BailoutReason reason) { - if (emit_debug_code()) { - STATIC_ASSERT(kSmiTag == 0); - Tst(object, kSmiTagMask); - Check(eq, reason); - } -} - - -void MacroAssembler::AssertNotSmi(Register object, BailoutReason reason) { - if (emit_debug_code()) { - STATIC_ASSERT(kSmiTag == 0); - Tst(object, kSmiTagMask); - Check(ne, reason); - } -} - - -void MacroAssembler::AssertName(Register object) { - if (emit_debug_code()) { - STATIC_ASSERT(kSmiTag == 0); - // TODO(jbramley): Add AbortIfSmi and related functions. - Label not_smi; - JumpIfNotSmi(object, ¬_smi); - Abort(kOperandIsASmiAndNotAName); - Bind(¬_smi); - - Ldr(Tmp1(), FieldMemOperand(object, HeapObject::kMapOffset)); - CompareInstanceType(Tmp1(), Tmp1(), LAST_NAME_TYPE); - Check(ls, kOperandIsNotAName); - } -} - - -void MacroAssembler::AssertString(Register object) { - if (emit_debug_code()) { - Register temp = Tmp1(); - STATIC_ASSERT(kSmiTag == 0); - Tst(object, kSmiTagMask); - Check(ne, kOperandIsASmiAndNotAString); - Ldr(temp, FieldMemOperand(object, HeapObject::kMapOffset)); - CompareInstanceType(temp, temp, FIRST_NONSTRING_TYPE); - Check(lo, kOperandIsNotAString); - } -} - - -void MacroAssembler::CallStub(CodeStub* stub, TypeFeedbackId ast_id) { - ASSERT(AllowThisStubCall(stub)); // Stub calls are not allowed in some stubs. - Call(stub->GetCode(isolate()), RelocInfo::CODE_TARGET, ast_id); -} - - -void MacroAssembler::TailCallStub(CodeStub* stub) { - Jump(stub->GetCode(isolate()), RelocInfo::CODE_TARGET); -} - - -void MacroAssembler::CallRuntime(const Runtime::Function* f, - int num_arguments, - SaveFPRegsMode save_doubles) { - // All arguments must be on the stack before this function is called. - // x0 holds the return value after the call. - - // Check that the number of arguments matches what the function expects. - // If f->nargs is -1, the function can accept a variable number of arguments. - if (f->nargs >= 0 && f->nargs != num_arguments) { - // Illegal operation: drop the stack arguments and return undefined. - if (num_arguments > 0) { - Drop(num_arguments); - } - LoadRoot(x0, Heap::kUndefinedValueRootIndex); - return; - } - - // Place the necessary arguments. - Mov(x0, num_arguments); - Mov(x1, Operand(ExternalReference(f, isolate()))); - - CEntryStub stub(1, save_doubles); - CallStub(&stub); -} - - -static int AddressOffset(ExternalReference ref0, ExternalReference ref1) { - return ref0.address() - ref1.address(); -} - - -void MacroAssembler::CallApiFunctionAndReturn( - Register function_address, - ExternalReference thunk_ref, - int stack_space, - int spill_offset, - MemOperand return_value_operand, - MemOperand* context_restore_operand) { - ASM_LOCATION("CallApiFunctionAndReturn"); - ExternalReference next_address = - ExternalReference::handle_scope_next_address(isolate()); - const int kNextOffset = 0; - const int kLimitOffset = AddressOffset( - ExternalReference::handle_scope_limit_address(isolate()), - next_address); - const int kLevelOffset = AddressOffset( - ExternalReference::handle_scope_level_address(isolate()), - next_address); - - ASSERT(function_address.is(x1) || function_address.is(x2)); - - Label profiler_disabled; - Label end_profiler_check; - bool* is_profiling_flag = isolate()->cpu_profiler()->is_profiling_address(); - STATIC_ASSERT(sizeof(*is_profiling_flag) == 1); - Mov(x10, reinterpret_cast(is_profiling_flag)); - Ldrb(w10, MemOperand(x10)); - Cbz(w10, &profiler_disabled); - Mov(x3, Operand(thunk_ref)); - B(&end_profiler_check); - - Bind(&profiler_disabled); - Mov(x3, function_address); - Bind(&end_profiler_check); - - // Save the callee-save registers we are going to use. - // TODO(all): Is this necessary? ARM doesn't do it. - STATIC_ASSERT(kCallApiFunctionSpillSpace == 4); - Poke(x19, (spill_offset + 0) * kXRegSizeInBytes); - Poke(x20, (spill_offset + 1) * kXRegSizeInBytes); - Poke(x21, (spill_offset + 2) * kXRegSizeInBytes); - Poke(x22, (spill_offset + 3) * kXRegSizeInBytes); - - // Allocate HandleScope in callee-save registers. - // We will need to restore the HandleScope after the call to the API function, - // by allocating it in callee-save registers they will be preserved by C code. - Register handle_scope_base = x22; - Register next_address_reg = x19; - Register limit_reg = x20; - Register level_reg = w21; - - Mov(handle_scope_base, Operand(next_address)); - Ldr(next_address_reg, MemOperand(handle_scope_base, kNextOffset)); - Ldr(limit_reg, MemOperand(handle_scope_base, kLimitOffset)); - Ldr(level_reg, MemOperand(handle_scope_base, kLevelOffset)); - Add(level_reg, level_reg, 1); - Str(level_reg, MemOperand(handle_scope_base, kLevelOffset)); - - if (FLAG_log_timer_events) { - FrameScope frame(this, StackFrame::MANUAL); - PushSafepointRegisters(); - Mov(x0, Operand(ExternalReference::isolate_address(isolate()))); - CallCFunction(ExternalReference::log_enter_external_function(isolate()), 1); - PopSafepointRegisters(); - } - - // Native call returns to the DirectCEntry stub which redirects to the - // return address pushed on stack (could have moved after GC). - // DirectCEntry stub itself is generated early and never moves. - DirectCEntryStub stub; - stub.GenerateCall(this, x3); - - if (FLAG_log_timer_events) { - FrameScope frame(this, StackFrame::MANUAL); - PushSafepointRegisters(); - Mov(x0, Operand(ExternalReference::isolate_address(isolate()))); - CallCFunction(ExternalReference::log_leave_external_function(isolate()), 1); - PopSafepointRegisters(); - } - - Label promote_scheduled_exception; - Label exception_handled; - Label delete_allocated_handles; - Label leave_exit_frame; - Label return_value_loaded; - - // Load value from ReturnValue. - Ldr(x0, return_value_operand); - Bind(&return_value_loaded); - // No more valid handles (the result handle was the last one). Restore - // previous handle scope. - Str(next_address_reg, MemOperand(handle_scope_base, kNextOffset)); - if (emit_debug_code()) { - Ldr(w1, MemOperand(handle_scope_base, kLevelOffset)); - Cmp(w1, level_reg); - Check(eq, kUnexpectedLevelAfterReturnFromApiCall); - } - Sub(level_reg, level_reg, 1); - Str(level_reg, MemOperand(handle_scope_base, kLevelOffset)); - Ldr(x1, MemOperand(handle_scope_base, kLimitOffset)); - Cmp(limit_reg, x1); - B(ne, &delete_allocated_handles); - - Bind(&leave_exit_frame); - // Restore callee-saved registers. - Peek(x19, (spill_offset + 0) * kXRegSizeInBytes); - Peek(x20, (spill_offset + 1) * kXRegSizeInBytes); - Peek(x21, (spill_offset + 2) * kXRegSizeInBytes); - Peek(x22, (spill_offset + 3) * kXRegSizeInBytes); - - // Check if the function scheduled an exception. - Mov(x5, Operand(ExternalReference::scheduled_exception_address(isolate()))); - Ldr(x5, MemOperand(x5)); - JumpIfNotRoot(x5, Heap::kTheHoleValueRootIndex, &promote_scheduled_exception); - Bind(&exception_handled); - - bool restore_context = context_restore_operand != NULL; - if (restore_context) { - Ldr(cp, *context_restore_operand); - } - - LeaveExitFrame(false, x1, !restore_context); - Drop(stack_space); - Ret(); - - Bind(&promote_scheduled_exception); - { - FrameScope frame(this, StackFrame::INTERNAL); - CallExternalReference( - ExternalReference(Runtime::kPromoteScheduledException, isolate()), 0); - } - B(&exception_handled); - - // HandleScope limit has changed. Delete allocated extensions. - Bind(&delete_allocated_handles); - Str(limit_reg, MemOperand(handle_scope_base, kLimitOffset)); - // Save the return value in a callee-save register. - Register saved_result = x19; - Mov(saved_result, x0); - Mov(x0, Operand(ExternalReference::isolate_address(isolate()))); - CallCFunction( - ExternalReference::delete_handle_scope_extensions(isolate()), 1); - Mov(x0, saved_result); - B(&leave_exit_frame); -} - - -void MacroAssembler::CallExternalReference(const ExternalReference& ext, - int num_arguments) { - Mov(x0, num_arguments); - Mov(x1, Operand(ext)); - - CEntryStub stub(1); - CallStub(&stub); -} - - -void MacroAssembler::JumpToExternalReference(const ExternalReference& builtin) { - Mov(x1, Operand(builtin)); - CEntryStub stub(1); - Jump(stub.GetCode(isolate()), RelocInfo::CODE_TARGET); -} - - -void MacroAssembler::GetBuiltinFunction(Register target, - Builtins::JavaScript id) { - // Load the builtins object into target register. - Ldr(target, GlobalObjectMemOperand()); - Ldr(target, FieldMemOperand(target, GlobalObject::kBuiltinsOffset)); - // Load the JavaScript builtin function from the builtins object. - Ldr(target, FieldMemOperand(target, - JSBuiltinsObject::OffsetOfFunctionWithId(id))); -} - - -void MacroAssembler::GetBuiltinEntry(Register target, Builtins::JavaScript id) { - ASSERT(!target.is(x1)); - GetBuiltinFunction(x1, id); - // Load the code entry point from the builtins object. - Ldr(target, FieldMemOperand(x1, JSFunction::kCodeEntryOffset)); -} - - -void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id, - InvokeFlag flag, - const CallWrapper& call_wrapper) { - ASM_LOCATION("MacroAssembler::InvokeBuiltin"); - // You can't call a builtin without a valid frame. - ASSERT(flag == JUMP_FUNCTION || has_frame()); - - GetBuiltinEntry(x2, id); - if (flag == CALL_FUNCTION) { - call_wrapper.BeforeCall(CallSize(x2)); - Call(x2); - call_wrapper.AfterCall(); - } else { - ASSERT(flag == JUMP_FUNCTION); - Jump(x2); - } -} - - -void MacroAssembler::TailCallExternalReference(const ExternalReference& ext, - int num_arguments, - int result_size) { - // 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(x0, num_arguments); - JumpToExternalReference(ext); -} - - -void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid, - int num_arguments, - int result_size) { - TailCallExternalReference(ExternalReference(fid, isolate()), - num_arguments, - result_size); -} - - -void MacroAssembler::InitializeNewString(Register string, - Register length, - Heap::RootListIndex map_index, - Register scratch1, - Register scratch2) { - ASSERT(!AreAliased(string, length, scratch1, scratch2)); - LoadRoot(scratch2, map_index); - SmiTag(scratch1, length); - Str(scratch2, FieldMemOperand(string, HeapObject::kMapOffset)); - - Mov(scratch2, String::kEmptyHashField); - Str(scratch1, FieldMemOperand(string, String::kLengthOffset)); - Str(scratch2, FieldMemOperand(string, String::kHashFieldOffset)); -} - - -int MacroAssembler::ActivationFrameAlignment() { -#if V8_HOST_ARCH_A64 - // Running on the real platform. Use the alignment as mandated by the local - // environment. - // Note: This will break if we ever start generating snapshots on one ARM - // platform for another ARM platform with a different alignment. - return OS::ActivationFrameAlignment(); -#else // V8_HOST_ARCH_A64 - // If we are using the simulator then we should always align to the expected - // alignment. As the simulator is used to generate snapshots we do not know - // if the target platform will need alignment, so this is controlled from a - // flag. - return FLAG_sim_stack_alignment; -#endif // V8_HOST_ARCH_A64 -} - - -void MacroAssembler::CallCFunction(ExternalReference function, - int num_of_reg_args) { - CallCFunction(function, num_of_reg_args, 0); -} - - -void MacroAssembler::CallCFunction(ExternalReference function, - int num_of_reg_args, - int num_of_double_args) { - Mov(Tmp0(), Operand(function)); - CallCFunction(Tmp0(), num_of_reg_args, num_of_double_args); -} - - -void MacroAssembler::CallCFunction(Register function, - int num_of_reg_args, - int num_of_double_args) { - ASSERT(has_frame()); - // We can pass 8 integer arguments in registers. If we need to pass more than - // that, we'll need to implement support for passing them on the stack. - ASSERT(num_of_reg_args <= 8); - - // If we're passing doubles, we're limited to the following prototypes - // (defined by ExternalReference::Type): - // BUILTIN_COMPARE_CALL: int f(double, double) - // BUILTIN_FP_FP_CALL: double f(double, double) - // BUILTIN_FP_CALL: double f(double) - // BUILTIN_FP_INT_CALL: double f(double, int) - if (num_of_double_args > 0) { - ASSERT(num_of_reg_args <= 1); - ASSERT((num_of_double_args + num_of_reg_args) <= 2); - } - - - // If the stack pointer is not csp, we need to derive an aligned csp from the - // current stack pointer. - const Register old_stack_pointer = StackPointer(); - if (!csp.Is(old_stack_pointer)) { - AssertStackConsistency(); - - int sp_alignment = ActivationFrameAlignment(); - // The ABI mandates at least 16-byte alignment. - ASSERT(sp_alignment >= 16); - ASSERT(IsPowerOf2(sp_alignment)); - - // The current stack pointer is a callee saved register, and is preserved - // across the call. - ASSERT(kCalleeSaved.IncludesAliasOf(old_stack_pointer)); - - // Align and synchronize the system stack pointer with jssp. - Bic(csp, old_stack_pointer, sp_alignment - 1); - SetStackPointer(csp); - } - - // Call directly. The function called cannot cause a GC, or allow preemption, - // so the return address in the link register stays correct. - Call(function); - - if (!csp.Is(old_stack_pointer)) { - if (emit_debug_code()) { - // Because the stack pointer must be aligned on a 16-byte boundary, the - // aligned csp can be up to 12 bytes below the jssp. This is the case - // where we only pushed one W register on top of an aligned jssp. - Register temp = Tmp1(); - ASSERT(ActivationFrameAlignment() == 16); - Sub(temp, csp, old_stack_pointer); - // We want temp <= 0 && temp >= -12. - Cmp(temp, 0); - Ccmp(temp, -12, NFlag, le); - Check(ge, kTheStackWasCorruptedByMacroAssemblerCall); - } - SetStackPointer(old_stack_pointer); - } -} - - -void MacroAssembler::Jump(Register target) { - Br(target); -} - - -void MacroAssembler::Jump(intptr_t target, RelocInfo::Mode rmode) { - Mov(Tmp0(), Operand(target, rmode)); - Br(Tmp0()); -} - - -void MacroAssembler::Jump(Address target, RelocInfo::Mode rmode) { - ASSERT(!RelocInfo::IsCodeTarget(rmode)); - Jump(reinterpret_cast(target), rmode); -} - - -void MacroAssembler::Jump(Handle code, RelocInfo::Mode rmode) { - ASSERT(RelocInfo::IsCodeTarget(rmode)); - AllowDeferredHandleDereference embedding_raw_address; - Jump(reinterpret_cast(code.location()), rmode); -} - - -void MacroAssembler::Call(Register target) { - BlockConstPoolScope scope(this); -#ifdef DEBUG - Label start_call; - Bind(&start_call); -#endif - - Blr(target); - -#ifdef DEBUG - AssertSizeOfCodeGeneratedSince(&start_call, CallSize(target)); -#endif -} - - -void MacroAssembler::Call(Label* target) { - BlockConstPoolScope scope(this); -#ifdef DEBUG - Label start_call; - Bind(&start_call); -#endif - - Bl(target); - -#ifdef DEBUG - AssertSizeOfCodeGeneratedSince(&start_call, CallSize(target)); -#endif -} - - -// MacroAssembler::CallSize is sensitive to changes in this function, as it -// requires to know how many instructions are used to branch to the target. -void MacroAssembler::Call(Address target, RelocInfo::Mode rmode) { - BlockConstPoolScope scope(this); -#ifdef DEBUG - Label start_call; - Bind(&start_call); -#endif - // Statement positions are expected to be recorded when the target - // address is loaded. - positions_recorder()->WriteRecordedPositions(); - - // Addresses always have 64 bits, so we shouldn't encounter NONE32. - ASSERT(rmode != RelocInfo::NONE32); - - if (rmode == RelocInfo::NONE64) { - uint64_t imm = reinterpret_cast(target); - movz(Tmp0(), (imm >> 0) & 0xffff, 0); - movk(Tmp0(), (imm >> 16) & 0xffff, 16); - movk(Tmp0(), (imm >> 32) & 0xffff, 32); - movk(Tmp0(), (imm >> 48) & 0xffff, 48); - } else { - LoadRelocated(Tmp0(), Operand(reinterpret_cast(target), rmode)); - } - Blr(Tmp0()); -#ifdef DEBUG - AssertSizeOfCodeGeneratedSince(&start_call, CallSize(target, rmode)); -#endif -} - - -void MacroAssembler::Call(Handle code, - RelocInfo::Mode rmode, - TypeFeedbackId ast_id) { -#ifdef DEBUG - Label start_call; - Bind(&start_call); -#endif - - if ((rmode == RelocInfo::CODE_TARGET) && (!ast_id.IsNone())) { - SetRecordedAstId(ast_id); - rmode = RelocInfo::CODE_TARGET_WITH_ID; - } - - AllowDeferredHandleDereference embedding_raw_address; - Call(reinterpret_cast
(code.location()), rmode); - -#ifdef DEBUG - // Check the size of the code generated. - AssertSizeOfCodeGeneratedSince(&start_call, CallSize(code, rmode, ast_id)); -#endif -} - - -int MacroAssembler::CallSize(Register target) { - USE(target); - return kInstructionSize; -} - - -int MacroAssembler::CallSize(Label* target) { - USE(target); - return kInstructionSize; -} - - -int MacroAssembler::CallSize(Address target, RelocInfo::Mode rmode) { - USE(target); - - // Addresses always have 64 bits, so we shouldn't encounter NONE32. - ASSERT(rmode != RelocInfo::NONE32); - - if (rmode == RelocInfo::NONE64) { - return kCallSizeWithoutRelocation; - } else { - return kCallSizeWithRelocation; - } -} - - -int MacroAssembler::CallSize(Handle code, - RelocInfo::Mode rmode, - TypeFeedbackId ast_id) { - USE(code); - USE(ast_id); - - // Addresses always have 64 bits, so we shouldn't encounter NONE32. - ASSERT(rmode != RelocInfo::NONE32); - - if (rmode == RelocInfo::NONE64) { - return kCallSizeWithoutRelocation; - } else { - return kCallSizeWithRelocation; - } -} - - - - - -void MacroAssembler::JumpForHeapNumber(Register object, - Register heap_number_map, - Label* on_heap_number, - Label* on_not_heap_number) { - ASSERT(on_heap_number || on_not_heap_number); - // Tmp0() is used as a scratch register. - ASSERT(!AreAliased(Tmp0(), heap_number_map)); - AssertNotSmi(object); - - // Load the HeapNumber map if it is not passed. - if (heap_number_map.Is(NoReg)) { - heap_number_map = Tmp1(); - LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex); - } else { - // This assert clobbers Tmp0(), so do it before loading Tmp0() with the map. - AssertRegisterIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex); - } - - Ldr(Tmp0(), FieldMemOperand(object, HeapObject::kMapOffset)); - Cmp(Tmp0(), heap_number_map); - - if (on_heap_number) { - B(eq, on_heap_number); - } - if (on_not_heap_number) { - B(ne, on_not_heap_number); - } -} - - -void MacroAssembler::JumpIfHeapNumber(Register object, - Label* on_heap_number, - Register heap_number_map) { - JumpForHeapNumber(object, - heap_number_map, - on_heap_number, - NULL); -} - - -void MacroAssembler::JumpIfNotHeapNumber(Register object, - Label* on_not_heap_number, - Register heap_number_map) { - JumpForHeapNumber(object, - heap_number_map, - NULL, - on_not_heap_number); -} - - -void MacroAssembler::LookupNumberStringCache(Register object, - Register result, - Register scratch1, - Register scratch2, - Register scratch3, - Label* not_found) { - ASSERT(!AreAliased(object, result, scratch1, scratch2, scratch3)); - - // Use of registers. Register result is used as a temporary. - Register number_string_cache = result; - Register mask = scratch3; - - // Load the number string cache. - LoadRoot(number_string_cache, Heap::kNumberStringCacheRootIndex); - - // Make the hash mask from the length of the number string cache. It - // contains two elements (number and string) for each cache entry. - Ldrsw(mask, UntagSmiFieldMemOperand(number_string_cache, - FixedArray::kLengthOffset)); - Asr(mask, mask, 1); // Divide length by two. - Sub(mask, mask, 1); // Make mask. - - // Calculate the entry in the number string cache. The hash value in the - // number string cache for smis is just the smi value, and the hash for - // doubles is the xor of the upper and lower words. See - // Heap::GetNumberStringCache. - Label is_smi; - Label load_result_from_cache; - - JumpIfSmi(object, &is_smi); - CheckMap(object, scratch1, Heap::kHeapNumberMapRootIndex, not_found, - DONT_DO_SMI_CHECK); - - STATIC_ASSERT(kDoubleSize == (kWRegSizeInBytes * 2)); - Add(scratch1, object, HeapNumber::kValueOffset - kHeapObjectTag); - Ldp(scratch1.W(), scratch2.W(), MemOperand(scratch1)); - Eor(scratch1, scratch1, scratch2); - And(scratch1, scratch1, mask); - - // Calculate address of entry in string cache: each entry consists of two - // pointer sized fields. - Add(scratch1, number_string_cache, - Operand(scratch1, LSL, kPointerSizeLog2 + 1)); - - Register probe = mask; - Ldr(probe, FieldMemOperand(scratch1, FixedArray::kHeaderSize)); - JumpIfSmi(probe, not_found); - Ldr(d0, FieldMemOperand(object, HeapNumber::kValueOffset)); - Ldr(d1, FieldMemOperand(probe, HeapNumber::kValueOffset)); - Fcmp(d0, d1); - B(ne, not_found); - B(&load_result_from_cache); - - Bind(&is_smi); - Register scratch = scratch1; - And(scratch, mask, Operand::UntagSmi(object)); - // Calculate address of entry in string cache: each entry consists - // of two pointer sized fields. - Add(scratch, number_string_cache, - Operand(scratch, LSL, kPointerSizeLog2 + 1)); - - // Check if the entry is the smi we are looking for. - Ldr(probe, FieldMemOperand(scratch, FixedArray::kHeaderSize)); - Cmp(object, probe); - B(ne, not_found); - - // Get the result from the cache. - Bind(&load_result_from_cache); - Ldr(result, FieldMemOperand(scratch, FixedArray::kHeaderSize + kPointerSize)); - IncrementCounter(isolate()->counters()->number_to_string_native(), 1, - scratch1, scratch2); -} - - -void MacroAssembler::TryConvertDoubleToInt(Register as_int, - FPRegister value, - FPRegister scratch_d, - Label* on_successful_conversion, - Label* on_failed_conversion) { - // Convert to an int and back again, then compare with the original value. - Fcvtzs(as_int, value); - Scvtf(scratch_d, as_int); - Fcmp(value, scratch_d); - - if (on_successful_conversion) { - B(on_successful_conversion, eq); - } - if (on_failed_conversion) { - B(on_failed_conversion, ne); - } -} - - -void MacroAssembler::JumpIfMinusZero(DoubleRegister input, - Label* on_negative_zero) { - // Floating point -0.0 is kMinInt as an integer, so subtracting 1 (cmp) will - // cause overflow. - Fmov(Tmp0(), input); - Cmp(Tmp0(), 1); - B(vs, on_negative_zero); -} - - -void MacroAssembler::ClampInt32ToUint8(Register output, Register input) { - // Clamp the value to [0..255]. - Cmp(input.W(), Operand(input.W(), UXTB)); - // If input < input & 0xff, it must be < 0, so saturate to 0. - Csel(output.W(), wzr, input.W(), lt); - // Create a constant 0xff. - Mov(WTmp0(), 255); - // If input > input & 0xff, it must be > 255, so saturate to 255. - Csel(output.W(), WTmp0(), output.W(), gt); -} - - -void MacroAssembler::ClampInt32ToUint8(Register in_out) { - ClampInt32ToUint8(in_out, in_out); -} - - -void MacroAssembler::ClampDoubleToUint8(Register output, - DoubleRegister input, - DoubleRegister dbl_scratch) { - // This conversion follows the WebIDL "[Clamp]" rules for PIXEL types: - // - Inputs lower than 0 (including -infinity) produce 0. - // - Inputs higher than 255 (including +infinity) produce 255. - // Also, it seems that PIXEL types use round-to-nearest rather than - // round-towards-zero. - - // Squash +infinity before the conversion, since Fcvtnu will normally - // convert it to 0. - Fmov(dbl_scratch, 255); - Fmin(dbl_scratch, dbl_scratch, input); - - // Convert double to unsigned integer. Values less than zero become zero. - // Values greater than 255 have already been clamped to 255. - Fcvtnu(output, dbl_scratch); -} - - -void MacroAssembler::CopyFieldsLoopPairsHelper(Register dst, - Register src, - unsigned count, - Register scratch1, - Register scratch2, - Register scratch3) { - // Untag src and dst into scratch registers. - // Copy src->dst in a tight loop. - ASSERT(!AreAliased(dst, src, scratch1, scratch2, scratch3, Tmp0(), Tmp1())); - ASSERT(count >= 2); - - const Register& remaining = scratch3; - Mov(remaining, count / 2); - - // Only use the Assembler, so we can use Tmp0() and Tmp1(). - InstructionAccurateScope scope(this); - - const Register& dst_untagged = scratch1; - const Register& src_untagged = scratch2; - sub(dst_untagged, dst, kHeapObjectTag); - sub(src_untagged, src, kHeapObjectTag); - - // Copy fields in pairs. - Label loop; - bind(&loop); - ldp(Tmp0(), Tmp1(), MemOperand(src_untagged, kXRegSizeInBytes * 2, - PostIndex)); - stp(Tmp0(), Tmp1(), MemOperand(dst_untagged, kXRegSizeInBytes * 2, - PostIndex)); - sub(remaining, remaining, 1); - cbnz(remaining, &loop); - - // Handle the leftovers. - if (count & 1) { - ldr(Tmp0(), MemOperand(src_untagged)); - str(Tmp0(), MemOperand(dst_untagged)); - } -} - - -void MacroAssembler::CopyFieldsUnrolledPairsHelper(Register dst, - Register src, - unsigned count, - Register scratch1, - Register scratch2) { - // Untag src and dst into scratch registers. - // Copy src->dst in an unrolled loop. - ASSERT(!AreAliased(dst, src, scratch1, scratch2, Tmp0(), Tmp1())); - - // Only use the Assembler, so we can use Tmp0() and Tmp1(). - InstructionAccurateScope scope(this); - - const Register& dst_untagged = scratch1; - const Register& src_untagged = scratch2; - sub(dst_untagged, dst, kHeapObjectTag); - sub(src_untagged, src, kHeapObjectTag); - - // Copy fields in pairs. - for (unsigned i = 0; i < count / 2; i++) { - ldp(Tmp0(), Tmp1(), MemOperand(src_untagged, kXRegSizeInBytes * 2, - PostIndex)); - stp(Tmp0(), Tmp1(), MemOperand(dst_untagged, kXRegSizeInBytes * 2, - PostIndex)); - } - - // Handle the leftovers. - if (count & 1) { - ldr(Tmp0(), MemOperand(src_untagged)); - str(Tmp0(), MemOperand(dst_untagged)); - } -} - - -void MacroAssembler::CopyFieldsUnrolledHelper(Register dst, - Register src, - unsigned count, - Register scratch1) { - // Untag src and dst into scratch registers. - // Copy src->dst in an unrolled loop. - ASSERT(!AreAliased(dst, src, scratch1, Tmp0(), Tmp1())); - - // Only use the Assembler, so we can use Tmp0() and Tmp1(). - InstructionAccurateScope scope(this); - - const Register& dst_untagged = scratch1; - const Register& src_untagged = Tmp1(); - sub(dst_untagged, dst, kHeapObjectTag); - sub(src_untagged, src, kHeapObjectTag); - - // Copy fields one by one. - for (unsigned i = 0; i < count; i++) { - ldr(Tmp0(), MemOperand(src_untagged, kXRegSizeInBytes, PostIndex)); - str(Tmp0(), MemOperand(dst_untagged, kXRegSizeInBytes, PostIndex)); - } -} - - -void MacroAssembler::CopyFields(Register dst, Register src, CPURegList temps, - unsigned count) { - // One of two methods is used: - // - // For high 'count' values where many scratch registers are available: - // Untag src and dst into scratch registers. - // Copy src->dst in a tight loop. - // - // For low 'count' values or where few scratch registers are available: - // Untag src and dst into scratch registers. - // Copy src->dst in an unrolled loop. - // - // In both cases, fields are copied in pairs if possible, and left-overs are - // handled separately. - ASSERT(!temps.IncludesAliasOf(dst)); - ASSERT(!temps.IncludesAliasOf(src)); - ASSERT(!temps.IncludesAliasOf(Tmp0())); - ASSERT(!temps.IncludesAliasOf(Tmp1())); - ASSERT(!temps.IncludesAliasOf(xzr)); - ASSERT(!AreAliased(dst, src, Tmp0(), Tmp1())); - - if (emit_debug_code()) { - Cmp(dst, src); - Check(ne, kTheSourceAndDestinationAreTheSame); - } - - // The value of 'count' at which a loop will be generated (if there are - // enough scratch registers). - static const unsigned kLoopThreshold = 8; - - ASSERT(!temps.IsEmpty()); - Register scratch1 = Register(temps.PopLowestIndex()); - Register scratch2 = Register(temps.PopLowestIndex()); - Register scratch3 = Register(temps.PopLowestIndex()); - - if (scratch3.IsValid() && (count >= kLoopThreshold)) { - CopyFieldsLoopPairsHelper(dst, src, count, scratch1, scratch2, scratch3); - } else if (scratch2.IsValid()) { - CopyFieldsUnrolledPairsHelper(dst, src, count, scratch1, scratch2); - } else if (scratch1.IsValid()) { - CopyFieldsUnrolledHelper(dst, src, count, scratch1); - } else { - UNREACHABLE(); - } -} - - -void MacroAssembler::CopyBytes(Register dst, - Register src, - Register length, - Register scratch, - CopyHint hint) { - ASSERT(!AreAliased(src, dst, length, scratch)); - - // TODO(all): Implement a faster copy function, and use hint to determine - // which algorithm to use for copies. - if (emit_debug_code()) { - // Check copy length. - Cmp(length, 0); - Assert(ge, kUnexpectedNegativeValue); - - // Check src and dst buffers don't overlap. - Add(scratch, src, length); // Calculate end of src buffer. - Cmp(scratch, dst); - Add(scratch, dst, length); // Calculate end of dst buffer. - Ccmp(scratch, src, ZFlag, gt); - Assert(le, kCopyBuffersOverlap); - } - - Label loop, done; - Cbz(length, &done); - - Bind(&loop); - Sub(length, length, 1); - Ldrb(scratch, MemOperand(src, 1, PostIndex)); - Strb(scratch, MemOperand(dst, 1, PostIndex)); - Cbnz(length, &loop); - Bind(&done); -} - - -void MacroAssembler::InitializeFieldsWithFiller(Register start_offset, - Register end_offset, - Register filler) { - Label loop, entry; - B(&entry); - Bind(&loop); - // TODO(all): consider using stp here. - Str(filler, MemOperand(start_offset, kPointerSize, PostIndex)); - Bind(&entry); - Cmp(start_offset, end_offset); - B(lt, &loop); -} - - -void MacroAssembler::JumpIfEitherIsNotSequentialAsciiStrings( - Register first, - Register second, - Register scratch1, - Register scratch2, - Label* failure, - SmiCheckType smi_check) { - - if (smi_check == DO_SMI_CHECK) { - JumpIfEitherSmi(first, second, failure); - } else if (emit_debug_code()) { - ASSERT(smi_check == DONT_DO_SMI_CHECK); - Label not_smi; - JumpIfEitherSmi(first, second, NULL, ¬_smi); - - // At least one input is a smi, but the flags indicated a smi check wasn't - // needed. - Abort(kUnexpectedSmi); - - Bind(¬_smi); - } - - // Test that both first and second are sequential ASCII strings. - Ldr(scratch1, FieldMemOperand(first, HeapObject::kMapOffset)); - Ldr(scratch2, FieldMemOperand(second, HeapObject::kMapOffset)); - Ldrb(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset)); - Ldrb(scratch2, FieldMemOperand(scratch2, Map::kInstanceTypeOffset)); - - JumpIfEitherInstanceTypeIsNotSequentialAscii(scratch1, - scratch2, - scratch1, - scratch2, - failure); -} - - -void MacroAssembler::JumpIfEitherInstanceTypeIsNotSequentialAscii( - Register first, - Register second, - Register scratch1, - Register scratch2, - Label* failure) { - ASSERT(!AreAliased(scratch1, second)); - ASSERT(!AreAliased(scratch1, scratch2)); - static const int kFlatAsciiStringMask = - kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask; - static const int kFlatAsciiStringTag = ASCII_STRING_TYPE; - And(scratch1, first, kFlatAsciiStringMask); - And(scratch2, second, kFlatAsciiStringMask); - Cmp(scratch1, kFlatAsciiStringTag); - Ccmp(scratch2, kFlatAsciiStringTag, NoFlag, eq); - B(ne, failure); -} - - -void MacroAssembler::JumpIfInstanceTypeIsNotSequentialAscii(Register type, - Register scratch, - Label* failure) { - const int kFlatAsciiStringMask = - kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask; - const int kFlatAsciiStringTag = - kStringTag | kOneByteStringTag | kSeqStringTag; - And(scratch, type, kFlatAsciiStringMask); - Cmp(scratch, kFlatAsciiStringTag); - B(ne, failure); -} - - -void MacroAssembler::JumpIfBothInstanceTypesAreNotSequentialAscii( - Register first, - Register second, - Register scratch1, - Register scratch2, - Label* failure) { - ASSERT(!AreAliased(first, second, scratch1, scratch2)); - const int kFlatAsciiStringMask = - kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask; - const int kFlatAsciiStringTag = - kStringTag | kOneByteStringTag | kSeqStringTag; - And(scratch1, first, kFlatAsciiStringMask); - And(scratch2, second, kFlatAsciiStringMask); - Cmp(scratch1, kFlatAsciiStringTag); - Ccmp(scratch2, kFlatAsciiStringTag, NoFlag, eq); - B(ne, failure); -} - - -void MacroAssembler::JumpIfNotUniqueName(Register type, - Label* not_unique_name) { - STATIC_ASSERT((kInternalizedTag == 0) && (kStringTag == 0)); - // if ((type is string && type is internalized) || type == SYMBOL_TYPE) { - // continue - // } else { - // goto not_unique_name - // } - Tst(type, kIsNotStringMask | kIsNotInternalizedMask); - Ccmp(type, SYMBOL_TYPE, ZFlag, ne); - B(ne, not_unique_name); -} - - -void MacroAssembler::InvokePrologue(const ParameterCount& expected, - const ParameterCount& actual, - Handle code_constant, - Register code_reg, - Label* done, - InvokeFlag flag, - bool* definitely_mismatches, - const CallWrapper& call_wrapper) { - bool definitely_matches = false; - *definitely_mismatches = false; - Label regular_invoke; - - // Check whether the expected and actual arguments count match. If not, - // setup registers according to contract with ArgumentsAdaptorTrampoline: - // x0: actual arguments count. - // x1: function (passed through to callee). - // x2: expected arguments count. - - // The code below is made a lot easier because the calling code already sets - // up actual and expected registers according to the contract if values are - // passed in registers. - ASSERT(actual.is_immediate() || actual.reg().is(x0)); - ASSERT(expected.is_immediate() || expected.reg().is(x2)); - ASSERT((!code_constant.is_null() && code_reg.is(no_reg)) || code_reg.is(x3)); - - if (expected.is_immediate()) { - ASSERT(actual.is_immediate()); - if (expected.immediate() == actual.immediate()) { - definitely_matches = true; - - } else { - Mov(x0, actual.immediate()); - if (expected.immediate() == - SharedFunctionInfo::kDontAdaptArgumentsSentinel) { - // 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; - // Set up x2 for the argument adaptor. - Mov(x2, expected.immediate()); - } - } - - } else { // expected is a register. - Operand actual_op = actual.is_immediate() ? Operand(actual.immediate()) - : Operand(actual.reg()); - // If actual == expected perform a regular invocation. - Cmp(expected.reg(), actual_op); - B(eq, ®ular_invoke); - // Otherwise set up x0 for the argument adaptor. - Mov(x0, actual_op); - } - - // If the argument counts may mismatch, generate a call to the argument - // adaptor. - if (!definitely_matches) { - if (!code_constant.is_null()) { - Mov(x3, Operand(code_constant)); - Add(x3, x3, Code::kHeaderSize - kHeapObjectTag); - } - - Handle adaptor = - isolate()->builtins()->ArgumentsAdaptorTrampoline(); - if (flag == CALL_FUNCTION) { - call_wrapper.BeforeCall(CallSize(adaptor)); - Call(adaptor); - call_wrapper.AfterCall(); - if (!*definitely_mismatches) { - // If the arg counts don't match, no extra code is emitted by - // MAsm::InvokeCode and we can just fall through. - B(done); - } - } else { - Jump(adaptor, RelocInfo::CODE_TARGET); - } - } - Bind(®ular_invoke); -} - - -void MacroAssembler::InvokeCode(Register code, - const ParameterCount& expected, - const ParameterCount& actual, - InvokeFlag flag, - const CallWrapper& call_wrapper) { - // You can't call a function without a valid frame. - ASSERT(flag == JUMP_FUNCTION || has_frame()); - - Label done; - - bool definitely_mismatches = false; - InvokePrologue(expected, actual, Handle::null(), code, &done, flag, - &definitely_mismatches, call_wrapper); - - // If we are certain that actual != expected, then we know InvokePrologue will - // have handled the call through the argument adaptor mechanism. - // The called function expects the call kind in x5. - if (!definitely_mismatches) { - if (flag == CALL_FUNCTION) { - call_wrapper.BeforeCall(CallSize(code)); - Call(code); - call_wrapper.AfterCall(); - } else { - ASSERT(flag == JUMP_FUNCTION); - Jump(code); - } - } - - // Continue here if InvokePrologue does handle the invocation due to - // mismatched parameter counts. - Bind(&done); -} - - -void MacroAssembler::InvokeFunction(Register function, - const ParameterCount& actual, - InvokeFlag flag, - const CallWrapper& call_wrapper) { - // You can't call a function without a valid frame. - ASSERT(flag == JUMP_FUNCTION || has_frame()); - - // Contract with called JS functions requires that function is passed in x1. - // (See FullCodeGenerator::Generate().) - ASSERT(function.is(x1)); - - Register expected_reg = x2; - Register code_reg = x3; - - Ldr(cp, FieldMemOperand(function, JSFunction::kContextOffset)); - // The number of arguments is stored as an int32_t, and -1 is a marker - // (SharedFunctionInfo::kDontAdaptArgumentsSentinel), so we need sign - // extension to correctly handle it. - Ldr(expected_reg, FieldMemOperand(function, - JSFunction::kSharedFunctionInfoOffset)); - Ldrsw(expected_reg, - FieldMemOperand(expected_reg, - SharedFunctionInfo::kFormalParameterCountOffset)); - Ldr(code_reg, - FieldMemOperand(function, JSFunction::kCodeEntryOffset)); - - ParameterCount expected(expected_reg); - InvokeCode(code_reg, expected, actual, flag, call_wrapper); -} - - -void MacroAssembler::InvokeFunction(Register function, - const ParameterCount& expected, - const ParameterCount& actual, - InvokeFlag flag, - const CallWrapper& call_wrapper) { - // You can't call a function without a valid frame. - ASSERT(flag == JUMP_FUNCTION || has_frame()); - - // Contract with called JS functions requires that function is passed in x1. - // (See FullCodeGenerator::Generate().) - ASSERT(function.Is(x1)); - - Register code_reg = x3; - - // Set up the context. - Ldr(cp, FieldMemOperand(function, JSFunction::kContextOffset)); - - // We call indirectly through the code field in the function to - // allow recompilation to take effect without changing any of the - // call sites. - Ldr(code_reg, FieldMemOperand(function, JSFunction::kCodeEntryOffset)); - InvokeCode(code_reg, expected, actual, flag, call_wrapper); -} - - -void MacroAssembler::InvokeFunction(Handle function, - const ParameterCount& expected, - const ParameterCount& actual, - InvokeFlag flag, - const CallWrapper& call_wrapper) { - // Contract with called JS functions requires that function is passed in x1. - // (See FullCodeGenerator::Generate().) - __ LoadObject(x1, function); - InvokeFunction(x1, expected, actual, flag, call_wrapper); -} - - -void MacroAssembler::TryInlineTruncateDoubleToI(Register result, - DoubleRegister double_input, - Label* done) { - STATIC_ASSERT(kSmiTag == 0); - STATIC_ASSERT(kSmiValueSize == 32); - - // Try to convert with a FPU convert instruction. It's trivial to compute - // the modulo operation on an integer register so we convert to a 64-bit - // integer, then find the 32-bit result from that. - // - // Fcvtzs will saturate to INT64_MIN (0x800...00) or INT64_MAX (0x7ff...ff) - // when the double is out of range. NaNs and infinities will be converted to 0 - // (as ECMA-262 requires). - Fcvtzs(result, double_input); - - // The values INT64_MIN (0x800...00) or INT64_MAX (0x7ff...ff) are not - // representable using a double, so if the result is one of those then we know - // that saturation occured, and we need to manually handle the conversion. - // - // It is easy to detect INT64_MIN and INT64_MAX because adding or subtracting - // 1 will cause signed overflow. - Cmp(result, 1); - Ccmp(result, -1, VFlag, vc); - - B(vc, done); -} - - -void MacroAssembler::TruncateDoubleToI(Register result, - DoubleRegister double_input) { - Label done; - ASSERT(jssp.Is(StackPointer())); - - TryInlineTruncateDoubleToI(result, double_input, &done); - - // If we fell through then inline version didn't succeed - call stub instead. - Push(lr); - Push(double_input); // Put input on stack. - - DoubleToIStub stub(jssp, - result, - 0, - true, // is_truncating - true); // skip_fastpath - CallStub(&stub); // DoubleToIStub preserves any registers it needs to clobber - - Drop(1, kDoubleSize); // Drop the double input on the stack. - Pop(lr); - - Bind(&done); - - // TODO(rmcilroy): Remove this Sxtw once the following bug is fixed: - // https://code.google.com/p/v8/issues/detail?id=3149 - Sxtw(result, result.W()); -} - - -void MacroAssembler::TruncateHeapNumberToI(Register result, - Register object) { - Label done; - ASSERT(!result.is(object)); - ASSERT(jssp.Is(StackPointer())); - - Ldr(fp_scratch, FieldMemOperand(object, HeapNumber::kValueOffset)); - TryInlineTruncateDoubleToI(result, fp_scratch, &done); - - // If we fell through then inline version didn't succeed - call stub instead. - Push(lr); - DoubleToIStub stub(object, - result, - HeapNumber::kValueOffset - kHeapObjectTag, - true, // is_truncating - true); // skip_fastpath - CallStub(&stub); // DoubleToIStub preserves any registers it needs to clobber - Pop(lr); - - Bind(&done); - - // TODO(rmcilroy): Remove this Sxtw once the following bug is fixed: - // https://code.google.com/p/v8/issues/detail?id=3149 - Sxtw(result, result.W()); -} - - -void MacroAssembler::Prologue(PrologueFrameMode frame_mode) { - if (frame_mode == BUILD_STUB_FRAME) { - ASSERT(StackPointer().Is(jssp)); - // TODO(jbramley): Does x1 contain a JSFunction here, or does it already - // have the special STUB smi? - __ Mov(Tmp0(), Operand(Smi::FromInt(StackFrame::STUB))); - // Compiled stubs don't age, and so they don't need the predictable code - // ageing sequence. - __ Push(lr, fp, cp, Tmp0()); - __ Add(fp, jssp, StandardFrameConstants::kFixedFrameSizeFromFp); - } else { - if (isolate()->IsCodePreAgingActive()) { - Code* stub = Code::GetPreAgedCodeAgeStub(isolate()); - __ EmitCodeAgeSequence(stub); - } else { - __ EmitFrameSetupForCodeAgePatching(); - } - } -} - - -void MacroAssembler::EnterFrame(StackFrame::Type type) { - ASSERT(jssp.Is(StackPointer())); - Push(lr, fp, cp); - Mov(Tmp1(), Operand(Smi::FromInt(type))); - Mov(Tmp0(), Operand(CodeObject())); - Push(Tmp1(), Tmp0()); - // jssp[4] : lr - // jssp[3] : fp - // jssp[2] : cp - // jssp[1] : type - // jssp[0] : code object - - // Adjust FP to point to saved FP. - add(fp, jssp, StandardFrameConstants::kFixedFrameSizeFromFp + kPointerSize); -} - - -void MacroAssembler::LeaveFrame(StackFrame::Type type) { - ASSERT(jssp.Is(StackPointer())); - // Drop the execution stack down to the frame pointer and restore - // the caller frame pointer and return address. - Mov(jssp, fp); - AssertStackConsistency(); - Pop(fp, lr); -} - - -void MacroAssembler::ExitFramePreserveFPRegs() { - PushCPURegList(kCallerSavedFP); -} - - -void MacroAssembler::ExitFrameRestoreFPRegs() { - // Read the registers from the stack without popping them. The stack pointer - // will be reset as part of the unwinding process. - CPURegList saved_fp_regs = kCallerSavedFP; - ASSERT(saved_fp_regs.Count() % 2 == 0); - - int offset = ExitFrameConstants::kLastExitFrameField; - while (!saved_fp_regs.IsEmpty()) { - const CPURegister& dst0 = saved_fp_regs.PopHighestIndex(); - const CPURegister& dst1 = saved_fp_regs.PopHighestIndex(); - offset -= 2 * kDRegSizeInBytes; - Ldp(dst1, dst0, MemOperand(fp, offset)); - } -} - - -// TODO(jbramley): Check that we're handling the frame pointer correctly. -void MacroAssembler::EnterExitFrame(bool save_doubles, - const Register& scratch, - int extra_space) { - ASSERT(jssp.Is(StackPointer())); - - // Set up the new stack frame. - Mov(scratch, Operand(CodeObject())); - Push(lr, fp); - Mov(fp, StackPointer()); - Push(xzr, scratch); - // fp[8]: CallerPC (lr) - // fp -> fp[0]: CallerFP (old fp) - // fp[-8]: Space reserved for SPOffset. - // jssp -> fp[-16]: CodeObject() - STATIC_ASSERT((2 * kPointerSize) == - ExitFrameConstants::kCallerSPDisplacement); - STATIC_ASSERT((1 * kPointerSize) == ExitFrameConstants::kCallerPCOffset); - STATIC_ASSERT((0 * kPointerSize) == ExitFrameConstants::kCallerFPOffset); - STATIC_ASSERT((-1 * kPointerSize) == ExitFrameConstants::kSPOffset); - STATIC_ASSERT((-2 * kPointerSize) == ExitFrameConstants::kCodeOffset); - - // Save the frame pointer and context pointer in the top frame. - Mov(scratch, Operand(ExternalReference(Isolate::kCEntryFPAddress, - isolate()))); - Str(fp, MemOperand(scratch)); - Mov(scratch, Operand(ExternalReference(Isolate::kContextAddress, - isolate()))); - Str(cp, MemOperand(scratch)); - - STATIC_ASSERT((-2 * kPointerSize) == - ExitFrameConstants::kLastExitFrameField); - if (save_doubles) { - ExitFramePreserveFPRegs(); - } - - // Reserve space for the return address and for user requested memory. - // We do this before aligning to make sure that we end up correctly - // aligned with the minimum of wasted space. - Claim(extra_space + 1, kXRegSizeInBytes); - // fp[8]: CallerPC (lr) - // fp -> fp[0]: CallerFP (old fp) - // fp[-8]: Space reserved for SPOffset. - // fp[-16]: CodeObject() - // jssp[-16 - fp_size]: Saved doubles (if save_doubles is true). - // jssp[8]: Extra space reserved for caller (if extra_space != 0). - // jssp -> jssp[0]: Space reserved for the return address. - - // Align and synchronize the system stack pointer with jssp. - AlignAndSetCSPForFrame(); - ASSERT(csp.Is(StackPointer())); - - // fp[8]: CallerPC (lr) - // fp -> fp[0]: CallerFP (old fp) - // fp[-8]: Space reserved for SPOffset. - // fp[-16]: CodeObject() - // csp[...]: Saved doubles, if saved_doubles is true. - // csp[8]: Memory reserved for the caller if extra_space != 0. - // Alignment padding, if necessary. - // csp -> csp[0]: Space reserved for the return address. - - // ExitFrame::GetStateForFramePointer expects to find the return address at - // the memory address immediately below the pointer stored in SPOffset. - // It is not safe to derive much else from SPOffset, because the size of the - // padding can vary. - Add(scratch, csp, kXRegSizeInBytes); - Str(scratch, MemOperand(fp, ExitFrameConstants::kSPOffset)); -} - - -// Leave the current exit frame. -void MacroAssembler::LeaveExitFrame(bool restore_doubles, - const Register& scratch, - bool restore_context) { - ASSERT(csp.Is(StackPointer())); - - if (restore_doubles) { - ExitFrameRestoreFPRegs(); - } - - // Restore the context pointer from the top frame. - if (restore_context) { - Mov(scratch, Operand(ExternalReference(Isolate::kContextAddress, - isolate()))); - Ldr(cp, MemOperand(scratch)); - } - - if (emit_debug_code()) { - // Also emit debug code to clear the cp in the top frame. - Mov(scratch, Operand(ExternalReference(Isolate::kContextAddress, - isolate()))); - Str(xzr, MemOperand(scratch)); - } - // Clear the frame pointer from the top frame. - Mov(scratch, Operand(ExternalReference(Isolate::kCEntryFPAddress, - isolate()))); - Str(xzr, MemOperand(scratch)); - - // Pop the exit frame. - // fp[8]: CallerPC (lr) - // fp -> fp[0]: CallerFP (old fp) - // fp[...]: The rest of the frame. - Mov(jssp, fp); - SetStackPointer(jssp); - AssertStackConsistency(); - Pop(fp, lr); -} - - -void MacroAssembler::SetCounter(StatsCounter* counter, int value, - Register scratch1, Register scratch2) { - if (FLAG_native_code_counters && counter->Enabled()) { - Mov(scratch1, value); - Mov(scratch2, Operand(ExternalReference(counter))); - Str(scratch1, MemOperand(scratch2)); - } -} - - -void MacroAssembler::IncrementCounter(StatsCounter* counter, int value, - Register scratch1, Register scratch2) { - ASSERT(value != 0); - if (FLAG_native_code_counters && counter->Enabled()) { - Mov(scratch2, Operand(ExternalReference(counter))); - Ldr(scratch1, MemOperand(scratch2)); - Add(scratch1, scratch1, value); - Str(scratch1, MemOperand(scratch2)); - } -} - - -void MacroAssembler::DecrementCounter(StatsCounter* counter, int value, - Register scratch1, Register scratch2) { - IncrementCounter(counter, -value, scratch1, scratch2); -} - - -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. - Ldr(dst, MemOperand(cp, Context::SlotOffset(Context::PREVIOUS_INDEX))); - for (int i = 1; i < context_chain_length; i++) { - Ldr(dst, MemOperand(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 cp). - Mov(dst, cp); - } -} - - -#ifdef ENABLE_DEBUGGER_SUPPORT -void MacroAssembler::DebugBreak() { - Mov(x0, 0); - Mov(x1, Operand(ExternalReference(Runtime::kDebugBreak, isolate()))); - CEntryStub ces(1); - ASSERT(AllowThisStubCall(&ces)); - Call(ces.GetCode(isolate()), RelocInfo::DEBUG_BREAK); -} -#endif - - -void MacroAssembler::PushTryHandler(StackHandler::Kind kind, - int handler_index) { - ASSERT(jssp.Is(StackPointer())); - // Adjust this code if the asserts don't hold. - STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kCodeOffset == 1 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kStateOffset == 2 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kContextOffset == 3 * kPointerSize); - STATIC_ASSERT(StackHandlerConstants::kFPOffset == 4 * kPointerSize); - - // For the JSEntry handler, we must preserve the live registers x0-x4. - // (See JSEntryStub::GenerateBody().) - - unsigned state = - StackHandler::IndexField::encode(handler_index) | - StackHandler::KindField::encode(kind); - - // Set up the code object and the state for pushing. - Mov(x10, Operand(CodeObject())); - Mov(x11, state); - - // Push the frame pointer, context, state, and code object. - if (kind == StackHandler::JS_ENTRY) { - ASSERT(Smi::FromInt(0) == 0); - Push(xzr, xzr, x11, x10); - } else { - Push(fp, cp, x11, x10); - } - - // Link the current handler as the next handler. - Mov(x11, Operand(ExternalReference(Isolate::kHandlerAddress, isolate()))); - Ldr(x10, MemOperand(x11)); - Push(x10); - // Set this new handler as the current one. - Str(jssp, MemOperand(x11)); -} - - -void MacroAssembler::PopTryHandler() { - STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); - Pop(x10); - Mov(x11, Operand(ExternalReference(Isolate::kHandlerAddress, isolate()))); - Drop(StackHandlerConstants::kSize - kXRegSizeInBytes, kByteSizeInBytes); - Str(x10, MemOperand(x11)); -} - - -void MacroAssembler::Allocate(int object_size, - Register result, - Register scratch1, - Register scratch2, - Label* gc_required, - AllocationFlags flags) { - ASSERT(object_size <= Page::kMaxRegularHeapObjectSize); - if (!FLAG_inline_new) { - if (emit_debug_code()) { - // Trash the registers to simulate an allocation failure. - // We apply salt to the original zap value to easily spot the values. - Mov(result, (kDebugZapValue & ~0xffL) | 0x11L); - Mov(scratch1, (kDebugZapValue & ~0xffL) | 0x21L); - Mov(scratch2, (kDebugZapValue & ~0xffL) | 0x21L); - } - B(gc_required); - return; - } - - ASSERT(!AreAliased(result, scratch1, scratch2, Tmp0(), Tmp1())); - ASSERT(result.Is64Bits() && scratch1.Is64Bits() && scratch2.Is64Bits() && - Tmp0().Is64Bits() && Tmp1().Is64Bits()); - - // Make object size into bytes. - if ((flags & SIZE_IN_WORDS) != 0) { - object_size *= kPointerSize; - } - ASSERT(0 == (object_size & kObjectAlignmentMask)); - - // Check relative positions of allocation top and limit addresses. - // The values must be adjacent in memory to allow the use of LDP. - ExternalReference heap_allocation_top = - AllocationUtils::GetAllocationTopReference(isolate(), flags); - ExternalReference heap_allocation_limit = - AllocationUtils::GetAllocationLimitReference(isolate(), flags); - intptr_t top = reinterpret_cast(heap_allocation_top.address()); - intptr_t limit = reinterpret_cast(heap_allocation_limit.address()); - ASSERT((limit - top) == kPointerSize); - - // Set up allocation top address and object size registers. - Register top_address = scratch1; - Register allocation_limit = scratch2; - Mov(top_address, Operand(heap_allocation_top)); - - if ((flags & RESULT_CONTAINS_TOP) == 0) { - // Load allocation top into result and the allocation limit. - Ldp(result, allocation_limit, MemOperand(top_address)); - } else { - if (emit_debug_code()) { - // Assert that result actually contains top on entry. - Ldr(Tmp0(), MemOperand(top_address)); - Cmp(result, Tmp0()); - Check(eq, kUnexpectedAllocationTop); - } - // Load the allocation limit. 'result' already contains the allocation top. - Ldr(allocation_limit, MemOperand(top_address, limit - top)); - } - - // We can ignore DOUBLE_ALIGNMENT flags here because doubles and pointers have - // the same alignment on A64. - STATIC_ASSERT(kPointerAlignment == kDoubleAlignment); - - // Calculate new top and bail out if new space is exhausted. - Adds(Tmp1(), result, object_size); - B(vs, gc_required); - Cmp(Tmp1(), allocation_limit); - B(hi, gc_required); - Str(Tmp1(), MemOperand(top_address)); - - // Tag the object if requested. - if ((flags & TAG_OBJECT) != 0) { - Orr(result, result, kHeapObjectTag); - } -} - - -void MacroAssembler::Allocate(Register object_size, - Register result, - Register scratch1, - Register scratch2, - Label* gc_required, - AllocationFlags flags) { - if (!FLAG_inline_new) { - if (emit_debug_code()) { - // Trash the registers to simulate an allocation failure. - // We apply salt to the original zap value to easily spot the values. - Mov(result, (kDebugZapValue & ~0xffL) | 0x11L); - Mov(scratch1, (kDebugZapValue & ~0xffL) | 0x21L); - Mov(scratch2, (kDebugZapValue & ~0xffL) | 0x21L); - } - B(gc_required); - return; - } - - ASSERT(!AreAliased(object_size, result, scratch1, scratch2, Tmp0(), Tmp1())); - ASSERT(object_size.Is64Bits() && result.Is64Bits() && scratch1.Is64Bits() && - scratch2.Is64Bits() && Tmp0().Is64Bits() && Tmp1().Is64Bits()); - - // Check relative positions of allocation top and limit addresses. - // The values must be adjacent in memory to allow the use of LDP. - ExternalReference heap_allocation_top = - AllocationUtils::GetAllocationTopReference(isolate(), flags); - ExternalReference heap_allocation_limit = - AllocationUtils::GetAllocationLimitReference(isolate(), flags); - intptr_t top = reinterpret_cast(heap_allocation_top.address()); - intptr_t limit = reinterpret_cast(heap_allocation_limit.address()); - ASSERT((limit - top) == kPointerSize); - - // Set up allocation top address and object size registers. - Register top_address = scratch1; - Register allocation_limit = scratch2; - Mov(top_address, Operand(heap_allocation_top)); - - if ((flags & RESULT_CONTAINS_TOP) == 0) { - // Load allocation top into result and the allocation limit. - Ldp(result, allocation_limit, MemOperand(top_address)); - } else { - if (emit_debug_code()) { - // Assert that result actually contains top on entry. - Ldr(Tmp0(), MemOperand(top_address)); - Cmp(result, Tmp0()); - Check(eq, kUnexpectedAllocationTop); - } - // Load the allocation limit. 'result' already contains the allocation top. - Ldr(allocation_limit, MemOperand(top_address, limit - top)); - } - - // We can ignore DOUBLE_ALIGNMENT flags here because doubles and pointers have - // the same alignment on A64. - STATIC_ASSERT(kPointerAlignment == kDoubleAlignment); - - // Calculate new top and bail out if new space is exhausted - if ((flags & SIZE_IN_WORDS) != 0) { - Adds(Tmp1(), result, Operand(object_size, LSL, kPointerSizeLog2)); - } else { - Adds(Tmp1(), result, object_size); - } - - if (emit_debug_code()) { - Tst(Tmp1(), kObjectAlignmentMask); - Check(eq, kUnalignedAllocationInNewSpace); - } - - B(vs, gc_required); - Cmp(Tmp1(), allocation_limit); - B(hi, gc_required); - Str(Tmp1(), MemOperand(top_address)); - - // Tag the object if requested. - if ((flags & TAG_OBJECT) != 0) { - Orr(result, result, kHeapObjectTag); - } -} - - -void MacroAssembler::UndoAllocationInNewSpace(Register object, - Register scratch) { - ExternalReference new_space_allocation_top = - ExternalReference::new_space_allocation_top_address(isolate()); - - // Make sure the object has no tag before resetting top. - Bic(object, object, kHeapObjectTagMask); -#ifdef DEBUG - // Check that the object un-allocated is below the current top. - Mov(scratch, Operand(new_space_allocation_top)); - Ldr(scratch, MemOperand(scratch)); - Cmp(object, scratch); - Check(lt, kUndoAllocationOfNonAllocatedMemory); -#endif - // Write the address of the object to un-allocate as the current top. - Mov(scratch, Operand(new_space_allocation_top)); - Str(object, MemOperand(scratch)); -} - - -void MacroAssembler::AllocateTwoByteString(Register result, - Register length, - Register scratch1, - Register scratch2, - Register scratch3, - Label* gc_required) { - ASSERT(!AreAliased(result, length, scratch1, scratch2, scratch3)); - // Calculate the number of bytes needed for the characters in the string while - // observing object alignment. - STATIC_ASSERT((SeqTwoByteString::kHeaderSize & kObjectAlignmentMask) == 0); - Add(scratch1, length, length); // Length in bytes, not chars. - Add(scratch1, scratch1, kObjectAlignmentMask + SeqTwoByteString::kHeaderSize); - Bic(scratch1, scratch1, kObjectAlignmentMask); - - // Allocate two-byte string in new space. - Allocate(scratch1, - result, - scratch2, - scratch3, - gc_required, - TAG_OBJECT); - - // Set the map, length and hash field. - InitializeNewString(result, - length, - Heap::kStringMapRootIndex, - scratch1, - scratch2); -} - - -void MacroAssembler::AllocateAsciiString(Register result, - Register length, - Register scratch1, - Register scratch2, - Register scratch3, - Label* gc_required) { - ASSERT(!AreAliased(result, length, scratch1, scratch2, scratch3)); - // Calculate the number of bytes needed for the characters in the string while - // observing object alignment. - STATIC_ASSERT((SeqOneByteString::kHeaderSize & kObjectAlignmentMask) == 0); - STATIC_ASSERT(kCharSize == 1); - Add(scratch1, length, kObjectAlignmentMask + SeqOneByteString::kHeaderSize); - Bic(scratch1, scratch1, kObjectAlignmentMask); - - // Allocate ASCII string in new space. - Allocate(scratch1, - result, - scratch2, - scratch3, - gc_required, - TAG_OBJECT); - - // Set the map, length and hash field. - InitializeNewString(result, - length, - Heap::kAsciiStringMapRootIndex, - scratch1, - scratch2); -} - - -void MacroAssembler::AllocateTwoByteConsString(Register result, - Register length, - Register scratch1, - Register scratch2, - Label* gc_required) { - Allocate(ConsString::kSize, result, scratch1, scratch2, gc_required, - TAG_OBJECT); - - InitializeNewString(result, - length, - Heap::kConsStringMapRootIndex, - scratch1, - scratch2); -} - - -void MacroAssembler::AllocateAsciiConsString(Register result, - Register length, - Register scratch1, - Register scratch2, - Label* gc_required) { - Label allocate_new_space, install_map; - AllocationFlags flags = TAG_OBJECT; - - ExternalReference high_promotion_mode = ExternalReference:: - new_space_high_promotion_mode_active_address(isolate()); - Mov(scratch1, Operand(high_promotion_mode)); - Ldr(scratch1, MemOperand(scratch1)); - Cbz(scratch1, &allocate_new_space); - - Allocate(ConsString::kSize, - result, - scratch1, - scratch2, - gc_required, - static_cast(flags | PRETENURE_OLD_POINTER_SPACE)); - - B(&install_map); - - Bind(&allocate_new_space); - Allocate(ConsString::kSize, - result, - scratch1, - scratch2, - gc_required, - flags); - - Bind(&install_map); - - InitializeNewString(result, - length, - Heap::kConsAsciiStringMapRootIndex, - scratch1, - scratch2); -} - - -void MacroAssembler::AllocateTwoByteSlicedString(Register result, - Register length, - Register scratch1, - Register scratch2, - Label* gc_required) { - ASSERT(!AreAliased(result, length, scratch1, scratch2)); - Allocate(SlicedString::kSize, result, scratch1, scratch2, gc_required, - TAG_OBJECT); - - InitializeNewString(result, - length, - Heap::kSlicedStringMapRootIndex, - scratch1, - scratch2); -} - - -void MacroAssembler::AllocateAsciiSlicedString(Register result, - Register length, - Register scratch1, - Register scratch2, - Label* gc_required) { - ASSERT(!AreAliased(result, length, scratch1, scratch2)); - Allocate(SlicedString::kSize, result, scratch1, scratch2, gc_required, - TAG_OBJECT); - - InitializeNewString(result, - length, - Heap::kSlicedAsciiStringMapRootIndex, - scratch1, - scratch2); -} - - -// Allocates a heap number or jumps to the need_gc label if the young space -// is full and a scavenge is needed. -void MacroAssembler::AllocateHeapNumber(Register result, - Label* gc_required, - Register scratch1, - Register scratch2, - Register heap_number_map) { - // Allocate an object in the heap for the heap number and tag it as a heap - // object. - Allocate(HeapNumber::kSize, result, scratch1, scratch2, gc_required, - TAG_OBJECT); - - // Store heap number map in the allocated object. - if (heap_number_map.Is(NoReg)) { - heap_number_map = scratch1; - LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex); - } - AssertRegisterIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex); - Str(heap_number_map, FieldMemOperand(result, HeapObject::kMapOffset)); -} - - -void MacroAssembler::AllocateHeapNumberWithValue(Register result, - DoubleRegister value, - Label* gc_required, - Register scratch1, - Register scratch2, - Register heap_number_map) { - // TODO(all): Check if it would be more efficient to use STP to store both - // the map and the value. - AllocateHeapNumber(result, gc_required, scratch1, scratch2, heap_number_map); - Str(value, FieldMemOperand(result, HeapNumber::kValueOffset)); -} - - -void MacroAssembler::JumpIfObjectType(Register object, - Register map, - Register type_reg, - InstanceType type, - Label* if_cond_pass, - Condition cond) { - CompareObjectType(object, map, type_reg, type); - B(cond, if_cond_pass); -} - - -void MacroAssembler::JumpIfNotObjectType(Register object, - Register map, - Register type_reg, - InstanceType type, - Label* if_not_object) { - JumpIfObjectType(object, map, type_reg, type, if_not_object, ne); -} - - -// Sets condition flags based on comparison, and returns type in type_reg. -void MacroAssembler::CompareObjectType(Register object, - Register map, - Register type_reg, - InstanceType type) { - Ldr(map, FieldMemOperand(object, HeapObject::kMapOffset)); - CompareInstanceType(map, type_reg, type); -} - - -// Sets condition flags based on comparison, and returns type in type_reg. -void MacroAssembler::CompareInstanceType(Register map, - Register type_reg, - InstanceType type) { - Ldrb(type_reg, FieldMemOperand(map, Map::kInstanceTypeOffset)); - Cmp(type_reg, type); -} - - -void MacroAssembler::CompareMap(Register obj, - Register scratch, - Handle map, - Label* early_success) { - // TODO(jbramley): The early_success label isn't used. Remove it. - Ldr(scratch, FieldMemOperand(obj, HeapObject::kMapOffset)); - CompareMap(scratch, map, early_success); -} - - -void MacroAssembler::CompareMap(Register obj_map, - Handle map, - Label* early_success) { - // TODO(jbramley): The early_success label isn't used. Remove it. - Cmp(obj_map, Operand(map)); -} - - -void MacroAssembler::CheckMap(Register obj, - Register scratch, - Handle map, - Label* fail, - SmiCheckType smi_check_type) { - if (smi_check_type == DO_SMI_CHECK) { - JumpIfSmi(obj, fail); - } - - Label success; - CompareMap(obj, scratch, map, &success); - B(ne, fail); - Bind(&success); -} - - -void MacroAssembler::CheckMap(Register obj, - Register scratch, - Heap::RootListIndex index, - Label* fail, - SmiCheckType smi_check_type) { - if (smi_check_type == DO_SMI_CHECK) { - JumpIfSmi(obj, fail); - } - Ldr(scratch, FieldMemOperand(obj, HeapObject::kMapOffset)); - JumpIfNotRoot(scratch, index, fail); -} - - -void MacroAssembler::CheckMap(Register obj_map, - Handle map, - Label* fail, - SmiCheckType smi_check_type) { - if (smi_check_type == DO_SMI_CHECK) { - JumpIfSmi(obj_map, fail); - } - Label success; - CompareMap(obj_map, map, &success); - B(ne, fail); - Bind(&success); -} - - -void MacroAssembler::DispatchMap(Register obj, - Register scratch, - Handle map, - Handle success, - SmiCheckType smi_check_type) { - Label fail; - if (smi_check_type == DO_SMI_CHECK) { - JumpIfSmi(obj, &fail); - } - Ldr(scratch, FieldMemOperand(obj, HeapObject::kMapOffset)); - Cmp(scratch, Operand(map)); - B(ne, &fail); - Jump(success, RelocInfo::CODE_TARGET); - Bind(&fail); -} - - -void MacroAssembler::TestMapBitfield(Register object, uint64_t mask) { - Ldr(Tmp0(), FieldMemOperand(object, HeapObject::kMapOffset)); - Ldrb(Tmp0(), FieldMemOperand(Tmp0(), Map::kBitFieldOffset)); - Tst(Tmp0(), mask); -} - - -void MacroAssembler::LoadElementsKind(Register result, Register object) { - // Load map. - __ Ldr(result, FieldMemOperand(object, HeapObject::kMapOffset)); - // Load the map's "bit field 2". - __ Ldrb(result, FieldMemOperand(result, Map::kBitField2Offset)); - // Retrieve elements_kind from bit field 2. - __ Ubfx(result, result, Map::kElementsKindShift, Map::kElementsKindBitCount); -} - - -void MacroAssembler::TryGetFunctionPrototype(Register function, - Register result, - Register scratch, - Label* miss, - BoundFunctionAction action) { - ASSERT(!AreAliased(function, result, scratch)); - - // Check that the receiver isn't a smi. - JumpIfSmi(function, miss); - - // Check that the function really is a function. Load map into result reg. - JumpIfNotObjectType(function, result, scratch, JS_FUNCTION_TYPE, miss); - - if (action == kMissOnBoundFunction) { - Register scratch_w = scratch.W(); - Ldr(scratch, - FieldMemOperand(function, JSFunction::kSharedFunctionInfoOffset)); - // On 64-bit platforms, compiler hints field is not a smi. See definition of - // kCompilerHintsOffset in src/objects.h. - Ldr(scratch_w, - FieldMemOperand(scratch, SharedFunctionInfo::kCompilerHintsOffset)); - Tbnz(scratch, SharedFunctionInfo::kBoundFunction, miss); - } - - // Make sure that the function has an instance prototype. - Label non_instance; - Ldrb(scratch, FieldMemOperand(result, Map::kBitFieldOffset)); - Tbnz(scratch, Map::kHasNonInstancePrototype, &non_instance); - - // Get the prototype or initial map from the function. - Ldr(result, - FieldMemOperand(function, JSFunction::kPrototypeOrInitialMapOffset)); - - // If the prototype or initial map is the hole, don't return it and simply - // miss the cache instead. This will allow us to allocate a prototype object - // on-demand in the runtime system. - JumpIfRoot(result, Heap::kTheHoleValueRootIndex, miss); - - // If the function does not have an initial map, we're done. - Label done; - JumpIfNotObjectType(result, scratch, scratch, MAP_TYPE, &done); - - // Get the prototype from the initial map. - Ldr(result, FieldMemOperand(result, Map::kPrototypeOffset)); - B(&done); - - // Non-instance prototype: fetch prototype from constructor field in initial - // map. - Bind(&non_instance); - Ldr(result, FieldMemOperand(result, Map::kConstructorOffset)); - - // All done. - Bind(&done); -} - - -void MacroAssembler::CompareRoot(const Register& obj, - Heap::RootListIndex index) { - ASSERT(!AreAliased(obj, Tmp0())); - LoadRoot(Tmp0(), index); - Cmp(obj, Tmp0()); -} - - -void MacroAssembler::JumpIfRoot(const Register& obj, - Heap::RootListIndex index, - Label* if_equal) { - CompareRoot(obj, index); - B(eq, if_equal); -} - - -void MacroAssembler::JumpIfNotRoot(const Register& obj, - Heap::RootListIndex index, - Label* if_not_equal) { - CompareRoot(obj, index); - B(ne, if_not_equal); -} - - -void MacroAssembler::CompareAndSplit(const Register& lhs, - const Operand& rhs, - Condition cond, - Label* if_true, - Label* if_false, - Label* fall_through) { - if ((if_true == if_false) && (if_false == fall_through)) { - // Fall through. - } else if (if_true == if_false) { - B(if_true); - } else if (if_false == fall_through) { - CompareAndBranch(lhs, rhs, cond, if_true); - } else if (if_true == fall_through) { - CompareAndBranch(lhs, rhs, InvertCondition(cond), if_false); - } else { - CompareAndBranch(lhs, rhs, cond, if_true); - B(if_false); - } -} - - -void MacroAssembler::TestAndSplit(const Register& reg, - uint64_t bit_pattern, - Label* if_all_clear, - Label* if_any_set, - Label* fall_through) { - if ((if_all_clear == if_any_set) && (if_any_set == fall_through)) { - // Fall through. - } else if (if_all_clear == if_any_set) { - B(if_all_clear); - } else if (if_all_clear == fall_through) { - TestAndBranchIfAnySet(reg, bit_pattern, if_any_set); - } else if (if_any_set == fall_through) { - TestAndBranchIfAllClear(reg, bit_pattern, if_all_clear); - } else { - TestAndBranchIfAnySet(reg, bit_pattern, if_any_set); - B(if_all_clear); - } -} - - -void MacroAssembler::CheckFastElements(Register map, - Register scratch, - Label* fail) { - STATIC_ASSERT(FAST_SMI_ELEMENTS == 0); - STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1); - STATIC_ASSERT(FAST_ELEMENTS == 2); - STATIC_ASSERT(FAST_HOLEY_ELEMENTS == 3); - Ldrb(scratch, FieldMemOperand(map, Map::kBitField2Offset)); - Cmp(scratch, Map::kMaximumBitField2FastHoleyElementValue); - B(hi, fail); -} - - -void MacroAssembler::CheckFastObjectElements(Register map, - Register scratch, - Label* fail) { - STATIC_ASSERT(FAST_SMI_ELEMENTS == 0); - STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1); - STATIC_ASSERT(FAST_ELEMENTS == 2); - STATIC_ASSERT(FAST_HOLEY_ELEMENTS == 3); - Ldrb(scratch, FieldMemOperand(map, Map::kBitField2Offset)); - Cmp(scratch, Operand(Map::kMaximumBitField2FastHoleySmiElementValue)); - // If cond==ls, set cond=hi, otherwise compare. - Ccmp(scratch, - Operand(Map::kMaximumBitField2FastHoleyElementValue), CFlag, hi); - B(hi, fail); -} - - -void MacroAssembler::CheckFastSmiElements(Register map, - Register scratch, - Label* fail) { - STATIC_ASSERT(FAST_SMI_ELEMENTS == 0); - STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1); - Ldrb(scratch, FieldMemOperand(map, Map::kBitField2Offset)); - Cmp(scratch, Map::kMaximumBitField2FastHoleySmiElementValue); - B(hi, fail); -} - - -// Note: The ARM version of this clobbers elements_reg, but this version does -// not. Some uses of this in A64 assume that elements_reg will be preserved. -void MacroAssembler::StoreNumberToDoubleElements(Register value_reg, - Register key_reg, - Register elements_reg, - Register scratch1, - FPRegister fpscratch1, - FPRegister fpscratch2, - Label* fail, - int elements_offset) { - ASSERT(!AreAliased(value_reg, key_reg, elements_reg, scratch1)); - Label store_num; - - // Speculatively convert the smi to a double - all smis can be exactly - // represented as a double. - SmiUntagToDouble(fpscratch1, value_reg, kSpeculativeUntag); - - // If value_reg is a smi, we're done. - JumpIfSmi(value_reg, &store_num); - - // Ensure that the object is a heap number. - CheckMap(value_reg, scratch1, isolate()->factory()->heap_number_map(), - fail, DONT_DO_SMI_CHECK); - - Ldr(fpscratch1, FieldMemOperand(value_reg, HeapNumber::kValueOffset)); - Fmov(fpscratch2, FixedDoubleArray::canonical_not_the_hole_nan_as_double()); - - // Check for NaN by comparing the number to itself: NaN comparison will - // report unordered, indicated by the overflow flag being set. - Fcmp(fpscratch1, fpscratch1); - Fcsel(fpscratch1, fpscratch2, fpscratch1, vs); - - // Store the result. - Bind(&store_num); - Add(scratch1, elements_reg, - Operand::UntagSmiAndScale(key_reg, kDoubleSizeLog2)); - Str(fpscratch1, - FieldMemOperand(scratch1, - FixedDoubleArray::kHeaderSize - elements_offset)); -} - - -bool MacroAssembler::AllowThisStubCall(CodeStub* stub) { - return has_frame_ || !stub->SometimesSetsUpAFrame(); -} - - -void MacroAssembler::IndexFromHash(Register hash, Register index) { - // If the hash field contains an array index pick it out. The assert checks - // that the constants for the maximum number of digits for an array index - // cached in the hash field and the number of bits reserved for it does not - // conflict. - ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) < - (1 << String::kArrayIndexValueBits)); - // We want the smi-tagged index in key. kArrayIndexValueMask has zeros in - // the low kHashShift bits. - STATIC_ASSERT(kSmiTag == 0); - Ubfx(hash, hash, String::kHashShift, String::kArrayIndexValueBits); - SmiTag(index, hash); -} - - -void MacroAssembler::EmitSeqStringSetCharCheck( - Register string, - Register index, - SeqStringSetCharCheckIndexType index_type, - Register scratch, - uint32_t encoding_mask) { - ASSERT(!AreAliased(string, index, scratch)); - - if (index_type == kIndexIsSmi) { - AssertSmi(index); - } - - // Check that string is an object. - AssertNotSmi(string, kNonObject); - - // Check that string has an appropriate map. - Ldr(scratch, FieldMemOperand(string, HeapObject::kMapOffset)); - Ldrb(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset)); - - And(scratch, scratch, kStringRepresentationMask | kStringEncodingMask); - Cmp(scratch, encoding_mask); - Check(eq, kUnexpectedStringType); - - Ldr(scratch, FieldMemOperand(string, String::kLengthOffset)); - Cmp(index, index_type == kIndexIsSmi ? scratch : Operand::UntagSmi(scratch)); - Check(lt, kIndexIsTooLarge); - - ASSERT_EQ(0, Smi::FromInt(0)); - Cmp(index, 0); - Check(ge, kIndexIsNegative); -} - - -void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg, - Register scratch, - Label* miss) { - // TODO(jbramley): Sort out the uses of Tmp0() and Tmp1() in this function. - // The ARM version takes two scratch registers, and that should be enough for - // all of the checks. - - Label same_contexts; - - ASSERT(!AreAliased(holder_reg, scratch)); - - // Load current lexical context from the stack frame. - Ldr(scratch, MemOperand(fp, StandardFrameConstants::kContextOffset)); - // In debug mode, make sure the lexical context is set. -#ifdef DEBUG - Cmp(scratch, 0); - Check(ne, kWeShouldNotHaveAnEmptyLexicalContext); -#endif - - // Load the native context of the current context. - int offset = - Context::kHeaderSize + Context::GLOBAL_OBJECT_INDEX * kPointerSize; - Ldr(scratch, FieldMemOperand(scratch, offset)); - Ldr(scratch, FieldMemOperand(scratch, GlobalObject::kNativeContextOffset)); - - // Check the context is a native context. - if (emit_debug_code()) { - // Read the first word and compare to the global_context_map. - Register temp = Tmp1(); - Ldr(temp, FieldMemOperand(scratch, HeapObject::kMapOffset)); - CompareRoot(temp, Heap::kNativeContextMapRootIndex); - Check(eq, kExpectedNativeContext); - } - - // Check if both contexts are the same. - ldr(Tmp0(), FieldMemOperand(holder_reg, JSGlobalProxy::kNativeContextOffset)); - cmp(scratch, Tmp0()); - b(&same_contexts, eq); - - // Check the context is a native context. - if (emit_debug_code()) { - // Move Tmp0() into a different register, as CompareRoot will use it. - Register temp = Tmp1(); - mov(temp, Tmp0()); - CompareRoot(temp, Heap::kNullValueRootIndex); - Check(ne, kExpectedNonNullContext); - - Ldr(temp, FieldMemOperand(temp, HeapObject::kMapOffset)); - CompareRoot(temp, Heap::kNativeContextMapRootIndex); - Check(eq, kExpectedNativeContext); - - // Let's consider that Tmp0() has been cloberred by the MacroAssembler. - // We reload it with its value. - ldr(Tmp0(), FieldMemOperand(holder_reg, - JSGlobalProxy::kNativeContextOffset)); - } - - // Check that the security token in the calling global object is - // compatible with the security token in the receiving global - // object. - int token_offset = Context::kHeaderSize + - Context::SECURITY_TOKEN_INDEX * kPointerSize; - - ldr(scratch, FieldMemOperand(scratch, token_offset)); - ldr(Tmp0(), FieldMemOperand(Tmp0(), token_offset)); - cmp(scratch, Tmp0()); - b(miss, ne); - - bind(&same_contexts); -} - - -// Compute the hash code from the untagged key. This must be kept in sync with -// ComputeIntegerHash in utils.h and KeyedLoadGenericElementStub in -// code-stub-hydrogen.cc -void MacroAssembler::GetNumberHash(Register key, Register scratch) { - ASSERT(!AreAliased(key, scratch)); - - // Xor original key with a seed. - LoadRoot(scratch, Heap::kHashSeedRootIndex); - Eor(key, key, Operand::UntagSmi(scratch)); - - // The algorithm uses 32-bit integer values. - key = key.W(); - scratch = scratch.W(); - - // Compute the hash code from the untagged key. This must be kept in sync - // with ComputeIntegerHash in utils.h. - // - // hash = ~hash + (hash <<1 15); - Mvn(scratch, key); - Add(key, scratch, Operand(key, LSL, 15)); - // hash = hash ^ (hash >> 12); - Eor(key, key, Operand(key, LSR, 12)); - // hash = hash + (hash << 2); - Add(key, key, Operand(key, LSL, 2)); - // hash = hash ^ (hash >> 4); - Eor(key, key, Operand(key, LSR, 4)); - // hash = hash * 2057; - Mov(scratch, Operand(key, LSL, 11)); - Add(key, key, Operand(key, LSL, 3)); - Add(key, key, scratch); - // hash = hash ^ (hash >> 16); - Eor(key, key, Operand(key, LSR, 16)); -} - - -void MacroAssembler::LoadFromNumberDictionary(Label* miss, - Register elements, - Register key, - Register result, - Register scratch0, - Register scratch1, - Register scratch2, - Register scratch3) { - ASSERT(!AreAliased(elements, key, scratch0, scratch1, scratch2, scratch3)); - - Label done; - - SmiUntag(scratch0, key); - GetNumberHash(scratch0, scratch1); - - // Compute the capacity mask. - Ldrsw(scratch1, - UntagSmiFieldMemOperand(elements, - SeededNumberDictionary::kCapacityOffset)); - Sub(scratch1, scratch1, 1); - - // Generate an unrolled loop that performs a few probes before giving up. - for (int i = 0; i < kNumberDictionaryProbes; i++) { - // Compute the masked index: (hash + i + i * i) & mask. - if (i > 0) { - Add(scratch2, scratch0, SeededNumberDictionary::GetProbeOffset(i)); - } else { - Mov(scratch2, scratch0); - } - And(scratch2, scratch2, scratch1); - - // Scale the index by multiplying by the element size. - ASSERT(SeededNumberDictionary::kEntrySize == 3); - Add(scratch2, scratch2, Operand(scratch2, LSL, 1)); - - // Check if the key is identical to the name. - Add(scratch2, elements, Operand(scratch2, LSL, kPointerSizeLog2)); - Ldr(scratch3, - FieldMemOperand(scratch2, - SeededNumberDictionary::kElementsStartOffset)); - Cmp(key, scratch3); - if (i != (kNumberDictionaryProbes - 1)) { - B(eq, &done); - } else { - B(ne, miss); - } - } - - Bind(&done); - // Check that the value is a normal property. - const int kDetailsOffset = - SeededNumberDictionary::kElementsStartOffset + 2 * kPointerSize; - Ldrsw(scratch1, UntagSmiFieldMemOperand(scratch2, kDetailsOffset)); - TestAndBranchIfAnySet(scratch1, PropertyDetails::TypeField::kMask, miss); - - // Get the value at the masked, scaled index and return. - const int kValueOffset = - SeededNumberDictionary::kElementsStartOffset + kPointerSize; - Ldr(result, FieldMemOperand(scratch2, kValueOffset)); -} - - -void MacroAssembler::RememberedSetHelper(Register object, // For debug tests. - Register address, - Register scratch, - SaveFPRegsMode fp_mode, - RememberedSetFinalAction and_then) { - ASSERT(!AreAliased(object, address, scratch)); - Label done, store_buffer_overflow; - if (emit_debug_code()) { - Label ok; - JumpIfNotInNewSpace(object, &ok); - Abort(kRememberedSetPointerInNewSpace); - bind(&ok); - } - // Load store buffer top. - Mov(Tmp0(), Operand(ExternalReference::store_buffer_top(isolate()))); - Ldr(scratch, MemOperand(Tmp0())); - // Store pointer to buffer and increment buffer top. - Str(address, MemOperand(scratch, kPointerSize, PostIndex)); - // Write back new top of buffer. - Str(scratch, MemOperand(Tmp0())); - // Call stub on end of buffer. - // Check for end of buffer. - ASSERT(StoreBuffer::kStoreBufferOverflowBit == - (1 << (14 + kPointerSizeLog2))); - if (and_then == kFallThroughAtEnd) { - Tbz(scratch, (14 + kPointerSizeLog2), &done); - } else { - ASSERT(and_then == kReturnAtEnd); - Tbnz(scratch, (14 + kPointerSizeLog2), &store_buffer_overflow); - Ret(); - } - - Bind(&store_buffer_overflow); - Push(lr); - StoreBufferOverflowStub store_buffer_overflow_stub = - StoreBufferOverflowStub(fp_mode); - CallStub(&store_buffer_overflow_stub); - Pop(lr); - - Bind(&done); - if (and_then == kReturnAtEnd) { - Ret(); - } -} - - -void MacroAssembler::PopSafepointRegisters() { - const int num_unsaved = kNumSafepointRegisters - kNumSafepointSavedRegisters; - PopXRegList(kSafepointSavedRegisters); - Drop(num_unsaved); -} - - -void MacroAssembler::PushSafepointRegisters() { - // Safepoints expect a block of kNumSafepointRegisters values on the stack, so - // adjust the stack for unsaved registers. - const int num_unsaved = kNumSafepointRegisters - kNumSafepointSavedRegisters; - ASSERT(num_unsaved >= 0); - Claim(num_unsaved); - PushXRegList(kSafepointSavedRegisters); -} - - -void MacroAssembler::PushSafepointFPRegisters() { - PushCPURegList(CPURegList(CPURegister::kFPRegister, kDRegSize, - FPRegister::kAllocatableFPRegisters)); -} - - -void MacroAssembler::PopSafepointFPRegisters() { - PopCPURegList(CPURegList(CPURegister::kFPRegister, kDRegSize, - FPRegister::kAllocatableFPRegisters)); -} - - -int MacroAssembler::SafepointRegisterStackIndex(int reg_code) { - // Make sure the safepoint registers list is what we expect. - ASSERT(CPURegList::GetSafepointSavedRegisters().list() == 0x6ffcffff); - - // Safepoint registers are stored contiguously on the stack, but not all the - // registers are saved. The following registers are excluded: - // - x16 and x17 (ip0 and ip1) because they shouldn't be preserved outside of - // the macro assembler. - // - x28 (jssp) because JS stack pointer doesn't need to be included in - // safepoint registers. - // - x31 (csp) because the system stack pointer doesn't need to be included - // in safepoint registers. - // - // This function implements the mapping of register code to index into the - // safepoint register slots. - if ((reg_code >= 0) && (reg_code <= 15)) { - return reg_code; - } else if ((reg_code >= 18) && (reg_code <= 27)) { - // Skip ip0 and ip1. - return reg_code - 2; - } else if ((reg_code == 29) || (reg_code == 30)) { - // Also skip jssp. - return reg_code - 3; - } else { - // This register has no safepoint register slot. - UNREACHABLE(); - return -1; - } -} - - -void MacroAssembler::CheckPageFlagSet(const Register& object, - const Register& scratch, - int mask, - Label* if_any_set) { - And(scratch, object, ~Page::kPageAlignmentMask); - Ldr(scratch, MemOperand(scratch, MemoryChunk::kFlagsOffset)); - TestAndBranchIfAnySet(scratch, mask, if_any_set); -} - - -void MacroAssembler::CheckPageFlagClear(const Register& object, - const Register& scratch, - int mask, - Label* if_all_clear) { - And(scratch, object, ~Page::kPageAlignmentMask); - Ldr(scratch, MemOperand(scratch, MemoryChunk::kFlagsOffset)); - TestAndBranchIfAllClear(scratch, mask, if_all_clear); -} - - -void MacroAssembler::RecordWriteField( - Register object, - int offset, - Register value, - Register scratch, - LinkRegisterStatus lr_status, - SaveFPRegsMode save_fp, - RememberedSetAction remembered_set_action, - SmiCheck smi_check) { - // First, check if a write barrier is even needed. The tests below - // catch stores of Smis. - Label done; - - // Skip the barrier if writing a smi. - if (smi_check == INLINE_SMI_CHECK) { - JumpIfSmi(value, &done); - } - - // Although the object register is tagged, the offset is relative to the start - // of the object, so offset must be a multiple of kPointerSize. - ASSERT(IsAligned(offset, kPointerSize)); - - Add(scratch, object, offset - kHeapObjectTag); - if (emit_debug_code()) { - Label ok; - Tst(scratch, (1 << kPointerSizeLog2) - 1); - B(eq, &ok); - Abort(kUnalignedCellInWriteBarrier); - Bind(&ok); - } - - RecordWrite(object, - scratch, - value, - lr_status, - save_fp, - remembered_set_action, - OMIT_SMI_CHECK); - - Bind(&done); - - // Clobber clobbered input registers when running with the debug-code flag - // turned on to provoke errors. - if (emit_debug_code()) { - Mov(value, Operand(BitCast(kZapValue + 4))); - Mov(scratch, Operand(BitCast(kZapValue + 8))); - } -} - - -// Will clobber: object, address, value, Tmp0(), Tmp1(). -// If lr_status is kLRHasBeenSaved, lr will also be clobbered. -// -// The register 'object' contains a heap object pointer. The heap object tag is -// shifted away. -void MacroAssembler::RecordWrite(Register object, - Register address, - Register value, - LinkRegisterStatus lr_status, - SaveFPRegsMode fp_mode, - RememberedSetAction remembered_set_action, - SmiCheck smi_check) { - ASM_LOCATION("MacroAssembler::RecordWrite"); - ASSERT(!AreAliased(object, value)); - - if (emit_debug_code()) { - Ldr(Tmp0(), MemOperand(address)); - Cmp(Tmp0(), value); - Check(eq, kWrongAddressOrValuePassedToRecordWrite); - } - - // Count number of write barriers in generated code. - isolate()->counters()->write_barriers_static()->Increment(); - // TODO(mstarzinger): Dynamic counter missing. - - // First, check if a write barrier is even needed. The tests below - // catch stores of smis and stores into the young generation. - Label done; - - if (smi_check == INLINE_SMI_CHECK) { - ASSERT_EQ(0, kSmiTag); - JumpIfSmi(value, &done); - } - - CheckPageFlagClear(value, - value, // Used as scratch. - MemoryChunk::kPointersToHereAreInterestingMask, - &done); - CheckPageFlagClear(object, - value, // Used as scratch. - MemoryChunk::kPointersFromHereAreInterestingMask, - &done); - - // Record the actual write. - if (lr_status == kLRHasNotBeenSaved) { - Push(lr); - } - RecordWriteStub stub(object, value, address, remembered_set_action, fp_mode); - CallStub(&stub); - if (lr_status == kLRHasNotBeenSaved) { - Pop(lr); - } - - Bind(&done); - - // Clobber clobbered registers when running with the debug-code flag - // turned on to provoke errors. - if (emit_debug_code()) { - Mov(address, Operand(BitCast(kZapValue + 12))); - Mov(value, Operand(BitCast(kZapValue + 16))); - } -} - - -void MacroAssembler::AssertHasValidColor(const Register& reg) { - if (emit_debug_code()) { - // The bit sequence is backward. The first character in the string - // represents the least significant bit. - ASSERT(strcmp(Marking::kImpossibleBitPattern, "01") == 0); - - Label color_is_valid; - Tbnz(reg, 0, &color_is_valid); - Tbz(reg, 1, &color_is_valid); - Abort(kUnexpectedColorFound); - Bind(&color_is_valid); - } -} - - -void MacroAssembler::GetMarkBits(Register addr_reg, - Register bitmap_reg, - Register shift_reg) { - ASSERT(!AreAliased(addr_reg, bitmap_reg, shift_reg, no_reg)); - // addr_reg is divided into fields: - // |63 page base 20|19 high 8|7 shift 3|2 0| - // 'high' gives the index of the cell holding color bits for the object. - // 'shift' gives the offset in the cell for this object's color. - const int kShiftBits = kPointerSizeLog2 + Bitmap::kBitsPerCellLog2; - Ubfx(Tmp0(), addr_reg, kShiftBits, kPageSizeBits - kShiftBits); - Bic(bitmap_reg, addr_reg, Page::kPageAlignmentMask); - Add(bitmap_reg, bitmap_reg, Operand(Tmp0(), LSL, Bitmap::kBytesPerCellLog2)); - // bitmap_reg: - // |63 page base 20|19 zeros 15|14 high 3|2 0| - Ubfx(shift_reg, addr_reg, kPointerSizeLog2, Bitmap::kBitsPerCellLog2); -} - - -void MacroAssembler::HasColor(Register object, - Register bitmap_scratch, - Register shift_scratch, - Label* has_color, - int first_bit, - int second_bit) { - // See mark-compact.h for color definitions. - ASSERT(!AreAliased(object, bitmap_scratch, shift_scratch)); - - GetMarkBits(object, bitmap_scratch, shift_scratch); - Ldr(bitmap_scratch, MemOperand(bitmap_scratch, MemoryChunk::kHeaderSize)); - // Shift the bitmap down to get the color of the object in bits [1:0]. - Lsr(bitmap_scratch, bitmap_scratch, shift_scratch); - - AssertHasValidColor(bitmap_scratch); - - // These bit sequences are backwards. The first character in the string - // represents the least significant bit. - ASSERT(strcmp(Marking::kWhiteBitPattern, "00") == 0); - ASSERT(strcmp(Marking::kBlackBitPattern, "10") == 0); - ASSERT(strcmp(Marking::kGreyBitPattern, "11") == 0); - - // Check for the color. - if (first_bit == 0) { - // Checking for white. - ASSERT(second_bit == 0); - // We only need to test the first bit. - Tbz(bitmap_scratch, 0, has_color); - } else { - Label other_color; - // Checking for grey or black. - Tbz(bitmap_scratch, 0, &other_color); - if (second_bit == 0) { - Tbz(bitmap_scratch, 1, has_color); - } else { - Tbnz(bitmap_scratch, 1, has_color); - } - Bind(&other_color); - } - - // Fall through if it does not have the right color. -} - - -void MacroAssembler::CheckMapDeprecated(Handle map, - Register scratch, - Label* if_deprecated) { - if (map->CanBeDeprecated()) { - Mov(scratch, Operand(map)); - Ldrsw(scratch, UntagSmiFieldMemOperand(scratch, Map::kBitField3Offset)); - TestAndBranchIfAnySet(scratch, Map::Deprecated::kMask, if_deprecated); - } -} - - -void MacroAssembler::JumpIfBlack(Register object, - Register scratch0, - Register scratch1, - Label* on_black) { - ASSERT(strcmp(Marking::kBlackBitPattern, "10") == 0); - HasColor(object, scratch0, scratch1, on_black, 1, 0); // kBlackBitPattern. -} - - -void MacroAssembler::JumpIfDictionaryInPrototypeChain( - Register object, - Register scratch0, - Register scratch1, - Label* found) { - ASSERT(!AreAliased(object, scratch0, scratch1)); - Factory* factory = isolate()->factory(); - Register current = scratch0; - Label loop_again; - - // Scratch contains elements pointer. - Mov(current, object); - - // Loop based on the map going up the prototype chain. - Bind(&loop_again); - Ldr(current, FieldMemOperand(current, HeapObject::kMapOffset)); - Ldrb(scratch1, FieldMemOperand(current, Map::kBitField2Offset)); - Ubfx(scratch1, scratch1, Map::kElementsKindShift, Map::kElementsKindBitCount); - CompareAndBranch(scratch1, DICTIONARY_ELEMENTS, eq, found); - Ldr(current, FieldMemOperand(current, Map::kPrototypeOffset)); - CompareAndBranch(current, Operand(factory->null_value()), ne, &loop_again); -} - - -void MacroAssembler::GetRelocatedValueLocation(Register ldr_location, - Register result) { - ASSERT(!result.Is(ldr_location)); - const uint32_t kLdrLitOffset_lsb = 5; - const uint32_t kLdrLitOffset_width = 19; - Ldr(result, MemOperand(ldr_location)); - if (emit_debug_code()) { - And(result, result, LoadLiteralFMask); - Cmp(result, LoadLiteralFixed); - Check(eq, kTheInstructionToPatchShouldBeAnLdrLiteral); - // The instruction was clobbered. Reload it. - Ldr(result, MemOperand(ldr_location)); - } - Sbfx(result, result, kLdrLitOffset_lsb, kLdrLitOffset_width); - Add(result, ldr_location, Operand(result, LSL, kWordSizeInBytesLog2)); -} - - -void MacroAssembler::EnsureNotWhite( - Register value, - Register bitmap_scratch, - Register shift_scratch, - Register load_scratch, - Register length_scratch, - Label* value_is_white_and_not_data) { - ASSERT(!AreAliased( - value, bitmap_scratch, shift_scratch, load_scratch, length_scratch)); - - // These bit sequences are backwards. The first character in the string - // represents the least significant bit. - ASSERT(strcmp(Marking::kWhiteBitPattern, "00") == 0); - ASSERT(strcmp(Marking::kBlackBitPattern, "10") == 0); - ASSERT(strcmp(Marking::kGreyBitPattern, "11") == 0); - - GetMarkBits(value, bitmap_scratch, shift_scratch); - Ldr(load_scratch, MemOperand(bitmap_scratch, MemoryChunk::kHeaderSize)); - Lsr(load_scratch, load_scratch, shift_scratch); - - AssertHasValidColor(load_scratch); - - // If the value is black or grey we don't need to do anything. - // 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. - Label done; - Tbnz(load_scratch, 0, &done); - - // Value is white. We check whether it is data that doesn't need scanning. - Register map = load_scratch; // Holds map while checking type. - Label is_data_object; - - // Check for heap-number. - Ldr(map, FieldMemOperand(value, HeapObject::kMapOffset)); - Mov(length_scratch, HeapNumber::kSize); - JumpIfRoot(map, Heap::kHeapNumberMapRootIndex, &is_data_object); - - // Check for strings. - ASSERT(kIsIndirectStringTag == 1 && kIsIndirectStringMask == 1); - ASSERT(kNotStringTag == 0x80 && kIsNotStringMask == 0x80); - // If it's a string and it's not a cons string then it's an object containing - // no GC pointers. - Register instance_type = load_scratch; - Ldrb(instance_type, FieldMemOperand(map, Map::kInstanceTypeOffset)); - TestAndBranchIfAnySet(instance_type, - kIsIndirectStringMask | kIsNotStringMask, - value_is_white_and_not_data); - - // It's a non-indirect (non-cons and non-slice) string. - // If it's external, the length is just ExternalString::kSize. - // Otherwise it's String::kHeaderSize + string->length() * (1 or 2). - // External strings are the only ones with the kExternalStringTag bit - // set. - ASSERT_EQ(0, kSeqStringTag & kExternalStringTag); - ASSERT_EQ(0, kConsStringTag & kExternalStringTag); - Mov(length_scratch, ExternalString::kSize); - TestAndBranchIfAnySet(instance_type, kExternalStringTag, &is_data_object); - - // Sequential string, either ASCII or UC16. - // For ASCII (char-size of 1) we shift the smi tag away to get the length. - // For UC16 (char-size of 2) we just leave the smi tag in place, thereby - // getting the length multiplied by 2. - ASSERT(kOneByteStringTag == 4 && kStringEncodingMask == 4); - Ldrsw(length_scratch, UntagSmiFieldMemOperand(value, - String::kLengthOffset)); - Tst(instance_type, kStringEncodingMask); - Cset(load_scratch, eq); - Lsl(length_scratch, length_scratch, load_scratch); - Add(length_scratch, - length_scratch, - SeqString::kHeaderSize + kObjectAlignmentMask); - Bic(length_scratch, length_scratch, kObjectAlignmentMask); - - Bind(&is_data_object); - // Value is a data object, and it is white. Mark it black. Since we know - // that the object is white we can make it black by flipping one bit. - Register mask = shift_scratch; - Mov(load_scratch, 1); - Lsl(mask, load_scratch, shift_scratch); - - Ldr(load_scratch, MemOperand(bitmap_scratch, MemoryChunk::kHeaderSize)); - Orr(load_scratch, load_scratch, mask); - Str(load_scratch, MemOperand(bitmap_scratch, MemoryChunk::kHeaderSize)); - - Bic(bitmap_scratch, bitmap_scratch, Page::kPageAlignmentMask); - Ldr(load_scratch, MemOperand(bitmap_scratch, MemoryChunk::kLiveBytesOffset)); - Add(load_scratch, load_scratch, length_scratch); - Str(load_scratch, MemOperand(bitmap_scratch, MemoryChunk::kLiveBytesOffset)); - - Bind(&done); -} - - -void MacroAssembler::Assert(Condition cond, BailoutReason reason) { - if (emit_debug_code()) { - Check(cond, reason); - } -} - - - -void MacroAssembler::AssertRegisterIsClear(Register reg, BailoutReason reason) { - if (emit_debug_code()) { - CheckRegisterIsClear(reg, reason); - } -} - - -void MacroAssembler::AssertRegisterIsRoot(Register reg, - Heap::RootListIndex index, - BailoutReason reason) { - // CompareRoot uses Tmp0(). - ASSERT(!reg.Is(Tmp0())); - if (emit_debug_code()) { - CompareRoot(reg, index); - Check(eq, reason); - } -} - - -void MacroAssembler::AssertFastElements(Register elements) { - if (emit_debug_code()) { - Register temp = Tmp1(); - Label ok; - Ldr(temp, FieldMemOperand(elements, HeapObject::kMapOffset)); - JumpIfRoot(temp, Heap::kFixedArrayMapRootIndex, &ok); - JumpIfRoot(temp, Heap::kFixedDoubleArrayMapRootIndex, &ok); - JumpIfRoot(temp, Heap::kFixedCOWArrayMapRootIndex, &ok); - Abort(kJSObjectWithFastElementsMapHasSlowElements); - Bind(&ok); - } -} - - -void MacroAssembler::AssertIsString(const Register& object) { - if (emit_debug_code()) { - Register temp = Tmp1(); - STATIC_ASSERT(kSmiTag == 0); - Tst(object, Operand(kSmiTagMask)); - Check(ne, kOperandIsNotAString); - Ldr(temp, FieldMemOperand(object, HeapObject::kMapOffset)); - CompareInstanceType(temp, temp, FIRST_NONSTRING_TYPE); - Check(lo, kOperandIsNotAString); - } -} - - -void MacroAssembler::Check(Condition cond, BailoutReason reason) { - Label ok; - B(cond, &ok); - Abort(reason); - // Will not return here. - Bind(&ok); -} - - -void MacroAssembler::CheckRegisterIsClear(Register reg, BailoutReason reason) { - Label ok; - Cbz(reg, &ok); - Abort(reason); - // Will not return here. - Bind(&ok); -} - - -void MacroAssembler::Abort(BailoutReason reason) { -#ifdef DEBUG - RecordComment("Abort message: "); - RecordComment(GetBailoutReason(reason)); - - if (FLAG_trap_on_abort) { - Brk(0); - return; - } -#endif - - // Abort is used in some contexts where csp is the stack pointer. In order to - // simplify the CallRuntime code, make sure that jssp is the stack pointer. - // There is no risk of register corruption here because Abort doesn't return. - Register old_stack_pointer = StackPointer(); - SetStackPointer(jssp); - Mov(jssp, old_stack_pointer); - - if (use_real_aborts()) { - Mov(x0, Operand(Smi::FromInt(reason))); - Push(x0); - - 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); - CallRuntime(Runtime::kAbort, 1); - } else { - CallRuntime(Runtime::kAbort, 1); - } - } else { - // Load the string to pass to Printf. - Label msg_address; - Adr(x0, &msg_address); - - // Call Printf directly to report the error. - CallPrintf(); - - // We need a way to stop execution on both the simulator and real hardware, - // and Unreachable() is the best option. - Unreachable(); - - // Emit the message string directly in the instruction stream. - { - BlockConstPoolScope scope(this); - Bind(&msg_address); - EmitStringData(GetBailoutReason(reason)); - } - } - - SetStackPointer(old_stack_pointer); -} - - -void MacroAssembler::LoadTransitionedArrayMapConditional( - ElementsKind expected_kind, - ElementsKind transitioned_kind, - Register map_in_out, - Register scratch, - Label* no_map_match) { - // Load the global or builtins object from the current context. - Ldr(scratch, GlobalObjectMemOperand()); - Ldr(scratch, FieldMemOperand(scratch, GlobalObject::kNativeContextOffset)); - - // Check that the function's map is the same as the expected cached map. - Ldr(scratch, ContextMemOperand(scratch, Context::JS_ARRAY_MAPS_INDEX)); - size_t offset = (expected_kind * kPointerSize) + FixedArrayBase::kHeaderSize; - Ldr(Tmp0(), FieldMemOperand(scratch, offset)); - Cmp(map_in_out, Tmp0()); - B(ne, no_map_match); - - // Use the transitioned cached map. - offset = (transitioned_kind * kPointerSize) + FixedArrayBase::kHeaderSize; - Ldr(map_in_out, FieldMemOperand(scratch, offset)); -} - - -void MacroAssembler::LoadInitialArrayMap(Register function_in, - Register scratch, - Register map_out, - ArrayHasHoles holes) { - ASSERT(!AreAliased(function_in, scratch, map_out)); - Label done; - Ldr(map_out, FieldMemOperand(function_in, - JSFunction::kPrototypeOrInitialMapOffset)); - - if (!FLAG_smi_only_arrays) { - ElementsKind kind = (holes == kArrayCanHaveHoles) ? FAST_HOLEY_ELEMENTS - : FAST_ELEMENTS; - LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, kind, map_out, - scratch, &done); - } else if (holes == kArrayCanHaveHoles) { - LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, - FAST_HOLEY_SMI_ELEMENTS, map_out, - scratch, &done); - } - Bind(&done); -} - - -void MacroAssembler::LoadArrayFunction(Register function) { - // Load the global or builtins object from the current context. - Ldr(function, GlobalObjectMemOperand()); - // Load the global context from the global or builtins object. - Ldr(function, - FieldMemOperand(function, GlobalObject::kGlobalContextOffset)); - // Load the array function from the native context. - Ldr(function, ContextMemOperand(function, Context::ARRAY_FUNCTION_INDEX)); -} - - -void MacroAssembler::LoadGlobalFunction(int index, Register function) { - // Load the global or builtins object from the current context. - Ldr(function, GlobalObjectMemOperand()); - // Load the native context from the global or builtins object. - Ldr(function, FieldMemOperand(function, - GlobalObject::kNativeContextOffset)); - // Load the function from the native context. - Ldr(function, ContextMemOperand(function, index)); -} - - -void MacroAssembler::LoadGlobalFunctionInitialMap(Register function, - Register map, - Register scratch) { - // Load the initial map. The global functions all have initial maps. - Ldr(map, FieldMemOperand(function, JSFunction::kPrototypeOrInitialMapOffset)); - if (emit_debug_code()) { - Label ok, fail; - CheckMap(map, scratch, Heap::kMetaMapRootIndex, &fail, DO_SMI_CHECK); - B(&ok); - Bind(&fail); - Abort(kGlobalFunctionsMustHaveInitialMap); - Bind(&ok); - } -} - - -// This is the main Printf implementation. All other Printf variants call -// PrintfNoPreserve after setting up one or more PreserveRegisterScopes. -void MacroAssembler::PrintfNoPreserve(const char * format, - const CPURegister& arg0, - const CPURegister& arg1, - const CPURegister& arg2, - const CPURegister& arg3) { - // We cannot handle a caller-saved stack pointer. It doesn't make much sense - // in most cases anyway, so this restriction shouldn't be too serious. - ASSERT(!kCallerSaved.IncludesAliasOf(__ StackPointer())); - - // We cannot print Tmp0() or Tmp1() as they're used internally by the macro - // assembler. We cannot print the stack pointer because it is typically used - // to preserve caller-saved registers (using other Printf variants which - // depend on this helper). - ASSERT(!AreAliased(Tmp0(), Tmp1(), StackPointer(), arg0)); - ASSERT(!AreAliased(Tmp0(), Tmp1(), StackPointer(), arg1)); - ASSERT(!AreAliased(Tmp0(), Tmp1(), StackPointer(), arg2)); - ASSERT(!AreAliased(Tmp0(), Tmp1(), StackPointer(), arg3)); - - static const int kMaxArgCount = 4; - // Assume that we have the maximum number of arguments until we know - // otherwise. - int arg_count = kMaxArgCount; - - // The provided arguments. - CPURegister args[kMaxArgCount] = {arg0, arg1, arg2, arg3}; - - // The PCS registers where the arguments need to end up. - CPURegister pcs[kMaxArgCount] = {NoCPUReg, NoCPUReg, NoCPUReg, NoCPUReg}; - - // Promote FP arguments to doubles, and integer arguments to X registers. - // Note that FP and integer arguments cannot be mixed, but we'll check - // AreSameSizeAndType once we've processed these promotions. - for (int i = 0; i < kMaxArgCount; i++) { - if (args[i].IsRegister()) { - // Note that we use x1 onwards, because x0 will hold the format string. - pcs[i] = Register::XRegFromCode(i + 1); - // For simplicity, we handle all integer arguments as X registers. An X - // register argument takes the same space as a W register argument in the - // PCS anyway. The only limitation is that we must explicitly clear the - // top word for W register arguments as the callee will expect it to be - // clear. - if (!args[i].Is64Bits()) { - const Register& as_x = args[i].X(); - And(as_x, as_x, 0x00000000ffffffff); - args[i] = as_x; - } - } else if (args[i].IsFPRegister()) { - pcs[i] = FPRegister::DRegFromCode(i); - // C and C++ varargs functions (such as printf) implicitly promote float - // arguments to doubles. - if (!args[i].Is64Bits()) { - FPRegister s(args[i]); - const FPRegister& as_d = args[i].D(); - Fcvt(as_d, s); - args[i] = as_d; - } - } else { - // This is the first empty (NoCPUReg) argument, so use it to set the - // argument count and bail out. - arg_count = i; - break; - } - } - ASSERT((arg_count >= 0) && (arg_count <= kMaxArgCount)); - // Check that every remaining argument is NoCPUReg. - for (int i = arg_count; i < kMaxArgCount; i++) { - ASSERT(args[i].IsNone()); - } - ASSERT((arg_count == 0) || AreSameSizeAndType(args[0], args[1], - args[2], args[3], - pcs[0], pcs[1], - pcs[2], pcs[3])); - - // Move the arguments into the appropriate PCS registers. - // - // Arranging an arbitrary list of registers into x1-x4 (or d0-d3) is - // surprisingly complicated. - // - // * For even numbers of registers, we push the arguments and then pop them - // into their final registers. This maintains 16-byte stack alignment in - // case csp is the stack pointer, since we're only handling X or D - // registers at this point. - // - // * For odd numbers of registers, we push and pop all but one register in - // the same way, but the left-over register is moved directly, since we - // can always safely move one register without clobbering any source. - if (arg_count >= 4) { - Push(args[3], args[2], args[1], args[0]); - } else if (arg_count >= 2) { - Push(args[1], args[0]); - } - - if ((arg_count % 2) != 0) { - // Move the left-over register directly. - const CPURegister& leftover_arg = args[arg_count - 1]; - const CPURegister& leftover_pcs = pcs[arg_count - 1]; - if (leftover_arg.IsRegister()) { - Mov(Register(leftover_pcs), Register(leftover_arg)); - } else { - Fmov(FPRegister(leftover_pcs), FPRegister(leftover_arg)); - } - } - - if (arg_count >= 4) { - Pop(pcs[0], pcs[1], pcs[2], pcs[3]); - } else if (arg_count >= 2) { - Pop(pcs[0], pcs[1]); - } - - // Load the format string into x0, as per the procedure-call standard. - // - // To make the code as portable as possible, the format string is encoded - // directly in the instruction stream. It might be cleaner to encode it in a - // literal pool, but since Printf is usually used for debugging, it is - // beneficial for it to be minimally dependent on other features. - Label format_address; - Adr(x0, &format_address); - - // Emit the format string directly in the instruction stream. - { BlockConstPoolScope scope(this); - Label after_data; - B(&after_data); - Bind(&format_address); - EmitStringData(format); - Unreachable(); - Bind(&after_data); - } - - // We don't pass any arguments on the stack, but we still need to align the C - // stack pointer to a 16-byte boundary for PCS compliance. - if (!csp.Is(StackPointer())) { - Bic(csp, StackPointer(), 0xf); - } - - CallPrintf(pcs[0].type()); -} - - -void MacroAssembler::CallPrintf(CPURegister::RegisterType type) { - // A call to printf needs special handling for the simulator, since the system - // printf function will use a different instruction set and the procedure-call - // standard will not be compatible. -#ifdef USE_SIMULATOR - { InstructionAccurateScope scope(this, kPrintfLength / kInstructionSize); - hlt(kImmExceptionIsPrintf); - dc32(type); - } -#else - Call(FUNCTION_ADDR(printf), RelocInfo::EXTERNAL_REFERENCE); -#endif -} - - -void MacroAssembler::Printf(const char * format, - const CPURegister& arg0, - const CPURegister& arg1, - const CPURegister& arg2, - const CPURegister& arg3) { - // Preserve all caller-saved registers as well as NZCV. - // If csp is the stack pointer, PushCPURegList asserts that the size of each - // list is a multiple of 16 bytes. - PushCPURegList(kCallerSaved); - PushCPURegList(kCallerSavedFP); - // Use Tmp0() as a scratch register. It is not accepted by Printf so it will - // never overlap an argument register. - Mrs(Tmp0(), NZCV); - Push(Tmp0(), xzr); - - PrintfNoPreserve(format, arg0, arg1, arg2, arg3); - - Pop(xzr, Tmp0()); - Msr(NZCV, Tmp0()); - PopCPURegList(kCallerSavedFP); - PopCPURegList(kCallerSaved); -} - - -void MacroAssembler::EmitFrameSetupForCodeAgePatching() { - // TODO(jbramley): Other architectures use the internal memcpy to copy the - // sequence. If this is a performance bottleneck, we should consider caching - // the sequence and copying it in the same way. - InstructionAccurateScope scope(this, kCodeAgeSequenceSize / kInstructionSize); - ASSERT(jssp.Is(StackPointer())); - EmitFrameSetupForCodeAgePatching(this); -} - - - -void MacroAssembler::EmitCodeAgeSequence(Code* stub) { - InstructionAccurateScope scope(this, kCodeAgeSequenceSize / kInstructionSize); - ASSERT(jssp.Is(StackPointer())); - EmitCodeAgeSequence(this, stub); -} - - -#undef __ -#define __ assm-> - - -void MacroAssembler::EmitFrameSetupForCodeAgePatching(Assembler * assm) { - Label start; - __ bind(&start); - - // We can do this sequence using four instructions, but the code ageing - // sequence that patches it needs five, so we use the extra space to try to - // simplify some addressing modes and remove some dependencies (compared to - // using two stp instructions with write-back). - __ sub(jssp, jssp, 4 * kXRegSizeInBytes); - __ sub(csp, csp, 4 * kXRegSizeInBytes); - __ stp(x1, cp, MemOperand(jssp, 0 * kXRegSizeInBytes)); - __ stp(fp, lr, MemOperand(jssp, 2 * kXRegSizeInBytes)); - __ add(fp, jssp, StandardFrameConstants::kFixedFrameSizeFromFp); - - __ AssertSizeOfCodeGeneratedSince(&start, kCodeAgeSequenceSize); -} - - -void MacroAssembler::EmitCodeAgeSequence(Assembler * assm, - Code * stub) { - Label start; - __ bind(&start); - // When the stub is called, the sequence is replaced with the young sequence - // (as in EmitFrameSetupForCodeAgePatching). After the code is replaced, the - // stub jumps to &start, stored in x0. The young sequence does not call the - // stub so there is no infinite loop here. - // - // A branch (br) is used rather than a call (blr) because this code replaces - // the frame setup code that would normally preserve lr. - __ LoadLiteral(ip0, kCodeAgeStubEntryOffset); - __ adr(x0, &start); - __ br(ip0); - // IsCodeAgeSequence in codegen-a64.cc assumes that the code generated up - // until now (kCodeAgeStubEntryOffset) is the same for all code age sequences. - __ AssertSizeOfCodeGeneratedSince(&start, kCodeAgeStubEntryOffset); - if (stub) { - __ dc64(reinterpret_cast(stub->instruction_start())); - __ AssertSizeOfCodeGeneratedSince(&start, kCodeAgeSequenceSize); - } -} - - -bool MacroAssembler::IsYoungSequence(byte* sequence) { - // Generate a young sequence to compare with. - const int length = kCodeAgeSequenceSize / kInstructionSize; - static bool initialized = false; - static byte young[kCodeAgeSequenceSize]; - if (!initialized) { - PatchingAssembler patcher(young, length); - // The young sequence is the frame setup code for FUNCTION code types. It is - // generated by FullCodeGenerator::Generate. - MacroAssembler::EmitFrameSetupForCodeAgePatching(&patcher); - initialized = true; - } - - bool is_young = (memcmp(sequence, young, kCodeAgeSequenceSize) == 0); - ASSERT(is_young || IsCodeAgeSequence(sequence)); - return is_young; -} - - -#ifdef DEBUG -bool MacroAssembler::IsCodeAgeSequence(byte* sequence) { - // The old sequence varies depending on the code age. However, the code up - // until kCodeAgeStubEntryOffset does not change, so we can check that part to - // get a reasonable level of verification. - const int length = kCodeAgeStubEntryOffset / kInstructionSize; - static bool initialized = false; - static byte old[kCodeAgeStubEntryOffset]; - if (!initialized) { - PatchingAssembler patcher(old, length); - MacroAssembler::EmitCodeAgeSequence(&patcher, NULL); - initialized = true; - } - return memcmp(sequence, old, kCodeAgeStubEntryOffset) == 0; -} -#endif - - -#undef __ -#define __ masm-> - - -void InlineSmiCheckInfo::Emit(MacroAssembler* masm, const Register& reg, - const Label* smi_check) { - Assembler::BlockConstPoolScope scope(masm); - if (reg.IsValid()) { - ASSERT(smi_check->is_bound()); - ASSERT(reg.Is64Bits()); - - // Encode the register (x0-x30) in the lowest 5 bits, then the offset to - // 'check' in the other bits. The possible offset is limited in that we - // use BitField to pack the data, and the underlying data type is a - // uint32_t. - uint32_t delta = __ InstructionsGeneratedSince(smi_check); - __ InlineData(RegisterBits::encode(reg.code()) | DeltaBits::encode(delta)); - } else { - ASSERT(!smi_check->is_bound()); - - // An offset of 0 indicates that there is no patch site. - __ InlineData(0); - } -} - - -InlineSmiCheckInfo::InlineSmiCheckInfo(Address info) - : reg_(NoReg), smi_check_(NULL) { - InstructionSequence* inline_data = InstructionSequence::At(info); - ASSERT(inline_data->IsInlineData()); - if (inline_data->IsInlineData()) { - uint64_t payload = inline_data->InlineData(); - // We use BitField to decode the payload, and BitField can only handle - // 32-bit values. - ASSERT(is_uint32(payload)); - if (payload != 0) { - int reg_code = RegisterBits::decode(payload); - reg_ = Register::XRegFromCode(reg_code); - uint64_t smi_check_delta = DeltaBits::decode(payload); - ASSERT(smi_check_delta != 0); - smi_check_ = inline_data - (smi_check_delta * kInstructionSize); - } - } -} - - -#undef __ - - -} } // namespace v8::internal - -#endif // V8_TARGET_ARCH_A64 diff --git a/deps/v8/src/a64/macro-assembler-a64.h b/deps/v8/src/a64/macro-assembler-a64.h deleted file mode 100644 index 7b8dd3f806..0000000000 --- a/deps/v8/src/a64/macro-assembler-a64.h +++ /dev/null @@ -1,2238 +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. - -#ifndef V8_A64_MACRO_ASSEMBLER_A64_H_ -#define V8_A64_MACRO_ASSEMBLER_A64_H_ - -#include "v8globals.h" -#include "globals.h" - -#include "a64/assembler-a64-inl.h" - -namespace v8 { -namespace internal { - -#define LS_MACRO_LIST(V) \ - V(Ldrb, Register&, rt, LDRB_w) \ - V(Strb, Register&, rt, STRB_w) \ - V(Ldrsb, Register&, rt, rt.Is64Bits() ? LDRSB_x : LDRSB_w) \ - V(Ldrh, Register&, rt, LDRH_w) \ - V(Strh, Register&, rt, STRH_w) \ - V(Ldrsh, Register&, rt, rt.Is64Bits() ? LDRSH_x : LDRSH_w) \ - V(Ldr, CPURegister&, rt, LoadOpFor(rt)) \ - V(Str, CPURegister&, rt, StoreOpFor(rt)) \ - V(Ldrsw, Register&, rt, LDRSW_x) - - -// ---------------------------------------------------------------------------- -// Static helper functions - -// Generate a MemOperand for loading a field from an object. -inline MemOperand FieldMemOperand(Register object, int offset); -inline MemOperand UntagSmiFieldMemOperand(Register object, int offset); - -// Generate a MemOperand for loading a SMI from memory. -inline MemOperand UntagSmiMemOperand(Register object, int offset); - - -// ---------------------------------------------------------------------------- -// MacroAssembler - -enum RememberedSetAction { EMIT_REMEMBERED_SET, OMIT_REMEMBERED_SET }; -enum SmiCheck { INLINE_SMI_CHECK, OMIT_SMI_CHECK }; -enum LinkRegisterStatus { kLRHasNotBeenSaved, kLRHasBeenSaved }; -enum TargetAddressStorageMode { - CAN_INLINE_TARGET_ADDRESS, - NEVER_INLINE_TARGET_ADDRESS -}; -enum UntagMode { kNotSpeculativeUntag, kSpeculativeUntag }; -enum ArrayHasHoles { kArrayCantHaveHoles, kArrayCanHaveHoles }; -enum CopyHint { kCopyUnknown, kCopyShort, kCopyLong }; -enum DiscardMoveMode { kDontDiscardForSameWReg, kDiscardForSameWReg }; -enum SeqStringSetCharCheckIndexType { kIndexIsSmi, kIndexIsInteger32 }; - -class MacroAssembler : public Assembler { - public: - MacroAssembler(Isolate* isolate, byte * buffer, unsigned buffer_size); - - inline Handle CodeObject(); - - // Instruction set functions ------------------------------------------------ - // Logical macros. - inline void And(const Register& rd, - const Register& rn, - const Operand& operand); - inline void Ands(const Register& rd, - const Register& rn, - const Operand& operand); - inline void Bic(const Register& rd, - const Register& rn, - const Operand& operand); - inline void Bics(const Register& rd, - const Register& rn, - const Operand& operand); - inline void Orr(const Register& rd, - const Register& rn, - const Operand& operand); - inline void Orn(const Register& rd, - const Register& rn, - const Operand& operand); - inline void Eor(const Register& rd, - const Register& rn, - const Operand& operand); - inline void Eon(const Register& rd, - const Register& rn, - const Operand& operand); - inline void Tst(const Register& rn, const Operand& operand); - void LogicalMacro(const Register& rd, - const Register& rn, - const Operand& operand, - LogicalOp op); - - // Add and sub macros. - inline void Add(const Register& rd, - const Register& rn, - const Operand& operand); - inline void Adds(const Register& rd, - const Register& rn, - const Operand& operand); - inline void Sub(const Register& rd, - const Register& rn, - const Operand& operand); - inline void Subs(const Register& rd, - const Register& rn, - const Operand& operand); - inline void Cmn(const Register& rn, const Operand& operand); - inline void Cmp(const Register& rn, const Operand& operand); - inline void Neg(const Register& rd, - const Operand& operand); - inline void Negs(const Register& rd, - const Operand& operand); - - void AddSubMacro(const Register& rd, - const Register& rn, - const Operand& operand, - FlagsUpdate S, - AddSubOp op); - - // Add/sub with carry macros. - inline void Adc(const Register& rd, - const Register& rn, - const Operand& operand); - inline void Adcs(const Register& rd, - const Register& rn, - const Operand& operand); - inline void Sbc(const Register& rd, - const Register& rn, - const Operand& operand); - inline void Sbcs(const Register& rd, - const Register& rn, - const Operand& operand); - inline void Ngc(const Register& rd, - const Operand& operand); - inline void Ngcs(const Register& rd, - const Operand& operand); - void AddSubWithCarryMacro(const Register& rd, - const Register& rn, - const Operand& operand, - FlagsUpdate S, - AddSubWithCarryOp op); - - // Move macros. - void Mov(const Register& rd, - const Operand& operand, - DiscardMoveMode discard_mode = kDontDiscardForSameWReg); - void Mov(const Register& rd, uint64_t imm); - inline void Mvn(const Register& rd, uint64_t imm); - void Mvn(const Register& rd, const Operand& operand); - static bool IsImmMovn(uint64_t imm, unsigned reg_size); - static bool IsImmMovz(uint64_t imm, unsigned reg_size); - static unsigned CountClearHalfWords(uint64_t imm, unsigned reg_size); - - // Conditional macros. - inline void Ccmp(const Register& rn, - const Operand& operand, - StatusFlags nzcv, - Condition cond); - inline void Ccmn(const Register& rn, - const Operand& operand, - StatusFlags nzcv, - Condition cond); - void ConditionalCompareMacro(const Register& rn, - const Operand& operand, - StatusFlags nzcv, - Condition cond, - ConditionalCompareOp op); - void Csel(const Register& rd, - const Register& rn, - const Operand& operand, - Condition cond); - - // Load/store macros. -#define DECLARE_FUNCTION(FN, REGTYPE, REG, OP) \ - inline void FN(const REGTYPE REG, const MemOperand& addr); - LS_MACRO_LIST(DECLARE_FUNCTION) -#undef DECLARE_FUNCTION - - void LoadStoreMacro(const CPURegister& rt, - const MemOperand& addr, - LoadStoreOp op); - - // V8-specific load/store helpers. - void Load(const Register& rt, const MemOperand& addr, Representation r); - void Store(const Register& rt, const MemOperand& addr, Representation r); - - // Remaining instructions are simple pass-through calls to the assembler. - inline void Adr(const Register& rd, Label* label); - inline void Asr(const Register& rd, const Register& rn, unsigned shift); - inline void Asr(const Register& rd, const Register& rn, const Register& rm); - inline void B(Label* label); - inline void B(Condition cond, Label* label); - void B(Label* label, Condition cond); - inline void Bfi(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width); - inline void Bfxil(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width); - inline void Bind(Label* label); - inline void Bl(Label* label); - inline void Blr(const Register& xn); - inline void Br(const Register& xn); - inline void Brk(int code); - void Cbnz(const Register& rt, Label* label); - void Cbz(const Register& rt, Label* label); - inline void Cinc(const Register& rd, const Register& rn, Condition cond); - inline void Cinv(const Register& rd, const Register& rn, Condition cond); - inline void Cls(const Register& rd, const Register& rn); - inline void Clz(const Register& rd, const Register& rn); - inline void Cneg(const Register& rd, const Register& rn, Condition cond); - inline void CzeroX(const Register& rd, Condition cond); - inline void CmovX(const Register& rd, const Register& rn, Condition cond); - inline void Cset(const Register& rd, Condition cond); - inline void Csetm(const Register& rd, Condition cond); - inline void Csinc(const Register& rd, - const Register& rn, - const Register& rm, - Condition cond); - inline void Csinv(const Register& rd, - const Register& rn, - const Register& rm, - Condition cond); - inline void Csneg(const Register& rd, - const Register& rn, - const Register& rm, - Condition cond); - inline void Dmb(BarrierDomain domain, BarrierType type); - inline void Dsb(BarrierDomain domain, BarrierType type); - inline void Debug(const char* message, uint32_t code, Instr params = BREAK); - inline void Extr(const Register& rd, - const Register& rn, - const Register& rm, - unsigned lsb); - inline void Fabs(const FPRegister& fd, const FPRegister& fn); - inline void Fadd(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm); - inline void Fccmp(const FPRegister& fn, - const FPRegister& fm, - StatusFlags nzcv, - Condition cond); - inline void Fcmp(const FPRegister& fn, const FPRegister& fm); - inline void Fcmp(const FPRegister& fn, double value); - inline void Fcsel(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm, - Condition cond); - inline void Fcvt(const FPRegister& fd, const FPRegister& fn); - inline void Fcvtas(const Register& rd, const FPRegister& fn); - inline void Fcvtau(const Register& rd, const FPRegister& fn); - inline void Fcvtms(const Register& rd, const FPRegister& fn); - inline void Fcvtmu(const Register& rd, const FPRegister& fn); - inline void Fcvtns(const Register& rd, const FPRegister& fn); - inline void Fcvtnu(const Register& rd, const FPRegister& fn); - inline void Fcvtzs(const Register& rd, const FPRegister& fn); - inline void Fcvtzu(const Register& rd, const FPRegister& fn); - inline void Fdiv(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm); - inline void Fmadd(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm, - const FPRegister& fa); - inline void Fmax(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm); - inline void Fmaxnm(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm); - inline void Fmin(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm); - inline void Fminnm(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm); - inline void Fmov(FPRegister fd, FPRegister fn); - inline void Fmov(FPRegister fd, Register rn); - inline void Fmov(FPRegister fd, double imm); - inline void Fmov(Register rd, FPRegister fn); - inline void Fmsub(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm, - const FPRegister& fa); - inline void Fmul(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm); - inline void Fneg(const FPRegister& fd, const FPRegister& fn); - inline void Fnmadd(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm, - const FPRegister& fa); - inline void Fnmsub(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm, - const FPRegister& fa); - inline void Frinta(const FPRegister& fd, const FPRegister& fn); - inline void Frintn(const FPRegister& fd, const FPRegister& fn); - inline void Frintz(const FPRegister& fd, const FPRegister& fn); - inline void Fsqrt(const FPRegister& fd, const FPRegister& fn); - inline void Fsub(const FPRegister& fd, - const FPRegister& fn, - const FPRegister& fm); - inline void Hint(SystemHint code); - inline void Hlt(int code); - inline void Isb(); - inline void Ldnp(const CPURegister& rt, - const CPURegister& rt2, - const MemOperand& src); - inline void Ldp(const CPURegister& rt, - const CPURegister& rt2, - const MemOperand& src); - inline void Ldpsw(const Register& rt, - const Register& rt2, - const MemOperand& src); - inline void Ldr(const FPRegister& ft, double imm); - inline void Ldr(const Register& rt, uint64_t imm); - inline void Lsl(const Register& rd, const Register& rn, unsigned shift); - inline void Lsl(const Register& rd, const Register& rn, const Register& rm); - inline void Lsr(const Register& rd, const Register& rn, unsigned shift); - inline void Lsr(const Register& rd, const Register& rn, const Register& rm); - inline void Madd(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra); - inline void Mneg(const Register& rd, const Register& rn, const Register& rm); - inline void Mov(const Register& rd, const Register& rm); - inline void Movk(const Register& rd, uint64_t imm, int shift = -1); - inline void Mrs(const Register& rt, SystemRegister sysreg); - inline void Msr(SystemRegister sysreg, const Register& rt); - inline void Msub(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra); - inline void Mul(const Register& rd, const Register& rn, const Register& rm); - inline void Nop() { nop(); } - inline void Rbit(const Register& rd, const Register& rn); - inline void Ret(const Register& xn = lr); - inline void Rev(const Register& rd, const Register& rn); - inline void Rev16(const Register& rd, const Register& rn); - inline void Rev32(const Register& rd, const Register& rn); - inline void Ror(const Register& rd, const Register& rs, unsigned shift); - inline void Ror(const Register& rd, const Register& rn, const Register& rm); - inline void Sbfiz(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width); - inline void Sbfx(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width); - inline void Scvtf(const FPRegister& fd, - const Register& rn, - unsigned fbits = 0); - inline void Sdiv(const Register& rd, const Register& rn, const Register& rm); - inline void Smaddl(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra); - inline void Smsubl(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra); - inline void Smull(const Register& rd, - const Register& rn, - const Register& rm); - inline void Smulh(const Register& rd, - const Register& rn, - const Register& rm); - inline void Stnp(const CPURegister& rt, - const CPURegister& rt2, - const MemOperand& dst); - inline void Stp(const CPURegister& rt, - const CPURegister& rt2, - const MemOperand& dst); - inline void Sxtb(const Register& rd, const Register& rn); - inline void Sxth(const Register& rd, const Register& rn); - inline void Sxtw(const Register& rd, const Register& rn); - void Tbnz(const Register& rt, unsigned bit_pos, Label* label); - void Tbz(const Register& rt, unsigned bit_pos, Label* label); - inline void Ubfiz(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width); - inline void Ubfx(const Register& rd, - const Register& rn, - unsigned lsb, - unsigned width); - inline void Ucvtf(const FPRegister& fd, - const Register& rn, - unsigned fbits = 0); - inline void Udiv(const Register& rd, const Register& rn, const Register& rm); - inline void Umaddl(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra); - inline void Umsubl(const Register& rd, - const Register& rn, - const Register& rm, - const Register& ra); - inline void Uxtb(const Register& rd, const Register& rn); - inline void Uxth(const Register& rd, const Register& rn); - inline void Uxtw(const Register& rd, const Register& rn); - - // Pseudo-instructions ------------------------------------------------------ - - // Compute rd = abs(rm). - // This function clobbers the condition flags. - // - // If rm is the minimum representable value, the result is not representable. - // Handlers for each case can be specified using the relevant labels. - void Abs(const Register& rd, const Register& rm, - Label * is_not_representable = NULL, - Label * is_representable = NULL); - - // Push or pop up to 4 registers of the same width to or from the stack, - // using the current stack pointer as set by SetStackPointer. - // - // If an argument register is 'NoReg', all further arguments are also assumed - // to be 'NoReg', and are thus not pushed or popped. - // - // Arguments are ordered such that "Push(a, b);" is functionally equivalent - // to "Push(a); Push(b);". - // - // It is valid to push the same register more than once, and there is no - // restriction on the order in which registers are specified. - // - // It is not valid to pop into the same register more than once in one - // operation, not even into the zero register. - // - // If the current stack pointer (as set by SetStackPointer) is csp, then it - // must be aligned to 16 bytes on entry and the total size of the specified - // registers must also be a multiple of 16 bytes. - // - // Even if the current stack pointer is not the system stack pointer (csp), - // Push (and derived methods) will still modify the system stack pointer in - // order to comply with ABI rules about accessing memory below the system - // stack pointer. - // - // Other than the registers passed into Pop, the stack pointer and (possibly) - // the system stack pointer, these methods do not modify any other registers. - // Scratch registers such as Tmp0() and Tmp1() are preserved. - void Push(const CPURegister& src0, const CPURegister& src1 = NoReg, - const CPURegister& src2 = NoReg, const CPURegister& src3 = NoReg); - void Pop(const CPURegister& dst0, const CPURegister& dst1 = NoReg, - const CPURegister& dst2 = NoReg, const CPURegister& dst3 = NoReg); - - // Alternative forms of Push and Pop, taking a RegList or CPURegList that - // specifies the registers that are to be pushed or popped. Higher-numbered - // registers are associated with higher memory addresses (as in the A32 push - // and pop instructions). - // - // (Push|Pop)SizeRegList allow you to specify the register size as a - // parameter. Only kXRegSize, kWRegSize, kDRegSize and kSRegSize are - // supported. - // - // Otherwise, (Push|Pop)(CPU|X|W|D|S)RegList is preferred. - void PushCPURegList(CPURegList registers); - void PopCPURegList(CPURegList registers); - - inline void PushSizeRegList(RegList registers, unsigned reg_size, - CPURegister::RegisterType type = CPURegister::kRegister) { - PushCPURegList(CPURegList(type, reg_size, registers)); - } - inline void PopSizeRegList(RegList registers, unsigned reg_size, - CPURegister::RegisterType type = CPURegister::kRegister) { - PopCPURegList(CPURegList(type, reg_size, registers)); - } - inline void PushXRegList(RegList regs) { - PushSizeRegList(regs, kXRegSize); - } - inline void PopXRegList(RegList regs) { - PopSizeRegList(regs, kXRegSize); - } - inline void PushWRegList(RegList regs) { - PushSizeRegList(regs, kWRegSize); - } - inline void PopWRegList(RegList regs) { - PopSizeRegList(regs, kWRegSize); - } - inline void PushDRegList(RegList regs) { - PushSizeRegList(regs, kDRegSize, CPURegister::kFPRegister); - } - inline void PopDRegList(RegList regs) { - PopSizeRegList(regs, kDRegSize, CPURegister::kFPRegister); - } - inline void PushSRegList(RegList regs) { - PushSizeRegList(regs, kSRegSize, CPURegister::kFPRegister); - } - inline void PopSRegList(RegList regs) { - PopSizeRegList(regs, kSRegSize, CPURegister::kFPRegister); - } - - // Push the specified register 'count' times. - void PushMultipleTimes(int count, Register src); - - // This is a convenience method for pushing a single Handle. - inline void Push(Handle handle); - void Push(Smi* smi) { Push(Handle(smi, isolate())); } - - // Aliases of Push and Pop, required for V8 compatibility. - inline void push(Register src) { - Push(src); - } - inline void pop(Register dst) { - Pop(dst); - } - - // Poke 'src' onto the stack. The offset is in bytes. - // - // If the current stack pointer (according to StackPointer()) is csp, then - // csp must be aligned to 16 bytes. - void Poke(const CPURegister& src, const Operand& offset); - - // Peek at a value on the stack, and put it in 'dst'. The offset is in bytes. - // - // If the current stack pointer (according to StackPointer()) is csp, then - // csp must be aligned to 16 bytes. - void Peek(const CPURegister& dst, const Operand& offset); - - // Poke 'src1' and 'src2' onto the stack. The values written will be adjacent - // with 'src2' at a higher address than 'src1'. The offset is in bytes. - // - // If the current stack pointer (according to StackPointer()) is csp, then - // csp must be aligned to 16 bytes. - void PokePair(const CPURegister& src1, const CPURegister& src2, int offset); - - // Peek at two values on the stack, and put them in 'dst1' and 'dst2'. The - // values peeked will be adjacent, with the value in 'dst2' being from a - // higher address than 'dst1'. The offset is in bytes. - // - // If the current stack pointer (according to StackPointer()) is csp, then - // csp must be aligned to 16 bytes. - void PeekPair(const CPURegister& dst1, const CPURegister& dst2, int offset); - - // Claim or drop stack space without actually accessing memory. - // - // In debug mode, both of these will write invalid data into the claimed or - // dropped space. - // - // If the current stack pointer (according to StackPointer()) is csp, then it - // must be aligned to 16 bytes and the size claimed or dropped must be a - // multiple of 16 bytes. - // - // Note that unit_size must be specified in bytes. For variants which take a - // Register count, the unit size must be a power of two. - inline void Claim(uint64_t count, uint64_t unit_size = kXRegSizeInBytes); - inline void Claim(const Register& count, - uint64_t unit_size = kXRegSizeInBytes); - inline void Drop(uint64_t count, uint64_t unit_size = kXRegSizeInBytes); - inline void Drop(const Register& count, - uint64_t unit_size = kXRegSizeInBytes); - - // Variants of Claim and Drop, where the 'count' parameter is a SMI held in a - // register. - inline void ClaimBySMI(const Register& count_smi, - uint64_t unit_size = kXRegSizeInBytes); - inline void DropBySMI(const Register& count_smi, - uint64_t unit_size = kXRegSizeInBytes); - - // Compare a register with an operand, and branch to label depending on the - // condition. May corrupt the status flags. - inline void CompareAndBranch(const Register& lhs, - const Operand& rhs, - Condition cond, - Label* label); - - // Test the bits of register defined by bit_pattern, and branch if ANY of - // those bits are set. May corrupt the status flags. - inline void TestAndBranchIfAnySet(const Register& reg, - const uint64_t bit_pattern, - Label* label); - - // Test the bits of register defined by bit_pattern, and branch if ALL of - // those bits are clear (ie. not set.) May corrupt the status flags. - inline void TestAndBranchIfAllClear(const Register& reg, - const uint64_t bit_pattern, - Label* label); - - // Insert one or more instructions into the instruction stream that encode - // some caller-defined data. The instructions used will be executable with no - // side effects. - inline void InlineData(uint64_t data); - - // Insert an instrumentation enable marker into the instruction stream. - inline void EnableInstrumentation(); - - // Insert an instrumentation disable marker into the instruction stream. - inline void DisableInstrumentation(); - - // Insert an instrumentation event marker into the instruction stream. These - // will be picked up by the instrumentation system to annotate an instruction - // profile. The argument marker_name must be a printable two character string; - // it will be encoded in the event marker. - inline void AnnotateInstrumentation(const char* marker_name); - - // If emit_debug_code() is true, emit a run-time check to ensure that - // StackPointer() does not point below the system stack pointer. - // - // Whilst it is architecturally legal for StackPointer() to point below csp, - // it can be evidence of a potential bug because the ABI forbids accesses - // below csp. - // - // If emit_debug_code() is false, this emits no code. - // - // If StackPointer() is the system stack pointer, this emits no code. - void AssertStackConsistency(); - - // Preserve the callee-saved registers (as defined by AAPCS64). - // - // Higher-numbered registers are pushed before lower-numbered registers, and - // thus get higher addresses. - // Floating-point registers are pushed before general-purpose registers, and - // thus get higher addresses. - // - // Note that registers are not checked for invalid values. Use this method - // only if you know that the GC won't try to examine the values on the stack. - // - // This method must not be called unless the current stack pointer (as set by - // SetStackPointer) is the system stack pointer (csp), and is aligned to - // ActivationFrameAlignment(). - void PushCalleeSavedRegisters(); - - // Restore the callee-saved registers (as defined by AAPCS64). - // - // Higher-numbered registers are popped after lower-numbered registers, and - // thus come from higher addresses. - // Floating-point registers are popped after general-purpose registers, and - // thus come from higher addresses. - // - // This method must not be called unless the current stack pointer (as set by - // SetStackPointer) is the system stack pointer (csp), and is aligned to - // ActivationFrameAlignment(). - void PopCalleeSavedRegisters(); - - // Set the current stack pointer, but don't generate any code. - inline void SetStackPointer(const Register& stack_pointer) { - ASSERT(!AreAliased(stack_pointer, Tmp0(), Tmp1())); - sp_ = stack_pointer; - } - - // Return the current stack pointer, as set by SetStackPointer. - inline const Register& StackPointer() const { - return sp_; - } - - // Align csp for a frame, as per ActivationFrameAlignment, and make it the - // current stack pointer. - inline void AlignAndSetCSPForFrame() { - int sp_alignment = ActivationFrameAlignment(); - // AAPCS64 mandates at least 16-byte alignment. - ASSERT(sp_alignment >= 16); - ASSERT(IsPowerOf2(sp_alignment)); - Bic(csp, StackPointer(), sp_alignment - 1); - SetStackPointer(csp); - } - - // Push the system stack pointer (csp) down to allow the same to be done to - // the current stack pointer (according to StackPointer()). This must be - // called _before_ accessing the memory. - // - // This is necessary when pushing or otherwise adding things to the stack, to - // satisfy the AAPCS64 constraint that the memory below the system stack - // pointer is not accessed. - // - // This method asserts that StackPointer() is not csp, since the call does - // not make sense in that context. - // - // TODO(jbramley): Currently, this method can only accept values of 'space' - // that can be encoded in one instruction. Refer to the implementation for - // details. - inline void BumpSystemStackPointer(const Operand& space); - - // Helpers ------------------------------------------------------------------ - // Root register. - inline void InitializeRootRegister(); - - // Load an object from the root table. - void LoadRoot(Register destination, - Heap::RootListIndex index); - // Store an object to the root table. - void StoreRoot(Register source, - Heap::RootListIndex index); - - // Load both TrueValue and FalseValue roots. - void LoadTrueFalseRoots(Register true_root, Register false_root); - - void LoadHeapObject(Register dst, Handle object); - - void LoadObject(Register result, Handle object) { - AllowDeferredHandleDereference heap_object_check; - if (object->IsHeapObject()) { - LoadHeapObject(result, Handle::cast(object)); - } else { - ASSERT(object->IsSmi()); - Mov(result, Operand(object)); - } - } - - static int SafepointRegisterStackIndex(int reg_code); - - // This is required for compatibility with architecture independant code. - // Remove if not needed. - inline void Move(Register dst, Register src) { Mov(dst, src); } - - void LoadInstanceDescriptors(Register map, - Register descriptors); - void EnumLengthUntagged(Register dst, Register map); - void EnumLengthSmi(Register dst, Register map); - void NumberOfOwnDescriptors(Register dst, Register map); - - template - void DecodeField(Register reg) { - static const uint64_t shift = Field::kShift + kSmiShift; - static const uint64_t setbits = CountSetBits(Field::kMask, 32); - Ubfx(reg, reg, shift, setbits); - } - - // ---- SMI and Number Utilities ---- - - inline void SmiTag(Register dst, Register src); - inline void SmiTag(Register smi); - inline void SmiUntag(Register dst, Register src); - inline void SmiUntag(Register smi); - inline void SmiUntagToDouble(FPRegister dst, - Register src, - UntagMode mode = kNotSpeculativeUntag); - inline void SmiUntagToFloat(FPRegister dst, - Register src, - UntagMode mode = kNotSpeculativeUntag); - - // Compute the absolute value of 'smi' and leave the result in 'smi' - // register. If 'smi' is the most negative SMI, the absolute value cannot - // be represented as a SMI and a jump to 'slow' is done. - void SmiAbs(const Register& smi, Label* slow); - - inline void JumpIfSmi(Register value, - Label* smi_label, - Label* not_smi_label = NULL); - inline void JumpIfNotSmi(Register value, Label* not_smi_label); - inline void JumpIfBothSmi(Register value1, - Register value2, - Label* both_smi_label, - Label* not_smi_label = NULL); - inline void JumpIfEitherSmi(Register value1, - Register value2, - Label* either_smi_label, - Label* not_smi_label = NULL); - inline void JumpIfEitherNotSmi(Register value1, - Register value2, - Label* not_smi_label); - inline void JumpIfBothNotSmi(Register value1, - Register value2, - Label* not_smi_label); - - // Abort execution if argument is a smi, enabled via --debug-code. - void AssertNotSmi(Register object, BailoutReason reason = kOperandIsASmi); - void AssertSmi(Register object, BailoutReason reason = kOperandIsNotASmi); - - // Abort execution if argument is not a name, enabled via --debug-code. - void AssertName(Register object); - - // Abort execution if argument is not a string, enabled via --debug-code. - void AssertString(Register object); - - void JumpForHeapNumber(Register object, - Register heap_number_map, - Label* on_heap_number, - Label* on_not_heap_number = NULL); - void JumpIfHeapNumber(Register object, - Label* on_heap_number, - Register heap_number_map = NoReg); - void JumpIfNotHeapNumber(Register object, - Label* on_not_heap_number, - Register heap_number_map = NoReg); - - // Jump to label if the input double register contains -0.0. - void JumpIfMinusZero(DoubleRegister input, Label* on_negative_zero); - - // Generate code to do a lookup in the number string cache. If the number in - // the register object is found in the cache the generated code falls through - // with the result in the result register. The object and the result register - // can be the same. If the number is not found in the cache the code jumps to - // the label not_found with only the content of register object unchanged. - void LookupNumberStringCache(Register object, - Register result, - Register scratch1, - Register scratch2, - Register scratch3, - Label* not_found); - - // Saturate a signed 32-bit integer in input to an unsigned 8-bit integer in - // output. - void ClampInt32ToUint8(Register in_out); - void ClampInt32ToUint8(Register output, Register input); - - // Saturate a double in input to an unsigned 8-bit integer in output. - void ClampDoubleToUint8(Register output, - DoubleRegister input, - DoubleRegister dbl_scratch); - - // Try to convert a double to a signed 32-bit int. - // This succeeds if the result compares equal to the input, so inputs of -0.0 - // are converted to 0 and handled as a success. - void TryConvertDoubleToInt32(Register as_int, - FPRegister value, - FPRegister scratch_d, - Label* on_successful_conversion, - Label* on_failed_conversion = NULL) { - ASSERT(as_int.Is32Bits()); - TryConvertDoubleToInt(as_int, value, scratch_d, on_successful_conversion, - on_failed_conversion); - } - - // Try to convert a double to a signed 64-bit int. - // This succeeds if the result compares equal to the input, so inputs of -0.0 - // are converted to 0 and handled as a success. - void TryConvertDoubleToInt64(Register as_int, - FPRegister value, - FPRegister scratch_d, - Label* on_successful_conversion, - Label* on_failed_conversion = NULL) { - ASSERT(as_int.Is64Bits()); - TryConvertDoubleToInt(as_int, value, scratch_d, on_successful_conversion, - on_failed_conversion); - } - - // ---- Object Utilities ---- - - // Copy fields from 'src' to 'dst', where both are tagged objects. - // The 'temps' list is a list of X registers which can be used for scratch - // values. The temps list must include at least one register, and it must not - // contain Tmp0() or Tmp1(). - // - // Currently, CopyFields cannot make use of more than three registers from - // the 'temps' list. - // - // As with several MacroAssembler methods, Tmp0() and Tmp1() will be used. - void CopyFields(Register dst, Register src, CPURegList temps, unsigned count); - - // Copies a number of bytes from src to dst. All passed registers are - // clobbered. On exit src and dst will point to the place just after where the - // last byte was read or written and length will be zero. Hint may be used to - // determine which is the most efficient algorithm to use for copying. - void CopyBytes(Register dst, - Register src, - Register length, - Register scratch, - CopyHint hint = kCopyUnknown); - - // Initialize fields with filler values. Fields starting at start_offset not - // including end_offset are overwritten with the value in filler. At the end - // of the loop, start_offset takes the value of end_offset. - void InitializeFieldsWithFiller(Register start_offset, - Register end_offset, - Register filler); - - // ---- String Utilities ---- - - - // Jump to label if either object is not a sequential ASCII string. - // Optionally perform a smi check on the objects first. - void JumpIfEitherIsNotSequentialAsciiStrings( - Register first, - Register second, - Register scratch1, - Register scratch2, - Label* failure, - SmiCheckType smi_check = DO_SMI_CHECK); - - // Check if instance type is sequential ASCII string and jump to label if - // it is not. - void JumpIfInstanceTypeIsNotSequentialAscii(Register type, - Register scratch, - Label* failure); - - // Checks if both instance types are sequential ASCII strings and jumps to - // label if either is not. - void JumpIfEitherInstanceTypeIsNotSequentialAscii( - Register first_object_instance_type, - Register second_object_instance_type, - Register scratch1, - Register scratch2, - Label* failure); - - // Checks if both instance types are sequential ASCII strings and jumps to - // label if either is not. - void JumpIfBothInstanceTypesAreNotSequentialAscii( - Register first_object_instance_type, - Register second_object_instance_type, - Register scratch1, - Register scratch2, - Label* failure); - - void JumpIfNotUniqueName(Register type, Label* not_unique_name); - - // ---- Calling / Jumping helpers ---- - - // This is required for compatibility in architecture indepenedant code. - inline void jmp(Label* L) { B(L); } - - // Passes thrown value to the handler of top of the try handler chain. - // Register value must be x0. - void Throw(Register value, - Register scratch1, - Register scratch2, - Register scratch3, - Register scratch4); - - // Propagates an uncatchable exception to the top of the current JS stack's - // handler chain. Register value must be x0. - void ThrowUncatchable(Register value, - Register scratch1, - Register scratch2, - Register scratch3, - Register scratch4); - - // Throw a message string as an exception. - void Throw(BailoutReason reason); - - // Throw a message string as an exception if a condition is not true. - void ThrowIf(Condition cc, BailoutReason reason); - - // Throw a message string as an exception if the value is a smi. - void ThrowIfSmi(const Register& value, BailoutReason reason); - - void CallStub(CodeStub* stub, TypeFeedbackId ast_id = TypeFeedbackId::None()); - void TailCallStub(CodeStub* stub); - - void CallRuntime(const Runtime::Function* f, - int num_arguments, - SaveFPRegsMode save_doubles = kDontSaveFPRegs); - - void CallRuntime(Runtime::FunctionId id, - int num_arguments, - SaveFPRegsMode save_doubles = kDontSaveFPRegs) { - CallRuntime(Runtime::FunctionForId(id), num_arguments, save_doubles); - } - - // TODO(all): Why does this variant save FP regs unconditionally? - void CallRuntimeSaveDoubles(Runtime::FunctionId id) { - const Runtime::Function* function = Runtime::FunctionForId(id); - CallRuntime(function, function->nargs, kSaveFPRegs); - } - - void TailCallRuntime(Runtime::FunctionId fid, - int num_arguments, - int result_size); - - int ActivationFrameAlignment(); - - // Calls a C function. - // 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_reg_arguments); - void CallCFunction(ExternalReference function, - int num_reg_arguments, - int num_double_arguments); - void CallCFunction(Register function, - int num_reg_arguments, - int num_double_arguments); - - // Calls an API function. Allocates HandleScope, extracts returned value - // from handle and propagates exceptions. - // 'stack_space' is the space to be unwound on exit (includes the call JS - // arguments space and the additional space allocated for the fast call). - // 'spill_offset' is the offset from the stack pointer where - // CallApiFunctionAndReturn can spill registers. - void CallApiFunctionAndReturn(Register function_address, - ExternalReference thunk_ref, - int stack_space, - int spill_offset, - MemOperand return_value_operand, - MemOperand* context_restore_operand); - - // The number of register that CallApiFunctionAndReturn will need to save on - // the stack. The space for these registers need to be allocated in the - // ExitFrame before calling CallApiFunctionAndReturn. - static const int kCallApiFunctionSpillSpace = 4; - - // Jump to a runtime routine. - void JumpToExternalReference(const ExternalReference& builtin); - // Tail call of a runtime routine (jump). - // Like JumpToExternalReference, but also takes care of passing the number - // of parameters. - void TailCallExternalReference(const ExternalReference& ext, - int num_arguments, - int result_size); - void CallExternalReference(const ExternalReference& ext, - int num_arguments); - - - // Invoke specified builtin JavaScript function. Adds an entry to - // the unresolved list if the name does not resolve. - void InvokeBuiltin(Builtins::JavaScript id, - InvokeFlag flag, - const CallWrapper& call_wrapper = NullCallWrapper()); - - // Store the code object for the given builtin in the target register and - // setup the function in x1. - // TODO(all): Can we use another register than x1? - void GetBuiltinEntry(Register target, Builtins::JavaScript id); - - // Store the function for the given builtin in the target register. - void GetBuiltinFunction(Register target, Builtins::JavaScript id); - - void Jump(Register target); - void Jump(Address target, RelocInfo::Mode rmode); - void Jump(Handle code, RelocInfo::Mode rmode); - void Jump(intptr_t target, RelocInfo::Mode rmode); - - void Call(Register target); - void Call(Label* target); - void Call(Address target, RelocInfo::Mode rmode); - void Call(Handle code, - RelocInfo::Mode rmode = RelocInfo::CODE_TARGET, - TypeFeedbackId ast_id = TypeFeedbackId::None()); - - // For every Call variant, there is a matching CallSize function that returns - // the size (in bytes) of the call sequence. - static int CallSize(Register target); - static int CallSize(Label* target); - static int CallSize(Address target, RelocInfo::Mode rmode); - static int CallSize(Handle code, - RelocInfo::Mode rmode = RelocInfo::CODE_TARGET, - TypeFeedbackId ast_id = TypeFeedbackId::None()); - - // Registers used through the invocation chain are hard-coded. - // We force passing the parameters to ensure the contracts are correctly - // honoured by the caller. - // 'function' must be x1. - // 'actual' must use an immediate or x0. - // 'expected' must use an immediate or x2. - // 'call_kind' must be x5. - void InvokePrologue(const ParameterCount& expected, - const ParameterCount& actual, - Handle code_constant, - Register code_reg, - Label* done, - InvokeFlag flag, - bool* definitely_mismatches, - const CallWrapper& call_wrapper); - void InvokeCode(Register code, - const ParameterCount& expected, - const ParameterCount& actual, - InvokeFlag flag, - const CallWrapper& call_wrapper); - // Invoke the JavaScript function in the given register. - // Changes the current context to the context in the function before invoking. - void InvokeFunction(Register function, - 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); - - - // ---- Floating point helpers ---- - - - // Performs a truncating conversion of a floating point number as used by - // the JS bitwise operations. See ECMA-262 9.5: ToInt32. Goes to 'done' if it - // succeeds, otherwise falls through if result is saturated. On return - // 'result' either holds answer, or is clobbered on fall through. - // - // Only public for the test code in test-code-stubs-a64.cc. - void TryInlineTruncateDoubleToI(Register result, - DoubleRegister input, - Label* done); - - // Performs a truncating conversion of a floating point number as used by - // the JS bitwise operations. See ECMA-262 9.5: ToInt32. - // Exits with 'result' holding the answer. - void TruncateDoubleToI(Register result, DoubleRegister double_input); - - // Performs a truncating conversion of a heap number as used by - // the JS bitwise operations. See ECMA-262 9.5: ToInt32. 'result' and 'input' - // must be different registers. Exits with 'result' holding the answer. - void TruncateHeapNumberToI(Register result, Register object); - - // Converts the smi or heap number in object to an int32 using the rules - // for ToInt32 as described in ECMAScript 9.5.: the value is truncated - // and brought into the range -2^31 .. +2^31 - 1. 'result' and 'input' must be - // different registers. - void TruncateNumberToI(Register object, - Register result, - Register heap_number_map, - Label* not_int32); - - // ---- Code generation helpers ---- - - void set_generating_stub(bool value) { generating_stub_ = value; } - bool generating_stub() const { return generating_stub_; } -#if DEBUG - void set_allow_macro_instructions(bool value) { - allow_macro_instructions_ = value; - } - bool allow_macro_instructions() const { return allow_macro_instructions_; } -#endif - bool use_real_aborts() const { return use_real_aborts_; } - void set_has_frame(bool value) { has_frame_ = value; } - bool has_frame() const { return has_frame_; } - bool AllowThisStubCall(CodeStub* stub); - - class NoUseRealAbortsScope { - public: - explicit NoUseRealAbortsScope(MacroAssembler* masm) : - saved_(masm->use_real_aborts_), masm_(masm) { - masm_->use_real_aborts_ = false; - } - ~NoUseRealAbortsScope() { - masm_->use_real_aborts_ = saved_; - } - private: - bool saved_; - MacroAssembler* masm_; - }; - -#ifdef ENABLE_DEBUGGER_SUPPORT - // --------------------------------------------------------------------------- - // Debugger Support - - void DebugBreak(); -#endif - // --------------------------------------------------------------------------- - // Exception handling - - // Push a new try handler and link into try handler chain. - void PushTryHandler(StackHandler::Kind kind, int handler_index); - - // Unlink the stack handler on top of the stack from the try handler chain. - // Must preserve the result register. - void PopTryHandler(); - - - // --------------------------------------------------------------------------- - // Allocation support - - // Allocate an object in new space or old pointer space. The object_size is - // specified either in bytes or in words if the allocation flag SIZE_IN_WORDS - // is passed. The allocated object is returned in result. - // - // If the new space is exhausted control continues at the gc_required label. - // In this case, the result and scratch registers may still be clobbered. - // If flags includes TAG_OBJECT, the result is tagged as as a heap object. - void Allocate(Register object_size, - Register result, - Register scratch1, - Register scratch2, - Label* gc_required, - AllocationFlags flags); - - void Allocate(int object_size, - Register result, - Register scratch1, - Register scratch2, - Label* gc_required, - AllocationFlags flags); - - // Undo allocation in new space. The object passed and objects allocated after - // it will no longer be allocated. The caller must make sure that no pointers - // are left to the object(s) no longer allocated as they would be invalid when - // allocation is undone. - void UndoAllocationInNewSpace(Register object, Register scratch); - - void AllocateTwoByteString(Register result, - Register length, - Register scratch1, - Register scratch2, - Register scratch3, - Label* gc_required); - void AllocateAsciiString(Register result, - Register length, - Register scratch1, - Register scratch2, - Register scratch3, - Label* gc_required); - void AllocateTwoByteConsString(Register result, - Register length, - Register scratch1, - Register scratch2, - Label* gc_required); - void AllocateAsciiConsString(Register result, - Register length, - Register scratch1, - Register scratch2, - Label* gc_required); - void AllocateTwoByteSlicedString(Register result, - Register length, - Register scratch1, - Register scratch2, - Label* gc_required); - void AllocateAsciiSlicedString(Register result, - Register length, - Register scratch1, - Register scratch2, - Label* gc_required); - - // Allocates a heap number or jumps to the gc_required label if the young - // space is full and a scavenge is needed. - // All registers are clobbered. - // If no heap_number_map register is provided, the function will take care of - // loading it. - void AllocateHeapNumber(Register result, - Label* gc_required, - Register scratch1, - Register scratch2, - Register heap_number_map = NoReg); - void AllocateHeapNumberWithValue(Register result, - DoubleRegister value, - Label* gc_required, - Register scratch1, - Register scratch2, - Register heap_number_map = NoReg); - - // --------------------------------------------------------------------------- - // Support functions. - - // Try to get function prototype of a function and puts the value in the - // result register. Checks that the function really is a function and jumps - // to the miss label if the fast checks fail. The function register will be - // untouched; the other registers may be clobbered. - enum BoundFunctionAction { - kMissOnBoundFunction, - kDontMissOnBoundFunction - }; - - void TryGetFunctionPrototype(Register function, - Register result, - Register scratch, - Label* miss, - BoundFunctionAction action = - kDontMissOnBoundFunction); - - // Compare object type for heap object. heap_object contains a non-Smi - // whose object type should be compared with the given type. This both - // sets the flags and leaves the object type in the type_reg register. - // It leaves the map in the map register (unless the type_reg and map register - // are the same register). It leaves the heap object in the heap_object - // register unless the heap_object register is the same register as one of the - // other registers. - void CompareObjectType(Register heap_object, - Register map, - Register type_reg, - InstanceType type); - - - // Compare object type for heap object, and branch if equal (or not.) - // heap_object contains a non-Smi whose object type should be compared with - // the given type. This both sets the flags and leaves the object type in - // the type_reg register. It leaves the map in the map register (unless the - // type_reg and map register are the same register). It leaves the heap - // object in the heap_object register unless the heap_object register is the - // same register as one of the other registers. - void JumpIfObjectType(Register object, - Register map, - Register type_reg, - InstanceType type, - Label* if_cond_pass, - Condition cond = eq); - - void JumpIfNotObjectType(Register object, - Register map, - Register type_reg, - InstanceType type, - Label* if_not_object); - - // Compare instance type in a map. map contains a valid map object whose - // object type should be compared with the given type. This both - // sets the flags and leaves the object type in the type_reg register. - void CompareInstanceType(Register map, - Register type_reg, - InstanceType type); - - // Compare an object's map with the specified map and its transitioned - // elements maps if mode is ALLOW_ELEMENT_TRANSITION_MAPS. Condition flags are - // set with result of map compare. If multiple map compares are required, the - // compare sequences branches to early_success. - void CompareMap(Register obj, - Register scratch, - Handle map, - Label* early_success = NULL); - - // As above, but the map of the object is already loaded into the register - // which is preserved by the code generated. - void CompareMap(Register obj_map, - Handle map, - Label* early_success = NULL); - - // 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, - Register scratch, - Handle map, - Label* fail, - SmiCheckType smi_check_type); - - - void CheckMap(Register obj, - Register scratch, - Heap::RootListIndex index, - Label* fail, - SmiCheckType smi_check_type); - - // As above, but the map of the object is already loaded into obj_map, and is - // preserved. - void CheckMap(Register obj_map, - Handle map, - Label* fail, - SmiCheckType smi_check_type); - - // Check if the map of an object is equal to a specified map and branch to a - // specified target if equal. Skip the smi check if not required (object is - // known to be a heap object) - void DispatchMap(Register obj, - Register scratch, - Handle map, - Handle success, - SmiCheckType smi_check_type); - - // Test the bitfield of the heap object map with mask and set the condition - // flags. The object register is preserved. - void TestMapBitfield(Register object, uint64_t mask); - - // Load the elements kind field of an object, and return it in the result - // register. - void LoadElementsKind(Register result, Register object); - - // Compare the object in a register to a value from the root list. - // Uses the Tmp0() register as scratch. - void CompareRoot(const Register& obj, Heap::RootListIndex index); - - // Compare the object in a register to a value and jump if they are equal. - void JumpIfRoot(const Register& obj, - Heap::RootListIndex index, - Label* if_equal); - - // Compare the object in a register to a value and jump if they are not equal. - void JumpIfNotRoot(const Register& obj, - Heap::RootListIndex index, - Label* if_not_equal); - - // Load and check the instance type of an object for being a unique name. - // Loads the type into the second argument register. - // The object and type arguments can be the same register; in that case it - // will be overwritten with the type. - // Fall-through if the object was a string and jump on fail otherwise. - inline void IsObjectNameType(Register object, Register type, Label* fail); - - inline void IsObjectJSObjectType(Register heap_object, - Register map, - Register scratch, - Label* fail); - - // Check the instance type in the given map to see if it corresponds to a - // JS object type. Jump to the fail label if this is not the case and fall - // through otherwise. However if fail label is NULL, no branch will be - // performed and the flag will be updated. You can test the flag for "le" - // condition to test if it is a valid JS object type. - inline void IsInstanceJSObjectType(Register map, - Register scratch, - Label* fail); - - // Load and check the instance type of an object for being a string. - // Loads the type into the second argument register. - // The object and type arguments can be the same register; in that case it - // will be overwritten with the type. - // Jumps to not_string or string appropriate. If the appropriate label is - // NULL, fall through. - inline void IsObjectJSStringType(Register object, Register type, - Label* not_string, Label* string = NULL); - - // Compare the contents of a register with an operand, and branch to true, - // false or fall through, depending on condition. - void CompareAndSplit(const Register& lhs, - const Operand& rhs, - Condition cond, - Label* if_true, - Label* if_false, - Label* fall_through); - - // Test the bits of register defined by bit_pattern, and branch to - // if_any_set, if_all_clear or fall_through accordingly. - void TestAndSplit(const Register& reg, - uint64_t bit_pattern, - Label* if_all_clear, - Label* if_any_set, - Label* fall_through); - - // Check if a map for a JSObject indicates that the object has fast elements. - // Jump to the specified label if it does not. - void CheckFastElements(Register map, - Register scratch, - Label* fail); - - // Check if a map for a JSObject indicates that the object can have both smi - // and HeapObject elements. Jump to the specified label if it does not. - void CheckFastObjectElements(Register map, - Register scratch, - Label* fail); - - // Check if a map for a JSObject indicates that the object has fast smi only - // elements. Jump to the specified label if it does not. - void CheckFastSmiElements(Register map, Register scratch, Label* fail); - - // Check to see if number can be stored as a double in FastDoubleElements. - // If it can, store it at the index specified by key_reg in the array, - // otherwise jump to fail. - void StoreNumberToDoubleElements(Register value_reg, - Register key_reg, - Register elements_reg, - Register scratch1, - FPRegister fpscratch1, - FPRegister fpscratch2, - Label* fail, - int elements_offset = 0); - - // Picks out an array index from the hash field. - // Register use: - // hash - holds the index's hash. Clobbered. - // index - holds the overwritten index on exit. - void IndexFromHash(Register hash, Register index); - - // --------------------------------------------------------------------------- - // Inline caching support. - - void EmitSeqStringSetCharCheck(Register string, - Register index, - SeqStringSetCharCheckIndexType index_type, - Register scratch, - uint32_t encoding_mask); - - // Generate code for checking access rights - used for security checks - // on access to global objects across environments. The holder register - // is left untouched, whereas both scratch registers are clobbered. - void CheckAccessGlobalProxy(Register holder_reg, - Register scratch, - Label* miss); - - // Hash the interger value in 'key' register. - // It uses the same algorithm as ComputeIntegerHash in utils.h. - void GetNumberHash(Register key, Register scratch); - - // Load value from the dictionary. - // - // elements - holds the slow-case elements of the receiver on entry. - // Unchanged unless 'result' is the same register. - // - // key - holds the smi key on entry. - // Unchanged unless 'result' is the same register. - // - // result - holds the result on exit if the load succeeded. - // Allowed to be the same as 'key' or 'result'. - // Unchanged on bailout so 'key' or 'result' can be used - // in further computation. - void LoadFromNumberDictionary(Label* miss, - Register elements, - Register key, - Register result, - Register scratch0, - Register scratch1, - Register scratch2, - Register scratch3); - - // --------------------------------------------------------------------------- - // Frames. - - // Activation support. - // Note that Tmp0() and Tmp1() are used as a scratch registers. This is safe - // because these methods are not used in Crankshaft. - void EnterFrame(StackFrame::Type type); - void LeaveFrame(StackFrame::Type type); - - // Returns map with validated enum cache in object register. - void CheckEnumCache(Register object, - Register null_value, - Register scratch0, - Register scratch1, - Register scratch2, - Register scratch3, - 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 should point to the array object. - // If allocation info is present, the Z flag is set (so that the eq - // condition will pass). - void TestJSArrayForAllocationMemento(Register receiver, - Register scratch1, - Register scratch2, - Label* no_memento_found); - - void JumpIfJSArrayHasAllocationMemento(Register receiver, - Register scratch1, - Register scratch2, - Label* memento_found) { - Label no_memento_found; - TestJSArrayForAllocationMemento(receiver, scratch1, scratch2, - &no_memento_found); - B(eq, memento_found); - Bind(&no_memento_found); - } - - // The stack pointer has to switch between csp and jssp when setting up and - // destroying the exit frame. Hence preserving/restoring the registers is - // slightly more complicated than simple push/pop operations. - void ExitFramePreserveFPRegs(); - void ExitFrameRestoreFPRegs(); - - // Generates function and stub prologue code. - void Prologue(PrologueFrameMode frame_mode); - - // Enter exit frame. Exit frames are used when calling C code from generated - // (JavaScript) code. - // - // The stack pointer must be jssp on entry, and will be set to csp by this - // function. The frame pointer is also configured, but the only other - // registers modified by this function are the provided scratch register, and - // jssp. - // - // The 'extra_space' argument can be used to allocate some space in the exit - // frame that will be ignored by the GC. This space will be reserved in the - // bottom of the frame immediately above the return address slot. - // - // Set up a stack frame and registers as follows: - // fp[8]: CallerPC (lr) - // fp -> fp[0]: CallerFP (old fp) - // fp[-8]: SPOffset (new csp) - // fp[-16]: CodeObject() - // fp[-16 - fp-size]: Saved doubles, if saved_doubles is true. - // csp[8]: Memory reserved for the caller if extra_space != 0. - // Alignment padding, if necessary. - // csp -> csp[0]: Space reserved for the return address. - // - // This function also stores the new frame information in the top frame, so - // that the new frame becomes the current frame. - void EnterExitFrame(bool save_doubles, - const Register& scratch, - int extra_space = 0); - - // Leave the current exit frame, after a C function has returned to generated - // (JavaScript) code. - // - // This effectively unwinds the operation of EnterExitFrame: - // * Preserved doubles are restored (if restore_doubles is true). - // * The frame information is removed from the top frame. - // * The exit frame is dropped. - // * The stack pointer is reset to jssp. - // - // The stack pointer must be csp on entry. - void LeaveExitFrame(bool save_doubles, - const Register& scratch, - bool restore_context); - - void LoadContext(Register dst, int context_chain_length); - - // --------------------------------------------------------------------------- - // StatsCounter support - - void SetCounter(StatsCounter* counter, int value, Register scratch1, - Register scratch2); - void IncrementCounter(StatsCounter* counter, int value, Register scratch1, - Register scratch2); - void DecrementCounter(StatsCounter* counter, int value, Register scratch1, - Register scratch2); - - // --------------------------------------------------------------------------- - // Garbage collector support (GC). - - 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); - - // Push and pop the registers that can hold pointers, as defined by the - // RegList constant kSafepointSavedRegisters. - void PushSafepointRegisters(); - void PopSafepointRegisters(); - - void PushSafepointFPRegisters(); - void PopSafepointFPRegisters(); - - // Store value in register src in the safepoint stack slot for register dst. - void StoreToSafepointRegisterSlot(Register src, Register dst) { - Poke(src, SafepointRegisterStackIndex(dst.code()) * kPointerSize); - } - - // Load the value of the src register from its safepoint stack slot - // into register dst. - void LoadFromSafepointRegisterSlot(Register dst, Register src) { - Peek(src, SafepointRegisterStackIndex(dst.code()) * kPointerSize); - } - - void CheckPageFlagSet(const Register& object, - const Register& scratch, - int mask, - Label* if_any_set); - - void CheckPageFlagClear(const Register& object, - const Register& scratch, - int mask, - Label* if_all_clear); - - void CheckMapDeprecated(Handle map, - Register scratch, - Label* if_deprecated); - - // Check if object is in new space and jump accordingly. - // Register 'object' is preserved. - void JumpIfNotInNewSpace(Register object, - Label* branch) { - InNewSpace(object, ne, branch); - } - - void JumpIfInNewSpace(Register object, - Label* branch) { - InNewSpace(object, eq, branch); - } - - // 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, - LinkRegisterStatus lr_status, - SaveFPRegsMode save_fp, - RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET, - SmiCheck smi_check = INLINE_SMI_CHECK); - - // As above, but the offset has the tag presubtracted. For use with - // MemOperand(reg, off). - inline void RecordWriteContextSlot( - Register context, - int offset, - Register value, - Register scratch, - LinkRegisterStatus lr_status, - SaveFPRegsMode save_fp, - RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET, - SmiCheck smi_check = INLINE_SMI_CHECK) { - RecordWriteField(context, - offset + kHeapObjectTag, - value, - scratch, - lr_status, - save_fp, - remembered_set_action, - smi_check); - } - - // For a given |object| notify the garbage collector that the slot |address| - // has been written. |value| is the object being stored. The value and - // address registers are clobbered by the operation. - void RecordWrite( - Register object, - Register address, - Register value, - LinkRegisterStatus lr_status, - SaveFPRegsMode save_fp, - RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET, - SmiCheck smi_check = INLINE_SMI_CHECK); - - // Checks the color of an object. If the object is already grey or black - // then we just fall through, since it is already live. If it is white and - // we can determine that it doesn't need to be scanned, then we just mark it - // black and fall through. For the rest we jump to the label so the - // incremental marker can fix its assumptions. - void EnsureNotWhite(Register object, - Register scratch1, - Register scratch2, - Register scratch3, - Register scratch4, - Label* object_is_white_and_not_data); - - // Detects conservatively whether an object is data-only, i.e. it does need to - // be scanned by the garbage collector. - void JumpIfDataObject(Register value, - Register scratch, - Label* not_data_object); - - // Helper for finding the mark bits for an address. - // Note that the behaviour slightly differs from other architectures. - // On exit: - // - addr_reg is unchanged. - // - The bitmap register points at the word with the mark bits. - // - The shift register contains the index of the first color bit for this - // object in the bitmap. - inline void GetMarkBits(Register addr_reg, - Register bitmap_reg, - Register shift_reg); - - // Check if an object has a given incremental marking color. - void HasColor(Register object, - Register scratch0, - Register scratch1, - Label* has_color, - int first_bit, - int second_bit); - - void JumpIfBlack(Register object, - Register scratch0, - Register scratch1, - Label* on_black); - - - // Get the location of a relocated constant (its address in the constant pool) - // from its load site. - void GetRelocatedValueLocation(Register ldr_location, - Register result); - - - // --------------------------------------------------------------------------- - // Debugging. - - // Calls Abort(msg) if the condition cond is not satisfied. - // Use --debug_code to enable. - void Assert(Condition cond, BailoutReason reason); - void AssertRegisterIsClear(Register reg, BailoutReason reason); - void AssertRegisterIsRoot( - Register reg, - Heap::RootListIndex index, - BailoutReason reason = kRegisterDidNotMatchExpectedRoot); - void AssertFastElements(Register elements); - - // Abort if the specified register contains the invalid color bit pattern. - // The pattern must be in bits [1:0] of 'reg' register. - // - // If emit_debug_code() is false, this emits no code. - void AssertHasValidColor(const Register& reg); - - // Abort if 'object' register doesn't point to a string object. - // - // If emit_debug_code() is false, this emits no code. - void AssertIsString(const Register& object); - - // Like Assert(), but always enabled. - void Check(Condition cond, BailoutReason reason); - void CheckRegisterIsClear(Register reg, BailoutReason reason); - - // Print a message to stderr and abort execution. - void Abort(BailoutReason reason); - - // Conditionally load the cached Array transitioned map of type - // transitioned_kind from the native context if the map in register - // map_in_out is the cached Array map in the native context of - // expected_kind. - void LoadTransitionedArrayMapConditional( - ElementsKind expected_kind, - ElementsKind transitioned_kind, - Register map_in_out, - Register scratch, - Label* no_map_match); - - // Load the initial map for new Arrays from a JSFunction. - void LoadInitialArrayMap(Register function_in, - Register scratch, - Register map_out, - ArrayHasHoles holes); - - void LoadArrayFunction(Register function); - void LoadGlobalFunction(int index, Register function); - - // Load the initial map from the global function. The registers function and - // map can be the same, function is then overwritten. - void LoadGlobalFunctionInitialMap(Register function, - Register map, - Register scratch); - - // -------------------------------------------------------------------------- - // Set the registers used internally by the MacroAssembler as scratch - // registers. These registers are used to implement behaviours which are not - // directly supported by A64, and where an intermediate result is required. - // - // Both tmp0 and tmp1 may be set to any X register except for xzr, sp, - // and StackPointer(). Also, they must not be the same register (though they - // may both be NoReg). - // - // It is valid to set either or both of these registers to NoReg if you don't - // want the MacroAssembler to use any scratch registers. In a debug build, the - // Assembler will assert that any registers it uses are valid. Be aware that - // this check is not present in release builds. If this is a problem, use the - // Assembler directly. - void SetScratchRegisters(const Register& tmp0, const Register& tmp1) { - // V8 assumes the macro assembler uses ip0 and ip1 as temp registers. - ASSERT(tmp0.IsNone() || tmp0.Is(ip0)); - ASSERT(tmp1.IsNone() || tmp1.Is(ip1)); - - ASSERT(!AreAliased(xzr, csp, tmp0, tmp1)); - ASSERT(!AreAliased(StackPointer(), tmp0, tmp1)); - tmp0_ = tmp0; - tmp1_ = tmp1; - } - - const Register& Tmp0() const { - return tmp0_; - } - - const Register& Tmp1() const { - return tmp1_; - } - - const Register WTmp0() const { - return Register::Create(tmp0_.code(), kWRegSize); - } - - const Register WTmp1() const { - return Register::Create(tmp1_.code(), kWRegSize); - } - - void SetFPScratchRegister(const FPRegister& fptmp0) { - fptmp0_ = fptmp0; - } - - const FPRegister& FPTmp0() const { - return fptmp0_; - } - - const Register AppropriateTempFor( - const Register& target, - const CPURegister& forbidden = NoCPUReg) const { - Register candidate = forbidden.Is(Tmp0()) ? Tmp1() : Tmp0(); - ASSERT(!candidate.Is(target)); - return Register::Create(candidate.code(), target.SizeInBits()); - } - - const FPRegister AppropriateTempFor( - const FPRegister& target, - const CPURegister& forbidden = NoCPUReg) const { - USE(forbidden); - FPRegister candidate = FPTmp0(); - ASSERT(!candidate.Is(forbidden)); - ASSERT(!candidate.Is(target)); - return FPRegister::Create(candidate.code(), target.SizeInBits()); - } - - // Like printf, but print at run-time from generated code. - // - // The caller must ensure that arguments for floating-point placeholders - // (such as %e, %f or %g) are FPRegisters, and that arguments for integer - // placeholders are Registers. - // - // A maximum of four arguments may be given to any single Printf call. The - // arguments must be of the same type, but they do not need to have the same - // size. - // - // The following registers cannot be printed: - // Tmp0(), Tmp1(), StackPointer(), csp. - // - // This function automatically preserves caller-saved registers so that - // calling code can use Printf at any point without having to worry about - // corruption. The preservation mechanism generates a lot of code. If this is - // a problem, preserve the important registers manually and then call - // PrintfNoPreserve. Callee-saved registers are not used by Printf, and are - // implicitly preserved. - // - // Unlike many MacroAssembler functions, x8 and x9 are guaranteed to be - // preserved, and can be printed. This allows Printf to be used during debug - // code. - // - // This function assumes (and asserts) that the current stack pointer is - // callee-saved, not caller-saved. This is most likely the case anyway, as a - // caller-saved stack pointer doesn't make a lot of sense. - void Printf(const char * format, - const CPURegister& arg0 = NoCPUReg, - const CPURegister& arg1 = NoCPUReg, - const CPURegister& arg2 = NoCPUReg, - const CPURegister& arg3 = NoCPUReg); - - // Like Printf, but don't preserve any caller-saved registers, not even 'lr'. - // - // The return code from the system printf call will be returned in x0. - void PrintfNoPreserve(const char * format, - const CPURegister& arg0 = NoCPUReg, - const CPURegister& arg1 = NoCPUReg, - const CPURegister& arg2 = NoCPUReg, - const CPURegister& arg3 = NoCPUReg); - - // Code ageing support functions. - - // Code ageing on A64 works similarly to on ARM. When V8 wants to mark a - // function as old, it replaces some of the function prologue (generated by - // FullCodeGenerator::Generate) with a call to a special stub (ultimately - // generated by GenerateMakeCodeYoungAgainCommon). The stub restores the - // function prologue to its initial young state (indicating that it has been - // recently run) and continues. A young function is therefore one which has a - // normal frame setup sequence, and an old function has a code age sequence - // which calls a code ageing stub. - - // Set up a basic stack frame for young code (or code exempt from ageing) with - // type FUNCTION. It may be patched later for code ageing support. This is - // done by to Code::PatchPlatformCodeAge and EmitCodeAgeSequence. - // - // This function takes an Assembler so it can be called from either a - // MacroAssembler or a PatchingAssembler context. - static void EmitFrameSetupForCodeAgePatching(Assembler* assm); - - // Call EmitFrameSetupForCodeAgePatching from a MacroAssembler context. - void EmitFrameSetupForCodeAgePatching(); - - // Emit a code age sequence that calls the relevant code age stub. The code - // generated by this sequence is expected to replace the code generated by - // EmitFrameSetupForCodeAgePatching, and represents an old function. - // - // If stub is NULL, this function generates the code age sequence but omits - // the stub address that is normally embedded in the instruction stream. This - // can be used by debug code to verify code age sequences. - static void EmitCodeAgeSequence(Assembler* assm, Code* stub); - - // Call EmitCodeAgeSequence from a MacroAssembler context. - void EmitCodeAgeSequence(Code* stub); - - // Return true if the sequence is a young sequence geneated by - // EmitFrameSetupForCodeAgePatching. Otherwise, this method asserts that the - // sequence is a code age sequence (emitted by EmitCodeAgeSequence). - static bool IsYoungSequence(byte* sequence); - -#ifdef DEBUG - // Return true if the sequence is a code age sequence generated by - // EmitCodeAgeSequence. - static bool IsCodeAgeSequence(byte* sequence); -#endif - - // Jumps to found label if a prototype map has dictionary elements. - void JumpIfDictionaryInPrototypeChain(Register object, Register scratch0, - Register scratch1, Label* found); - - private: - // Helpers for CopyFields. - // These each implement CopyFields in a different way. - void CopyFieldsLoopPairsHelper(Register dst, Register src, unsigned count, - Register scratch1, Register scratch2, - Register scratch3); - void CopyFieldsUnrolledPairsHelper(Register dst, Register src, unsigned count, - Register scratch1, Register scratch2); - void CopyFieldsUnrolledHelper(Register dst, Register src, unsigned count, - Register scratch1); - - // The actual Push and Pop implementations. These don't generate any code - // other than that required for the push or pop. This allows - // (Push|Pop)CPURegList to bundle together run-time assertions for a large - // block of registers. - // - // Note that size is per register, and is specified in bytes. - void PushHelper(int count, int size, - const CPURegister& src0, const CPURegister& src1, - const CPURegister& src2, const CPURegister& src3); - void PopHelper(int count, int size, - const CPURegister& dst0, const CPURegister& dst1, - const CPURegister& dst2, const CPURegister& dst3); - - // Perform necessary maintenance operations before a push or pop. - // - // Note that size is per register, and is specified in bytes. - void PrepareForPush(int count, int size); - void PrepareForPop(int count, int size); - - // Call Printf. On a native build, a simple call will be generated, but if the - // simulator is being used then a suitable pseudo-instruction is used. The - // arguments and stack (csp) must be prepared by the caller as for a normal - // AAPCS64 call to 'printf'. - // - // The 'type' argument specifies the type of the optional arguments. - void CallPrintf(CPURegister::RegisterType type = CPURegister::kNoRegister); - - // Helper for throwing exceptions. Compute a handler address and jump to - // it. See the implementation for register usage. - void JumpToHandlerEntry(Register exception, - Register object, - Register state, - Register scratch1, - Register scratch2); - - // Helper for implementing JumpIfNotInNewSpace and JumpIfInNewSpace. - void InNewSpace(Register object, - Condition cond, // eq for new space, ne otherwise. - Label* branch); - - // Try to convert a double to an int so that integer fast-paths may be - // used. Not every valid integer value is guaranteed to be caught. - // It supports both 32-bit and 64-bit integers depending whether 'as_int' - // is a W or X register. - // - // This does not distinguish between +0 and -0, so if this distinction is - // important it must be checked separately. - void TryConvertDoubleToInt(Register as_int, - FPRegister value, - FPRegister scratch_d, - Label* on_successful_conversion, - Label* on_failed_conversion = NULL); - - bool generating_stub_; -#if DEBUG - // Tell whether any of the macro instruction can be used. When false the - // MacroAssembler will assert if a method which can emit a variable number - // of instructions is called. - bool allow_macro_instructions_; -#endif - bool has_frame_; - - // The Abort method should call a V8 runtime function, but the CallRuntime - // mechanism depends on CEntryStub. If use_real_aborts is false, Abort will - // use a simpler abort mechanism that doesn't depend on CEntryStub. - // - // The purpose of this is to allow Aborts to be compiled whilst CEntryStub is - // being generated. - bool use_real_aborts_; - - // This handle will be patched with the code object on installation. - Handle code_object_; - - // The register to use as a stack pointer for stack operations. - Register sp_; - - // Scratch registers used internally by the MacroAssembler. - Register tmp0_; - Register tmp1_; - FPRegister fptmp0_; - - void InitializeNewString(Register string, - Register length, - Heap::RootListIndex map_index, - Register scratch1, - Register scratch2); - - public: - // Far branches resolving. - // - // The various classes of branch instructions with immediate offsets have - // different ranges. While the Assembler will fail to assemble a branch - // exceeding its range, the MacroAssembler offers a mechanism to resolve - // branches to too distant targets, either by tweaking the generated code to - // use branch instructions with wider ranges or generating veneers. - // - // Currently branches to distant targets are resolved using unconditional - // branch isntructions with a range of +-128MB. If that becomes too little - // (!), the mechanism can be extended to generate special veneers for really - // far targets. - - // Returns true if we should emit a veneer as soon as possible for a branch - // which can at most reach to specified pc. - bool ShouldEmitVeneer(int max_reachable_pc, - int margin = kVeneerDistanceMargin); - - // The maximum code size generated for a veneer. Currently one branch - // instruction. This is for code size checking purposes, and can be extended - // in the future for example if we decide to add nops between the veneers. - static const int kMaxVeneerCodeSize = 1 * kInstructionSize; - - // Emits veneers for branches that are approaching their maximum range. - // If need_protection is true, the veneers are protected by a branch jumping - // over the code. - void EmitVeneers(bool need_protection); - void EmitVeneersGuard(); - // Checks wether veneers need to be emitted at this point. - void CheckVeneers(bool need_protection); - - // Helps resolve branching to labels potentially out of range. - // If the label is not bound, it registers the information necessary to later - // be able to emit a veneer for this branch if necessary. - // If the label is bound, it returns true if the label (or the previous link - // in the label chain) is out of range. In that case the caller is responsible - // for generating appropriate code. - // Otherwise it returns false. - // This function also checks wether veneers need to be emitted. - bool NeedExtraInstructionsOrRegisterBranch(Label *label, - ImmBranchType branch_type); - - private: - // We generate a veneer for a branch if we reach within this distance of the - // limit of the range. - static const int kVeneerDistanceMargin = 2 * KB; - int unresolved_branches_first_limit() const { - ASSERT(!unresolved_branches_.empty()); - return unresolved_branches_.begin()->first; - } -}; - - -// Use this scope when you need a one-to-one mapping bewteen methods and -// instructions. This scope prevents the MacroAssembler from being called and -// literal pools from being emitted. It also asserts the number of instructions -// emitted is what you specified when creating the scope. -class InstructionAccurateScope BASE_EMBEDDED { - public: - InstructionAccurateScope(MacroAssembler* masm, size_t count = 0) - : masm_(masm), size_(count * kInstructionSize) { - masm_->StartBlockConstPool(); -#ifdef DEBUG - if (count != 0) { - masm_->bind(&start_); - } - previous_allow_macro_instructions_ = masm_->allow_macro_instructions(); - masm_->set_allow_macro_instructions(false); -#endif - } - - ~InstructionAccurateScope() { - masm_->EndBlockConstPool(); -#ifdef DEBUG - if (start_.is_bound()) { - ASSERT(masm_->SizeOfCodeGeneratedSince(&start_) == size_); - } - masm_->set_allow_macro_instructions(previous_allow_macro_instructions_); -#endif - } - - private: - MacroAssembler* masm_; - size_t size_; -#ifdef DEBUG - Label start_; - bool previous_allow_macro_instructions_; -#endif -}; - - -inline MemOperand ContextMemOperand(Register context, int index) { - return MemOperand(context, Context::SlotOffset(index)); -} - -inline MemOperand GlobalObjectMemOperand() { - return ContextMemOperand(cp, Context::GLOBAL_OBJECT_INDEX); -} - - -// Encode and decode information about patchable inline SMI checks. -class InlineSmiCheckInfo { - public: - explicit InlineSmiCheckInfo(Address info); - - bool HasSmiCheck() const { - return smi_check_ != NULL; - } - - const Register& SmiRegister() const { - return reg_; - } - - Instruction* SmiCheck() const { - return smi_check_; - } - - // Use MacroAssembler::InlineData to emit information about patchable inline - // SMI checks. The caller may specify 'reg' as NoReg and an unbound 'site' to - // indicate that there is no inline SMI check. Note that 'reg' cannot be csp. - // - // The generated patch information can be read using the InlineSMICheckInfo - // class. - static void Emit(MacroAssembler* masm, const Register& reg, - const Label* smi_check); - - // Emit information to indicate that there is no inline SMI check. - static void EmitNotInlined(MacroAssembler* masm) { - Label unbound; - Emit(masm, NoReg, &unbound); - } - - private: - Register reg_; - Instruction* smi_check_; - - // Fields in the data encoded by InlineData. - - // A width of 5 (Rd_width) for the SMI register preclues the use of csp, - // since kSPRegInternalCode is 63. However, csp should never hold a SMI or be - // used in a patchable check. The Emit() method checks this. - // - // Note that the total size of the fields is restricted by the underlying - // storage size handled by the BitField class, which is a uint32_t. - class RegisterBits : public BitField {}; - class DeltaBits : public BitField {}; -}; - -} } // namespace v8::internal - -#ifdef GENERATED_CODE_COVERAGE -#error "Unsupported option" -#define CODE_COVERAGE_STRINGIFY(x) #x -#define CODE_COVERAGE_TOSTRING(x) CODE_COVERAGE_STRINGIFY(x) -#define __FILE_LINE__ __FILE__ ":" CODE_COVERAGE_TOSTRING(__LINE__) -#define ACCESS_MASM(masm) masm->stop(__FILE_LINE__); masm-> -#else -#define ACCESS_MASM(masm) masm-> -#endif - -#endif // V8_A64_MACRO_ASSEMBLER_A64_H_ diff --git a/deps/v8/src/a64/regexp-macro-assembler-a64.cc b/deps/v8/src/a64/regexp-macro-assembler-a64.cc deleted file mode 100644 index 00558c017b..0000000000 --- a/deps/v8/src/a64/regexp-macro-assembler-a64.cc +++ /dev/null @@ -1,1730 +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 "v8.h" - -#if V8_TARGET_ARCH_A64 - -#include "cpu-profiler.h" -#include "unicode.h" -#include "log.h" -#include "code-stubs.h" -#include "regexp-stack.h" -#include "macro-assembler.h" -#include "regexp-macro-assembler.h" -#include "a64/regexp-macro-assembler-a64.h" - -namespace v8 { -namespace internal { - -#ifndef V8_INTERPRETED_REGEXP -/* - * This assembler uses the following register assignment convention: - * - w19 : Used to temporarely store a value before a call to C code. - * See CheckNotBackReferenceIgnoreCase. - * - x20 : Pointer to the current code object (Code*), - * it includes the heap object tag. - * - w21 : Current position in input, as negative offset from - * the end of the string. Please notice that this is - * the byte offset, not the character offset! - * - w22 : Currently loaded character. Must be loaded using - * LoadCurrentCharacter before using any of the dispatch methods. - * - x23 : Points to tip of backtrack stack. - * - w24 : Position of the first character minus one: non_position_value. - * Used to initialize capture registers. - * - x25 : Address at the end of the input string: input_end. - * Points to byte after last character in input. - * - x26 : Address at the start of the input string: input_start. - * - w27 : Where to start in the input string. - * - x28 : Output array pointer. - * - x29/fp : Frame pointer. Used to access arguments, local variables and - * RegExp registers. - * - x16/x17 : IP registers, used by assembler. Very volatile. - * - csp : Points to tip of C stack. - * - * - x0-x7 : Used as a cache to store 32 bit capture registers. These - * registers need to be retained every time a call to C code - * is done. - * - * The remaining registers are free for computations. - * Each call to a public method should retain this convention. - * - * The stack will have the following structure: - * - * Location Name Description - * (as referred to in - * the code) - * - * - fp[104] isolate Address of the current isolate. - * - fp[96] return_address Secondary link/return address - * used by an exit frame if this is a - * native call. - * ^^^ csp when called ^^^ - * - fp[88] lr Return from the RegExp code. - * - fp[80] r29 Old frame pointer (CalleeSaved). - * - fp[0..72] r19-r28 Backup of CalleeSaved registers. - * - fp[-8] direct_call 1 => Direct call from JavaScript code. - * 0 => Call through the runtime system. - * - fp[-16] stack_base High end of the memory area to use as - * the backtracking stack. - * - fp[-24] output_size Output may fit multiple sets of matches. - * - fp[-32] input Handle containing the input string. - * - fp[-40] success_counter - * ^^^^^^^^^^^^^ From here and downwards we store 32 bit values ^^^^^^^^^^^^^ - * - fp[-44] register N Capture registers initialized with - * - fp[-48] register N + 1 non_position_value. - * ... The first kNumCachedRegisters (N) registers - * ... are cached in x0 to x7. - * ... Only positions must be stored in the first - * - ... num_saved_registers_ registers. - * - ... - * - register N + num_registers - 1 - * ^^^^^^^^^ csp ^^^^^^^^^ - * - * 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 start out as garbage. - * - * The data up to the return address must be placed there by the calling - * code and the remaining arguments are passed in registers, e.g. by calling the - * code entry as cast to a function with the signature: - * int (*match)(String* input, - * int start_offset, - * Address input_start, - * Address input_end, - * int* output, - * int output_size, - * Address stack_base, - * bool direct_call = false, - * Address secondary_return_address, // Only used by native call. - * Isolate* isolate) - * The call is performed by NativeRegExpMacroAssembler::Execute() - * (in regexp-macro-assembler.cc) via the CALL_GENERATED_REGEXP_CODE macro - * in a64/simulator-a64.h. - * When calling as a non-direct call (i.e., from C++ code), the return address - * area is overwritten with the LR register by the RegExp code. When doing a - * direct call from generated code, the return address is placed there by - * the calling code, as in a normal exit frame. - */ - -#define __ ACCESS_MASM(masm_) - -RegExpMacroAssemblerA64::RegExpMacroAssemblerA64( - Mode mode, - int registers_to_save, - Zone* zone) - : NativeRegExpMacroAssembler(zone), - masm_(new MacroAssembler(zone->isolate(), NULL, kRegExpCodeSize)), - mode_(mode), - num_registers_(registers_to_save), - num_saved_registers_(registers_to_save), - entry_label_(), - start_label_(), - success_label_(), - backtrack_label_(), - exit_label_() { - __ SetStackPointer(csp); - ASSERT_EQ(0, registers_to_save % 2); - // We can cache at most 16 W registers in x0-x7. - STATIC_ASSERT(kNumCachedRegisters <= 16); - STATIC_ASSERT((kNumCachedRegisters % 2) == 0); - __ B(&entry_label_); // We'll write the entry code later. - __ Bind(&start_label_); // And then continue from here. -} - - -RegExpMacroAssemblerA64::~RegExpMacroAssemblerA64() { - 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 RegExpMacroAssemblerA64::stack_limit_slack() { - return RegExpStack::kStackLimitSlack; -} - - -void RegExpMacroAssemblerA64::AdvanceCurrentPosition(int by) { - if (by != 0) { - __ Add(current_input_offset(), - current_input_offset(), by * char_size()); - } -} - - -void RegExpMacroAssemblerA64::AdvanceRegister(int reg, int by) { - ASSERT((reg >= 0) && (reg < num_registers_)); - if (by != 0) { - Register to_advance; - RegisterState register_state = GetRegisterState(reg); - switch (register_state) { - case STACKED: - __ Ldr(w10, register_location(reg)); - __ Add(w10, w10, by); - __ Str(w10, register_location(reg)); - break; - case CACHED_LSW: - to_advance = GetCachedRegister(reg); - __ Add(to_advance, to_advance, by); - break; - case CACHED_MSW: - to_advance = GetCachedRegister(reg); - __ Add(to_advance, to_advance, static_cast(by) << kWRegSize); - break; - default: - UNREACHABLE(); - break; - } - } -} - - -void RegExpMacroAssemblerA64::Backtrack() { - CheckPreemption(); - Pop(w10); - __ Add(x10, code_pointer(), Operand(w10, UXTW)); - __ Br(x10); -} - - -void RegExpMacroAssemblerA64::Bind(Label* label) { - __ Bind(label); -} - - -void RegExpMacroAssemblerA64::CheckCharacter(uint32_t c, Label* on_equal) { - CompareAndBranchOrBacktrack(current_character(), c, eq, on_equal); -} - - -void RegExpMacroAssemblerA64::CheckCharacterGT(uc16 limit, Label* on_greater) { - CompareAndBranchOrBacktrack(current_character(), limit, hi, on_greater); -} - - -void RegExpMacroAssemblerA64::CheckAtStart(Label* on_at_start) { - Label not_at_start; - // Did we start the match at the start of the input string? - CompareAndBranchOrBacktrack(start_offset(), 0, ne, ¬_at_start); - // If we did, are we still at the start of the input string? - __ Add(x10, input_end(), Operand(current_input_offset(), SXTW)); - __ Cmp(x10, input_start()); - BranchOrBacktrack(eq, on_at_start); - __ Bind(¬_at_start); -} - - -void RegExpMacroAssemblerA64::CheckNotAtStart(Label* on_not_at_start) { - // Did we start the match at the start of the input string? - CompareAndBranchOrBacktrack(start_offset(), 0, ne, on_not_at_start); - // If we did, are we still at the start of the input string? - __ Add(x10, input_end(), Operand(current_input_offset(), SXTW)); - __ Cmp(x10, input_start()); - BranchOrBacktrack(ne, on_not_at_start); -} - - -void RegExpMacroAssemblerA64::CheckCharacterLT(uc16 limit, Label* on_less) { - CompareAndBranchOrBacktrack(current_character(), limit, lo, on_less); -} - - -void RegExpMacroAssemblerA64::CheckCharacters(Vector str, - int cp_offset, - Label* on_failure, - bool check_end_of_string) { - // This method is only ever called from the cctests. - - if (check_end_of_string) { - // Is last character of required match inside string. - CheckPosition(cp_offset + str.length() - 1, on_failure); - } - - Register characters_address = x11; - - __ Add(characters_address, - input_end(), - Operand(current_input_offset(), SXTW)); - if (cp_offset != 0) { - __ Add(characters_address, characters_address, cp_offset * char_size()); - } - - for (int i = 0; i < str.length(); i++) { - if (mode_ == ASCII) { - __ Ldrb(w10, MemOperand(characters_address, 1, PostIndex)); - ASSERT(str[i] <= String::kMaxOneByteCharCode); - } else { - __ Ldrh(w10, MemOperand(characters_address, 2, PostIndex)); - } - CompareAndBranchOrBacktrack(w10, str[i], ne, on_failure); - } -} - - -void RegExpMacroAssemblerA64::CheckGreedyLoop(Label* on_equal) { - __ Ldr(w10, MemOperand(backtrack_stackpointer())); - __ Cmp(current_input_offset(), w10); - __ Cset(x11, eq); - __ Add(backtrack_stackpointer(), - backtrack_stackpointer(), Operand(x11, LSL, kWRegSizeInBytesLog2)); - BranchOrBacktrack(eq, on_equal); -} - -void RegExpMacroAssemblerA64::CheckNotBackReferenceIgnoreCase( - int start_reg, - Label* on_no_match) { - Label fallthrough; - - Register capture_start_offset = w10; - // Save the capture length in a callee-saved register so it will - // be preserved if we call a C helper. - Register capture_length = w19; - ASSERT(kCalleeSaved.IncludesAliasOf(capture_length)); - - // Find length of back-referenced capture. - ASSERT((start_reg % 2) == 0); - if (start_reg < kNumCachedRegisters) { - __ Mov(capture_start_offset.X(), GetCachedRegister(start_reg)); - __ Lsr(x11, GetCachedRegister(start_reg), kWRegSize); - } else { - __ Ldp(w11, capture_start_offset, capture_location(start_reg, x10)); - } - __ Sub(capture_length, w11, capture_start_offset); // Length to check. - // Succeed on empty capture (including no capture). - __ Cbz(capture_length, &fallthrough); - - // Check that there are enough characters left in the input. - __ Cmn(capture_length, current_input_offset()); - BranchOrBacktrack(gt, on_no_match); - - if (mode_ == ASCII) { - Label success; - Label fail; - Label loop_check; - - Register capture_start_address = x12; - Register capture_end_addresss = x13; - Register current_position_address = x14; - - __ Add(capture_start_address, - input_end(), - Operand(capture_start_offset, SXTW)); - __ Add(capture_end_addresss, - capture_start_address, - Operand(capture_length, SXTW)); - __ Add(current_position_address, - input_end(), - Operand(current_input_offset(), SXTW)); - - Label loop; - __ Bind(&loop); - __ Ldrb(w10, MemOperand(capture_start_address, 1, PostIndex)); - __ Ldrb(w11, MemOperand(current_position_address, 1, PostIndex)); - __ Cmp(w10, w11); - __ B(eq, &loop_check); - - // Mismatch, try case-insensitive match (converting letters to lower-case). - __ Orr(w10, w10, 0x20); // Convert capture character to lower-case. - __ Orr(w11, w11, 0x20); // Also convert input character. - __ Cmp(w11, w10); - __ B(ne, &fail); - __ Sub(w10, w10, 'a'); - __ Cmp(w10, 'z' - 'a'); // Is w10 a lowercase letter? - __ B(ls, &loop_check); // In range 'a'-'z'. - // Latin-1: Check for values in range [224,254] but not 247. - __ Sub(w10, w10, 224 - 'a'); - // TODO(jbramley): Use Ccmp here. - __ Cmp(w10, 254 - 224); - __ B(hi, &fail); // Weren't Latin-1 letters. - __ Cmp(w10, 247 - 224); // Check for 247. - __ B(eq, &fail); - - __ Bind(&loop_check); - __ Cmp(capture_start_address, capture_end_addresss); - __ B(lt, &loop); - __ B(&success); - - __ Bind(&fail); - BranchOrBacktrack(al, on_no_match); - - __ Bind(&success); - // Compute new value of character position after the matched part. - __ Sub(current_input_offset().X(), current_position_address, input_end()); - if (masm_->emit_debug_code()) { - __ Cmp(current_input_offset().X(), Operand(current_input_offset(), SXTW)); - __ Ccmp(current_input_offset(), 0, NoFlag, eq); - // The current input offset should be <= 0, and fit in a W register. - __ Check(le, kOffsetOutOfRange); - } - } else { - ASSERT(mode_ == UC16); - int argument_count = 4; - - // The cached registers need to be retained. - CPURegList cached_registers(CPURegister::kRegister, kXRegSize, 0, 7); - ASSERT((cached_registers.Count() * 2) == kNumCachedRegisters); - __ PushCPURegList(cached_registers); - - // Put arguments into arguments registers. - // Parameters are - // x0: Address byte_offset1 - Address captured substring's start. - // x1: Address byte_offset2 - Address of current character position. - // w2: size_t byte_length - length of capture in bytes(!) - // x3: Isolate* isolate - - // Address of start of capture. - __ Add(x0, input_end(), Operand(capture_start_offset, SXTW)); - // Length of capture. - __ Mov(w2, capture_length); - // Address of current input position. - __ Add(x1, input_end(), Operand(current_input_offset(), SXTW)); - // Isolate. - __ Mov(x3, Operand(ExternalReference::isolate_address(isolate()))); - - { - AllowExternalCallThatCantCauseGC scope(masm_); - ExternalReference function = - ExternalReference::re_case_insensitive_compare_uc16(isolate()); - __ CallCFunction(function, argument_count); - } - - // Check if function returned non-zero for success or zero for failure. - CompareAndBranchOrBacktrack(x0, 0, eq, on_no_match); - // On success, increment position by length of capture. - __ Add(current_input_offset(), current_input_offset(), capture_length); - // Reset the cached registers. - __ PopCPURegList(cached_registers); - } - - __ Bind(&fallthrough); -} - -void RegExpMacroAssemblerA64::CheckNotBackReference( - int start_reg, - Label* on_no_match) { - Label fallthrough; - - Register capture_start_address = x12; - Register capture_end_address = x13; - Register current_position_address = x14; - Register capture_length = w15; - - // Find length of back-referenced capture. - ASSERT((start_reg % 2) == 0); - if (start_reg < kNumCachedRegisters) { - __ Mov(x10, GetCachedRegister(start_reg)); - __ Lsr(x11, GetCachedRegister(start_reg), kWRegSize); - } else { - __ Ldp(w11, w10, capture_location(start_reg, x10)); - } - __ Sub(capture_length, w11, w10); // Length to check. - // Succeed on empty capture (including no capture). - __ Cbz(capture_length, &fallthrough); - - // Check that there are enough characters left in the input. - __ Cmn(capture_length, current_input_offset()); - BranchOrBacktrack(gt, on_no_match); - - // Compute pointers to match string and capture string - __ Add(capture_start_address, input_end(), Operand(w10, SXTW)); - __ Add(capture_end_address, - capture_start_address, - Operand(capture_length, SXTW)); - __ Add(current_position_address, - input_end(), - Operand(current_input_offset(), SXTW)); - - Label loop; - __ Bind(&loop); - if (mode_ == ASCII) { - __ Ldrb(w10, MemOperand(capture_start_address, 1, PostIndex)); - __ Ldrb(w11, MemOperand(current_position_address, 1, PostIndex)); - } else { - ASSERT(mode_ == UC16); - __ Ldrh(w10, MemOperand(capture_start_address, 2, PostIndex)); - __ Ldrh(w11, MemOperand(current_position_address, 2, PostIndex)); - } - __ Cmp(w10, w11); - BranchOrBacktrack(ne, on_no_match); - __ Cmp(capture_start_address, capture_end_address); - __ B(lt, &loop); - - // Move current character position to position after match. - __ Sub(current_input_offset().X(), current_position_address, input_end()); - if (masm_->emit_debug_code()) { - __ Cmp(current_input_offset().X(), Operand(current_input_offset(), SXTW)); - __ Ccmp(current_input_offset(), 0, NoFlag, eq); - // The current input offset should be <= 0, and fit in a W register. - __ Check(le, kOffsetOutOfRange); - } - __ Bind(&fallthrough); -} - - -void RegExpMacroAssemblerA64::CheckNotCharacter(unsigned c, - Label* on_not_equal) { - CompareAndBranchOrBacktrack(current_character(), c, ne, on_not_equal); -} - - -void RegExpMacroAssemblerA64::CheckCharacterAfterAnd(uint32_t c, - uint32_t mask, - Label* on_equal) { - __ And(w10, current_character(), mask); - CompareAndBranchOrBacktrack(w10, c, eq, on_equal); -} - - -void RegExpMacroAssemblerA64::CheckNotCharacterAfterAnd(unsigned c, - unsigned mask, - Label* on_not_equal) { - __ And(w10, current_character(), mask); - CompareAndBranchOrBacktrack(w10, c, ne, on_not_equal); -} - - -void RegExpMacroAssemblerA64::CheckNotCharacterAfterMinusAnd( - uc16 c, - uc16 minus, - uc16 mask, - Label* on_not_equal) { - ASSERT(minus < String::kMaxUtf16CodeUnit); - __ Sub(w10, current_character(), minus); - __ And(w10, w10, mask); - CompareAndBranchOrBacktrack(w10, c, ne, on_not_equal); -} - - -void RegExpMacroAssemblerA64::CheckCharacterInRange( - uc16 from, - uc16 to, - Label* on_in_range) { - __ Sub(w10, current_character(), from); - // Unsigned lower-or-same condition. - CompareAndBranchOrBacktrack(w10, to - from, ls, on_in_range); -} - - -void RegExpMacroAssemblerA64::CheckCharacterNotInRange( - uc16 from, - uc16 to, - Label* on_not_in_range) { - __ Sub(w10, current_character(), from); - // Unsigned higher condition. - CompareAndBranchOrBacktrack(w10, to - from, hi, on_not_in_range); -} - - -void RegExpMacroAssemblerA64::CheckBitInTable( - Handle table, - Label* on_bit_set) { - __ Mov(x11, Operand(table)); - if ((mode_ != ASCII) || (kTableMask != String::kMaxOneByteCharCode)) { - __ And(w10, current_character(), kTableMask); - __ Add(w10, w10, ByteArray::kHeaderSize - kHeapObjectTag); - } else { - __ Add(w10, current_character(), ByteArray::kHeaderSize - kHeapObjectTag); - } - __ Ldrb(w11, MemOperand(x11, w10, UXTW)); - CompareAndBranchOrBacktrack(w11, 0, ne, on_bit_set); -} - - -bool RegExpMacroAssemblerA64::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_ == ASCII) { - // One byte space characters are '\t'..'\r', ' ' and \u00a0. - Label success; - // Check for ' ' or 0x00a0. - __ Cmp(current_character(), ' '); - __ Ccmp(current_character(), 0x00a0, ZFlag, ne); - __ B(eq, &success); - // Check range 0x09..0x0d. - __ Sub(w10, current_character(), '\t'); - CompareAndBranchOrBacktrack(w10, '\r' - '\t', hi, 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'). - __ Sub(w10, current_character(), '0'); - CompareAndBranchOrBacktrack(w10, '9' - '0', hi, on_no_match); - return true; - case 'D': - // Match ASCII non-digits. - __ Sub(w10, current_character(), '0'); - CompareAndBranchOrBacktrack(w10, '9' - '0', ls, on_no_match); - return true; - case '.': { - // Match non-newlines (not 0x0a('\n'), 0x0d('\r'), 0x2028 and 0x2029) - // Here we emit the conditional branch only once at the end to make branch - // prediction more efficient, even though we could branch out of here - // as soon as a character matches. - __ Cmp(current_character(), 0x0a); - __ Ccmp(current_character(), 0x0d, ZFlag, ne); - if (mode_ == UC16) { - __ Sub(w10, current_character(), 0x2028); - // If the Z flag was set we clear the flags to force a branch. - __ Ccmp(w10, 0x2029 - 0x2028, NoFlag, ne); - // ls -> !((C==1) && (Z==0)) - BranchOrBacktrack(ls, on_no_match); - } else { - BranchOrBacktrack(eq, on_no_match); - } - return true; - } - case 'n': { - // Match newlines (0x0a('\n'), 0x0d('\r'), 0x2028 and 0x2029) - // We have to check all 4 newline characters before emitting - // the conditional branch. - __ Cmp(current_character(), 0x0a); - __ Ccmp(current_character(), 0x0d, ZFlag, ne); - if (mode_ == UC16) { - __ Sub(w10, current_character(), 0x2028); - // If the Z flag was set we clear the flags to force a fall-through. - __ Ccmp(w10, 0x2029 - 0x2028, NoFlag, ne); - // hi -> (C==1) && (Z==0) - BranchOrBacktrack(hi, on_no_match); - } else { - BranchOrBacktrack(ne, on_no_match); - } - return true; - } - case 'w': { - if (mode_ != ASCII) { - // Table is 128 entries, so all ASCII characters can be tested. - CompareAndBranchOrBacktrack(current_character(), 'z', hi, on_no_match); - } - ExternalReference map = ExternalReference::re_word_character_map(); - __ Mov(x10, Operand(map)); - __ Ldrb(w10, MemOperand(x10, current_character(), UXTW)); - CompareAndBranchOrBacktrack(w10, 0, eq, on_no_match); - return true; - } - case 'W': { - Label done; - if (mode_ != ASCII) { - // Table is 128 entries, so all ASCII characters can be tested. - __ Cmp(current_character(), 'z'); - __ B(hi, &done); - } - ExternalReference map = ExternalReference::re_word_character_map(); - __ Mov(x10, Operand(map)); - __ Ldrb(w10, MemOperand(x10, current_character(), UXTW)); - CompareAndBranchOrBacktrack(w10, 0, ne, on_no_match); - __ Bind(&done); - return true; - } - case '*': - // Match any character. - return true; - // No custom implementation (yet): s(UC16), S(UC16). - default: - return false; - } -} - - -void RegExpMacroAssemblerA64::Fail() { - __ Mov(w0, FAILURE); - __ B(&exit_label_); -} - - -Handle RegExpMacroAssemblerA64::GetCode(Handle source) { - Label return_w0; - // Finalize code - write the entry point code now we know how many - // registers we need. - - // Entry code: - __ Bind(&entry_label_); - - // Arguments on entry: - // x0: String* input - // x1: int start_offset - // x2: byte* input_start - // x3: byte* input_end - // x4: int* output array - // x5: int output array size - // x6: Address stack_base - // x7: int direct_call - - // The stack pointer should be csp on entry. - // csp[8]: address of the current isolate - // csp[0]: secondary link/return address used by native call - - // Tell the system that we have a stack frame. Because the type is MANUAL, no - // code is generated. - FrameScope scope(masm_, StackFrame::MANUAL); - - // Push registers on the stack, only push the argument registers that we need. - CPURegList argument_registers(x0, x5, x6, x7); - - CPURegList registers_to_retain = kCalleeSaved; - ASSERT(kCalleeSaved.Count() == 11); - registers_to_retain.Combine(lr); - - ASSERT(csp.Is(__ StackPointer())); - __ PushCPURegList(registers_to_retain); - __ PushCPURegList(argument_registers); - - // Set frame pointer in place. - __ Add(frame_pointer(), csp, argument_registers.Count() * kPointerSize); - - // Initialize callee-saved registers. - __ Mov(start_offset(), w1); - __ Mov(input_start(), x2); - __ Mov(input_end(), x3); - __ Mov(output_array(), x4); - - // Set the number of registers we will need to allocate, that is: - // - success_counter (X register) - // - (num_registers_ - kNumCachedRegisters) (W registers) - int num_wreg_to_allocate = num_registers_ - kNumCachedRegisters; - // Do not allocate registers on the stack if they can all be cached. - if (num_wreg_to_allocate < 0) { num_wreg_to_allocate = 0; } - // Make room for the success_counter. - num_wreg_to_allocate += 2; - - // Make sure the stack alignment will be respected. - int alignment = masm_->ActivationFrameAlignment(); - ASSERT_EQ(alignment % 16, 0); - int align_mask = (alignment / kWRegSizeInBytes) - 1; - num_wreg_to_allocate = (num_wreg_to_allocate + align_mask) & ~align_mask; - - // Check if we have space on the stack. - Label stack_limit_hit; - Label stack_ok; - - ExternalReference stack_limit = - ExternalReference::address_of_stack_limit(isolate()); - __ Mov(x10, Operand(stack_limit)); - __ Ldr(x10, MemOperand(x10)); - __ Subs(x10, csp, x10); - - // Handle it if the stack pointer is already below the stack limit. - __ B(ls, &stack_limit_hit); - - // Check if there is room for the variable number of registers above - // the stack limit. - __ Cmp(x10, num_wreg_to_allocate * kWRegSizeInBytes); - __ B(hs, &stack_ok); - - // Exit with OutOfMemory exception. There is not enough space on the stack - // for our working registers. - __ Mov(w0, EXCEPTION); - __ B(&return_w0); - - __ Bind(&stack_limit_hit); - CallCheckStackGuardState(x10); - // If returned value is non-zero, we exit with the returned value as result. - __ Cbnz(w0, &return_w0); - - __ Bind(&stack_ok); - - // Allocate space on stack. - __ Claim(num_wreg_to_allocate, kWRegSizeInBytes); - - // Initialize success_counter with 0. - __ Str(wzr, MemOperand(frame_pointer(), kSuccessCounter)); - - // Find negative length (offset of start relative to end). - __ Sub(x10, input_start(), input_end()); - if (masm_->emit_debug_code()) { - // Check that the input string length is < 2^30. - __ Neg(x11, x10); - __ Cmp(x11, (1<<30) - 1); - __ Check(ls, kInputStringTooLong); - } - __ Mov(current_input_offset(), w10); - - // The non-position value is used as a clearing value for the - // capture registers, it corresponds to the position of the first character - // minus one. - __ Sub(non_position_value(), current_input_offset(), char_size()); - __ Sub(non_position_value(), non_position_value(), - Operand(start_offset(), LSL, (mode_ == UC16) ? 1 : 0)); - // We can store this value twice in an X register for initializing - // on-stack registers later. - __ Orr(twice_non_position_value(), - non_position_value().X(), - Operand(non_position_value().X(), LSL, kWRegSize)); - - // Initialize code pointer register. - __ Mov(code_pointer(), Operand(masm_->CodeObject())); - - Label load_char_start_regexp, start_regexp; - // Load newline if index is at start, previous character otherwise. - __ Cbnz(start_offset(), &load_char_start_regexp); - __ Mov(current_character(), '\n'); - __ B(&start_regexp); - - // 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) { - ClearRegisters(0, num_saved_registers_ - 1); - } - - // Initialize backtrack stack pointer. - __ Ldr(backtrack_stackpointer(), MemOperand(frame_pointer(), kStackBase)); - - // Execute - __ B(&start_label_); - - if (backtrack_label_.is_linked()) { - __ Bind(&backtrack_label_); - Backtrack(); - } - - if (success_label_.is_linked()) { - Register first_capture_start = w15; - - // Save captures when successful. - __ Bind(&success_label_); - - if (num_saved_registers_ > 0) { - // V8 expects the output to be an int32_t array. - Register capture_start = w12; - Register capture_end = w13; - Register input_length = w14; - - // Copy captures to output. - - // Get string length. - __ Sub(x10, input_end(), input_start()); - if (masm_->emit_debug_code()) { - // Check that the input string length is < 2^30. - __ Cmp(x10, (1<<30) - 1); - __ Check(ls, kInputStringTooLong); - } - // input_start has a start_offset offset on entry. We need to include - // it when computing the length of the whole string. - if (mode_ == UC16) { - __ Add(input_length, start_offset(), Operand(w10, LSR, 1)); - } else { - __ Add(input_length, start_offset(), w10); - } - - // Copy the results to the output array from the cached registers first. - for (int i = 0; - (i < num_saved_registers_) && (i < kNumCachedRegisters); - i += 2) { - __ Mov(capture_start.X(), GetCachedRegister(i)); - __ Lsr(capture_end.X(), capture_start.X(), kWRegSize); - if ((i == 0) && global_with_zero_length_check()) { - // Keep capture start for the zero-length check later. - __ Mov(first_capture_start, capture_start); - } - // Offsets need to be relative to the start of the string. - if (mode_ == UC16) { - __ Add(capture_start, input_length, Operand(capture_start, ASR, 1)); - __ Add(capture_end, input_length, Operand(capture_end, ASR, 1)); - } else { - __ Add(capture_start, input_length, capture_start); - __ Add(capture_end, input_length, capture_end); - } - // The output pointer advances for a possible global match. - __ Stp(capture_start, - capture_end, - MemOperand(output_array(), kPointerSize, PostIndex)); - } - - // Only carry on if there are more than kNumCachedRegisters capture - // registers. - int num_registers_left_on_stack = - num_saved_registers_ - kNumCachedRegisters; - if (num_registers_left_on_stack > 0) { - Register base = x10; - // There are always an even number of capture registers. A couple of - // registers determine one match with two offsets. - ASSERT_EQ(0, num_registers_left_on_stack % 2); - __ Add(base, frame_pointer(), kFirstCaptureOnStack); - - // We can unroll the loop here, we should not unroll for less than 2 - // registers. - STATIC_ASSERT(kNumRegistersToUnroll > 2); - if (num_registers_left_on_stack <= kNumRegistersToUnroll) { - for (int i = 0; i < num_registers_left_on_stack / 2; i++) { - __ Ldp(capture_end, - capture_start, - MemOperand(base, -kPointerSize, PostIndex)); - if ((i == 0) && global_with_zero_length_check()) { - // Keep capture start for the zero-length check later. - __ Mov(first_capture_start, capture_start); - } - // Offsets need to be relative to the start of the string. - if (mode_ == UC16) { - __ Add(capture_start, - input_length, - Operand(capture_start, ASR, 1)); - __ Add(capture_end, input_length, Operand(capture_end, ASR, 1)); - } else { - __ Add(capture_start, input_length, capture_start); - __ Add(capture_end, input_length, capture_end); - } - // The output pointer advances for a possible global match. - __ Stp(capture_start, - capture_end, - MemOperand(output_array(), kPointerSize, PostIndex)); - } - } else { - Label loop, start; - __ Mov(x11, num_registers_left_on_stack); - - __ Ldp(capture_end, - capture_start, - MemOperand(base, -kPointerSize, PostIndex)); - if (global_with_zero_length_check()) { - __ Mov(first_capture_start, capture_start); - } - __ B(&start); - - __ Bind(&loop); - __ Ldp(capture_end, - capture_start, - MemOperand(base, -kPointerSize, PostIndex)); - __ Bind(&start); - if (mode_ == UC16) { - __ Add(capture_start, input_length, Operand(capture_start, ASR, 1)); - __ Add(capture_end, input_length, Operand(capture_end, ASR, 1)); - } else { - __ Add(capture_start, input_length, capture_start); - __ Add(capture_end, input_length, capture_end); - } - // The output pointer advances for a possible global match. - __ Stp(capture_start, - capture_end, - MemOperand(output_array(), kPointerSize, PostIndex)); - __ Sub(x11, x11, 2); - __ Cbnz(x11, &loop); - } - } - } - - if (global()) { - Register success_counter = w0; - Register output_size = x10; - // Restart matching if the regular expression is flagged as global. - - // Increment success counter. - __ Ldr(success_counter, MemOperand(frame_pointer(), kSuccessCounter)); - __ Add(success_counter, success_counter, 1); - __ Str(success_counter, MemOperand(frame_pointer(), kSuccessCounter)); - - // Capture results have been stored, so the number of remaining global - // output registers is reduced by the number of stored captures. - __ Ldr(output_size, MemOperand(frame_pointer(), kOutputSize)); - __ Sub(output_size, output_size, num_saved_registers_); - // Check whether we have enough room for another set of capture results. - __ Cmp(output_size, num_saved_registers_); - __ B(lt, &return_w0); - - // The output pointer is already set to the next field in the output - // array. - // Update output size on the frame before we restart matching. - __ Str(output_size, MemOperand(frame_pointer(), kOutputSize)); - - if (global_with_zero_length_check()) { - // Special case for zero-length matches. - __ Cmp(current_input_offset(), first_capture_start); - // Not a zero-length match, restart. - __ B(ne, &load_char_start_regexp); - // Offset from the end is zero if we already reached the end. - __ Cbz(current_input_offset(), &return_w0); - // Advance current position after a zero-length match. - __ Add(current_input_offset(), - current_input_offset(), - Operand((mode_ == UC16) ? 2 : 1)); - } - - __ B(&load_char_start_regexp); - } else { - __ Mov(w0, SUCCESS); - } - } - - if (exit_label_.is_linked()) { - // Exit and return w0 - __ Bind(&exit_label_); - if (global()) { - __ Ldr(w0, MemOperand(frame_pointer(), kSuccessCounter)); - } - } - - __ Bind(&return_w0); - - // Set stack pointer back to first register to retain - ASSERT(csp.Is(__ StackPointer())); - __ Mov(csp, fp); - - // Restore registers. - __ PopCPURegList(registers_to_retain); - - __ Ret(); - - Label exit_with_exception; - // Registers x0 to x7 are used to store the first captures, they need to be - // retained over calls to C++ code. - CPURegList cached_registers(CPURegister::kRegister, kXRegSize, 0, 7); - ASSERT((cached_registers.Count() * 2) == kNumCachedRegisters); - - if (check_preempt_label_.is_linked()) { - __ Bind(&check_preempt_label_); - SaveLinkRegister(); - // The cached registers need to be retained. - __ PushCPURegList(cached_registers); - CallCheckStackGuardState(x10); - // Returning from the regexp code restores the stack (csp <- fp) - // so we don't need to drop the link register from it before exiting. - __ Cbnz(w0, &return_w0); - // Reset the cached registers. - __ PopCPURegList(cached_registers); - RestoreLinkRegister(); - __ Ret(); - } - - if (stack_overflow_label_.is_linked()) { - __ Bind(&stack_overflow_label_); - SaveLinkRegister(); - // The cached registers need to be retained. - __ PushCPURegList(cached_registers); - // Call GrowStack(backtrack_stackpointer(), &stack_base) - __ Mov(x2, Operand(ExternalReference::isolate_address(isolate()))); - __ Add(x1, frame_pointer(), kStackBase); - __ Mov(x0, backtrack_stackpointer()); - ExternalReference grow_stack = - ExternalReference::re_grow_stack(isolate()); - __ CallCFunction(grow_stack, 3); - // If return NULL, we have failed to grow the stack, and - // must exit with a stack-overflow exception. - // Returning from the regexp code restores the stack (csp <- fp) - // so we don't need to drop the link register from it before exiting. - __ Cbz(w0, &exit_with_exception); - // Otherwise use return value as new stack pointer. - __ Mov(backtrack_stackpointer(), x0); - // Reset the cached registers. - __ PopCPURegList(cached_registers); - RestoreLinkRegister(); - __ Ret(); - } - - if (exit_with_exception.is_linked()) { - __ Bind(&exit_with_exception); - __ Mov(w0, EXCEPTION); - __ B(&return_w0); - } - - CodeDesc code_desc; - masm_->GetCode(&code_desc); - Handle code = isolate()->factory()->NewCode( - code_desc, Code::ComputeFlags(Code::REGEXP), masm_->CodeObject()); - PROFILE(masm_->isolate(), RegExpCodeCreateEvent(*code, *source)); - return Handle::cast(code); -} - - -void RegExpMacroAssemblerA64::GoTo(Label* to) { - BranchOrBacktrack(al, to); -} - -void RegExpMacroAssemblerA64::IfRegisterGE(int reg, - int comparand, - Label* if_ge) { - Register to_compare = GetRegister(reg, w10); - CompareAndBranchOrBacktrack(to_compare, comparand, ge, if_ge); -} - - -void RegExpMacroAssemblerA64::IfRegisterLT(int reg, - int comparand, - Label* if_lt) { - Register to_compare = GetRegister(reg, w10); - CompareAndBranchOrBacktrack(to_compare, comparand, lt, if_lt); -} - - -void RegExpMacroAssemblerA64::IfRegisterEqPos(int reg, - Label* if_eq) { - Register to_compare = GetRegister(reg, w10); - __ Cmp(to_compare, current_input_offset()); - BranchOrBacktrack(eq, if_eq); -} - -RegExpMacroAssembler::IrregexpImplementation - RegExpMacroAssemblerA64::Implementation() { - return kA64Implementation; -} - - -void RegExpMacroAssemblerA64::LoadCurrentCharacter(int cp_offset, - Label* on_end_of_input, - bool check_bounds, - int characters) { - // TODO(pielan): Make sure long strings are caught before this, and not - // just asserted in debug mode. - ASSERT(cp_offset >= -1); // ^ and \b can look behind one character. - // Be sane! (And ensure that an int32_t can be used to index the string) - ASSERT(cp_offset < (1<<30)); - if (check_bounds) { - CheckPosition(cp_offset + characters - 1, on_end_of_input); - } - LoadCurrentCharacterUnchecked(cp_offset, characters); -} - - -void RegExpMacroAssemblerA64::PopCurrentPosition() { - Pop(current_input_offset()); -} - - -void RegExpMacroAssemblerA64::PopRegister(int register_index) { - Pop(w10); - StoreRegister(register_index, w10); -} - - -void RegExpMacroAssemblerA64::PushBacktrack(Label* label) { - if (label->is_bound()) { - int target = label->pos(); - __ Mov(w10, target + Code::kHeaderSize - kHeapObjectTag); - } else { - __ Adr(x10, label); - __ Sub(x10, x10, code_pointer()); - if (masm_->emit_debug_code()) { - __ Cmp(x10, kWRegMask); - // The code offset has to fit in a W register. - __ Check(ls, kOffsetOutOfRange); - } - } - Push(w10); - CheckStackLimit(); -} - - -void RegExpMacroAssemblerA64::PushCurrentPosition() { - Push(current_input_offset()); -} - - -void RegExpMacroAssemblerA64::PushRegister(int register_index, - StackCheckFlag check_stack_limit) { - Register to_push = GetRegister(register_index, w10); - Push(to_push); - if (check_stack_limit) CheckStackLimit(); -} - - -void RegExpMacroAssemblerA64::ReadCurrentPositionFromRegister(int reg) { - Register cached_register; - RegisterState register_state = GetRegisterState(reg); - switch (register_state) { - case STACKED: - __ Ldr(current_input_offset(), register_location(reg)); - break; - case CACHED_LSW: - cached_register = GetCachedRegister(reg); - __ Mov(current_input_offset(), cached_register.W()); - break; - case CACHED_MSW: - cached_register = GetCachedRegister(reg); - __ Lsr(current_input_offset().X(), cached_register, kWRegSize); - break; - default: - UNREACHABLE(); - break; - } -} - - -void RegExpMacroAssemblerA64::ReadStackPointerFromRegister(int reg) { - Register read_from = GetRegister(reg, w10); - __ Ldr(x11, MemOperand(frame_pointer(), kStackBase)); - __ Add(backtrack_stackpointer(), x11, Operand(read_from, SXTW)); -} - - -void RegExpMacroAssemblerA64::SetCurrentPositionFromEnd(int by) { - Label after_position; - __ Cmp(current_input_offset(), -by * char_size()); - __ B(ge, &after_position); - __ Mov(current_input_offset(), -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 RegExpMacroAssemblerA64::SetRegister(int register_index, int to) { - ASSERT(register_index >= num_saved_registers_); // Reserved for positions! - Register set_to = wzr; - if (to != 0) { - set_to = w10; - __ Mov(set_to, to); - } - StoreRegister(register_index, set_to); -} - - -bool RegExpMacroAssemblerA64::Succeed() { - __ B(&success_label_); - return global(); -} - - -void RegExpMacroAssemblerA64::WriteCurrentPositionToRegister(int reg, - int cp_offset) { - Register position = current_input_offset(); - if (cp_offset != 0) { - position = w10; - __ Add(position, current_input_offset(), cp_offset * char_size()); - } - StoreRegister(reg, position); -} - - -void RegExpMacroAssemblerA64::ClearRegisters(int reg_from, int reg_to) { - ASSERT(reg_from <= reg_to); - int num_registers = reg_to - reg_from + 1; - - // If the first capture register is cached in a hardware register but not - // aligned on a 64-bit one, we need to clear the first one specifically. - if ((reg_from < kNumCachedRegisters) && ((reg_from % 2) != 0)) { - StoreRegister(reg_from, non_position_value()); - num_registers--; - reg_from++; - } - - // Clear cached registers in pairs as far as possible. - while ((num_registers >= 2) && (reg_from < kNumCachedRegisters)) { - ASSERT(GetRegisterState(reg_from) == CACHED_LSW); - __ Mov(GetCachedRegister(reg_from), twice_non_position_value()); - reg_from += 2; - num_registers -= 2; - } - - if ((num_registers % 2) == 1) { - StoreRegister(reg_from, non_position_value()); - num_registers--; - reg_from++; - } - - if (num_registers > 0) { - // If there are some remaining registers, they are stored on the stack. - ASSERT(reg_from >= kNumCachedRegisters); - - // Move down the indexes of the registers on stack to get the correct offset - // in memory. - reg_from -= kNumCachedRegisters; - reg_to -= kNumCachedRegisters; - // We should not unroll the loop for less than 2 registers. - STATIC_ASSERT(kNumRegistersToUnroll > 2); - // We position the base pointer to (reg_from + 1). - int base_offset = kFirstRegisterOnStack - - kWRegSizeInBytes - (kWRegSizeInBytes * reg_from); - if (num_registers > kNumRegistersToUnroll) { - Register base = x10; - __ Add(base, frame_pointer(), base_offset); - - Label loop; - __ Mov(x11, num_registers); - __ Bind(&loop); - __ Str(twice_non_position_value(), - MemOperand(base, -kPointerSize, PostIndex)); - __ Sub(x11, x11, 2); - __ Cbnz(x11, &loop); - } else { - for (int i = reg_from; i <= reg_to; i += 2) { - __ Str(twice_non_position_value(), - MemOperand(frame_pointer(), base_offset)); - base_offset -= kWRegSizeInBytes * 2; - } - } - } -} - - -void RegExpMacroAssemblerA64::WriteStackPointerToRegister(int reg) { - __ Ldr(x10, MemOperand(frame_pointer(), kStackBase)); - __ Sub(x10, backtrack_stackpointer(), x10); - if (masm_->emit_debug_code()) { - __ Cmp(x10, Operand(w10, SXTW)); - // The stack offset needs to fit in a W register. - __ Check(eq, kOffsetOutOfRange); - } - StoreRegister(reg, w10); -} - - -// 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(re_frame + frame_offset); -} - - -int RegExpMacroAssemblerA64::CheckStackGuardState(Address* return_address, - Code* re_code, - Address re_frame, - int start_offset, - const byte** input_start, - const byte** input_end) { - Isolate* isolate = frame_entry(re_frame, kIsolate); - if (isolate->stack_guard()->IsStackOverflow()) { - isolate->StackOverflow(); - return EXCEPTION; - } - - // If not real stack overflow the stack guard was used to interrupt - // execution for another purpose. - - // If this is a direct call from JavaScript retry the RegExp forcing the call - // through the runtime system. Currently the direct call cannot handle a GC. - if (frame_entry(re_frame, kDirectCall) == 1) { - return RETRY; - } - - // Prepare for possible GC. - HandleScope handles(isolate); - Handle code_handle(re_code); - - Handle subject(frame_entry(re_frame, kInput)); - - // Current string. - bool is_ascii = subject->IsOneByteRepresentationUnderneath(); - - ASSERT(re_code->instruction_start() <= *return_address); - ASSERT(*return_address <= - re_code->instruction_start() + re_code->instruction_size()); - - MaybeObject* result = Execution::HandleStackGuardInterrupt(isolate); - - if (*code_handle != re_code) { // Return address no longer valid - int delta = code_handle->address() - re_code->address(); - // Overwrite the return address on the stack. - *return_address += delta; - } - - if (result->IsException()) { - return EXCEPTION; - } - - Handle subject_tmp = subject; - int slice_offset = 0; - - // Extract the underlying string and the slice offset. - if (StringShape(*subject_tmp).IsCons()) { - subject_tmp = Handle(ConsString::cast(*subject_tmp)->first()); - } else if (StringShape(*subject_tmp).IsSliced()) { - SlicedString* slice = SlicedString::cast(*subject_tmp); - subject_tmp = Handle(slice->parent()); - slice_offset = slice->offset(); - } - - // String might have changed. - if (subject_tmp->IsOneByteRepresentation() != is_ascii) { - // If we changed between an ASCII and an UC16 string, the specialized - // code cannot be used, and we need to restart regexp matching from - // scratch (including, potentially, compiling a new version of the code). - return RETRY; - } - - // Otherwise, the content of the string might have moved. It must still - // be a sequential or external string with the same content. - // Update the start and end pointers in the stack frame to the current - // location (whether it has actually moved or not). - ASSERT(StringShape(*subject_tmp).IsSequential() || - StringShape(*subject_tmp).IsExternal()); - - // The original start address of the characters to match. - const byte* start_address = *input_start; - - // Find the current start address of the same character at the current string - // position. - const byte* new_address = StringCharacterPosition(*subject_tmp, - start_offset + slice_offset); - - if (start_address != new_address) { - // If there is a difference, update the object pointer and start and end - // addresses in the RegExp stack frame to match the new value. - const byte* end_address = *input_end; - int byte_length = static_cast(end_address - start_address); - frame_entry(re_frame, kInput) = *subject; - *input_start = new_address; - *input_end = new_address + byte_length; - } else if (frame_entry(re_frame, kInput) != *subject) { - // Subject string might have been a ConsString that underwent - // short-circuiting during GC. That will not change start_address but - // will change pointer inside the subject handle. - frame_entry(re_frame, kInput) = *subject; - } - - return 0; -} - - -void RegExpMacroAssemblerA64::CheckPosition(int cp_offset, - Label* on_outside_input) { - CompareAndBranchOrBacktrack(current_input_offset(), - -cp_offset * char_size(), - ge, - on_outside_input); -} - - -bool RegExpMacroAssemblerA64::CanReadUnaligned() { - // TODO(pielan): See whether or not we should disable unaligned accesses. - return !slow_safe(); -} - - -// Private methods: - -void RegExpMacroAssemblerA64::CallCheckStackGuardState(Register scratch) { - // Allocate space on the stack to store the return address. The - // CheckStackGuardState C++ function will override it if the code - // moved. Allocate extra space for 2 arguments passed by pointers. - // AAPCS64 requires the stack to be 16 byte aligned. - int alignment = masm_->ActivationFrameAlignment(); - ASSERT_EQ(alignment % 16, 0); - int align_mask = (alignment / kXRegSizeInBytes) - 1; - int xreg_to_claim = (3 + align_mask) & ~align_mask; - - ASSERT(csp.Is(__ StackPointer())); - __ Claim(xreg_to_claim); - - // CheckStackGuardState needs the end and start addresses of the input string. - __ Poke(input_end(), 2 * kPointerSize); - __ Add(x5, csp, 2 * kPointerSize); - __ Poke(input_start(), kPointerSize); - __ Add(x4, csp, kPointerSize); - - __ Mov(w3, start_offset()); - // RegExp code frame pointer. - __ Mov(x2, frame_pointer()); - // Code* of self. - __ Mov(x1, Operand(masm_->CodeObject())); - - // We need to pass a pointer to the return address as first argument. - // The DirectCEntry stub will place the return address on the stack before - // calling so the stack pointer will point to it. - __ Mov(x0, csp); - - ExternalReference check_stack_guard_state = - ExternalReference::re_check_stack_guard_state(isolate()); - __ Mov(scratch, Operand(check_stack_guard_state)); - DirectCEntryStub stub; - stub.GenerateCall(masm_, scratch); - - // The input string may have been moved in memory, we need to reload it. - __ Peek(input_start(), kPointerSize); - __ Peek(input_end(), 2 * kPointerSize); - - ASSERT(csp.Is(__ StackPointer())); - __ Drop(xreg_to_claim); - - // Reload the Code pointer. - __ Mov(code_pointer(), Operand(masm_->CodeObject())); -} - -void RegExpMacroAssemblerA64::BranchOrBacktrack(Condition condition, - Label* to) { - if (condition == al) { // Unconditional. - if (to == NULL) { - Backtrack(); - return; - } - __ B(to); - return; - } - if (to == NULL) { - to = &backtrack_label_; - } - // TODO(ulan): do direct jump when jump distance is known and fits in imm19. - Condition inverted_condition = InvertCondition(condition); - Label no_branch; - __ B(inverted_condition, &no_branch); - __ B(to); - __ Bind(&no_branch); -} - -void RegExpMacroAssemblerA64::CompareAndBranchOrBacktrack(Register reg, - int immediate, - Condition condition, - Label* to) { - if ((immediate == 0) && ((condition == eq) || (condition == ne))) { - if (to == NULL) { - to = &backtrack_label_; - } - // TODO(ulan): do direct jump when jump distance is known and fits in imm19. - Label no_branch; - if (condition == eq) { - __ Cbnz(reg, &no_branch); - } else { - __ Cbz(reg, &no_branch); - } - __ B(to); - __ Bind(&no_branch); - } else { - __ Cmp(reg, immediate); - BranchOrBacktrack(condition, to); - } -} - - -void RegExpMacroAssemblerA64::CheckPreemption() { - // Check for preemption. - ExternalReference stack_limit = - ExternalReference::address_of_stack_limit(isolate()); - __ Mov(x10, Operand(stack_limit)); - __ Ldr(x10, MemOperand(x10)); - ASSERT(csp.Is(__ StackPointer())); - __ Cmp(csp, x10); - CallIf(&check_preempt_label_, ls); -} - - -void RegExpMacroAssemblerA64::CheckStackLimit() { - ExternalReference stack_limit = - ExternalReference::address_of_regexp_stack_limit(isolate()); - __ Mov(x10, Operand(stack_limit)); - __ Ldr(x10, MemOperand(x10)); - __ Cmp(backtrack_stackpointer(), x10); - CallIf(&stack_overflow_label_, ls); -} - - -void RegExpMacroAssemblerA64::Push(Register source) { - ASSERT(source.Is32Bits()); - ASSERT(!source.is(backtrack_stackpointer())); - __ Str(source, - MemOperand(backtrack_stackpointer(), - -static_cast(kWRegSizeInBytes), - PreIndex)); -} - - -void RegExpMacroAssemblerA64::Pop(Register target) { - ASSERT(target.Is32Bits()); - ASSERT(!target.is(backtrack_stackpointer())); - __ Ldr(target, - MemOperand(backtrack_stackpointer(), kWRegSizeInBytes, PostIndex)); -} - - -Register RegExpMacroAssemblerA64::GetCachedRegister(int register_index) { - ASSERT(register_index < kNumCachedRegisters); - return Register::Create(register_index / 2, kXRegSize); -} - - -Register RegExpMacroAssemblerA64::GetRegister(int register_index, - Register maybe_result) { - ASSERT(maybe_result.Is32Bits()); - ASSERT(register_index >= 0); - if (num_registers_ <= register_index) { - num_registers_ = register_index + 1; - } - Register result; - RegisterState register_state = GetRegisterState(register_index); - switch (register_state) { - case STACKED: - __ Ldr(maybe_result, register_location(register_index)); - result = maybe_result; - break; - case CACHED_LSW: - result = GetCachedRegister(register_index).W(); - break; - case CACHED_MSW: - __ Lsr(maybe_result.X(), GetCachedRegister(register_index), kWRegSize); - result = maybe_result; - break; - default: - UNREACHABLE(); - break; - } - ASSERT(result.Is32Bits()); - return result; -} - - -void RegExpMacroAssemblerA64::StoreRegister(int register_index, - Register source) { - ASSERT(source.Is32Bits()); - ASSERT(register_index >= 0); - if (num_registers_ <= register_index) { - num_registers_ = register_index + 1; - } - - Register cached_register; - RegisterState register_state = GetRegisterState(register_index); - switch (register_state) { - case STACKED: - __ Str(source, register_location(register_index)); - break; - case CACHED_LSW: - cached_register = GetCachedRegister(register_index); - if (!source.Is(cached_register.W())) { - __ Bfi(cached_register, source.X(), 0, kWRegSize); - } - break; - case CACHED_MSW: - cached_register = GetCachedRegister(register_index); - __ Bfi(cached_register, source.X(), kWRegSize, kWRegSize); - break; - default: - UNREACHABLE(); - break; - } -} - - -void RegExpMacroAssemblerA64::CallIf(Label* to, Condition condition) { - Label skip_call; - if (condition != al) __ B(&skip_call, InvertCondition(condition)); - __ Bl(to); - __ Bind(&skip_call); -} - - -void RegExpMacroAssemblerA64::RestoreLinkRegister() { - ASSERT(csp.Is(__ StackPointer())); - __ Pop(lr, xzr); - __ Add(lr, lr, Operand(masm_->CodeObject())); -} - - -void RegExpMacroAssemblerA64::SaveLinkRegister() { - ASSERT(csp.Is(__ StackPointer())); - __ Sub(lr, lr, Operand(masm_->CodeObject())); - __ Push(xzr, lr); -} - - -MemOperand RegExpMacroAssemblerA64::register_location(int register_index) { - ASSERT(register_index < (1<<30)); - ASSERT(register_index >= kNumCachedRegisters); - if (num_registers_ <= register_index) { - num_registers_ = register_index + 1; - } - register_index -= kNumCachedRegisters; - int offset = kFirstRegisterOnStack - register_index * kWRegSizeInBytes; - return MemOperand(frame_pointer(), offset); -} - -MemOperand RegExpMacroAssemblerA64::capture_location(int register_index, - Register scratch) { - ASSERT(register_index < (1<<30)); - ASSERT(register_index < num_saved_registers_); - ASSERT(register_index >= kNumCachedRegisters); - ASSERT_EQ(register_index % 2, 0); - register_index -= kNumCachedRegisters; - int offset = kFirstCaptureOnStack - register_index * kWRegSizeInBytes; - // capture_location is used with Stp instructions to load/store 2 registers. - // The immediate field in the encoding is limited to 7 bits (signed). - if (is_int7(offset)) { - return MemOperand(frame_pointer(), offset); - } else { - __ Add(scratch, frame_pointer(), offset); - return MemOperand(scratch); - } -} - -void RegExpMacroAssemblerA64::LoadCurrentCharacterUnchecked(int cp_offset, - int characters) { - Register offset = current_input_offset(); - - // The ldr, str, ldrh, strh instructions can do unaligned accesses, if the CPU - // and the operating system running on the target allow it. - // If unaligned load/stores are not supported then this function must only - // be used to load a single character at a time. - - // ARMv8 supports unaligned accesses but V8 or the kernel can decide to - // disable it. - // TODO(pielan): See whether or not we should disable unaligned accesses. - if (!CanReadUnaligned()) { - ASSERT(characters == 1); - } - - if (cp_offset != 0) { - if (masm_->emit_debug_code()) { - __ Mov(x10, cp_offset * char_size()); - __ Add(x10, x10, Operand(current_input_offset(), SXTW)); - __ Cmp(x10, Operand(w10, SXTW)); - // The offset needs to fit in a W register. - __ Check(eq, kOffsetOutOfRange); - } else { - __ Add(w10, current_input_offset(), cp_offset * char_size()); - } - offset = w10; - } - - if (mode_ == ASCII) { - if (characters == 4) { - __ Ldr(current_character(), MemOperand(input_end(), offset, SXTW)); - } else if (characters == 2) { - __ Ldrh(current_character(), MemOperand(input_end(), offset, SXTW)); - } else { - ASSERT(characters == 1); - __ Ldrb(current_character(), MemOperand(input_end(), offset, SXTW)); - } - } else { - ASSERT(mode_ == UC16); - if (characters == 2) { - __ Ldr(current_character(), MemOperand(input_end(), offset, SXTW)); - } else { - ASSERT(characters == 1); - __ Ldrh(current_character(), MemOperand(input_end(), offset, SXTW)); - } - } -} - -#endif // V8_INTERPRETED_REGEXP - -}} // namespace v8::internal - -#endif // V8_TARGET_ARCH_A64 diff --git a/deps/v8/src/a64/regexp-macro-assembler-a64.h b/deps/v8/src/a64/regexp-macro-assembler-a64.h deleted file mode 100644 index 0f6b44b9fe..0000000000 --- a/deps/v8/src/a64/regexp-macro-assembler-a64.h +++ /dev/null @@ -1,315 +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. - -#ifndef V8_A64_REGEXP_MACRO_ASSEMBLER_A64_H_ -#define V8_A64_REGEXP_MACRO_ASSEMBLER_A64_H_ - -#include "a64/assembler-a64.h" -#include "a64/assembler-a64-inl.h" -#include "macro-assembler.h" - -namespace v8 { -namespace internal { - - -#ifndef V8_INTERPRETED_REGEXP -class RegExpMacroAssemblerA64: public NativeRegExpMacroAssembler { - public: - RegExpMacroAssemblerA64(Mode mode, int registers_to_save, Zone* zone); - virtual ~RegExpMacroAssemblerA64(); - 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(unsigned c, Label* on_equal); - virtual void CheckCharacterAfterAnd(unsigned c, - unsigned mask, - Label* on_equal); - virtual void CheckCharacterGT(uc16 limit, Label* on_greater); - virtual void CheckCharacterLT(uc16 limit, Label* on_less); - virtual void CheckCharacters(Vector str, - int cp_offset, - Label* on_failure, - bool check_end_of_string); - // 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(Label* on_not_at_start); - virtual void CheckNotBackReference(int start_reg, Label* on_no_match); - virtual void CheckNotBackReferenceIgnoreCase(int start_reg, - Label* on_no_match); - virtual void CheckNotCharacter(unsigned c, Label* on_not_equal); - virtual void CheckNotCharacterAfterAnd(unsigned c, - unsigned 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); - virtual bool CanReadUnaligned(); - - // 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, - int start_offset, - const byte** input_start, - const byte** input_end); - - private: - // Above the frame pointer - Stored registers and stack passed parameters. - // Callee-saved registers x19-x29, where x29 is the old frame pointer. - static const int kCalleeSavedRegisters = 0; - // Return address. - // It is placed above the 11 callee-saved registers. - static const int kReturnAddress = kCalleeSavedRegisters + 11 * kPointerSize; - static const int kSecondaryReturnAddress = kReturnAddress + kPointerSize; - // Stack parameter placed by caller. - static const int kIsolate = kSecondaryReturnAddress + kPointerSize; - - // Below the frame pointer. - // Register parameters stored by setup code. - static const int kDirectCall = kCalleeSavedRegisters - kPointerSize; - static const int kStackBase = kDirectCall - kPointerSize; - static const int kOutputSize = kStackBase - kPointerSize; - static const int kInput = kOutputSize - kPointerSize; - // When adding local variables remember to push space for them in - // the frame in GetCode. - static const int kSuccessCounter = kInput - kPointerSize; - // First position register address on the stack. Following positions are - // below it. A position is a 32 bit value. - static const int kFirstRegisterOnStack = kSuccessCounter - kWRegSizeInBytes; - // A capture is a 64 bit value holding two position. - static const int kFirstCaptureOnStack = kSuccessCounter - kXRegSizeInBytes; - - // Initial size of code buffer. - static const size_t kRegExpCodeSize = 1024; - - // When initializing registers to a non-position value we can unroll - // the loop. Set the limit of registers to unroll. - static const int kNumRegistersToUnroll = 16; - - // We are using x0 to x7 as a register cache. Each hardware register must - // contain one capture, that is two 32 bit registers. We can cache at most - // 16 registers. - static const int kNumCachedRegisters = 16; - - // 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); - - // Location of a 32 bit position register. - MemOperand register_location(int register_index); - - // Location of a 64 bit capture, combining two position registers. - MemOperand capture_location(int register_index, Register scratch); - - // Register holding the current input position as negative offset from - // the end of the string. - Register current_input_offset() { return w21; } - - // The register containing the current character after LoadCurrentCharacter. - Register current_character() { return w22; } - - // Register holding address of the end of the input string. - Register input_end() { return x25; } - - // Register holding address of the start of the input string. - Register input_start() { return x26; } - - // Register holding the offset from the start of the string where we should - // start matching. - Register start_offset() { return w27; } - - // Pointer to the output array's first element. - Register output_array() { return x28; } - - // Register holding the frame address. Local variables, parameters and - // regexp registers are addressed relative to this. - Register frame_pointer() { return fp; } - - // The register containing the backtrack stack top. Provides a meaningful - // name to the register. - Register backtrack_stackpointer() { return x23; } - - // Register holding pointer to the current code object. - Register code_pointer() { return x20; } - - // Register holding the value used for clearing capture registers. - Register non_position_value() { return w24; } - // The top 32 bit of this register is used to store this value - // twice. This is used for clearing more than one register at a time. - Register twice_non_position_value() { return x24; } - - // Byte size of chars in the string to match (decided by the Mode argument) - 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); - - // Compares reg against immmediate before calling BranchOrBacktrack. - // It makes use of the Cbz and Cbnz instructions. - void CompareAndBranchOrBacktrack(Register reg, - int immediate, - Condition condition, - Label* to); - - inline void CallIf(Label* to, Condition condition); - - // Save and restore the link register on the stack in a way that - // is GC-safe. - inline void SaveLinkRegister(); - inline void RestoreLinkRegister(); - - // Pushes the value of a register on the backtrack stack. Decrements the - // stack pointer by a word size and stores the register's value there. - inline void Push(Register source); - - // Pops a value from the backtrack stack. Reads the word at the stack pointer - // and increments it by a word size. - inline void Pop(Register target); - - // This state indicates where the register actually is. - enum RegisterState { - STACKED, // Resides in memory. - CACHED_LSW, // Least Significant Word of a 64 bit hardware register. - CACHED_MSW // Most Significant Word of a 64 bit hardware register. - }; - - RegisterState GetRegisterState(int register_index) { - ASSERT(register_index >= 0); - if (register_index >= kNumCachedRegisters) { - return STACKED; - } else { - if ((register_index % 2) == 0) { - return CACHED_LSW; - } else { - return CACHED_MSW; - } - } - } - - // Store helper that takes the state of the register into account. - inline void StoreRegister(int register_index, Register source); - - // Returns a hardware W register that holds the value of the capture - // register. - // - // This function will try to use an existing cache register (w0-w7) for the - // result. Otherwise, it will load the value into maybe_result. - // - // If the returned register is anything other than maybe_result, calling code - // must not write to it. - inline Register GetRegister(int register_index, Register maybe_result); - - // Returns the harware register (x0-x7) holding the value of the capture - // register. - // This assumes that the state of the register is not STACKED. - inline Register GetCachedRegister(int register_index); - - Isolate* isolate() const { return masm_->isolate(); } - - MacroAssembler* masm_; - - // Which mode to generate code for (ASCII 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 v8::internal - -#endif // V8_A64_REGEXP_MACRO_ASSEMBLER_A64_H_ diff --git a/deps/v8/src/a64/simulator-a64.cc b/deps/v8/src/a64/simulator-a64.cc deleted file mode 100644 index 014b71477d..0000000000 --- a/deps/v8/src/a64/simulator-a64.cc +++ /dev/null @@ -1,3414 +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 -#include "v8.h" - -#if V8_TARGET_ARCH_A64 - -#include "disasm.h" -#include "assembler.h" -#include "a64/simulator-a64.h" -#include "macro-assembler.h" - -namespace v8 { -namespace internal { - -#if defined(USE_SIMULATOR) - - -// This macro provides a platform independent use of sscanf. The reason for -// SScanF not being implemented in a platform independent way through -// ::v8::internal::OS in the same way as SNPrintF is that the -// Windows C Run-Time Library does not provide vsscanf. -#define SScanF sscanf // NOLINT - - -// This is basically the same as PrintF, with a guard for FLAG_trace_sim. -void PRINTF_CHECKING TraceSim(const char* format, ...) { - if (FLAG_trace_sim) { - va_list arguments; - va_start(arguments, format); - OS::VPrint(format, arguments); - va_end(arguments); - } -} - - -const Instruction* Simulator::kEndOfSimAddress = NULL; - - -void SimSystemRegister::SetBits(int msb, int lsb, uint32_t bits) { - int width = msb - lsb + 1; - ASSERT(is_uintn(bits, width) || is_intn(bits, width)); - - bits <<= lsb; - uint32_t mask = ((1 << width) - 1) << lsb; - ASSERT((mask & write_ignore_mask_) == 0); - - value_ = (value_ & ~mask) | (bits & mask); -} - - -SimSystemRegister SimSystemRegister::DefaultValueFor(SystemRegister id) { - switch (id) { - case NZCV: - return SimSystemRegister(0x00000000, NZCVWriteIgnoreMask); - case FPCR: - return SimSystemRegister(0x00000000, FPCRWriteIgnoreMask); - default: - UNREACHABLE(); - return SimSystemRegister(); - } -} - - -void Simulator::Initialize(Isolate* isolate) { - if (isolate->simulator_initialized()) return; - isolate->set_simulator_initialized(true); - ExternalReference::set_redirector(isolate, &RedirectExternalReference); -} - - -// Get the active Simulator for the current thread. -Simulator* Simulator::current(Isolate* isolate) { - Isolate::PerIsolateThreadData* isolate_data = - isolate->FindOrAllocatePerThreadDataForThisThread(); - ASSERT(isolate_data != NULL); - - Simulator* sim = isolate_data->simulator(); - if (sim == NULL) { - // TODO(146): delete the simulator object when a thread/isolate goes away. - sim = new Simulator(new Decoder(), isolate); - isolate_data->set_simulator(sim); - } - return sim; -} - - -void Simulator::CallVoid(byte* entry, CallArgument* args) { - int index_x = 0; - int index_d = 0; - - std::vector stack_args(0); - for (int i = 0; !args[i].IsEnd(); i++) { - CallArgument arg = args[i]; - if (arg.IsX() && (index_x < 8)) { - set_xreg(index_x++, arg.bits()); - } else if (arg.IsD() && (index_d < 8)) { - set_dreg_bits(index_d++, arg.bits()); - } else { - ASSERT(arg.IsD() || arg.IsX()); - stack_args.push_back(arg.bits()); - } - } - - // Process stack arguments, and make sure the stack is suitably aligned. - uintptr_t original_stack = sp(); - uintptr_t entry_stack = original_stack - - stack_args.size() * sizeof(stack_args[0]); - if (OS::ActivationFrameAlignment() != 0) { - entry_stack &= -OS::ActivationFrameAlignment(); - } - char * stack = reinterpret_cast(entry_stack); - std::vector::const_iterator it; - for (it = stack_args.begin(); it != stack_args.end(); it++) { - memcpy(stack, &(*it), sizeof(*it)); - stack += sizeof(*it); - } - - ASSERT(reinterpret_cast(stack) <= original_stack); - set_sp(entry_stack); - - // Call the generated code. - set_pc(entry); - set_lr(kEndOfSimAddress); - CheckPCSComplianceAndRun(); - - set_sp(original_stack); -} - - -int64_t Simulator::CallInt64(byte* entry, CallArgument* args) { - CallVoid(entry, args); - return xreg(0); -} - - -double Simulator::CallDouble(byte* entry, CallArgument* args) { - CallVoid(entry, args); - return dreg(0); -} - - -int64_t Simulator::CallJS(byte* entry, - byte* function_entry, - JSFunction* func, - Object* revc, - int64_t argc, - Object*** argv) { - CallArgument args[] = { - CallArgument(function_entry), - CallArgument(func), - CallArgument(revc), - CallArgument(argc), - CallArgument(argv), - CallArgument::End() - }; - return CallInt64(entry, args); -} - -int64_t Simulator::CallRegExp(byte* entry, - String* input, - int64_t start_offset, - const byte* input_start, - const byte* input_end, - int* output, - int64_t output_size, - Address stack_base, - int64_t direct_call, - void* return_address, - Isolate* isolate) { - CallArgument args[] = { - CallArgument(input), - CallArgument(start_offset), - CallArgument(input_start), - CallArgument(input_end), - CallArgument(output), - CallArgument(output_size), - CallArgument(stack_base), - CallArgument(direct_call), - CallArgument(return_address), - CallArgument(isolate), - CallArgument::End() - }; - return CallInt64(entry, args); -} - - -void Simulator::CheckPCSComplianceAndRun() { -#ifdef DEBUG - CHECK_EQ(kNumberOfCalleeSavedRegisters, kCalleeSaved.Count()); - CHECK_EQ(kNumberOfCalleeSavedFPRegisters, kCalleeSavedFP.Count()); - - int64_t saved_registers[kNumberOfCalleeSavedRegisters]; - uint64_t saved_fpregisters[kNumberOfCalleeSavedFPRegisters]; - - CPURegList register_list = kCalleeSaved; - CPURegList fpregister_list = kCalleeSavedFP; - - for (int i = 0; i < kNumberOfCalleeSavedRegisters; i++) { - // x31 is not a caller saved register, so no need to specify if we want - // the stack or zero. - saved_registers[i] = xreg(register_list.PopLowestIndex().code()); - } - for (int i = 0; i < kNumberOfCalleeSavedFPRegisters; i++) { - saved_fpregisters[i] = - dreg_bits(fpregister_list.PopLowestIndex().code()); - } - int64_t original_stack = sp(); -#endif - // Start the simulation! - Run(); -#ifdef DEBUG - CHECK_EQ(original_stack, sp()); - // Check that callee-saved registers have been preserved. - register_list = kCalleeSaved; - fpregister_list = kCalleeSavedFP; - for (int i = 0; i < kNumberOfCalleeSavedRegisters; i++) { - CHECK_EQ(saved_registers[i], xreg(register_list.PopLowestIndex().code())); - } - for (int i = 0; i < kNumberOfCalleeSavedFPRegisters; i++) { - ASSERT(saved_fpregisters[i] == - dreg_bits(fpregister_list.PopLowestIndex().code())); - } - - // Corrupt caller saved register minus the return regiters. - - // In theory x0 to x7 can be used for return values, but V8 only uses x0, x1 - // for now . - register_list = kCallerSaved; - register_list.Remove(x0); - register_list.Remove(x1); - - // In theory d0 to d7 can be used for return values, but V8 only uses d0 - // for now . - fpregister_list = kCallerSavedFP; - fpregister_list.Remove(d0); - - CorruptRegisters(®ister_list, kCallerSavedRegisterCorruptionValue); - CorruptRegisters(&fpregister_list, kCallerSavedFPRegisterCorruptionValue); -#endif -} - - -#ifdef DEBUG -// The least significant byte of the curruption value holds the corresponding -// register's code. -void Simulator::CorruptRegisters(CPURegList* list, uint64_t value) { - if (list->type() == CPURegister::kRegister) { - while (!list->IsEmpty()) { - unsigned code = list->PopLowestIndex().code(); - set_xreg(code, value | code); - } - } else { - ASSERT(list->type() == CPURegister::kFPRegister); - while (!list->IsEmpty()) { - unsigned code = list->PopLowestIndex().code(); - set_dreg_bits(code, value | code); - } - } -} - - -void Simulator::CorruptAllCallerSavedCPURegisters() { - // Corrupt alters its parameter so copy them first. - CPURegList register_list = kCallerSaved; - CPURegList fpregister_list = kCallerSavedFP; - - CorruptRegisters(®ister_list, kCallerSavedRegisterCorruptionValue); - CorruptRegisters(&fpregister_list, kCallerSavedFPRegisterCorruptionValue); -} -#endif - - -// Extending the stack by 2 * 64 bits is required for stack alignment purposes. -// TODO(all): Insert a marker in the extra space allocated on the stack. -uintptr_t Simulator::PushAddress(uintptr_t address) { - ASSERT(sizeof(uintptr_t) < 2 * kXRegSizeInBytes); - intptr_t new_sp = sp() - 2 * kXRegSizeInBytes; - uintptr_t* stack_slot = reinterpret_cast(new_sp); - *stack_slot = address; - set_sp(new_sp); - return new_sp; -} - - -uintptr_t Simulator::PopAddress() { - intptr_t current_sp = sp(); - uintptr_t* stack_slot = reinterpret_cast(current_sp); - uintptr_t address = *stack_slot; - ASSERT(sizeof(uintptr_t) < 2 * kXRegSizeInBytes); - set_sp(current_sp + 2 * kXRegSizeInBytes); - return address; -} - - -// Returns the limit of the stack area to enable checking for stack overflows. -uintptr_t Simulator::StackLimit() const { - // Leave a safety margin of 1024 bytes to prevent overrunning the stack when - // pushing values. - // TODO(all): Increase the stack limit protection. - - // The margin was decreased to 256 bytes, because we are intensively using - // the stack. The stack usage should decrease when our code improves. Then - // we can set it to 1024 again. - return reinterpret_cast(stack_limit_) + 256; -} - - -Simulator::Simulator(Decoder* decoder, Isolate* isolate, FILE* stream) - : decoder_(decoder), last_debugger_input_(NULL), log_parameters_(NO_PARAM), - isolate_(isolate) { - // Setup the decoder. - decoder_->AppendVisitor(this); - - ResetState(); - - // Allocate and setup the simulator stack. - stack_size_ = (FLAG_sim_stack_size * KB) + (2 * stack_protection_size_); - stack_ = new byte[stack_size_]; - stack_limit_ = stack_ + stack_protection_size_; - byte* tos = stack_ + stack_size_ - stack_protection_size_; - // The stack pointer must be 16 bytes aligned. - set_sp(reinterpret_cast(tos) & ~0xfUL); - - stream_ = stream; - print_disasm_ = new PrintDisassembler(stream_); - - if (FLAG_trace_sim) { - decoder_->InsertVisitorBefore(print_disasm_, this); - log_parameters_ = LOG_ALL; - } - - // The debugger needs to disassemble code without the simulator executing an - // instruction, so we create a dedicated decoder. - disassembler_decoder_ = new Decoder(); - disassembler_decoder_->AppendVisitor(print_disasm_); - - if (FLAG_log_instruction_stats) { - instrument_ = new Instrument(FLAG_log_instruction_file, - FLAG_log_instruction_period); - decoder_->AppendVisitor(instrument_); - } -} - - -void Simulator::ResetState() { - // Reset the system registers. - nzcv_ = SimSystemRegister::DefaultValueFor(NZCV); - fpcr_ = SimSystemRegister::DefaultValueFor(FPCR); - - // Reset registers to 0. - pc_ = NULL; - for (unsigned i = 0; i < kNumberOfRegisters; i++) { - set_xreg(i, 0xbadbeef); - } - for (unsigned i = 0; i < kNumberOfFPRegisters; i++) { - // Set FP registers to a value that is NaN in both 32-bit and 64-bit FP. - set_dreg_bits(i, 0x7ff000007f800001UL); - } - // Returning to address 0 exits the Simulator. - set_lr(kEndOfSimAddress); - - // Reset debug helpers. - breakpoints_.empty(); - break_on_next_= false; -} - - -Simulator::~Simulator() { - delete[] stack_; - if (FLAG_log_instruction_stats) { - delete instrument_; - } - delete disassembler_decoder_; - delete print_disasm_; - DeleteArray(last_debugger_input_); -} - - -void Simulator::Run() { - pc_modified_ = false; - while (pc_ != kEndOfSimAddress) { - ExecuteInstruction(); - } -} - - -void Simulator::RunFrom(Instruction* start) { - set_pc(start); - Run(); -} - - -void Simulator::CheckStackAlignment() { - // TODO(aleram): The sp alignment check to perform depends on the processor - // state. Check the specifications for more details. -} - - -// When the generated code calls an external reference we need to catch that in -// the simulator. The external reference will be a function compiled for the -// host architecture. We need to call that function instead of trying to -// execute it with the simulator. We do that by redirecting the external -// reference to a svc (Supervisor Call) instruction that is handled by -// the simulator. We write the original destination of the jump just at a known -// offset from the svc instruction so the simulator knows what to call. -class Redirection { - public: - Redirection(void* external_function, ExternalReference::Type type) - : external_function_(external_function), - type_(type), - next_(NULL) { - redirect_call_.SetInstructionBits( - HLT | Assembler::ImmException(kImmExceptionIsRedirectedCall)); - Isolate* isolate = Isolate::Current(); - next_ = isolate->simulator_redirection(); - // TODO(all): Simulator flush I cache - isolate->set_simulator_redirection(this); - } - - void* address_of_redirect_call() { - return reinterpret_cast(&redirect_call_); - } - - void* external_function() { return external_function_; } - ExternalReference::Type type() { return type_; } - - static Redirection* Get(void* external_function, - ExternalReference::Type type) { - Isolate* isolate = Isolate::Current(); - Redirection* current = isolate->simulator_redirection(); - for (; current != NULL; current = current->next_) { - if (current->external_function_ == external_function) { - ASSERT_EQ(current->type(), type); - return current; - } - } - return new Redirection(external_function, type); - } - - static Redirection* FromHltInstruction(Instruction* redirect_call) { - char* addr_of_hlt = reinterpret_cast(redirect_call); - char* addr_of_redirection = - addr_of_hlt - OFFSET_OF(Redirection, redirect_call_); - return reinterpret_cast(addr_of_redirection); - } - - static void* ReverseRedirection(int64_t reg) { - Redirection* redirection = - FromHltInstruction(reinterpret_cast(reg)); - return redirection->external_function(); - } - - private: - void* external_function_; - Instruction redirect_call_; - ExternalReference::Type type_; - Redirection* next_; -}; - - -void* Simulator::RedirectExternalReference(void* external_function, - ExternalReference::Type type) { - Redirection* redirection = Redirection::Get(external_function, type); - return redirection->address_of_redirect_call(); -} - - -const char* Simulator::xreg_names[] = { -"x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", -"x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", -"ip0", "ip1", "x18", "x19", "x20", "x21", "x22", "x23", -"x24", "x25", "x26", "cp", "jssp", "fp", "lr", "xzr", "csp"}; - -const char* Simulator::wreg_names[] = { -"w0", "w1", "w2", "w3", "w4", "w5", "w6", "w7", -"w8", "w9", "w10", "w11", "w12", "w13", "w14", "w15", -"w16", "w17", "w18", "w19", "w20", "w21", "w22", "w23", -"w24", "w25", "w26", "wcp", "wjssp", "wfp", "wlr", "wzr", "wcsp"}; - -const char* Simulator::sreg_names[] = { -"s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", -"s8", "s9", "s10", "s11", "s12", "s13", "s14", "s15", -"s16", "s17", "s18", "s19", "s20", "s21", "s22", "s23", -"s24", "s25", "s26", "s27", "s28", "s29", "s30", "s31"}; - -const char* Simulator::dreg_names[] = { -"d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", -"d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15", -"d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23", -"d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31"}; - -const char* Simulator::vreg_names[] = { -"v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", -"v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15", -"v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23", -"v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31"}; - - -const char* Simulator::WRegNameForCode(unsigned code, Reg31Mode mode) { - ASSERT(code < kNumberOfRegisters); - // If the code represents the stack pointer, index the name after zr. - if ((code == kZeroRegCode) && (mode == Reg31IsStackPointer)) { - code = kZeroRegCode + 1; - } - return wreg_names[code]; -} - - -const char* Simulator::XRegNameForCode(unsigned code, Reg31Mode mode) { - ASSERT(code < kNumberOfRegisters); - // If the code represents the stack pointer, index the name after zr. - if ((code == kZeroRegCode) && (mode == Reg31IsStackPointer)) { - code = kZeroRegCode + 1; - } - return xreg_names[code]; -} - - -const char* Simulator::SRegNameForCode(unsigned code) { - ASSERT(code < kNumberOfFPRegisters); - return sreg_names[code]; -} - - -const char* Simulator::DRegNameForCode(unsigned code) { - ASSERT(code < kNumberOfFPRegisters); - return dreg_names[code]; -} - - -const char* Simulator::VRegNameForCode(unsigned code) { - ASSERT(code < kNumberOfFPRegisters); - return vreg_names[code]; -} - - -int Simulator::CodeFromName(const char* name) { - for (unsigned i = 0; i < kNumberOfRegisters; i++) { - if ((strcmp(xreg_names[i], name) == 0) || - (strcmp(wreg_names[i], name) == 0)) { - return i; - } - } - for (unsigned i = 0; i < kNumberOfFPRegisters; i++) { - if ((strcmp(vreg_names[i], name) == 0) || - (strcmp(dreg_names[i], name) == 0) || - (strcmp(sreg_names[i], name) == 0)) { - return i; - } - } - if ((strcmp("csp", name) == 0) || (strcmp("wcsp", name) == 0)) { - return kSPRegInternalCode; - } - return -1; -} - - -// Helpers --------------------------------------------------------------------- -int64_t Simulator::AddWithCarry(unsigned reg_size, - bool set_flags, - int64_t src1, - int64_t src2, - int64_t carry_in) { - ASSERT((carry_in == 0) || (carry_in == 1)); - ASSERT((reg_size == kXRegSize) || (reg_size == kWRegSize)); - - uint64_t u1, u2; - int64_t result; - int64_t signed_sum = src1 + src2 + carry_in; - - uint32_t N, Z, C, V; - - if (reg_size == kWRegSize) { - u1 = static_cast(src1) & kWRegMask; - u2 = static_cast(src2) & kWRegMask; - - result = signed_sum & kWRegMask; - // Compute the C flag by comparing the sum to the max unsigned integer. - C = ((kWMaxUInt - u1) < (u2 + carry_in)) || - ((kWMaxUInt - u1 - carry_in) < u2); - // Overflow iff the sign bit is the same for the two inputs and different - // for the result. - int64_t s_src1 = src1 << (kXRegSize - kWRegSize); - int64_t s_src2 = src2 << (kXRegSize - kWRegSize); - int64_t s_result = result << (kXRegSize - kWRegSize); - V = ((s_src1 ^ s_src2) >= 0) && ((s_src1 ^ s_result) < 0); - - } else { - u1 = static_cast(src1); - u2 = static_cast(src2); - - result = signed_sum; - // Compute the C flag by comparing the sum to the max unsigned integer. - C = ((kXMaxUInt - u1) < (u2 + carry_in)) || - ((kXMaxUInt - u1 - carry_in) < u2); - // Overflow iff the sign bit is the same for the two inputs and different - // for the result. - V = ((src1 ^ src2) >= 0) && ((src1 ^ result) < 0); - } - - N = CalcNFlag(result, reg_size); - Z = CalcZFlag(result); - - if (set_flags) { - nzcv().SetN(N); - nzcv().SetZ(Z); - nzcv().SetC(C); - nzcv().SetV(V); - } - return result; -} - - -int64_t Simulator::ShiftOperand(unsigned reg_size, - int64_t value, - Shift shift_type, - unsigned amount) { - if (amount == 0) { - return value; - } - int64_t mask = reg_size == kXRegSize ? kXRegMask : kWRegMask; - switch (shift_type) { - case LSL: - return (value << amount) & mask; - case LSR: - return static_cast(value) >> amount; - case ASR: { - // Shift used to restore the sign. - unsigned s_shift = kXRegSize - reg_size; - // Value with its sign restored. - int64_t s_value = (value << s_shift) >> s_shift; - return (s_value >> amount) & mask; - } - case ROR: { - if (reg_size == kWRegSize) { - value &= kWRegMask; - } - return (static_cast(value) >> amount) | - ((value & ((1L << amount) - 1L)) << (reg_size - amount)); - } - default: - UNIMPLEMENTED(); - return 0; - } -} - - -int64_t Simulator::ExtendValue(unsigned reg_size, - int64_t value, - Extend extend_type, - unsigned left_shift) { - switch (extend_type) { - case UXTB: - value &= kByteMask; - break; - case UXTH: - value &= kHalfWordMask; - break; - case UXTW: - value &= kWordMask; - break; - case SXTB: - value = (value << 56) >> 56; - break; - case SXTH: - value = (value << 48) >> 48; - break; - case SXTW: - value = (value << 32) >> 32; - break; - case UXTX: - case SXTX: - break; - default: - UNREACHABLE(); - } - int64_t mask = (reg_size == kXRegSize) ? kXRegMask : kWRegMask; - return (value << left_shift) & mask; -} - - -void Simulator::FPCompare(double val0, double val1) { - AssertSupportedFPCR(); - - // TODO(jbramley): This assumes that the C++ implementation handles - // comparisons in the way that we expect (as per AssertSupportedFPCR()). - if ((std::isnan(val0) != 0) || (std::isnan(val1) != 0)) { - nzcv().SetRawValue(FPUnorderedFlag); - } else if (val0 < val1) { - nzcv().SetRawValue(FPLessThanFlag); - } else if (val0 > val1) { - nzcv().SetRawValue(FPGreaterThanFlag); - } else if (val0 == val1) { - nzcv().SetRawValue(FPEqualFlag); - } else { - UNREACHABLE(); - } -} - - -void Simulator::SetBreakpoint(Instruction* location) { - for (unsigned i = 0; i < breakpoints_.size(); i++) { - if (breakpoints_.at(i).location == location) { - PrintF("Existing breakpoint at %p was %s\n", - reinterpret_cast(location), - breakpoints_.at(i).enabled ? "disabled" : "enabled"); - breakpoints_.at(i).enabled = !breakpoints_.at(i).enabled; - return; - } - } - Breakpoint new_breakpoint = {location, true}; - breakpoints_.push_back(new_breakpoint); - PrintF("Set a breakpoint at %p\n", reinterpret_cast(location)); -} - - -void Simulator::ListBreakpoints() { - PrintF("Breakpoints:\n"); - for (unsigned i = 0; i < breakpoints_.size(); i++) { - PrintF("%p : %s\n", - reinterpret_cast(breakpoints_.at(i).location), - breakpoints_.at(i).enabled ? "enabled" : "disabled"); - } -} - - -void Simulator::CheckBreakpoints() { - bool hit_a_breakpoint = false; - for (unsigned i = 0; i < breakpoints_.size(); i++) { - if ((breakpoints_.at(i).location == pc_) && - breakpoints_.at(i).enabled) { - hit_a_breakpoint = true; - // Disable this breakpoint. - breakpoints_.at(i).enabled = false; - } - } - if (hit_a_breakpoint) { - PrintF("Hit and disabled a breakpoint at %p.\n", - reinterpret_cast(pc_)); - Debug(); - } -} - - -void Simulator::CheckBreakNext() { - // If the current instruction is a BL, insert a breakpoint just after it. - if (break_on_next_ && pc_->IsBranchAndLinkToRegister()) { - SetBreakpoint(pc_->NextInstruction()); - break_on_next_ = false; - } -} - - -void Simulator::PrintInstructionsAt(Instruction* start, uint64_t count) { - Instruction* end = start->InstructionAtOffset(count * kInstructionSize); - for (Instruction* pc = start; pc < end; pc = pc->NextInstruction()) { - disassembler_decoder_->Decode(pc); - } -} - - -void Simulator::PrintSystemRegisters(bool print_all) { - static bool first_run = true; - - // Define some colour codes to use for the register dump. - // TODO(jbramley): Find a more elegant way of defining these. - char const * const clr_normal = (FLAG_log_colour) ? ("\033[m") : (""); - char const * const clr_flag_name = (FLAG_log_colour) ? ("\033[1;30m") : (""); - char const * const clr_flag_value = (FLAG_log_colour) ? ("\033[1;37m") : (""); - - static SimSystemRegister last_nzcv; - if (print_all || first_run || (last_nzcv.RawValue() != nzcv().RawValue())) { - fprintf(stream_, "# %sFLAGS: %sN:%d Z:%d C:%d V:%d%s\n", - clr_flag_name, - clr_flag_value, - N(), Z(), C(), V(), - clr_normal); - } - last_nzcv = nzcv(); - - static SimSystemRegister last_fpcr; - if (print_all || first_run || (last_fpcr.RawValue() != fpcr().RawValue())) { - static const char * rmode[] = { - "0b00 (Round to Nearest)", - "0b01 (Round towards Plus Infinity)", - "0b10 (Round towards Minus Infinity)", - "0b11 (Round towards Zero)" - }; - ASSERT(fpcr().RMode() <= (sizeof(rmode) / sizeof(rmode[0]))); - fprintf(stream_, "# %sFPCR: %sAHP:%d DN:%d FZ:%d RMode:%s%s\n", - clr_flag_name, - clr_flag_value, - fpcr().AHP(), fpcr().DN(), fpcr().FZ(), rmode[fpcr().RMode()], - clr_normal); - } - last_fpcr = fpcr(); - - first_run = false; -} - - -void Simulator::PrintRegisters(bool print_all_regs) { - static bool first_run = true; - static int64_t last_regs[kNumberOfRegisters]; - - // Define some colour codes to use for the register dump. - // TODO(jbramley): Find a more elegant way of defining these. - char const * const clr_normal = (FLAG_log_colour) ? ("\033[m") : (""); - char const * const clr_reg_name = (FLAG_log_colour) ? ("\033[1;34m") : (""); - char const * const clr_reg_value = (FLAG_log_colour) ? ("\033[1;36m") : (""); - - for (unsigned i = 0; i < kNumberOfRegisters; i++) { - if (print_all_regs || first_run || - (last_regs[i] != xreg(i, Reg31IsStackPointer))) { - fprintf(stream_, - "# %s%4s:%s 0x%016" PRIx64 "%s\n", - clr_reg_name, - XRegNameForCode(i, Reg31IsStackPointer), - clr_reg_value, - xreg(i, Reg31IsStackPointer), - clr_normal); - } - // Cache the new register value so the next run can detect any changes. - last_regs[i] = xreg(i, Reg31IsStackPointer); - } - first_run = false; -} - - -void Simulator::PrintFPRegisters(bool print_all_regs) { - static bool first_run = true; - static uint64_t last_regs[kNumberOfFPRegisters]; - - // Define some colour codes to use for the register dump. - // TODO(jbramley): Find a more elegant way of defining these. - char const * const clr_normal = (FLAG_log_colour) ? ("\033[m") : (""); - char const * const clr_reg_name = (FLAG_log_colour) ? ("\033[1;33m") : (""); - char const * const clr_reg_value = (FLAG_log_colour) ? ("\033[1;35m") : (""); - - // Print as many rows of registers as necessary, keeping each individual - // register in the same column each time (to make it easy to visually scan - // for changes). - for (unsigned i = 0; i < kNumberOfFPRegisters; i++) { - if (print_all_regs || first_run || (last_regs[i] != dreg_bits(i))) { - fprintf(stream_, - "# %s %4s:%s 0x%016" PRIx64 "%s (%s%s:%s %g%s %s:%s %g%s)\n", - clr_reg_name, - VRegNameForCode(i), - clr_reg_value, - dreg_bits(i), - clr_normal, - clr_reg_name, - DRegNameForCode(i), - clr_reg_value, - dreg(i), - clr_reg_name, - SRegNameForCode(i), - clr_reg_value, - sreg(i), - clr_normal); - } - // Cache the new register value so the next run can detect any changes. - last_regs[i] = dreg_bits(i); - } - first_run = false; -} - - -void Simulator::PrintProcessorState() { - PrintSystemRegisters(); - PrintRegisters(); - PrintFPRegisters(); -} - - -void Simulator::PrintWrite(uint8_t* address, - uint64_t value, - unsigned num_bytes) { - // Define some color codes to use for memory logging. - const char* const clr_normal = (FLAG_log_colour) ? ("\033[m") - : (""); - const char* const clr_memory_value = (FLAG_log_colour) ? ("\033[1;32m") - : (""); - const char* const clr_memory_address = (FLAG_log_colour) ? ("\033[32m") - : (""); - - // The template is "# value -> address". The template is not directly used - // in the printf since compilers tend to struggle with the parametrized - // width (%0*). - const char* format = "# %s0x%0*" PRIx64 "%s -> %s0x%016" PRIx64 "%s\n"; - fprintf(stream_, - format, - clr_memory_value, - num_bytes * 2, // The width in hexa characters. - value, - clr_normal, - clr_memory_address, - address, - clr_normal); -} - - -// Visitors--------------------------------------------------------------------- - -void Simulator::VisitUnimplemented(Instruction* instr) { - fprintf(stream_, "Unimplemented instruction at %p: 0x%08" PRIx32 "\n", - reinterpret_cast(instr), instr->InstructionBits()); - UNIMPLEMENTED(); -} - - -void Simulator::VisitUnallocated(Instruction* instr) { - fprintf(stream_, "Unallocated instruction at %p: 0x%08" PRIx32 "\n", - reinterpret_cast(instr), instr->InstructionBits()); - UNIMPLEMENTED(); -} - - -void Simulator::VisitPCRelAddressing(Instruction* instr) { - switch (instr->Mask(PCRelAddressingMask)) { - case ADR: - set_reg(instr->Rd(), instr->ImmPCOffsetTarget()); - break; - case ADRP: // Not implemented in the assembler. - UNIMPLEMENTED(); - break; - default: - UNREACHABLE(); - break; - } -} - - -void Simulator::VisitUnconditionalBranch(Instruction* instr) { - switch (instr->Mask(UnconditionalBranchMask)) { - case BL: - set_lr(instr->NextInstruction()); - // Fall through. - case B: - set_pc(instr->ImmPCOffsetTarget()); - break; - default: - UNREACHABLE(); - } -} - - -void Simulator::VisitConditionalBranch(Instruction* instr) { - ASSERT(instr->Mask(ConditionalBranchMask) == B_cond); - if (ConditionPassed(static_cast(instr->ConditionBranch()))) { - set_pc(instr->ImmPCOffsetTarget()); - } -} - - -void Simulator::VisitUnconditionalBranchToRegister(Instruction* instr) { - Instruction* target = reg(instr->Rn()); - switch (instr->Mask(UnconditionalBranchToRegisterMask)) { - case BLR: { - set_lr(instr->NextInstruction()); - if (instr->Rn() == 31) { - // BLR XZR is used as a guard for the constant pool. We should never hit - // this, but if we do trap to allow debugging. - Debug(); - } - // Fall through. - } - case BR: - case RET: set_pc(target); break; - default: UNIMPLEMENTED(); - } -} - - -void Simulator::VisitTestBranch(Instruction* instr) { - unsigned bit_pos = (instr->ImmTestBranchBit5() << 5) | - instr->ImmTestBranchBit40(); - bool take_branch = ((xreg(instr->Rt()) & (1UL << bit_pos)) == 0); - switch (instr->Mask(TestBranchMask)) { - case TBZ: break; - case TBNZ: take_branch = !take_branch; break; - default: UNIMPLEMENTED(); - } - if (take_branch) { - set_pc(instr->ImmPCOffsetTarget()); - } -} - - -void Simulator::VisitCompareBranch(Instruction* instr) { - unsigned rt = instr->Rt(); - bool take_branch = false; - switch (instr->Mask(CompareBranchMask)) { - case CBZ_w: take_branch = (wreg(rt) == 0); break; - case CBZ_x: take_branch = (xreg(rt) == 0); break; - case CBNZ_w: take_branch = (wreg(rt) != 0); break; - case CBNZ_x: take_branch = (xreg(rt) != 0); break; - default: UNIMPLEMENTED(); - } - if (take_branch) { - set_pc(instr->ImmPCOffsetTarget()); - } -} - - -void Simulator::AddSubHelper(Instruction* instr, int64_t op2) { - unsigned reg_size = instr->SixtyFourBits() ? kXRegSize : kWRegSize; - bool set_flags = instr->FlagsUpdate(); - int64_t new_val = 0; - Instr operation = instr->Mask(AddSubOpMask); - - switch (operation) { - case ADD: - case ADDS: { - new_val = AddWithCarry(reg_size, - set_flags, - reg(reg_size, instr->Rn(), instr->RnMode()), - op2); - break; - } - case SUB: - case SUBS: { - new_val = AddWithCarry(reg_size, - set_flags, - reg(reg_size, instr->Rn(), instr->RnMode()), - ~op2, - 1); - break; - } - default: UNREACHABLE(); - } - - set_reg(reg_size, instr->Rd(), new_val, instr->RdMode()); -} - - -void Simulator::VisitAddSubShifted(Instruction* instr) { - unsigned reg_size = instr->SixtyFourBits() ? kXRegSize : kWRegSize; - int64_t op2 = ShiftOperand(reg_size, - reg(reg_size, instr->Rm()), - static_cast(instr->ShiftDP()), - instr->ImmDPShift()); - AddSubHelper(instr, op2); -} - - -void Simulator::VisitAddSubImmediate(Instruction* instr) { - int64_t op2 = instr->ImmAddSub() << ((instr->ShiftAddSub() == 1) ? 12 : 0); - AddSubHelper(instr, op2); -} - - -void Simulator::VisitAddSubExtended(Instruction* instr) { - unsigned reg_size = instr->SixtyFourBits() ? kXRegSize : kWRegSize; - int64_t op2 = ExtendValue(reg_size, - reg(reg_size, instr->Rm()), - static_cast(instr->ExtendMode()), - instr->ImmExtendShift()); - AddSubHelper(instr, op2); -} - - -void Simulator::VisitAddSubWithCarry(Instruction* instr) { - unsigned reg_size = instr->SixtyFourBits() ? kXRegSize : kWRegSize; - int64_t op2 = reg(reg_size, instr->Rm()); - int64_t new_val; - - if ((instr->Mask(AddSubOpMask) == SUB) || instr->Mask(AddSubOpMask) == SUBS) { - op2 = ~op2; - } - - new_val = AddWithCarry(reg_size, - instr->FlagsUpdate(), - reg(reg_size, instr->Rn()), - op2, - C()); - - set_reg(reg_size, instr->Rd(), new_val); -} - - -void Simulator::VisitLogicalShifted(Instruction* instr) { - unsigned reg_size = instr->SixtyFourBits() ? kXRegSize : kWRegSize; - Shift shift_type = static_cast(instr->ShiftDP()); - unsigned shift_amount = instr->ImmDPShift(); - int64_t op2 = ShiftOperand(reg_size, reg(reg_size, instr->Rm()), shift_type, - shift_amount); - if (instr->Mask(NOT) == NOT) { - op2 = ~op2; - } - LogicalHelper(instr, op2); -} - - -void Simulator::VisitLogicalImmediate(Instruction* instr) { - LogicalHelper(instr, instr->ImmLogical()); -} - - -void Simulator::LogicalHelper(Instruction* instr, int64_t op2) { - unsigned reg_size = instr->SixtyFourBits() ? kXRegSize : kWRegSize; - int64_t op1 = reg(reg_size, instr->Rn()); - int64_t result = 0; - bool update_flags = false; - - // Switch on the logical operation, stripping out the NOT bit, as it has a - // different meaning for logical immediate instructions. - switch (instr->Mask(LogicalOpMask & ~NOT)) { - case ANDS: update_flags = true; // Fall through. - case AND: result = op1 & op2; break; - case ORR: result = op1 | op2; break; - case EOR: result = op1 ^ op2; break; - default: - UNIMPLEMENTED(); - } - - if (update_flags) { - nzcv().SetN(CalcNFlag(result, reg_size)); - nzcv().SetZ(CalcZFlag(result)); - nzcv().SetC(0); - nzcv().SetV(0); - } - - set_reg(reg_size, instr->Rd(), result, instr->RdMode()); -} - - -void Simulator::VisitConditionalCompareRegister(Instruction* instr) { - unsigned reg_size = instr->SixtyFourBits() ? kXRegSize : kWRegSize; - ConditionalCompareHelper(instr, reg(reg_size, instr->Rm())); -} - - -void Simulator::VisitConditionalCompareImmediate(Instruction* instr) { - ConditionalCompareHelper(instr, instr->ImmCondCmp()); -} - - -void Simulator::ConditionalCompareHelper(Instruction* instr, int64_t op2) { - unsigned reg_size = instr->SixtyFourBits() ? kXRegSize : kWRegSize; - int64_t op1 = reg(reg_size, instr->Rn()); - - if (ConditionPassed(static_cast(instr->Condition()))) { - // If the condition passes, set the status flags to the result of comparing - // the operands. - if (instr->Mask(ConditionalCompareMask) == CCMP) { - AddWithCarry(reg_size, true, op1, ~op2, 1); - } else { - ASSERT(instr->Mask(ConditionalCompareMask) == CCMN); - AddWithCarry(reg_size, true, op1, op2, 0); - } - } else { - // If the condition fails, set the status flags to the nzcv immediate. - nzcv().SetFlags(instr->Nzcv()); - } -} - - -void Simulator::VisitLoadStoreUnsignedOffset(Instruction* instr) { - int offset = instr->ImmLSUnsigned() << instr->SizeLS(); - LoadStoreHelper(instr, offset, Offset); -} - - -void Simulator::VisitLoadStoreUnscaledOffset(Instruction* instr) { - LoadStoreHelper(instr, instr->ImmLS(), Offset); -} - - -void Simulator::VisitLoadStorePreIndex(Instruction* instr) { - LoadStoreHelper(instr, instr->ImmLS(), PreIndex); -} - - -void Simulator::VisitLoadStorePostIndex(Instruction* instr) { - LoadStoreHelper(instr, instr->ImmLS(), PostIndex); -} - - -void Simulator::VisitLoadStoreRegisterOffset(Instruction* instr) { - Extend ext = static_cast(instr->ExtendMode()); - ASSERT((ext == UXTW) || (ext == UXTX) || (ext == SXTW) || (ext == SXTX)); - unsigned shift_amount = instr->ImmShiftLS() * instr->SizeLS(); - - int64_t offset = ExtendValue(kXRegSize, xreg(instr->Rm()), ext, - shift_amount); - LoadStoreHelper(instr, offset, Offset); -} - - -void Simulator::LoadStoreHelper(Instruction* instr, - int64_t offset, - AddrMode addrmode) { - unsigned srcdst = instr->Rt(); - unsigned addr_reg = instr->Rn(); - uint8_t* address = LoadStoreAddress(addr_reg, offset, addrmode); - int num_bytes = 1 << instr->SizeLS(); - uint8_t* stack = NULL; - - // Handle the writeback for stores before the store. On a CPU the writeback - // and the store are atomic, but when running on the simulator it is possible - // to be interrupted in between. The simulator is not thread safe and V8 does - // not require it to be to run JavaScript therefore the profiler may sample - // the "simulated" CPU in the middle of load/store with writeback. The code - // below ensures that push operations are safe even when interrupted: the - // stack pointer will be decremented before adding an element to the stack. - if (instr->IsStore()) { - LoadStoreWriteBack(addr_reg, offset, addrmode); - - // For store the address post writeback is used to check access below the - // stack. - stack = reinterpret_cast(sp()); - } - - LoadStoreOp op = static_cast(instr->Mask(LoadStoreOpMask)); - switch (op) { - case LDRB_w: - case LDRH_w: - case LDR_w: - case LDR_x: set_xreg(srcdst, MemoryRead(address, num_bytes)); break; - case STRB_w: - case STRH_w: - case STR_w: - case STR_x: MemoryWrite(address, xreg(srcdst), num_bytes); break; - case LDRSB_w: { - set_wreg(srcdst, ExtendValue(kWRegSize, MemoryRead8(address), SXTB)); - break; - } - case LDRSB_x: { - set_xreg(srcdst, ExtendValue(kXRegSize, MemoryRead8(address), SXTB)); - break; - } - case LDRSH_w: { - set_wreg(srcdst, ExtendValue(kWRegSize, MemoryRead16(address), SXTH)); - break; - } - case LDRSH_x: { - set_xreg(srcdst, ExtendValue(kXRegSize, MemoryRead16(address), SXTH)); - break; - } - case LDRSW_x: { - set_xreg(srcdst, ExtendValue(kXRegSize, MemoryRead32(address), SXTW)); - break; - } - case LDR_s: set_sreg(srcdst, MemoryReadFP32(address)); break; - case LDR_d: set_dreg(srcdst, MemoryReadFP64(address)); break; - case STR_s: MemoryWriteFP32(address, sreg(srcdst)); break; - case STR_d: MemoryWriteFP64(address, dreg(srcdst)); break; - default: UNIMPLEMENTED(); - } - - // Handle the writeback for loads after the load to ensure safe pop - // operation even when interrupted in the middle of it. The stack pointer - // is only updated after the load so pop(fp) will never break the invariant - // sp <= fp expected while walking the stack in the sampler. - if (instr->IsLoad()) { - // For loads the address pre writeback is used to check access below the - // stack. - stack = reinterpret_cast(sp()); - - LoadStoreWriteBack(addr_reg, offset, addrmode); - } - - // Accesses below the stack pointer (but above the platform stack limit) are - // not allowed in the ABI. - CheckMemoryAccess(address, stack); -} - - -void Simulator::VisitLoadStorePairOffset(Instruction* instr) { - LoadStorePairHelper(instr, Offset); -} - - -void Simulator::VisitLoadStorePairPreIndex(Instruction* instr) { - LoadStorePairHelper(instr, PreIndex); -} - - -void Simulator::VisitLoadStorePairPostIndex(Instruction* instr) { - LoadStorePairHelper(instr, PostIndex); -} - - -void Simulator::VisitLoadStorePairNonTemporal(Instruction* instr) { - LoadStorePairHelper(instr, Offset); -} - - -void Simulator::LoadStorePairHelper(Instruction* instr, - AddrMode addrmode) { - unsigned rt = instr->Rt(); - unsigned rt2 = instr->Rt2(); - unsigned addr_reg = instr->Rn(); - int offset = instr->ImmLSPair() << instr->SizeLSPair(); - uint8_t* address = LoadStoreAddress(addr_reg, offset, addrmode); - uint8_t* stack = NULL; - - // Handle the writeback for stores before the store. On a CPU the writeback - // and the store are atomic, but when running on the simulator it is possible - // to be interrupted in between. The simulator is not thread safe and V8 does - // not require it to be to run JavaScript therefore the profiler may sample - // the "simulated" CPU in the middle of load/store with writeback. The code - // below ensures that push operations are safe even when interrupted: the - // stack pointer will be decremented before adding an element to the stack. - if (instr->IsStore()) { - LoadStoreWriteBack(addr_reg, offset, addrmode); - - // For store the address post writeback is used to check access below the - // stack. - stack = reinterpret_cast(sp()); - } - - LoadStorePairOp op = - static_cast(instr->Mask(LoadStorePairMask)); - - // 'rt' and 'rt2' can only be aliased for stores. - ASSERT(((op & LoadStorePairLBit) == 0) || (rt != rt2)); - - switch (op) { - case LDP_w: { - set_wreg(rt, MemoryRead32(address)); - set_wreg(rt2, MemoryRead32(address + kWRegSizeInBytes)); - break; - } - case LDP_s: { - set_sreg(rt, MemoryReadFP32(address)); - set_sreg(rt2, MemoryReadFP32(address + kSRegSizeInBytes)); - break; - } - case LDP_x: { - set_xreg(rt, MemoryRead64(address)); - set_xreg(rt2, MemoryRead64(address + kXRegSizeInBytes)); - break; - } - case LDP_d: { - set_dreg(rt, MemoryReadFP64(address)); - set_dreg(rt2, MemoryReadFP64(address + kDRegSizeInBytes)); - break; - } - case LDPSW_x: { - set_xreg(rt, ExtendValue(kXRegSize, MemoryRead32(address), SXTW)); - set_xreg(rt2, ExtendValue(kXRegSize, - MemoryRead32(address + kWRegSizeInBytes), SXTW)); - break; - } - case STP_w: { - MemoryWrite32(address, wreg(rt)); - MemoryWrite32(address + kWRegSizeInBytes, wreg(rt2)); - break; - } - case STP_s: { - MemoryWriteFP32(address, sreg(rt)); - MemoryWriteFP32(address + kSRegSizeInBytes, sreg(rt2)); - break; - } - case STP_x: { - MemoryWrite64(address, xreg(rt)); - MemoryWrite64(address + kXRegSizeInBytes, xreg(rt2)); - break; - } - case STP_d: { - MemoryWriteFP64(address, dreg(rt)); - MemoryWriteFP64(address + kDRegSizeInBytes, dreg(rt2)); - break; - } - default: UNREACHABLE(); - } - - // Handle the writeback for loads after the load to ensure safe pop - // operation even when interrupted in the middle of it. The stack pointer - // is only updated after the load so pop(fp) will never break the invariant - // sp <= fp expected while walking the stack in the sampler. - if (instr->IsLoad()) { - // For loads the address pre writeback is used to check access below the - // stack. - stack = reinterpret_cast(sp()); - - LoadStoreWriteBack(addr_reg, offset, addrmode); - } - - // Accesses below the stack pointer (but above the platform stack limit) are - // not allowed in the ABI. - CheckMemoryAccess(address, stack); -} - - -void Simulator::VisitLoadLiteral(Instruction* instr) { - uint8_t* address = instr->LiteralAddress(); - unsigned rt = instr->Rt(); - - switch (instr->Mask(LoadLiteralMask)) { - case LDR_w_lit: set_wreg(rt, MemoryRead32(address)); break; - case LDR_x_lit: set_xreg(rt, MemoryRead64(address)); break; - case LDR_s_lit: set_sreg(rt, MemoryReadFP32(address)); break; - case LDR_d_lit: set_dreg(rt, MemoryReadFP64(address)); break; - default: UNREACHABLE(); - } -} - - -uint8_t* Simulator::LoadStoreAddress(unsigned addr_reg, - int64_t offset, - AddrMode addrmode) { - const unsigned kSPRegCode = kSPRegInternalCode & kRegCodeMask; - int64_t address = xreg(addr_reg, Reg31IsStackPointer); - if ((addr_reg == kSPRegCode) && ((address % 16) != 0)) { - // When the base register is SP the stack pointer is required to be - // quadword aligned prior to the address calculation and write-backs. - // Misalignment will cause a stack alignment fault. - FATAL("ALIGNMENT EXCEPTION"); - } - - if ((addrmode == Offset) || (addrmode == PreIndex)) { - address += offset; - } - - return reinterpret_cast(address); -} - - -void Simulator::LoadStoreWriteBack(unsigned addr_reg, - int64_t offset, - AddrMode addrmode) { - if ((addrmode == PreIndex) || (addrmode == PostIndex)) { - ASSERT(offset != 0); - uint64_t address = xreg(addr_reg, Reg31IsStackPointer); - set_reg(addr_reg, address + offset, Reg31IsStackPointer); - } -} - - -void Simulator::CheckMemoryAccess(uint8_t* address, uint8_t* stack) { - if ((address >= stack_limit_) && (address < stack)) { - fprintf(stream_, "ACCESS BELOW STACK POINTER:\n"); - fprintf(stream_, " sp is here: 0x%16p\n", stack); - fprintf(stream_, " access was here: 0x%16p\n", address); - fprintf(stream_, " stack limit is here: 0x%16p\n", stack_limit_); - fprintf(stream_, "\n"); - FATAL("ACCESS BELOW STACK POINTER"); - } -} - - -uint64_t Simulator::MemoryRead(uint8_t* address, unsigned num_bytes) { - ASSERT(address != NULL); - ASSERT((num_bytes > 0) && (num_bytes <= sizeof(uint64_t))); - uint64_t read = 0; - memcpy(&read, address, num_bytes); - return read; -} - - -uint8_t Simulator::MemoryRead8(uint8_t* address) { - return MemoryRead(address, sizeof(uint8_t)); -} - - -uint16_t Simulator::MemoryRead16(uint8_t* address) { - return MemoryRead(address, sizeof(uint16_t)); -} - - -uint32_t Simulator::MemoryRead32(uint8_t* address) { - return MemoryRead(address, sizeof(uint32_t)); -} - - -float Simulator::MemoryReadFP32(uint8_t* address) { - return rawbits_to_float(MemoryRead32(address)); -} - - -uint64_t Simulator::MemoryRead64(uint8_t* address) { - return MemoryRead(address, sizeof(uint64_t)); -} - - -double Simulator::MemoryReadFP64(uint8_t* address) { - return rawbits_to_double(MemoryRead64(address)); -} - - -void Simulator::MemoryWrite(uint8_t* address, - uint64_t value, - unsigned num_bytes) { - ASSERT(address != NULL); - ASSERT((num_bytes > 0) && (num_bytes <= sizeof(uint64_t))); - - LogWrite(address, value, num_bytes); - memcpy(address, &value, num_bytes); -} - - -void Simulator::MemoryWrite32(uint8_t* address, uint32_t value) { - MemoryWrite(address, value, sizeof(uint32_t)); -} - - -void Simulator::MemoryWriteFP32(uint8_t* address, float value) { - MemoryWrite32(address, float_to_rawbits(value)); -} - - -void Simulator::MemoryWrite64(uint8_t* address, uint64_t value) { - MemoryWrite(address, value, sizeof(uint64_t)); -} - - -void Simulator::MemoryWriteFP64(uint8_t* address, double value) { - MemoryWrite64(address, double_to_rawbits(value)); -} - - -void Simulator::VisitMoveWideImmediate(Instruction* instr) { - MoveWideImmediateOp mov_op = - static_cast(instr->Mask(MoveWideImmediateMask)); - int64_t new_xn_val = 0; - - bool is_64_bits = instr->SixtyFourBits() == 1; - // Shift is limited for W operations. - ASSERT(is_64_bits || (instr->ShiftMoveWide() < 2)); - - // Get the shifted immediate. - int64_t shift = instr->ShiftMoveWide() * 16; - int64_t shifted_imm16 = instr->ImmMoveWide() << shift; - - // Compute the new value. - switch (mov_op) { - case MOVN_w: - case MOVN_x: { - new_xn_val = ~shifted_imm16; - if (!is_64_bits) new_xn_val &= kWRegMask; - break; - } - case MOVK_w: - case MOVK_x: { - unsigned reg_code = instr->Rd(); - int64_t prev_xn_val = is_64_bits ? xreg(reg_code) - : wreg(reg_code); - new_xn_val = (prev_xn_val & ~(0xffffL << shift)) | shifted_imm16; - break; - } - case MOVZ_w: - case MOVZ_x: { - new_xn_val = shifted_imm16; - break; - } - default: - UNREACHABLE(); - } - - // Update the destination register. - set_xreg(instr->Rd(), new_xn_val); -} - - -void Simulator::VisitConditionalSelect(Instruction* instr) { - uint64_t new_val = xreg(instr->Rn()); - - if (ConditionFailed(static_cast(instr->Condition()))) { - new_val = xreg(instr->Rm()); - switch (instr->Mask(ConditionalSelectMask)) { - case CSEL_w: - case CSEL_x: break; - case CSINC_w: - case CSINC_x: new_val++; break; - case CSINV_w: - case CSINV_x: new_val = ~new_val; break; - case CSNEG_w: - case CSNEG_x: new_val = -new_val; break; - default: UNIMPLEMENTED(); - } - } - unsigned reg_size = instr->SixtyFourBits() ? kXRegSize : kWRegSize; - set_reg(reg_size, instr->Rd(), new_val); -} - - -void Simulator::VisitDataProcessing1Source(Instruction* instr) { - unsigned dst = instr->Rd(); - unsigned src = instr->Rn(); - - switch (instr->Mask(DataProcessing1SourceMask)) { - case RBIT_w: set_wreg(dst, ReverseBits(wreg(src), kWRegSize)); break; - case RBIT_x: set_xreg(dst, ReverseBits(xreg(src), kXRegSize)); break; - case REV16_w: set_wreg(dst, ReverseBytes(wreg(src), Reverse16)); break; - case REV16_x: set_xreg(dst, ReverseBytes(xreg(src), Reverse16)); break; - case REV_w: set_wreg(dst, ReverseBytes(wreg(src), Reverse32)); break; - case REV32_x: set_xreg(dst, ReverseBytes(xreg(src), Reverse32)); break; - case REV_x: set_xreg(dst, ReverseBytes(xreg(src), Reverse64)); break; - case CLZ_w: set_wreg(dst, CountLeadingZeros(wreg(src), kWRegSize)); break; - case CLZ_x: set_xreg(dst, CountLeadingZeros(xreg(src), kXRegSize)); break; - case CLS_w: { - set_wreg(dst, CountLeadingSignBits(wreg(src), kWRegSize)); - break; - } - case CLS_x: { - set_xreg(dst, CountLeadingSignBits(xreg(src), kXRegSize)); - break; - } - default: UNIMPLEMENTED(); - } -} - - -uint64_t Simulator::ReverseBits(uint64_t value, unsigned num_bits) { - ASSERT((num_bits == kWRegSize) || (num_bits == kXRegSize)); - uint64_t result = 0; - for (unsigned i = 0; i < num_bits; i++) { - result = (result << 1) | (value & 1); - value >>= 1; - } - return result; -} - - -uint64_t Simulator::ReverseBytes(uint64_t value, ReverseByteMode mode) { - // Split the 64-bit value into an 8-bit array, where b[0] is the least - // significant byte, and b[7] is the most significant. - uint8_t bytes[8]; - uint64_t mask = 0xff00000000000000UL; - for (int i = 7; i >= 0; i--) { - bytes[i] = (value & mask) >> (i * 8); - mask >>= 8; - } - - // Permutation tables for REV instructions. - // permute_table[Reverse16] is used by REV16_x, REV16_w - // permute_table[Reverse32] is used by REV32_x, REV_w - // permute_table[Reverse64] is used by REV_x - ASSERT((Reverse16 == 0) && (Reverse32 == 1) && (Reverse64 == 2)); - static const uint8_t permute_table[3][8] = { {6, 7, 4, 5, 2, 3, 0, 1}, - {4, 5, 6, 7, 0, 1, 2, 3}, - {0, 1, 2, 3, 4, 5, 6, 7} }; - uint64_t result = 0; - for (int i = 0; i < 8; i++) { - result <<= 8; - result |= bytes[permute_table[mode][i]]; - } - return result; -} - - -void Simulator::VisitDataProcessing2Source(Instruction* instr) { - // TODO(mcapewel) move these to a higher level file, as they are global - // assumptions. - ASSERT((static_cast(-1) >> 1) == -1); - ASSERT((static_cast(-1) >> 1) == 0x7FFFFFFF); - - Shift shift_op = NO_SHIFT; - int64_t result = 0; - switch (instr->Mask(DataProcessing2SourceMask)) { - case SDIV_w: { - int32_t rn = wreg(instr->Rn()); - int32_t rm = wreg(instr->Rm()); - if ((rn == kWMinInt) && (rm == -1)) { - result = kWMinInt; - } else if (rm == 0) { - // Division by zero can be trapped, but not on A-class processors. - result = 0; - } else { - result = rn / rm; - } - break; - } - case SDIV_x: { - int64_t rn = xreg(instr->Rn()); - int64_t rm = xreg(instr->Rm()); - if ((rn == kXMinInt) && (rm == -1)) { - result = kXMinInt; - } else if (rm == 0) { - // Division by zero can be trapped, but not on A-class processors. - result = 0; - } else { - result = rn / rm; - } - break; - } - case UDIV_w: { - uint32_t rn = static_cast(wreg(instr->Rn())); - uint32_t rm = static_cast(wreg(instr->Rm())); - if (rm == 0) { - // Division by zero can be trapped, but not on A-class processors. - result = 0; - } else { - result = rn / rm; - } - break; - } - case UDIV_x: { - uint64_t rn = static_cast(xreg(instr->Rn())); - uint64_t rm = static_cast(xreg(instr->Rm())); - if (rm == 0) { - // Division by zero can be trapped, but not on A-class processors. - result = 0; - } else { - result = rn / rm; - } - break; - } - case LSLV_w: - case LSLV_x: shift_op = LSL; break; - case LSRV_w: - case LSRV_x: shift_op = LSR; break; - case ASRV_w: - case ASRV_x: shift_op = ASR; break; - case RORV_w: - case RORV_x: shift_op = ROR; break; - default: UNIMPLEMENTED(); - } - - unsigned reg_size = instr->SixtyFourBits() ? kXRegSize : kWRegSize; - if (shift_op != NO_SHIFT) { - // Shift distance encoded in the least-significant five/six bits of the - // register. - int mask = (instr->SixtyFourBits() == 1) ? 0x3f : 0x1f; - unsigned shift = wreg(instr->Rm()) & mask; - result = ShiftOperand(reg_size, reg(reg_size, instr->Rn()), shift_op, - shift); - } - set_reg(reg_size, instr->Rd(), result); -} - - -// The algorithm used is described in section 8.2 of -// Hacker's Delight, by Henry S. Warren, Jr. -// It assumes that a right shift on a signed integer is an arithmetic shift. -static int64_t MultiplyHighSigned(int64_t u, int64_t v) { - uint64_t u0, v0, w0; - int64_t u1, v1, w1, w2, t; - - u0 = u & 0xffffffffL; - u1 = u >> 32; - v0 = v & 0xffffffffL; - v1 = v >> 32; - - w0 = u0 * v0; - t = u1 * v0 + (w0 >> 32); - w1 = t & 0xffffffffL; - w2 = t >> 32; - w1 = u0 * v1 + w1; - - return u1 * v1 + w2 + (w1 >> 32); -} - - -void Simulator::VisitDataProcessing3Source(Instruction* instr) { - unsigned reg_size = instr->SixtyFourBits() ? kXRegSize : kWRegSize; - - int64_t result = 0; - // Extract and sign- or zero-extend 32-bit arguments for widening operations. - uint64_t rn_u32 = reg(instr->Rn()); - uint64_t rm_u32 = reg(instr->Rm()); - int64_t rn_s32 = reg(instr->Rn()); - int64_t rm_s32 = reg(instr->Rm()); - switch (instr->Mask(DataProcessing3SourceMask)) { - case MADD_w: - case MADD_x: - result = xreg(instr->Ra()) + (xreg(instr->Rn()) * xreg(instr->Rm())); - break; - case MSUB_w: - case MSUB_x: - result = xreg(instr->Ra()) - (xreg(instr->Rn()) * xreg(instr->Rm())); - break; - case SMADDL_x: result = xreg(instr->Ra()) + (rn_s32 * rm_s32); break; - case SMSUBL_x: result = xreg(instr->Ra()) - (rn_s32 * rm_s32); break; - case UMADDL_x: result = xreg(instr->Ra()) + (rn_u32 * rm_u32); break; - case UMSUBL_x: result = xreg(instr->Ra()) - (rn_u32 * rm_u32); break; - case SMULH_x: - ASSERT(instr->Ra() == kZeroRegCode); - result = MultiplyHighSigned(xreg(instr->Rn()), xreg(instr->Rm())); - break; - default: UNIMPLEMENTED(); - } - set_reg(reg_size, instr->Rd(), result); -} - - -void Simulator::VisitBitfield(Instruction* instr) { - unsigned reg_size = instr->SixtyFourBits() ? kXRegSize : kWRegSize; - int64_t reg_mask = instr->SixtyFourBits() ? kXRegMask : kWRegMask; - int64_t R = instr->ImmR(); - int64_t S = instr->ImmS(); - int64_t diff = S - R; - int64_t mask; - if (diff >= 0) { - mask = diff < reg_size - 1 ? (1L << (diff + 1)) - 1 - : reg_mask; - } else { - mask = ((1L << (S + 1)) - 1); - mask = (static_cast(mask) >> R) | (mask << (reg_size - R)); - diff += reg_size; - } - - // inzero indicates if the extracted bitfield is inserted into the - // destination register value or in zero. - // If extend is true, extend the sign of the extracted bitfield. - bool inzero = false; - bool extend = false; - switch (instr->Mask(BitfieldMask)) { - case BFM_x: - case BFM_w: - break; - case SBFM_x: - case SBFM_w: - inzero = true; - extend = true; - break; - case UBFM_x: - case UBFM_w: - inzero = true; - break; - default: - UNIMPLEMENTED(); - } - - int64_t dst = inzero ? 0 : reg(reg_size, instr->Rd()); - int64_t src = reg(reg_size, instr->Rn()); - // Rotate source bitfield into place. - int64_t result = (static_cast(src) >> R) | (src << (reg_size - R)); - // Determine the sign extension. - int64_t topbits = ((1L << (reg_size - diff - 1)) - 1) << (diff + 1); - int64_t signbits = extend && ((src >> S) & 1) ? topbits : 0; - - // Merge sign extension, dest/zero and bitfield. - result = signbits | (result & mask) | (dst & ~mask); - - set_reg(reg_size, instr->Rd(), result); -} - - -void Simulator::VisitExtract(Instruction* instr) { - unsigned lsb = instr->ImmS(); - unsigned reg_size = (instr->SixtyFourBits() == 1) ? kXRegSize - : kWRegSize; - set_reg(reg_size, - instr->Rd(), - (static_cast(reg(reg_size, instr->Rm())) >> lsb) | - (reg(reg_size, instr->Rn()) << (reg_size - lsb))); -} - - -void Simulator::VisitFPImmediate(Instruction* instr) { - AssertSupportedFPCR(); - - unsigned dest = instr->Rd(); - switch (instr->Mask(FPImmediateMask)) { - case FMOV_s_imm: set_sreg(dest, instr->ImmFP32()); break; - case FMOV_d_imm: set_dreg(dest, instr->ImmFP64()); break; - default: UNREACHABLE(); - } -} - - -void Simulator::VisitFPIntegerConvert(Instruction* instr) { - AssertSupportedFPCR(); - - unsigned dst = instr->Rd(); - unsigned src = instr->Rn(); - - FPRounding round = RMode(); - - switch (instr->Mask(FPIntegerConvertMask)) { - case FCVTAS_ws: set_wreg(dst, FPToInt32(sreg(src), FPTieAway)); break; - case FCVTAS_xs: set_xreg(dst, FPToInt64(sreg(src), FPTieAway)); break; - case FCVTAS_wd: set_wreg(dst, FPToInt32(dreg(src), FPTieAway)); break; - case FCVTAS_xd: set_xreg(dst, FPToInt64(dreg(src), FPTieAway)); break; - case FCVTAU_ws: set_wreg(dst, FPToUInt32(sreg(src), FPTieAway)); break; - case FCVTAU_xs: set_xreg(dst, FPToUInt64(sreg(src), FPTieAway)); break; - case FCVTAU_wd: set_wreg(dst, FPToUInt32(dreg(src), FPTieAway)); break; - case FCVTAU_xd: set_xreg(dst, FPToUInt64(dreg(src), FPTieAway)); break; - case FCVTMS_ws: - set_wreg(dst, FPToInt32(sreg(src), FPNegativeInfinity)); - break; - case FCVTMS_xs: - set_xreg(dst, FPToInt64(sreg(src), FPNegativeInfinity)); - break; - case FCVTMS_wd: - set_wreg(dst, FPToInt32(dreg(src), FPNegativeInfinity)); - break; - case FCVTMS_xd: - set_xreg(dst, FPToInt64(dreg(src), FPNegativeInfinity)); - break; - case FCVTMU_ws: - set_wreg(dst, FPToUInt32(sreg(src), FPNegativeInfinity)); - break; - case FCVTMU_xs: - set_xreg(dst, FPToUInt64(sreg(src), FPNegativeInfinity)); - break; - case FCVTMU_wd: - set_wreg(dst, FPToUInt32(dreg(src), FPNegativeInfinity)); - break; - case FCVTMU_xd: - set_xreg(dst, FPToUInt64(dreg(src), FPNegativeInfinity)); - break; - case FCVTNS_ws: set_wreg(dst, FPToInt32(sreg(src), FPTieEven)); break; - case FCVTNS_xs: set_xreg(dst, FPToInt64(sreg(src), FPTieEven)); break; - case FCVTNS_wd: set_wreg(dst, FPToInt32(dreg(src), FPTieEven)); break; - case FCVTNS_xd: set_xreg(dst, FPToInt64(dreg(src), FPTieEven)); break; - case FCVTNU_ws: set_wreg(dst, FPToUInt32(sreg(src), FPTieEven)); break; - case FCVTNU_xs: set_xreg(dst, FPToUInt64(sreg(src), FPTieEven)); break; - case FCVTNU_wd: set_wreg(dst, FPToUInt32(dreg(src), FPTieEven)); break; - case FCVTNU_xd: set_xreg(dst, FPToUInt64(dreg(src), FPTieEven)); break; - case FCVTZS_ws: set_wreg(dst, FPToInt32(sreg(src), FPZero)); break; - case FCVTZS_xs: set_xreg(dst, FPToInt64(sreg(src), FPZero)); break; - case FCVTZS_wd: set_wreg(dst, FPToInt32(dreg(src), FPZero)); break; - case FCVTZS_xd: set_xreg(dst, FPToInt64(dreg(src), FPZero)); break; - case FCVTZU_ws: set_wreg(dst, FPToUInt32(sreg(src), FPZero)); break; - case FCVTZU_xs: set_xreg(dst, FPToUInt64(sreg(src), FPZero)); break; - case FCVTZU_wd: set_wreg(dst, FPToUInt32(dreg(src), FPZero)); break; - case FCVTZU_xd: set_xreg(dst, FPToUInt64(dreg(src), FPZero)); break; - case FMOV_ws: set_wreg(dst, sreg_bits(src)); break; - case FMOV_xd: set_xreg(dst, dreg_bits(src)); break; - case FMOV_sw: set_sreg_bits(dst, wreg(src)); break; - case FMOV_dx: set_dreg_bits(dst, xreg(src)); break; - - // A 32-bit input can be handled in the same way as a 64-bit input, since - // the sign- or zero-extension will not affect the conversion. - case SCVTF_dx: set_dreg(dst, FixedToDouble(xreg(src), 0, round)); break; - case SCVTF_dw: set_dreg(dst, FixedToDouble(wreg(src), 0, round)); break; - case UCVTF_dx: set_dreg(dst, UFixedToDouble(xreg(src), 0, round)); break; - case UCVTF_dw: { - set_dreg(dst, UFixedToDouble(reg(src), 0, round)); - break; - } - case SCVTF_sx: set_sreg(dst, FixedToFloat(xreg(src), 0, round)); break; - case SCVTF_sw: set_sreg(dst, FixedToFloat(wreg(src), 0, round)); break; - case UCVTF_sx: set_sreg(dst, UFixedToFloat(xreg(src), 0, round)); break; - case UCVTF_sw: { - set_sreg(dst, UFixedToFloat(reg(src), 0, round)); - break; - } - - default: UNREACHABLE(); - } -} - - -void Simulator::VisitFPFixedPointConvert(Instruction* instr) { - AssertSupportedFPCR(); - - unsigned dst = instr->Rd(); - unsigned src = instr->Rn(); - int fbits = 64 - instr->FPScale(); - - FPRounding round = RMode(); - - switch (instr->Mask(FPFixedPointConvertMask)) { - // A 32-bit input can be handled in the same way as a 64-bit input, since - // the sign- or zero-extension will not affect the conversion. - case SCVTF_dx_fixed: - set_dreg(dst, FixedToDouble(xreg(src), fbits, round)); - break; - case SCVTF_dw_fixed: - set_dreg(dst, FixedToDouble(wreg(src), fbits, round)); - break; - case UCVTF_dx_fixed: - set_dreg(dst, UFixedToDouble(xreg(src), fbits, round)); - break; - case UCVTF_dw_fixed: { - set_dreg(dst, - UFixedToDouble(reg(src), fbits, round)); - break; - } - case SCVTF_sx_fixed: - set_sreg(dst, FixedToFloat(xreg(src), fbits, round)); - break; - case SCVTF_sw_fixed: - set_sreg(dst, FixedToFloat(wreg(src), fbits, round)); - break; - case UCVTF_sx_fixed: - set_sreg(dst, UFixedToFloat(xreg(src), fbits, round)); - break; - case UCVTF_sw_fixed: { - set_sreg(dst, - UFixedToFloat(reg(src), fbits, round)); - break; - } - default: UNREACHABLE(); - } -} - - -int32_t Simulator::FPToInt32(double value, FPRounding rmode) { - value = FPRoundInt(value, rmode); - if (value >= kWMaxInt) { - return kWMaxInt; - } else if (value < kWMinInt) { - return kWMinInt; - } - return std::isnan(value) ? 0 : static_cast(value); -} - - -int64_t Simulator::FPToInt64(double value, FPRounding rmode) { - value = FPRoundInt(value, rmode); - if (value >= kXMaxInt) { - return kXMaxInt; - } else if (value < kXMinInt) { - return kXMinInt; - } - return std::isnan(value) ? 0 : static_cast(value); -} - - -uint32_t Simulator::FPToUInt32(double value, FPRounding rmode) { - value = FPRoundInt(value, rmode); - if (value >= kWMaxUInt) { - return kWMaxUInt; - } else if (value < 0.0) { - return 0; - } - return std::isnan(value) ? 0 : static_cast(value); -} - - -uint64_t Simulator::FPToUInt64(double value, FPRounding rmode) { - value = FPRoundInt(value, rmode); - if (value >= kXMaxUInt) { - return kXMaxUInt; - } else if (value < 0.0) { - return 0; - } - return std::isnan(value) ? 0 : static_cast(value); -} - - -void Simulator::VisitFPCompare(Instruction* instr) { - AssertSupportedFPCR(); - - unsigned reg_size = instr->FPType() == FP32 ? kSRegSize : kDRegSize; - double fn_val = fpreg(reg_size, instr->Rn()); - - switch (instr->Mask(FPCompareMask)) { - case FCMP_s: - case FCMP_d: FPCompare(fn_val, fpreg(reg_size, instr->Rm())); break; - case FCMP_s_zero: - case FCMP_d_zero: FPCompare(fn_val, 0.0); break; - default: UNIMPLEMENTED(); - } -} - - -void Simulator::VisitFPConditionalCompare(Instruction* instr) { - AssertSupportedFPCR(); - - switch (instr->Mask(FPConditionalCompareMask)) { - case FCCMP_s: - case FCCMP_d: { - if (ConditionPassed(static_cast(instr->Condition()))) { - // If the condition passes, set the status flags to the result of - // comparing the operands. - unsigned reg_size = instr->FPType() == FP32 ? kSRegSize : kDRegSize; - FPCompare(fpreg(reg_size, instr->Rn()), fpreg(reg_size, instr->Rm())); - } else { - // If the condition fails, set the status flags to the nzcv immediate. - nzcv().SetFlags(instr->Nzcv()); - } - break; - } - default: UNIMPLEMENTED(); - } -} - - -void Simulator::VisitFPConditionalSelect(Instruction* instr) { - AssertSupportedFPCR(); - - Instr selected; - if (ConditionPassed(static_cast(instr->Condition()))) { - selected = instr->Rn(); - } else { - selected = instr->Rm(); - } - - switch (instr->Mask(FPConditionalSelectMask)) { - case FCSEL_s: set_sreg(instr->Rd(), sreg(selected)); break; - case FCSEL_d: set_dreg(instr->Rd(), dreg(selected)); break; - default: UNIMPLEMENTED(); - } -} - - -void Simulator::VisitFPDataProcessing1Source(Instruction* instr) { - AssertSupportedFPCR(); - - unsigned fd = instr->Rd(); - unsigned fn = instr->Rn(); - - switch (instr->Mask(FPDataProcessing1SourceMask)) { - case FMOV_s: set_sreg(fd, sreg(fn)); break; - case FMOV_d: set_dreg(fd, dreg(fn)); break; - case FABS_s: set_sreg(fd, std::fabs(sreg(fn))); break; - case FABS_d: set_dreg(fd, std::fabs(dreg(fn))); break; - case FNEG_s: set_sreg(fd, -sreg(fn)); break; - case FNEG_d: set_dreg(fd, -dreg(fn)); break; - case FSQRT_s: set_sreg(fd, std::sqrt(sreg(fn))); break; - case FSQRT_d: set_dreg(fd, std::sqrt(dreg(fn))); break; - case FRINTA_s: set_sreg(fd, FPRoundInt(sreg(fn), FPTieAway)); break; - case FRINTA_d: set_dreg(fd, FPRoundInt(dreg(fn), FPTieAway)); break; - case FRINTN_s: set_sreg(fd, FPRoundInt(sreg(fn), FPTieEven)); break; - case FRINTN_d: set_dreg(fd, FPRoundInt(dreg(fn), FPTieEven)); break; - case FRINTZ_s: set_sreg(fd, FPRoundInt(sreg(fn), FPZero)); break; - case FRINTZ_d: set_dreg(fd, FPRoundInt(dreg(fn), FPZero)); break; - case FCVT_ds: set_dreg(fd, FPToDouble(sreg(fn))); break; - case FCVT_sd: set_sreg(fd, FPToFloat(dreg(fn), FPTieEven)); break; - default: UNIMPLEMENTED(); - } -} - - -// Assemble the specified IEEE-754 components into the target type and apply -// appropriate rounding. -// sign: 0 = positive, 1 = negative -// exponent: Unbiased IEEE-754 exponent. -// mantissa: The mantissa of the input. The top bit (which is not encoded for -// normal IEEE-754 values) must not be omitted. This bit has the -// value 'pow(2, exponent)'. -// -// The input value is assumed to be a normalized value. That is, the input may -// not be infinity or NaN. If the source value is subnormal, it must be -// normalized before calling this function such that the highest set bit in the -// mantissa has the value 'pow(2, exponent)'. -// -// Callers should use FPRoundToFloat or FPRoundToDouble directly, rather than -// calling a templated FPRound. -template -static T FPRound(int64_t sign, int64_t exponent, uint64_t mantissa, - FPRounding round_mode) { - ASSERT((sign == 0) || (sign == 1)); - - // Only the FPTieEven rounding mode is implemented. - ASSERT(round_mode == FPTieEven); - USE(round_mode); - - // Rounding can promote subnormals to normals, and normals to infinities. For - // example, a double with exponent 127 (FLT_MAX_EXP) would appear to be - // encodable as a float, but rounding based on the low-order mantissa bits - // could make it overflow. With ties-to-even rounding, this value would become - // an infinity. - - // ---- Rounding Method ---- - // - // The exponent is irrelevant in the rounding operation, so we treat the - // lowest-order bit that will fit into the result ('onebit') as having - // the value '1'. Similarly, the highest-order bit that won't fit into - // the result ('halfbit') has the value '0.5'. The 'point' sits between - // 'onebit' and 'halfbit': - // - // These bits fit into the result. - // |---------------------| - // mantissa = 0bxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - // || - // / | - // / halfbit - // onebit - // - // For subnormal outputs, the range of representable bits is smaller and - // the position of onebit and halfbit depends on the exponent of the - // input, but the method is otherwise similar. - // - // onebit(frac) - // | - // | halfbit(frac) halfbit(adjusted) - // | / / - // | | | - // 0b00.0 (exact) -> 0b00.0 (exact) -> 0b00 - // 0b00.0... -> 0b00.0... -> 0b00 - // 0b00.1 (exact) -> 0b00.0111..111 -> 0b00 - // 0b00.1... -> 0b00.1... -> 0b01 - // 0b01.0 (exact) -> 0b01.0 (exact) -> 0b01 - // 0b01.0... -> 0b01.0... -> 0b01 - // 0b01.1 (exact) -> 0b01.1 (exact) -> 0b10 - // 0b01.1... -> 0b01.1... -> 0b10 - // 0b10.0 (exact) -> 0b10.0 (exact) -> 0b10 - // 0b10.0... -> 0b10.0... -> 0b10 - // 0b10.1 (exact) -> 0b10.0111..111 -> 0b10 - // 0b10.1... -> 0b10.1... -> 0b11 - // 0b11.0 (exact) -> 0b11.0 (exact) -> 0b11 - // ... / | / | - // / | / | - // / | - // adjusted = frac - (halfbit(mantissa) & ~onebit(frac)); / | - // - // mantissa = (mantissa >> shift) + halfbit(adjusted); - - static const int mantissa_offset = 0; - static const int exponent_offset = mantissa_offset + mbits; - static const int sign_offset = exponent_offset + ebits; - STATIC_ASSERT(sign_offset == (sizeof(T) * kByteSize - 1)); - - // Bail out early for zero inputs. - if (mantissa == 0) { - return sign << sign_offset; - } - - // If all bits in the exponent are set, the value is infinite or NaN. - // This is true for all binary IEEE-754 formats. - static const int infinite_exponent = (1 << ebits) - 1; - static const int max_normal_exponent = infinite_exponent - 1; - - // Apply the exponent bias to encode it for the result. Doing this early makes - // it easy to detect values that will be infinite or subnormal. - exponent += max_normal_exponent >> 1; - - if (exponent > max_normal_exponent) { - // Overflow: The input is too large for the result type to represent. The - // FPTieEven rounding mode handles overflows using infinities. - exponent = infinite_exponent; - mantissa = 0; - return (sign << sign_offset) | - (exponent << exponent_offset) | - (mantissa << mantissa_offset); - } - - // Calculate the shift required to move the top mantissa bit to the proper - // place in the destination type. - const int highest_significant_bit = 63 - CountLeadingZeros(mantissa, 64); - int shift = highest_significant_bit - mbits; - - if (exponent <= 0) { - // The output will be subnormal (before rounding). - - // For subnormal outputs, the shift must be adjusted by the exponent. The +1 - // is necessary because the exponent of a subnormal value (encoded as 0) is - // the same as the exponent of the smallest normal value (encoded as 1). - shift += -exponent + 1; - - // Handle inputs that would produce a zero output. - // - // Shifts higher than highest_significant_bit+1 will always produce a zero - // result. A shift of exactly highest_significant_bit+1 might produce a - // non-zero result after rounding. - if (shift > (highest_significant_bit + 1)) { - // The result will always be +/-0.0. - return sign << sign_offset; - } - - // Properly encode the exponent for a subnormal output. - exponent = 0; - } else { - // Clear the topmost mantissa bit, since this is not encoded in IEEE-754 - // normal values. - mantissa &= ~(1UL << highest_significant_bit); - } - - if (shift > 0) { - // We have to shift the mantissa to the right. Some precision is lost, so we - // need to apply rounding. - uint64_t onebit_mantissa = (mantissa >> (shift)) & 1; - uint64_t halfbit_mantissa = (mantissa >> (shift-1)) & 1; - uint64_t adjusted = mantissa - (halfbit_mantissa & ~onebit_mantissa); - T halfbit_adjusted = (adjusted >> (shift-1)) & 1; - - T result = (sign << sign_offset) | - (exponent << exponent_offset) | - ((mantissa >> shift) << mantissa_offset); - - // A very large mantissa can overflow during rounding. If this happens, the - // exponent should be incremented and the mantissa set to 1.0 (encoded as - // 0). Applying halfbit_adjusted after assembling the float has the nice - // side-effect that this case is handled for free. - // - // This also handles cases where a very large finite value overflows to - // infinity, or where a very large subnormal value overflows to become - // normal. - return result + halfbit_adjusted; - } else { - // We have to shift the mantissa to the left (or not at all). The input - // mantissa is exactly representable in the output mantissa, so apply no - // rounding correction. - return (sign << sign_offset) | - (exponent << exponent_offset) | - ((mantissa << -shift) << mantissa_offset); - } -} - - -// See FPRound for a description of this function. -static inline double FPRoundToDouble(int64_t sign, int64_t exponent, - uint64_t mantissa, FPRounding round_mode) { - int64_t bits = - FPRound(sign, - exponent, - mantissa, - round_mode); - return rawbits_to_double(bits); -} - - -// See FPRound for a description of this function. -static inline float FPRoundToFloat(int64_t sign, int64_t exponent, - uint64_t mantissa, FPRounding round_mode) { - int32_t bits = - FPRound(sign, - exponent, - mantissa, - round_mode); - return rawbits_to_float(bits); -} - - -double Simulator::FixedToDouble(int64_t src, int fbits, FPRounding round) { - if (src >= 0) { - return UFixedToDouble(src, fbits, round); - } else { - // This works for all negative values, including INT64_MIN. - return -UFixedToDouble(-src, fbits, round); - } -} - - -double Simulator::UFixedToDouble(uint64_t src, int fbits, FPRounding round) { - // An input of 0 is a special case because the result is effectively - // subnormal: The exponent is encoded as 0 and there is no implicit 1 bit. - if (src == 0) { - return 0.0; - } - - // Calculate the exponent. The highest significant bit will have the value - // 2^exponent. - const int highest_significant_bit = 63 - CountLeadingZeros(src, 64); - const int64_t exponent = highest_significant_bit - fbits; - - return FPRoundToDouble(0, exponent, src, round); -} - - -float Simulator::FixedToFloat(int64_t src, int fbits, FPRounding round) { - if (src >= 0) { - return UFixedToFloat(src, fbits, round); - } else { - // This works for all negative values, including INT64_MIN. - return -UFixedToFloat(-src, fbits, round); - } -} - - -float Simulator::UFixedToFloat(uint64_t src, int fbits, FPRounding round) { - // An input of 0 is a special case because the result is effectively - // subnormal: The exponent is encoded as 0 and there is no implicit 1 bit. - if (src == 0) { - return 0.0f; - } - - // Calculate the exponent. The highest significant bit will have the value - // 2^exponent. - const int highest_significant_bit = 63 - CountLeadingZeros(src, 64); - const int32_t exponent = highest_significant_bit - fbits; - - return FPRoundToFloat(0, exponent, src, round); -} - - -double Simulator::FPRoundInt(double value, FPRounding round_mode) { - if ((value == 0.0) || (value == kFP64PositiveInfinity) || - (value == kFP64NegativeInfinity) || std::isnan(value)) { - return value; - } - - double int_result = floor(value); - double error = value - int_result; - switch (round_mode) { - case FPTieAway: { - // If the error is greater than 0.5, or is equal to 0.5 and the integer - // result is positive, round up. - if ((error > 0.5) || ((error == 0.5) && (int_result >= 0.0))) { - int_result++; - } - break; - } - case FPTieEven: { - // If the error is greater than 0.5, or is equal to 0.5 and the integer - // result is odd, round up. - if ((error > 0.5) || - ((error == 0.5) && (fmod(int_result, 2) != 0))) { - int_result++; - } - break; - } - case FPZero: { - // If value > 0 then we take floor(value) - // otherwise, ceil(value) - if (value < 0) { - int_result = ceil(value); - } - break; - } - case FPNegativeInfinity: { - // We always use floor(value). - break; - } - default: UNIMPLEMENTED(); - } - return int_result; -} - - -double Simulator::FPToDouble(float value) { - switch (std::fpclassify(value)) { - case FP_NAN: { - // Convert NaNs as the processor would, assuming that FPCR.DN (default - // NaN) is not set: - // - The sign is propagated. - // - The payload (mantissa) is transferred entirely, except that the top - // bit is forced to '1', making the result a quiet NaN. The unused - // (low-order) payload bits are set to 0. - uint32_t raw = float_to_rawbits(value); - - uint64_t sign = raw >> 31; - uint64_t exponent = (1 << 11) - 1; - uint64_t payload = unsigned_bitextract_64(21, 0, raw); - payload <<= (52 - 23); // The unused low-order bits should be 0. - payload |= (1L << 51); // Force a quiet NaN. - - return rawbits_to_double((sign << 63) | (exponent << 52) | payload); - } - - case FP_ZERO: - case FP_NORMAL: - case FP_SUBNORMAL: - case FP_INFINITE: { - // All other inputs are preserved in a standard cast, because every value - // representable using an IEEE-754 float is also representable using an - // IEEE-754 double. - return static_cast(value); - } - } - - UNREACHABLE(); - return static_cast(value); -} - - -float Simulator::FPToFloat(double value, FPRounding round_mode) { - // Only the FPTieEven rounding mode is implemented. - ASSERT(round_mode == FPTieEven); - USE(round_mode); - - switch (std::fpclassify(value)) { - case FP_NAN: { - // Convert NaNs as the processor would, assuming that FPCR.DN (default - // NaN) is not set: - // - The sign is propagated. - // - The payload (mantissa) is transferred as much as possible, except - // that the top bit is forced to '1', making the result a quiet NaN. - uint64_t raw = double_to_rawbits(value); - - uint32_t sign = raw >> 63; - uint32_t exponent = (1 << 8) - 1; - uint32_t payload = unsigned_bitextract_64(50, 52 - 23, raw); - payload |= (1 << 22); // Force a quiet NaN. - - return rawbits_to_float((sign << 31) | (exponent << 23) | payload); - } - - case FP_ZERO: - case FP_INFINITE: { - // In a C++ cast, any value representable in the target type will be - // unchanged. This is always the case for +/-0.0 and infinities. - return static_cast(value); - } - - case FP_NORMAL: - case FP_SUBNORMAL: { - // Convert double-to-float as the processor would, assuming that FPCR.FZ - // (flush-to-zero) is not set. - uint64_t raw = double_to_rawbits(value); - // Extract the IEEE-754 double components. - uint32_t sign = raw >> 63; - // Extract the exponent and remove the IEEE-754 encoding bias. - int32_t exponent = unsigned_bitextract_64(62, 52, raw) - 1023; - // Extract the mantissa and add the implicit '1' bit. - uint64_t mantissa = unsigned_bitextract_64(51, 0, raw); - if (std::fpclassify(value) == FP_NORMAL) { - mantissa |= (1UL << 52); - } - return FPRoundToFloat(sign, exponent, mantissa, round_mode); - } - } - - UNREACHABLE(); - return value; -} - - -void Simulator::VisitFPDataProcessing2Source(Instruction* instr) { - AssertSupportedFPCR(); - - unsigned fd = instr->Rd(); - unsigned fn = instr->Rn(); - unsigned fm = instr->Rm(); - - switch (instr->Mask(FPDataProcessing2SourceMask)) { - case FADD_s: set_sreg(fd, sreg(fn) + sreg(fm)); break; - case FADD_d: set_dreg(fd, dreg(fn) + dreg(fm)); break; - case FSUB_s: set_sreg(fd, sreg(fn) - sreg(fm)); break; - case FSUB_d: set_dreg(fd, dreg(fn) - dreg(fm)); break; - case FMUL_s: set_sreg(fd, sreg(fn) * sreg(fm)); break; - case FMUL_d: set_dreg(fd, dreg(fn) * dreg(fm)); break; - case FDIV_s: set_sreg(fd, sreg(fn) / sreg(fm)); break; - case FDIV_d: set_dreg(fd, dreg(fn) / dreg(fm)); break; - case FMAX_s: set_sreg(fd, FPMax(sreg(fn), sreg(fm))); break; - case FMAX_d: set_dreg(fd, FPMax(dreg(fn), dreg(fm))); break; - case FMIN_s: set_sreg(fd, FPMin(sreg(fn), sreg(fm))); break; - case FMIN_d: set_dreg(fd, FPMin(dreg(fn), dreg(fm))); break; - case FMAXNM_s: set_sreg(fd, FPMaxNM(sreg(fn), sreg(fm))); break; - case FMAXNM_d: set_dreg(fd, FPMaxNM(dreg(fn), dreg(fm))); break; - case FMINNM_s: set_sreg(fd, FPMinNM(sreg(fn), sreg(fm))); break; - case FMINNM_d: set_dreg(fd, FPMinNM(dreg(fn), dreg(fm))); break; - default: UNIMPLEMENTED(); - } -} - - -void Simulator::VisitFPDataProcessing3Source(Instruction* instr) { - AssertSupportedFPCR(); - - unsigned fd = instr->Rd(); - unsigned fn = instr->Rn(); - unsigned fm = instr->Rm(); - unsigned fa = instr->Ra(); - - // The C99 (and C++11) fma function performs a fused multiply-accumulate. - switch (instr->Mask(FPDataProcessing3SourceMask)) { - // fd = fa +/- (fn * fm) - case FMADD_s: set_sreg(fd, fmaf(sreg(fn), sreg(fm), sreg(fa))); break; - case FMSUB_s: set_sreg(fd, fmaf(-sreg(fn), sreg(fm), sreg(fa))); break; - case FMADD_d: set_dreg(fd, fma(dreg(fn), dreg(fm), dreg(fa))); break; - case FMSUB_d: set_dreg(fd, fma(-dreg(fn), dreg(fm), dreg(fa))); break; - // Variants of the above where the result is negated. - case FNMADD_s: set_sreg(fd, -fmaf(sreg(fn), sreg(fm), sreg(fa))); break; - case FNMSUB_s: set_sreg(fd, -fmaf(-sreg(fn), sreg(fm), sreg(fa))); break; - case FNMADD_d: set_dreg(fd, -fma(dreg(fn), dreg(fm), dreg(fa))); break; - case FNMSUB_d: set_dreg(fd, -fma(-dreg(fn), dreg(fm), dreg(fa))); break; - default: UNIMPLEMENTED(); - } -} - - -template -T Simulator::FPMax(T a, T b) { - if (IsSignallingNaN(a)) { - return a; - } else if (IsSignallingNaN(b)) { - return b; - } else if (std::isnan(a)) { - ASSERT(IsQuietNaN(a)); - return a; - } else if (std::isnan(b)) { - ASSERT(IsQuietNaN(b)); - return b; - } - - if ((a == 0.0) && (b == 0.0) && - (copysign(1.0, a) != copysign(1.0, b))) { - // a and b are zero, and the sign differs: return +0.0. - return 0.0; - } else { - return (a > b) ? a : b; - } -} - - -template -T Simulator::FPMaxNM(T a, T b) { - if (IsQuietNaN(a) && !IsQuietNaN(b)) { - a = kFP64NegativeInfinity; - } else if (!IsQuietNaN(a) && IsQuietNaN(b)) { - b = kFP64NegativeInfinity; - } - return FPMax(a, b); -} - -template -T Simulator::FPMin(T a, T b) { - if (IsSignallingNaN(a)) { - return a; - } else if (IsSignallingNaN(b)) { - return b; - } else if (std::isnan(a)) { - ASSERT(IsQuietNaN(a)); - return a; - } else if (std::isnan(b)) { - ASSERT(IsQuietNaN(b)); - return b; - } - - if ((a == 0.0) && (b == 0.0) && - (copysign(1.0, a) != copysign(1.0, b))) { - // a and b are zero, and the sign differs: return -0.0. - return -0.0; - } else { - return (a < b) ? a : b; - } -} - - -template -T Simulator::FPMinNM(T a, T b) { - if (IsQuietNaN(a) && !IsQuietNaN(b)) { - a = kFP64PositiveInfinity; - } else if (!IsQuietNaN(a) && IsQuietNaN(b)) { - b = kFP64PositiveInfinity; - } - return FPMin(a, b); -} - - -void Simulator::VisitSystem(Instruction* instr) { - // Some system instructions hijack their Op and Cp fields to represent a - // range of immediates instead of indicating a different instruction. This - // makes the decoding tricky. - if (instr->Mask(SystemSysRegFMask) == SystemSysRegFixed) { - switch (instr->Mask(SystemSysRegMask)) { - case MRS: { - switch (instr->ImmSystemRegister()) { - case NZCV: set_xreg(instr->Rt(), nzcv().RawValue()); break; - case FPCR: set_xreg(instr->Rt(), fpcr().RawValue()); break; - default: UNIMPLEMENTED(); - } - break; - } - case MSR: { - switch (instr->ImmSystemRegister()) { - case NZCV: nzcv().SetRawValue(xreg(instr->Rt())); break; - case FPCR: fpcr().SetRawValue(xreg(instr->Rt())); break; - default: UNIMPLEMENTED(); - } - break; - } - } - } else if (instr->Mask(SystemHintFMask) == SystemHintFixed) { - ASSERT(instr->Mask(SystemHintMask) == HINT); - switch (instr->ImmHint()) { - case NOP: break; - default: UNIMPLEMENTED(); - } - } else if (instr->Mask(MemBarrierFMask) == MemBarrierFixed) { - __sync_synchronize(); - } else { - UNIMPLEMENTED(); - } -} - - -bool Simulator::GetValue(const char* desc, int64_t* value) { - int regnum = CodeFromName(desc); - if (regnum >= 0) { - unsigned code = regnum; - if (code == kZeroRegCode) { - // Catch the zero register and return 0. - *value = 0; - return true; - } else if (code == kSPRegInternalCode) { - // Translate the stack pointer code to 31, for Reg31IsStackPointer. - code = 31; - } - if (desc[0] == 'w') { - *value = wreg(code, Reg31IsStackPointer); - } else { - *value = xreg(code, Reg31IsStackPointer); - } - return true; - } else if (strncmp(desc, "0x", 2) == 0) { - return SScanF(desc + 2, "%" SCNx64, - reinterpret_cast(value)) == 1; - } else { - return SScanF(desc, "%" SCNu64, - reinterpret_cast(value)) == 1; - } -} - - -bool Simulator::PrintValue(const char* desc) { - // Define some colour codes to use for the register dump. - // TODO(jbramley): Find a more elegant way of defining these. - char const * const clr_normal = FLAG_log_colour ? "\033[m" : ""; - char const * const clr_reg_name = FLAG_log_colour ? "\033[1;34m" : ""; - char const * const clr_reg_value = FLAG_log_colour ? "\033[1;36m" : ""; - char const * const clr_fpreg_name = FLAG_log_colour ? "\033[1;33m" : ""; - char const * const clr_fpreg_value = FLAG_log_colour ? "\033[1;35m" : ""; - - if (strcmp(desc, "csp") == 0) { - ASSERT(CodeFromName(desc) == static_cast(kSPRegInternalCode)); - PrintF("%s csp:%s 0x%016" PRIx64 "%s\n", - clr_reg_name, clr_reg_value, xreg(31, Reg31IsStackPointer), clr_normal); - return true; - } else if (strcmp(desc, "wcsp") == 0) { - ASSERT(CodeFromName(desc) == static_cast(kSPRegInternalCode)); - PrintF("%s wcsp:%s 0x%08" PRIx32 "%s\n", - clr_reg_name, clr_reg_value, wreg(31, Reg31IsStackPointer), clr_normal); - return true; - } - - int i = CodeFromName(desc); - STATIC_ASSERT(kNumberOfRegisters == kNumberOfFPRegisters); - if (i < 0 || static_cast(i) >= kNumberOfFPRegisters) return false; - - if (desc[0] == 'v') { - PrintF("%s %s:%s 0x%016" PRIx64 "%s (%s%s:%s %g%s %s:%s %g%s)\n", - clr_fpreg_name, VRegNameForCode(i), - clr_fpreg_value, double_to_rawbits(dreg(i)), - clr_normal, - clr_fpreg_name, DRegNameForCode(i), - clr_fpreg_value, dreg(i), - clr_fpreg_name, SRegNameForCode(i), - clr_fpreg_value, sreg(i), - clr_normal); - return true; - } else if (desc[0] == 'd') { - PrintF("%s %s:%s %g%s\n", - clr_fpreg_name, DRegNameForCode(i), - clr_fpreg_value, dreg(i), - clr_normal); - return true; - } else if (desc[0] == 's') { - PrintF("%s %s:%s %g%s\n", - clr_fpreg_name, SRegNameForCode(i), - clr_fpreg_value, sreg(i), - clr_normal); - return true; - } else if (desc[0] == 'w') { - PrintF("%s %s:%s 0x%08" PRIx32 "%s\n", - clr_reg_name, WRegNameForCode(i), clr_reg_value, wreg(i), clr_normal); - return true; - } else { - // X register names have a wide variety of starting characters, but anything - // else will be an X register. - PrintF("%s %s:%s 0x%016" PRIx64 "%s\n", - clr_reg_name, XRegNameForCode(i), clr_reg_value, xreg(i), clr_normal); - return true; - } -} - - -void Simulator::Debug() { -#define COMMAND_SIZE 63 -#define ARG_SIZE 255 - -#define STR(a) #a -#define XSTR(a) STR(a) - - char cmd[COMMAND_SIZE + 1]; - char arg1[ARG_SIZE + 1]; - char arg2[ARG_SIZE + 1]; - char* argv[3] = { cmd, arg1, arg2 }; - - // Make sure to have a proper terminating character if reaching the limit. - cmd[COMMAND_SIZE] = 0; - arg1[ARG_SIZE] = 0; - arg2[ARG_SIZE] = 0; - - bool done = false; - bool cleared_log_disasm_bit = false; - - while (!done) { - // Disassemble the next instruction to execute before doing anything else. - PrintInstructionsAt(pc_, 1); - // Read the command line. - char* line = ReadLine("sim> "); - if (line == NULL) { - break; - } else { - // Repeat last command by default. - char* last_input = last_debugger_input(); - if (strcmp(line, "\n") == 0 && (last_input != NULL)) { - DeleteArray(line); - line = last_input; - } else { - // Update the latest command ran - set_last_debugger_input(line); - } - - // Use sscanf to parse the individual parts of the command line. At the - // moment no command expects more than two parameters. - int argc = SScanF(line, - "%" XSTR(COMMAND_SIZE) "s " - "%" XSTR(ARG_SIZE) "s " - "%" XSTR(ARG_SIZE) "s", - cmd, arg1, arg2); - - // stepi / si ------------------------------------------------------------ - if ((strcmp(cmd, "si") == 0) || (strcmp(cmd, "stepi") == 0)) { - // We are about to execute instructions, after which by default we - // should increment the pc_. If it was set when reaching this debug - // instruction, it has not been cleared because this instruction has not - // completed yet. So clear it manually. - pc_modified_ = false; - - if (argc == 1) { - ExecuteInstruction(); - } else { - int64_t number_of_instructions_to_execute = 1; - GetValue(arg1, &number_of_instructions_to_execute); - - set_log_parameters(log_parameters() | LOG_DISASM); - while (number_of_instructions_to_execute-- > 0) { - ExecuteInstruction(); - } - set_log_parameters(log_parameters() & ~LOG_DISASM); - PrintF("\n"); - } - - // If it was necessary, the pc has already been updated or incremented - // when executing the instruction. So we do not want it to be updated - // again. It will be cleared when exiting. - pc_modified_ = true; - - // next / n -------------------------------------------------------------- - } else if ((strcmp(cmd, "next") == 0) || (strcmp(cmd, "n") == 0)) { - // Tell the simulator to break after the next executed BL. - break_on_next_ = true; - // Continue. - done = true; - - // continue / cont / c --------------------------------------------------- - } else if ((strcmp(cmd, "continue") == 0) || - (strcmp(cmd, "cont") == 0) || - (strcmp(cmd, "c") == 0)) { - // Leave the debugger shell. - done = true; - - // disassemble / disasm / di --------------------------------------------- - } else if (strcmp(cmd, "disassemble") == 0 || - strcmp(cmd, "disasm") == 0 || - strcmp(cmd, "di") == 0) { - int64_t n_of_instrs_to_disasm = 10; // default value. - int64_t address = reinterpret_cast(pc_); // default value. - if (argc >= 2) { // disasm - GetValue(arg1, &n_of_instrs_to_disasm); - } - if (argc >= 3) { // disasm
- GetValue(arg2, &address); - } - - // Disassemble. - PrintInstructionsAt(reinterpret_cast(address), - n_of_instrs_to_disasm); - PrintF("\n"); - - // print / p ------------------------------------------------------------- - } else if ((strcmp(cmd, "print") == 0) || (strcmp(cmd, "p") == 0)) { - if (argc == 2) { - if (strcmp(arg1, "all") == 0) { - // TODO(all): better support for printing in the debugger. - PrintRegisters(true); - PrintFPRegisters(true); - } else { - if (!PrintValue(arg1)) { - PrintF("%s unrecognized\n", arg1); - } - } - } else { - PrintF( - "print \n" - " Print the content of a register. (alias 'p')\n" - " 'print all' will print all registers.\n" - " Use 'printobject' to get more details about the value.\n"); - } - - // printobject / po ------------------------------------------------------ - } else if ((strcmp(cmd, "printobject") == 0) || - (strcmp(cmd, "po") == 0)) { - if (argc == 2) { - int64_t value; - if (GetValue(arg1, &value)) { - Object* obj = reinterpret_cast(value); - PrintF("%s: \n", arg1); -#ifdef DEBUG - obj->PrintLn(); -#else - obj->ShortPrint(); - PrintF("\n"); -#endif - } else { - PrintF("%s unrecognized\n", arg1); - } - } else { - PrintF("printobject \n" - "printobject \n" - " Print details about the value. (alias 'po')\n"); - } - - // stack / mem ---------------------------------------------------------- - } else if (strcmp(cmd, "stack") == 0 || strcmp(cmd, "mem") == 0) { - int64_t* cur = NULL; - int64_t* end = NULL; - int next_arg = 1; - - if (strcmp(cmd, "stack") == 0) { - cur = reinterpret_cast(jssp()); - - } else { // "mem" - int64_t value; - if (!GetValue(arg1, &value)) { - PrintF("%s unrecognized\n", arg1); - continue; - } - cur = reinterpret_cast(value); - next_arg++; - } - - int64_t words = 0; - if (argc == next_arg) { - words = 10; - } else if (argc == next_arg + 1) { - if (!GetValue(argv[next_arg], &words)) { - PrintF("%s unrecognized\n", argv[next_arg]); - PrintF("Printing 10 double words by default"); - words = 10; - } - } else { - UNREACHABLE(); - } - end = cur + words; - - while (cur < end) { - PrintF(" 0x%016" PRIx64 ": 0x%016" PRIx64 " %10" PRId64, - reinterpret_cast(cur), *cur, *cur); - HeapObject* obj = reinterpret_cast(*cur); - int64_t value = *cur; - Heap* current_heap = v8::internal::Isolate::Current()->heap(); - if (((value & 1) == 0) || current_heap->Contains(obj)) { - PrintF(" ("); - if ((value & kSmiTagMask) == 0) { - STATIC_ASSERT(kSmiValueSize == 32); - int32_t untagged = (value >> kSmiShift) & 0xffffffff; - PrintF("smi %" PRId32, untagged); - } else { - obj->ShortPrint(); - } - PrintF(")"); - } - PrintF("\n"); - cur++; - } - - // trace / t ------------------------------------------------------------- - } else if (strcmp(cmd, "trace") == 0 || strcmp(cmd, "t") == 0) { - if ((log_parameters() & (LOG_DISASM | LOG_REGS)) != - (LOG_DISASM | LOG_REGS)) { - PrintF("Enabling disassembly and registers tracing\n"); - set_log_parameters(log_parameters() | LOG_DISASM | LOG_REGS); - } else { - PrintF("Disabling disassembly and registers tracing\n"); - set_log_parameters(log_parameters() & ~(LOG_DISASM | LOG_REGS)); - } - - // break / b ------------------------------------------------------------- - } else if (strcmp(cmd, "break") == 0 || strcmp(cmd, "b") == 0) { - if (argc == 2) { - int64_t value; - if (GetValue(arg1, &value)) { - SetBreakpoint(reinterpret_cast(value)); - } else { - PrintF("%s unrecognized\n", arg1); - } - } else { - ListBreakpoints(); - PrintF("Use `break
` to set or disable a breakpoint\n"); - } - - // gdb ------------------------------------------------------------------- - } else if (strcmp(cmd, "gdb") == 0) { - PrintF("Relinquishing control to gdb.\n"); - OS::DebugBreak(); - PrintF("Regaining control from gdb.\n"); - - // sysregs --------------------------------------------------------------- - } else if (strcmp(cmd, "sysregs") == 0) { - PrintSystemRegisters(); - - // help / h -------------------------------------------------------------- - } else if (strcmp(cmd, "help") == 0 || strcmp(cmd, "h") == 0) { - PrintF( - "stepi / si\n" - " stepi \n" - " Step instructions.\n" - "next / n\n" - " Continue execution until a BL instruction is reached.\n" - " At this point a breakpoint is set just after this BL.\n" - " Then execution is resumed. It will probably later hit the\n" - " breakpoint just set.\n" - "continue / cont / c\n" - " Continue execution from here.\n" - "disassemble / disasm / di\n" - " disassemble
\n" - " Disassemble instructions from current
.\n" - " By default is 20 and
is the current pc.\n" - "print / p\n" - " print \n" - " Print the content of a register.\n" - " 'print all' will print all registers.\n" - " Use 'printobject' to get more details about the value.\n" - "printobject / po\n" - " printobject \n" - " printobject \n" - " Print details about the value.\n" - "stack\n" - " stack []\n" - " Dump stack content, default dump 10 words\n" - "mem\n" - " mem
[]\n" - " Dump memory content, default dump 10 words\n" - "trace / t\n" - " Toggle disassembly and register tracing\n" - "break / b\n" - " break : list all breakpoints\n" - " break
: set / enable / disable a breakpoint.\n" - "gdb\n" - " Enter gdb.\n" - "sysregs\n" - " Print all system registers (including NZCV).\n"); - } else { - PrintF("Unknown command: %s\n", cmd); - PrintF("Use 'help' for more information.\n"); - } - } - if (cleared_log_disasm_bit == true) { - set_log_parameters(log_parameters_ | LOG_DISASM); - } - } -} - - -// Calls into the V8 runtime are based on this very simple interface. -// Note: To be able to return two values from some calls the code in runtime.cc -// uses the ObjectPair structure. -// The simulator assumes all runtime calls return two 64-bits values. If they -// don't, register x1 is clobbered. This is fine because x1 is caller-saved. -struct ObjectPair { - int64_t res0; - int64_t res1; -}; - - -typedef ObjectPair (*SimulatorRuntimeCall)(int64_t arg0, - int64_t arg1, - int64_t arg2, - int64_t arg3, - int64_t arg4, - int64_t arg5, - int64_t arg6, - int64_t arg7); - -typedef int64_t (*SimulatorRuntimeCompareCall)(double arg1, double arg2); -typedef double (*SimulatorRuntimeFPFPCall)(double arg1, double arg2); -typedef double (*SimulatorRuntimeFPCall)(double arg1); -typedef double (*SimulatorRuntimeFPIntCall)(double arg1, int32_t arg2); - -// This signature supports direct call in to API function native callback -// (refer to InvocationCallback in v8.h). -typedef void (*SimulatorRuntimeDirectApiCall)(int64_t arg0); -typedef void (*SimulatorRuntimeProfilingApiCall)(int64_t arg0, void* arg1); - -// This signature supports direct call to accessor getter callback. -typedef void (*SimulatorRuntimeDirectGetterCall)(int64_t arg0, int64_t arg1); -typedef void (*SimulatorRuntimeProfilingGetterCall)(int64_t arg0, int64_t arg1, - void* arg2); - -void Simulator::VisitException(Instruction* instr) { - // Define some colour codes to use for log messages. - // TODO(jbramley): Find a more elegant way of defining these. - char const* const clr_normal = (FLAG_log_colour) ? ("\033[m") - : (""); - char const* const clr_debug_number = (FLAG_log_colour) ? ("\033[1;33m") - : (""); - char const* const clr_debug_message = (FLAG_log_colour) ? ("\033[0;33m") - : (""); - char const* const clr_printf = (FLAG_log_colour) ? ("\033[0;32m") - : (""); - - switch (instr->Mask(ExceptionMask)) { - case HLT: { - if (instr->ImmException() == kImmExceptionIsDebug) { - // Read the arguments encoded inline in the instruction stream. - uint32_t code; - uint32_t parameters; - char const * message; - - ASSERT(sizeof(*pc_) == 1); - memcpy(&code, pc_ + kDebugCodeOffset, sizeof(code)); - memcpy(¶meters, pc_ + kDebugParamsOffset, sizeof(parameters)); - message = reinterpret_cast(pc_ + kDebugMessageOffset); - - // Always print something when we hit a debug point that breaks. - // We are going to break, so printing something is not an issue in - // terms of speed. - if (FLAG_trace_sim_messages || FLAG_trace_sim || (parameters & BREAK)) { - if (message != NULL) { - PrintF("%sDebugger hit %d: %s%s%s\n", - clr_debug_number, - code, - clr_debug_message, - message, - clr_normal); - } else { - PrintF("%sDebugger hit %d.%s\n", - clr_debug_number, - code, - clr_normal); - } - } - - // Other options. - switch (parameters & kDebuggerTracingDirectivesMask) { - case TRACE_ENABLE: - set_log_parameters(log_parameters() | parameters); - if (parameters & LOG_SYS_REGS) { PrintSystemRegisters(); } - if (parameters & LOG_REGS) { PrintRegisters(); } - if (parameters & LOG_FP_REGS) { PrintFPRegisters(); } - break; - case TRACE_DISABLE: - set_log_parameters(log_parameters() & ~parameters); - break; - case TRACE_OVERRIDE: - set_log_parameters(parameters); - break; - default: - // We don't support a one-shot LOG_DISASM. - ASSERT((parameters & LOG_DISASM) == 0); - // Don't print information that is already being traced. - parameters &= ~log_parameters(); - // Print the requested information. - if (parameters & LOG_SYS_REGS) PrintSystemRegisters(true); - if (parameters & LOG_REGS) PrintRegisters(true); - if (parameters & LOG_FP_REGS) PrintFPRegisters(true); - } - - // The stop parameters are inlined in the code. Skip them: - // - Skip to the end of the message string. - pc_ += kDebugMessageOffset + strlen(message) + 1; - // - Advance to the next aligned location. - pc_ = AlignUp(pc_, kInstructionSize); - // - Verify that the unreachable marker is present. - ASSERT(pc_->Mask(ExceptionMask) == HLT); - ASSERT(pc_->ImmException() == kImmExceptionIsUnreachable); - // - Skip past the unreachable marker. - set_pc(pc_->NextInstruction()); - - // Check if the debugger should break. - if (parameters & BREAK) Debug(); - - } else if (instr->ImmException() == kImmExceptionIsRedirectedCall) { - // TODO(all): Extract the call redirection code into a separate - // function. - - Redirection* redirection = Redirection::FromHltInstruction(instr); - - // The called C code might itself call simulated code, so any - // caller-saved registers (including lr) could still be clobbered by a - // redirected call. - Instruction* return_address = lr(); - - // TODO(jbramley): Make external_function() a template so that we don't - // have to explicitly cast the result for each redirection type. - int64_t external = - reinterpret_cast(redirection->external_function()); - - TraceSim("Call to host function at %p\n", - reinterpret_cast(redirection->external_function())); - - // SP must be 16 bytes aligned at the call interface. - bool stack_alignment_exception = ((sp() & 0xf) != 0); - if (stack_alignment_exception) { - TraceSim(" with unaligned stack 0x%016" PRIx64 ".\n", sp()); - FATAL("ALIGNMENT EXCEPTION"); - } - - switch (redirection->type()) { - default: - TraceSim("Type: Unknown.\n"); - UNREACHABLE(); - break; - - case ExternalReference::BUILTIN_CALL: { - // MaybeObject* f(v8::internal::Arguments). - TraceSim("Type: BUILTIN_CALL\n"); - SimulatorRuntimeCall target = - reinterpret_cast(external); - - // We don't know how many arguments are being passed, but we can - // pass 8 without touching the stack. They will be ignored by the - // host function if they aren't used. - TraceSim("Arguments: " - "0x%016" PRIx64 ", 0x%016" PRIx64 ", " - "0x%016" PRIx64 ", 0x%016" PRIx64 ", " - "0x%016" PRIx64 ", 0x%016" PRIx64 ", " - "0x%016" PRIx64 ", 0x%016" PRIx64, - xreg(0), xreg(1), xreg(2), xreg(3), - xreg(4), xreg(5), xreg(6), xreg(7)); - ObjectPair result = target(xreg(0), xreg(1), xreg(2), xreg(3), - xreg(4), xreg(5), xreg(6), xreg(7)); - TraceSim("Returned: {0x%" PRIx64 ", 0x%" PRIx64"}\n", - result.res0, result.res1); -#ifdef DEBUG - CorruptAllCallerSavedCPURegisters(); -#endif - set_xreg(0, result.res0); - set_xreg(1, result.res1); - break; - } - - case ExternalReference::DIRECT_API_CALL: { - // void f(v8::FunctionCallbackInfo&) - TraceSim("Type: DIRECT_API_CALL\n"); - SimulatorRuntimeDirectApiCall target = - reinterpret_cast(external); - TraceSim("Arguments: 0x%016" PRIx64 "\n", xreg(0)); - target(xreg(0)); - TraceSim("No return value."); -#ifdef DEBUG - CorruptAllCallerSavedCPURegisters(); -#endif - break; - } - - case ExternalReference::BUILTIN_COMPARE_CALL: { - // int f(double, double) - TraceSim("Type: BUILTIN_COMPARE_CALL\n"); - SimulatorRuntimeCompareCall target = - reinterpret_cast(external); - TraceSim("Arguments: %f, %f\n", dreg(0), dreg(1)); - int64_t result = target(dreg(0), dreg(1)); - TraceSim("Returned: %" PRId64 "\n", result); -#ifdef DEBUG - CorruptAllCallerSavedCPURegisters(); -#endif - set_xreg(0, result); - break; - } - - case ExternalReference::BUILTIN_FP_CALL: { - // double f(double) - TraceSim("Type: BUILTIN_FP_CALL\n"); - SimulatorRuntimeFPCall target = - reinterpret_cast(external); - TraceSim("Argument: %f\n", dreg(0)); - double result = target(dreg(0)); - TraceSim("Returned: %f\n", result); -#ifdef DEBUG - CorruptAllCallerSavedCPURegisters(); -#endif - set_dreg(0, result); - break; - } - - case ExternalReference::BUILTIN_FP_FP_CALL: { - // double f(double, double) - TraceSim("Type: BUILTIN_FP_FP_CALL\n"); - SimulatorRuntimeFPFPCall target = - reinterpret_cast(external); - TraceSim("Arguments: %f, %f\n", dreg(0), dreg(1)); - double result = target(dreg(0), dreg(1)); - TraceSim("Returned: %f\n", result); -#ifdef DEBUG - CorruptAllCallerSavedCPURegisters(); -#endif - set_dreg(0, result); - break; - } - - case ExternalReference::BUILTIN_FP_INT_CALL: { - // double f(double, int) - TraceSim("Type: BUILTIN_FP_INT_CALL\n"); - SimulatorRuntimeFPIntCall target = - reinterpret_cast(external); - TraceSim("Arguments: %f, %d\n", dreg(0), wreg(0)); - double result = target(dreg(0), wreg(0)); - TraceSim("Returned: %f\n", result); -#ifdef DEBUG - CorruptAllCallerSavedCPURegisters(); -#endif - set_dreg(0, result); - break; - } - - case ExternalReference::DIRECT_GETTER_CALL: { - // void f(Local property, PropertyCallbackInfo& info) - TraceSim("Type: DIRECT_GETTER_CALL\n"); - SimulatorRuntimeDirectGetterCall target = - reinterpret_cast(external); - TraceSim("Arguments: 0x%016" PRIx64 ", 0x%016" PRIx64 "\n", - xreg(0), xreg(1)); - target(xreg(0), xreg(1)); - TraceSim("No return value."); -#ifdef DEBUG - CorruptAllCallerSavedCPURegisters(); -#endif - break; - } - - case ExternalReference::PROFILING_API_CALL: { - // void f(v8::FunctionCallbackInfo&, v8::FunctionCallback) - TraceSim("Type: PROFILING_API_CALL\n"); - SimulatorRuntimeProfilingApiCall target = - reinterpret_cast(external); - void* arg1 = Redirection::ReverseRedirection(xreg(1)); - TraceSim("Arguments: 0x%016" PRIx64 ", %p\n", xreg(0), arg1); - target(xreg(0), arg1); - TraceSim("No return value."); -#ifdef DEBUG - CorruptAllCallerSavedCPURegisters(); -#endif - break; - } - - case ExternalReference::PROFILING_GETTER_CALL: { - // void f(Local property, PropertyCallbackInfo& info, - // AccessorGetterCallback callback) - TraceSim("Type: PROFILING_GETTER_CALL\n"); - SimulatorRuntimeProfilingGetterCall target = - reinterpret_cast( - external); - void* arg2 = Redirection::ReverseRedirection(xreg(2)); - TraceSim("Arguments: 0x%016" PRIx64 ", 0x%016" PRIx64 ", %p\n", - xreg(0), xreg(1), arg2); - target(xreg(0), xreg(1), arg2); - TraceSim("No return value."); -#ifdef DEBUG - CorruptAllCallerSavedCPURegisters(); -#endif - break; - } - } - - set_lr(return_address); - set_pc(return_address); - } else if (instr->ImmException() == kImmExceptionIsPrintf) { - // Read the argument encoded inline in the instruction stream. - uint32_t type; - ASSERT(sizeof(*pc_) == 1); - memcpy(&type, pc_ + kPrintfTypeOffset, sizeof(type)); - - const char* format = reg(0); - - // Pass all of the relevant PCS registers onto printf. It doesn't - // matter if we pass too many as the extra ones won't be read. - int result; - fputs(clr_printf, stream_); - if (type == CPURegister::kRegister) { - result = fprintf(stream_, format, - xreg(1), xreg(2), xreg(3), xreg(4), - xreg(5), xreg(6), xreg(7)); - } else if (type == CPURegister::kFPRegister) { - result = fprintf(stream_, format, - dreg(0), dreg(1), dreg(2), dreg(3), - dreg(4), dreg(5), dreg(6), dreg(7)); - } else { - ASSERT(type == CPURegister::kNoRegister); - result = fprintf(stream_, "%s", format); - } - fputs(clr_normal, stream_); - set_xreg(0, result); - - // TODO(jbramley): Consider clobbering all caller-saved registers here. - - // The printf parameters are inlined in the code, so skip them. - set_pc(pc_->InstructionAtOffset(kPrintfLength)); - - // Set LR as if we'd just called a native printf function. - set_lr(pc()); - - } else if (instr->ImmException() == kImmExceptionIsUnreachable) { - fprintf(stream_, "Hit UNREACHABLE marker at PC=%p.\n", - reinterpret_cast(pc_)); - abort(); - - } else { - OS::DebugBreak(); - } - break; - } - - default: - UNIMPLEMENTED(); - } -} - -#endif // USE_SIMULATOR - -} } // namespace v8::internal - -#endif // V8_TARGET_ARCH_A64 diff --git a/deps/v8/src/a64/simulator-a64.h b/deps/v8/src/a64/simulator-a64.h deleted file mode 100644 index 535f287096..0000000000 --- a/deps/v8/src/a64/simulator-a64.h +++ /dev/null @@ -1,868 +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. - -#ifndef V8_A64_SIMULATOR_A64_H_ -#define V8_A64_SIMULATOR_A64_H_ - -#include -#include - -#include "v8.h" - -#include "globals.h" -#include "utils.h" -#include "allocation.h" -#include "assembler.h" -#include "a64/assembler-a64.h" -#include "a64/decoder-a64.h" -#include "a64/disasm-a64.h" -#include "a64/instrument-a64.h" - -#define REGISTER_CODE_LIST(R) \ -R(0) R(1) R(2) R(3) R(4) R(5) R(6) R(7) \ -R(8) R(9) R(10) R(11) R(12) R(13) R(14) R(15) \ -R(16) R(17) R(18) R(19) R(20) R(21) R(22) R(23) \ -R(24) R(25) R(26) R(27) R(28) R(29) R(30) R(31) - -namespace v8 { -namespace internal { - -#if !defined(USE_SIMULATOR) - -// Running without a simulator on a native A64 platform. -// When running without a simulator we call the entry directly. -#define CALL_GENERATED_CODE(entry, p0, p1, p2, p3, p4) \ - (entry(p0, p1, p2, p3, p4)) - -typedef int (*a64_regexp_matcher)(String* input, - int64_t start_offset, - const byte* input_start, - const byte* input_end, - int* output, - int64_t output_size, - Address stack_base, - int64_t direct_call, - void* return_address, - Isolate* isolate); - -// Call the generated regexp code directly. The code at the entry address -// should act as a function matching the type a64_regexp_matcher. -// The ninth argument is a dummy that reserves the space used for -// the return address added by the ExitFrame in native calls. -#define CALL_GENERATED_REGEXP_CODE(entry, p0, p1, p2, p3, p4, p5, p6, p7, p8) \ - (FUNCTION_CAST(entry)( \ - p0, p1, p2, p3, p4, p5, p6, p7, NULL, p8)) - -#define TRY_CATCH_FROM_ADDRESS(try_catch_address) \ - reinterpret_cast(try_catch_address) - -// Running without a simulator there is nothing to do. -class SimulatorStack : public v8::internal::AllStatic { - public: - static uintptr_t JsLimitFromCLimit(v8::internal::Isolate* isolate, - uintptr_t c_limit) { - USE(isolate); - return c_limit; - } - - static uintptr_t RegisterCTryCatch(uintptr_t try_catch_address) { - return try_catch_address; - } - - static void UnregisterCTryCatch() { } -}; - -#else // !defined(USE_SIMULATOR) - -enum ReverseByteMode { - Reverse16 = 0, - Reverse32 = 1, - Reverse64 = 2 -}; - - -// The proper way to initialize a simulated system register (such as NZCV) is as -// follows: -// SimSystemRegister nzcv = SimSystemRegister::DefaultValueFor(NZCV); -class SimSystemRegister { - public: - // The default constructor represents a register which has no writable bits. - // It is not possible to set its value to anything other than 0. - SimSystemRegister() : value_(0), write_ignore_mask_(0xffffffff) { } - - uint32_t RawValue() const { - return value_; - } - - void SetRawValue(uint32_t new_value) { - value_ = (value_ & write_ignore_mask_) | (new_value & ~write_ignore_mask_); - } - - uint32_t Bits(int msb, int lsb) const { - return unsigned_bitextract_32(msb, lsb, value_); - } - - int32_t SignedBits(int msb, int lsb) const { - return signed_bitextract_32(msb, lsb, value_); - } - - void SetBits(int msb, int lsb, uint32_t bits); - - // Default system register values. - static SimSystemRegister DefaultValueFor(SystemRegister id); - -#define DEFINE_GETTER(Name, HighBit, LowBit, Func) \ - uint32_t Name() const { return Func(HighBit, LowBit); } \ - void Set##Name(uint32_t bits) { SetBits(HighBit, LowBit, bits); } -#define DEFINE_WRITE_IGNORE_MASK(Name, Mask) \ - static const uint32_t Name##WriteIgnoreMask = ~static_cast(Mask); - - SYSTEM_REGISTER_FIELDS_LIST(DEFINE_GETTER, DEFINE_WRITE_IGNORE_MASK) - -#undef DEFINE_ZERO_BITS -#undef DEFINE_GETTER - - protected: - // Most system registers only implement a few of the bits in the word. Other - // bits are "read-as-zero, write-ignored". The write_ignore_mask argument - // describes the bits which are not modifiable. - SimSystemRegister(uint32_t value, uint32_t write_ignore_mask) - : value_(value), write_ignore_mask_(write_ignore_mask) { } - - uint32_t value_; - uint32_t write_ignore_mask_; -}; - - -// Represent a register (r0-r31, v0-v31). -template -class SimRegisterBase { - public: - template - void Set(T new_value, unsigned size = sizeof(T)) { - ASSERT(size <= kSizeInBytes); - ASSERT(size <= sizeof(new_value)); - // All AArch64 registers are zero-extending; Writing a W register clears the - // top bits of the corresponding X register. - memset(value_, 0, kSizeInBytes); - memcpy(value_, &new_value, size); - } - - // Copy 'size' bytes of the register to the result, and zero-extend to fill - // the result. - template - T Get(unsigned size = sizeof(T)) const { - ASSERT(size <= kSizeInBytes); - T result; - memset(&result, 0, sizeof(result)); - memcpy(&result, value_, size); - return result; - } - - protected: - uint8_t value_[kSizeInBytes]; -}; -typedef SimRegisterBase SimRegister; // r0-r31 -typedef SimRegisterBase SimFPRegister; // v0-v31 - - -class Simulator : public DecoderVisitor { - public: - explicit Simulator(Decoder* decoder, - Isolate* isolate = NULL, - FILE* stream = stderr); - ~Simulator(); - - // System functions. - - static void Initialize(Isolate* isolate); - - static Simulator* current(v8::internal::Isolate* isolate); - - class CallArgument; - - // Call an arbitrary function taking an arbitrary number of arguments. The - // varargs list must be a set of arguments with type CallArgument, and - // terminated by CallArgument::End(). - void CallVoid(byte* entry, CallArgument* args); - - // Like CallVoid, but expect a return value. - int64_t CallInt64(byte* entry, CallArgument* args); - double CallDouble(byte* entry, CallArgument* args); - - // V8 calls into generated JS code with 5 parameters and into - // generated RegExp code with 10 parameters. These are convenience functions, - // which set up the simulator state and grab the result on return. - int64_t CallJS(byte* entry, - byte* function_entry, - JSFunction* func, - Object* revc, - int64_t argc, - Object*** argv); - int64_t CallRegExp(byte* entry, - String* input, - int64_t start_offset, - const byte* input_start, - const byte* input_end, - int* output, - int64_t output_size, - Address stack_base, - int64_t direct_call, - void* return_address, - Isolate* isolate); - - // A wrapper class that stores an argument for one of the above Call - // functions. - // - // Only arguments up to 64 bits in size are supported. - class CallArgument { - public: - template - explicit CallArgument(T argument) { - ASSERT(sizeof(argument) <= sizeof(bits_)); - memcpy(&bits_, &argument, sizeof(argument)); - type_ = X_ARG; - } - - explicit CallArgument(double argument) { - ASSERT(sizeof(argument) == sizeof(bits_)); - memcpy(&bits_, &argument, sizeof(argument)); - type_ = D_ARG; - } - - explicit CallArgument(float argument) { - // TODO(all): CallArgument(float) is untested, remove this check once - // tested. - UNIMPLEMENTED(); - // Make the D register a NaN to try to trap errors if the callee expects a - // double. If it expects a float, the callee should ignore the top word. - ASSERT(sizeof(kFP64SignallingNaN) == sizeof(bits_)); - memcpy(&bits_, &kFP64SignallingNaN, sizeof(kFP64SignallingNaN)); - // Write the float payload to the S register. - ASSERT(sizeof(argument) <= sizeof(bits_)); - memcpy(&bits_, &argument, sizeof(argument)); - type_ = D_ARG; - } - - // This indicates the end of the arguments list, so that CallArgument - // objects can be passed into varargs functions. - static CallArgument End() { return CallArgument(); } - - int64_t bits() const { return bits_; } - bool IsEnd() const { return type_ == NO_ARG; } - bool IsX() const { return type_ == X_ARG; } - bool IsD() const { return type_ == D_ARG; } - - private: - enum CallArgumentType { X_ARG, D_ARG, NO_ARG }; - - // All arguments are aligned to at least 64 bits and we don't support - // passing bigger arguments, so the payload size can be fixed at 64 bits. - int64_t bits_; - CallArgumentType type_; - - CallArgument() { type_ = NO_ARG; } - }; - - - // Start the debugging command line. - void Debug(); - - bool GetValue(const char* desc, int64_t* value); - - bool PrintValue(const char* desc); - - // Push an address onto the JS stack. - uintptr_t PushAddress(uintptr_t address); - - // Pop an address from the JS stack. - uintptr_t PopAddress(); - - // Accessor to the internal simulator stack area. - uintptr_t StackLimit() const; - - void ResetState(); - - // Runtime call support. - static void* RedirectExternalReference(void* external_function, - ExternalReference::Type type); - - // Run the simulator. - static const Instruction* kEndOfSimAddress; - void DecodeInstruction(); - void Run(); - void RunFrom(Instruction* start); - - // Simulation helpers. - template - void set_pc(T new_pc) { - ASSERT(sizeof(T) == sizeof(pc_)); - memcpy(&pc_, &new_pc, sizeof(T)); - pc_modified_ = true; - } - Instruction* pc() { return pc_; } - - void increment_pc() { - if (!pc_modified_) { - pc_ = pc_->NextInstruction(); - } - - pc_modified_ = false; - } - - void ExecuteInstruction() { - ASSERT(IsAligned(reinterpret_cast(pc_), kInstructionSize)); - CheckBreakNext(); - decoder_->Decode(pc_); - LogProcessorState(); - increment_pc(); - CheckBreakpoints(); - } - - // Declare all Visitor functions. - #define DECLARE(A) void Visit##A(Instruction* instr); - VISITOR_LIST(DECLARE) - #undef DECLARE - - // Register accessors. - - // Return 'size' bits of the value of an integer register, as the specified - // type. The value is zero-extended to fill the result. - // - // The only supported values of 'size' are kXRegSize and kWRegSize. - template - T reg(unsigned size, unsigned code, - Reg31Mode r31mode = Reg31IsZeroRegister) const { - unsigned size_in_bytes = size / 8; - ASSERT(size_in_bytes <= sizeof(T)); - ASSERT((size == kXRegSize) || (size == kWRegSize)); - ASSERT(code < kNumberOfRegisters); - - if ((code == 31) && (r31mode == Reg31IsZeroRegister)) { - T result; - memset(&result, 0, sizeof(result)); - return result; - } - return registers_[code].Get(size_in_bytes); - } - - // Like reg(), but infer the access size from the template type. - template - T reg(unsigned code, Reg31Mode r31mode = Reg31IsZeroRegister) const { - return reg(sizeof(T) * 8, code, r31mode); - } - - // Common specialized accessors for the reg() template. - int32_t wreg(unsigned code, - Reg31Mode r31mode = Reg31IsZeroRegister) const { - return reg(code, r31mode); - } - - int64_t xreg(unsigned code, - Reg31Mode r31mode = Reg31IsZeroRegister) const { - return reg(code, r31mode); - } - - int64_t reg(unsigned size, unsigned code, - Reg31Mode r31mode = Reg31IsZeroRegister) const { - return reg(size, code, r31mode); - } - - // Write 'size' bits of 'value' into an integer register. The value is - // zero-extended. This behaviour matches AArch64 register writes. - // - // The only supported values of 'size' are kXRegSize and kWRegSize. - template - void set_reg(unsigned size, unsigned code, T value, - Reg31Mode r31mode = Reg31IsZeroRegister) { - unsigned size_in_bytes = size / 8; - ASSERT(size_in_bytes <= sizeof(T)); - ASSERT((size == kXRegSize) || (size == kWRegSize)); - ASSERT(code < kNumberOfRegisters); - - if ((code == 31) && (r31mode == Reg31IsZeroRegister)) { - return; - } - return registers_[code].Set(value, size_in_bytes); - } - - // Like set_reg(), but infer the access size from the template type. - template - void set_reg(unsigned code, T value, - Reg31Mode r31mode = Reg31IsZeroRegister) { - set_reg(sizeof(value) * 8, code, value, r31mode); - } - - // Common specialized accessors for the set_reg() template. - void set_wreg(unsigned code, int32_t value, - Reg31Mode r31mode = Reg31IsZeroRegister) { - set_reg(kWRegSize, code, value, r31mode); - } - - void set_xreg(unsigned code, int64_t value, - Reg31Mode r31mode = Reg31IsZeroRegister) { - set_reg(kXRegSize, code, value, r31mode); - } - - // Commonly-used special cases. - template - void set_lr(T value) { - ASSERT(sizeof(T) == kPointerSize); - set_reg(kLinkRegCode, value); - } - - template - void set_sp(T value) { - ASSERT(sizeof(T) == kPointerSize); - set_reg(31, value, Reg31IsStackPointer); - } - - int64_t sp() { return xreg(31, Reg31IsStackPointer); } - int64_t jssp() { return xreg(kJSSPCode, Reg31IsStackPointer); } - int64_t fp() { - return xreg(kFramePointerRegCode, Reg31IsStackPointer); - } - Instruction* lr() { return reg(kLinkRegCode); } - - Address get_sp() { return reg
(31, Reg31IsStackPointer); } - - // Return 'size' bits of the value of a floating-point register, as the - // specified type. The value is zero-extended to fill the result. - // - // The only supported values of 'size' are kDRegSize and kSRegSize. - template - T fpreg(unsigned size, unsigned code) const { - unsigned size_in_bytes = size / 8; - ASSERT(size_in_bytes <= sizeof(T)); - ASSERT((size == kDRegSize) || (size == kSRegSize)); - ASSERT(code < kNumberOfFPRegisters); - return fpregisters_[code].Get(size_in_bytes); - } - - // Like fpreg(), but infer the access size from the template type. - template - T fpreg(unsigned code) const { - return fpreg(sizeof(T) * 8, code); - } - - // Common specialized accessors for the fpreg() template. - float sreg(unsigned code) const { - return fpreg(code); - } - - uint32_t sreg_bits(unsigned code) const { - return fpreg(code); - } - - double dreg(unsigned code) const { - return fpreg(code); - } - - uint64_t dreg_bits(unsigned code) const { - return fpreg(code); - } - - double fpreg(unsigned size, unsigned code) const { - switch (size) { - case kSRegSize: return sreg(code); - case kDRegSize: return dreg(code); - default: - UNREACHABLE(); - return 0.0; - } - } - - // Write 'value' into a floating-point register. The value is zero-extended. - // This behaviour matches AArch64 register writes. - template - void set_fpreg(unsigned code, T value) { - ASSERT((sizeof(value) == kDRegSizeInBytes) || - (sizeof(value) == kSRegSizeInBytes)); - ASSERT(code < kNumberOfFPRegisters); - fpregisters_[code].Set(value, sizeof(value)); - } - - // Common specialized accessors for the set_fpreg() template. - void set_sreg(unsigned code, float value) { - set_fpreg(code, value); - } - - void set_sreg_bits(unsigned code, uint32_t value) { - set_fpreg(code, value); - } - - void set_dreg(unsigned code, double value) { - set_fpreg(code, value); - } - - void set_dreg_bits(unsigned code, uint64_t value) { - set_fpreg(code, value); - } - - bool N() { return nzcv_.N() != 0; } - bool Z() { return nzcv_.Z() != 0; } - bool C() { return nzcv_.C() != 0; } - bool V() { return nzcv_.V() != 0; } - SimSystemRegister& nzcv() { return nzcv_; } - - // TODO(jbramley): Find a way to make the fpcr_ members return the proper - // types, so this accessor is not necessary. - FPRounding RMode() { return static_cast(fpcr_.RMode()); } - SimSystemRegister& fpcr() { return fpcr_; } - - // Debug helpers - - // Simulator breakpoints. - struct Breakpoint { - Instruction* location; - bool enabled; - }; - std::vector breakpoints_; - void SetBreakpoint(Instruction* breakpoint); - void ListBreakpoints(); - void CheckBreakpoints(); - - // Helpers for the 'next' command. - // When this is set, the Simulator will insert a breakpoint after the next BL - // instruction it meets. - bool break_on_next_; - // Check if the Simulator should insert a break after the current instruction - // for the 'next' command. - void CheckBreakNext(); - - // Disassemble instruction at the given address. - void PrintInstructionsAt(Instruction* pc, uint64_t count); - - void PrintSystemRegisters(bool print_all = false); - void PrintRegisters(bool print_all_regs = false); - void PrintFPRegisters(bool print_all_regs = false); - void PrintProcessorState(); - void PrintWrite(uint8_t* address, uint64_t value, unsigned num_bytes); - void LogSystemRegisters() { - if (log_parameters_ & LOG_SYS_REGS) PrintSystemRegisters(); - } - void LogRegisters() { - if (log_parameters_ & LOG_REGS) PrintRegisters(); - } - void LogFPRegisters() { - if (log_parameters_ & LOG_FP_REGS) PrintFPRegisters(); - } - void LogProcessorState() { - LogSystemRegisters(); - LogRegisters(); - LogFPRegisters(); - } - void LogWrite(uint8_t* address, uint64_t value, unsigned num_bytes) { - if (log_parameters_ & LOG_WRITE) PrintWrite(address, value, num_bytes); - } - - int log_parameters() { return log_parameters_; } - void set_log_parameters(int new_parameters) { - if (new_parameters & LOG_DISASM) { - decoder_->InsertVisitorBefore(print_disasm_, this); - } else { - decoder_->RemoveVisitor(print_disasm_); - } - log_parameters_ = new_parameters; - } - - static inline const char* WRegNameForCode(unsigned code, - Reg31Mode mode = Reg31IsZeroRegister); - static inline const char* XRegNameForCode(unsigned code, - Reg31Mode mode = Reg31IsZeroRegister); - static inline const char* SRegNameForCode(unsigned code); - static inline const char* DRegNameForCode(unsigned code); - static inline const char* VRegNameForCode(unsigned code); - static inline int CodeFromName(const char* name); - - protected: - // Simulation helpers ------------------------------------ - bool ConditionPassed(Condition cond) { - switch (cond) { - case eq: - return Z(); - case ne: - return !Z(); - case hs: - return C(); - case lo: - return !C(); - case mi: - return N(); - case pl: - return !N(); - case vs: - return V(); - case vc: - return !V(); - case hi: - return C() && !Z(); - case ls: - return !(C() && !Z()); - case ge: - return N() == V(); - case lt: - return N() != V(); - case gt: - return !Z() && (N() == V()); - case le: - return !(!Z() && (N() == V())); - case nv: // Fall through. - case al: - return true; - default: - UNREACHABLE(); - return false; - } - } - - bool ConditionFailed(Condition cond) { - return !ConditionPassed(cond); - } - - void AddSubHelper(Instruction* instr, int64_t op2); - int64_t AddWithCarry(unsigned reg_size, - bool set_flags, - int64_t src1, - int64_t src2, - int64_t carry_in = 0); - void LogicalHelper(Instruction* instr, int64_t op2); - void ConditionalCompareHelper(Instruction* instr, int64_t op2); - void LoadStoreHelper(Instruction* instr, - int64_t offset, - AddrMode addrmode); - void LoadStorePairHelper(Instruction* instr, AddrMode addrmode); - uint8_t* LoadStoreAddress(unsigned addr_reg, - int64_t offset, - AddrMode addrmode); - void LoadStoreWriteBack(unsigned addr_reg, - int64_t offset, - AddrMode addrmode); - void CheckMemoryAccess(uint8_t* address, uint8_t* stack); - - uint64_t MemoryRead(uint8_t* address, unsigned num_bytes); - uint8_t MemoryRead8(uint8_t* address); - uint16_t MemoryRead16(uint8_t* address); - uint32_t MemoryRead32(uint8_t* address); - float MemoryReadFP32(uint8_t* address); - uint64_t MemoryRead64(uint8_t* address); - double MemoryReadFP64(uint8_t* address); - - void MemoryWrite(uint8_t* address, uint64_t value, unsigned num_bytes); - void MemoryWrite32(uint8_t* address, uint32_t value); - void MemoryWriteFP32(uint8_t* address, float value); - void MemoryWrite64(uint8_t* address, uint64_t value); - void MemoryWriteFP64(uint8_t* address, double value); - - int64_t ShiftOperand(unsigned reg_size, - int64_t value, - Shift shift_type, - unsigned amount); - int64_t Rotate(unsigned reg_width, - int64_t value, - Shift shift_type, - unsigned amount); - int64_t ExtendValue(unsigned reg_width, - int64_t value, - Extend extend_type, - unsigned left_shift = 0); - - uint64_t ReverseBits(uint64_t value, unsigned num_bits); - uint64_t ReverseBytes(uint64_t value, ReverseByteMode mode); - - void FPCompare(double val0, double val1); - double FPRoundInt(double value, FPRounding round_mode); - double FPToDouble(float value); - float FPToFloat(double value, FPRounding round_mode); - double FixedToDouble(int64_t src, int fbits, FPRounding round_mode); - double UFixedToDouble(uint64_t src, int fbits, FPRounding round_mode); - float FixedToFloat(int64_t src, int fbits, FPRounding round_mode); - float UFixedToFloat(uint64_t src, int fbits, FPRounding round_mode); - int32_t FPToInt32(double value, FPRounding rmode); - int64_t FPToInt64(double value, FPRounding rmode); - uint32_t FPToUInt32(double value, FPRounding rmode); - uint64_t FPToUInt64(double value, FPRounding rmode); - - template - T FPMax(T a, T b); - - template - T FPMin(T a, T b); - - template - T FPMaxNM(T a, T b); - - template - T FPMinNM(T a, T b); - - void CheckStackAlignment(); - - inline void CheckPCSComplianceAndRun(); - -#ifdef DEBUG - // Corruption values should have their least significant byte cleared to - // allow the code of the register being corrupted to be inserted. - static const uint64_t kCallerSavedRegisterCorruptionValue = - 0xca11edc0de000000UL; - // This value is a NaN in both 32-bit and 64-bit FP. - static const uint64_t kCallerSavedFPRegisterCorruptionValue = - 0x7ff000007f801000UL; - // This value is a mix of 32/64-bits NaN and "verbose" immediate. - static const uint64_t kDefaultCPURegisterCorruptionValue = - 0x7ffbad007f8bad00UL; - - void CorruptRegisters(CPURegList* list, - uint64_t value = kDefaultCPURegisterCorruptionValue); - void CorruptAllCallerSavedCPURegisters(); -#endif - - // Processor state --------------------------------------- - - // Output stream. - FILE* stream_; - PrintDisassembler* print_disasm_; - - // Instrumentation. - Instrument* instrument_; - - // General purpose registers. Register 31 is the stack pointer. - SimRegister registers_[kNumberOfRegisters]; - - // Floating point registers - SimFPRegister fpregisters_[kNumberOfFPRegisters]; - - // Processor state - // bits[31, 27]: Condition flags N, Z, C, and V. - // (Negative, Zero, Carry, Overflow) - SimSystemRegister nzcv_; - - // Floating-Point Control Register - SimSystemRegister fpcr_; - - // Only a subset of FPCR features are supported by the simulator. This helper - // checks that the FPCR settings are supported. - // - // This is checked when floating-point instructions are executed, not when - // FPCR is set. This allows generated code to modify FPCR for external - // functions, or to save and restore it when entering and leaving generated - // code. - void AssertSupportedFPCR() { - ASSERT(fpcr().DN() == 0); // No default-NaN support. - ASSERT(fpcr().FZ() == 0); // No flush-to-zero support. - ASSERT(fpcr().RMode() == FPTieEven); // Ties-to-even rounding only. - - // The simulator does not support half-precision operations so fpcr().AHP() - // is irrelevant, and is not checked here. - } - - static int CalcNFlag(uint64_t result, unsigned reg_size) { - return (result >> (reg_size - 1)) & 1; - } - - static int CalcZFlag(uint64_t result) { - return result == 0; - } - - static const uint32_t kConditionFlagsMask = 0xf0000000; - - // Stack - byte* stack_; - static const intptr_t stack_protection_size_ = KB; - intptr_t stack_size_; - byte* stack_limit_; - // TODO(aleram): protect the stack. - - Decoder* decoder_; - Decoder* disassembler_decoder_; - - // Indicates if the pc has been modified by the instruction and should not be - // automatically incremented. - bool pc_modified_; - Instruction* pc_; - - static const char* xreg_names[]; - static const char* wreg_names[]; - static const char* sreg_names[]; - static const char* dreg_names[]; - static const char* vreg_names[]; - - // Debugger input. - void set_last_debugger_input(char* input) { - DeleteArray(last_debugger_input_); - last_debugger_input_ = input; - } - char* last_debugger_input() { return last_debugger_input_; } - char* last_debugger_input_; - - private: - int log_parameters_; - Isolate* isolate_; -}; - - -// When running with the simulator transition into simulated execution at this -// point. -#define CALL_GENERATED_CODE(entry, p0, p1, p2, p3, p4) \ - reinterpret_cast(Simulator::current(Isolate::Current())->CallJS( \ - FUNCTION_ADDR(entry), \ - p0, p1, p2, p3, p4)) - -#define CALL_GENERATED_REGEXP_CODE(entry, p0, p1, p2, p3, p4, p5, p6, p7, p8) \ - Simulator::current(Isolate::Current())->CallRegExp( \ - entry, \ - p0, p1, p2, p3, p4, p5, p6, p7, NULL, p8) - -#define TRY_CATCH_FROM_ADDRESS(try_catch_address) \ - try_catch_address == NULL ? \ - NULL : *(reinterpret_cast(try_catch_address)) - - -// The simulator has its own stack. Thus it has a different stack limit from -// the C-based native code. -// See also 'class SimulatorStack' in arm/simulator-arm.h. -class SimulatorStack : public v8::internal::AllStatic { - public: - static uintptr_t JsLimitFromCLimit(v8::internal::Isolate* isolate, - uintptr_t c_limit) { - return Simulator::current(isolate)->StackLimit(); - } - - static uintptr_t RegisterCTryCatch(uintptr_t try_catch_address) { - Simulator* sim = Simulator::current(Isolate::Current()); - return sim->PushAddress(try_catch_address); - } - - static void UnregisterCTryCatch() { - Simulator::current(Isolate::Current())->PopAddress(); - } -}; - -#endif // !defined(USE_SIMULATOR) - -} } // namespace v8::internal - -#endif // V8_A64_SIMULATOR_A64_H_ diff --git a/deps/v8/src/a64/stub-cache-a64.cc b/deps/v8/src/a64/stub-cache-a64.cc deleted file mode 100644 index 57c03e8b96..0000000000 --- a/deps/v8/src/a64/stub-cache-a64.cc +++ /dev/null @@ -1,1548 +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 "v8.h" - -#if V8_TARGET_ARCH_A64 - -#include "ic-inl.h" -#include "codegen.h" -#include "stub-cache.h" - -namespace v8 { -namespace internal { - - -#define __ ACCESS_MASM(masm) - - -void StubCompiler::GenerateDictionaryNegativeLookup(MacroAssembler* masm, - Label* miss_label, - Register receiver, - Handle name, - Register scratch0, - Register scratch1) { - ASSERT(!AreAliased(receiver, scratch0, scratch1)); - ASSERT(name->IsUniqueName()); - Counters* counters = masm->isolate()->counters(); - __ IncrementCounter(counters->negative_lookups(), 1, scratch0, scratch1); - __ IncrementCounter(counters->negative_lookups_miss(), 1, scratch0, scratch1); - - Label done; - - const int kInterceptorOrAccessCheckNeededMask = - (1 << Map::kHasNamedInterceptor) | (1 << Map::kIsAccessCheckNeeded); - - // Bail out if the receiver has a named interceptor or requires access checks. - Register map = scratch1; - __ Ldr(map, FieldMemOperand(receiver, HeapObject::kMapOffset)); - __ Ldrb(scratch0, FieldMemOperand(map, Map::kBitFieldOffset)); - __ Tst(scratch0, kInterceptorOrAccessCheckNeededMask); - __ B(ne, miss_label); - - // Check that receiver is a JSObject. - __ Ldrb(scratch0, FieldMemOperand(map, Map::kInstanceTypeOffset)); - __ Cmp(scratch0, FIRST_SPEC_OBJECT_TYPE); - __ B(lt, miss_label); - - // Load properties array. - Register properties = scratch0; - __ Ldr(properties, FieldMemOperand(receiver, JSObject::kPropertiesOffset)); - // Check that the properties array is a dictionary. - __ Ldr(map, FieldMemOperand(properties, HeapObject::kMapOffset)); - __ JumpIfNotRoot(map, Heap::kHashTableMapRootIndex, miss_label); - - NameDictionaryLookupStub::GenerateNegativeLookup(masm, - miss_label, - &done, - receiver, - properties, - name, - scratch1); - __ Bind(&done); - __ DecrementCounter(counters->negative_lookups_miss(), 1, scratch0, scratch1); -} - - -// Probe primary or secondary table. -// If the entry is found in the cache, the generated code jump to the first -// instruction of the stub in the cache. -// If there is a miss the code fall trough. -// -// 'receiver', 'name' and 'offset' registers are preserved on miss. -static void ProbeTable(Isolate* isolate, - MacroAssembler* masm, - Code::Flags flags, - StubCache::Table table, - Register receiver, - Register name, - Register offset, - Register scratch, - Register scratch2, - Register scratch3) { - // Some code below relies on the fact that the Entry struct contains - // 3 pointers (name, code, map). - STATIC_ASSERT(sizeof(StubCache::Entry) == (3 * kPointerSize)); - - ExternalReference key_offset(isolate->stub_cache()->key_reference(table)); - ExternalReference value_offset(isolate->stub_cache()->value_reference(table)); - ExternalReference map_offset(isolate->stub_cache()->map_reference(table)); - - uintptr_t key_off_addr = reinterpret_cast(key_offset.address()); - uintptr_t value_off_addr = - reinterpret_cast(value_offset.address()); - uintptr_t map_off_addr = reinterpret_cast(map_offset.address()); - - Label miss; - - ASSERT(!AreAliased(name, offset, scratch, scratch2, scratch3)); - - // Multiply by 3 because there are 3 fields per entry. - __ Add(scratch3, offset, Operand(offset, LSL, 1)); - - // Calculate the base address of the entry. - __ Mov(scratch, Operand(key_offset)); - __ Add(scratch, scratch, Operand(scratch3, LSL, kPointerSizeLog2)); - - // Check that the key in the entry matches the name. - __ Ldr(scratch2, MemOperand(scratch)); - __ Cmp(name, scratch2); - __ B(ne, &miss); - - // Check the map matches. - __ Ldr(scratch2, MemOperand(scratch, map_off_addr - key_off_addr)); - __ Ldr(scratch3, FieldMemOperand(receiver, HeapObject::kMapOffset)); - __ Cmp(scratch2, scratch3); - __ B(ne, &miss); - - // Get the code entry from the cache. - __ Ldr(scratch, MemOperand(scratch, value_off_addr - key_off_addr)); - - // Check that the flags match what we're looking for. - __ Ldr(scratch2.W(), FieldMemOperand(scratch, Code::kFlagsOffset)); - __ Bic(scratch2.W(), scratch2.W(), Code::kFlagsNotUsedInLookup); - __ Cmp(scratch2.W(), flags); - __ B(ne, &miss); - -#ifdef DEBUG - if (FLAG_test_secondary_stub_cache && table == StubCache::kPrimary) { - __ B(&miss); - } else if (FLAG_test_primary_stub_cache && table == StubCache::kSecondary) { - __ B(&miss); - } -#endif - - // Jump to the first instruction in the code stub. - __ Add(scratch, scratch, Code::kHeaderSize - kHeapObjectTag); - __ Br(scratch); - - // Miss: fall through. - __ Bind(&miss); -} - - -void StubCache::GenerateProbe(MacroAssembler* masm, - Code::Flags flags, - Register receiver, - Register name, - Register scratch, - Register extra, - Register extra2, - Register extra3) { - Isolate* isolate = masm->isolate(); - Label miss; - - // Make sure the flags does not name a specific type. - ASSERT(Code::ExtractTypeFromFlags(flags) == 0); - - // Make sure that there are no register conflicts. - ASSERT(!AreAliased(receiver, name, scratch, extra, extra2, extra3)); - - // Make sure extra and extra2 registers are valid. - ASSERT(!extra.is(no_reg)); - ASSERT(!extra2.is(no_reg)); - ASSERT(!extra3.is(no_reg)); - - Counters* counters = masm->isolate()->counters(); - __ IncrementCounter(counters->megamorphic_stub_cache_probes(), 1, - extra2, extra3); - - // Check that the receiver isn't a smi. - __ JumpIfSmi(receiver, &miss); - - // Compute the hash for primary table. - __ Ldr(scratch, FieldMemOperand(name, Name::kHashFieldOffset)); - __ Ldr(extra, FieldMemOperand(receiver, HeapObject::kMapOffset)); - __ Add(scratch, scratch, extra); - __ Eor(scratch, scratch, flags); - // We shift out the last two bits because they are not part of the hash. - __ Ubfx(scratch, scratch, kHeapObjectTagSize, - CountTrailingZeros(kPrimaryTableSize, 64)); - - // Probe the primary table. - ProbeTable(isolate, masm, flags, kPrimary, receiver, name, - scratch, extra, extra2, extra3); - - // Primary miss: Compute hash for secondary table. - __ Sub(scratch, scratch, Operand(name, LSR, kHeapObjectTagSize)); - __ Add(scratch, scratch, flags >> kHeapObjectTagSize); - __ And(scratch, scratch, kSecondaryTableSize - 1); - - // Probe the secondary table. - ProbeTable(isolate, masm, flags, kSecondary, receiver, name, - scratch, extra, extra2, extra3); - - // Cache miss: Fall-through and let caller handle the miss by - // entering the runtime system. - __ Bind(&miss); - __ IncrementCounter(counters->megamorphic_stub_cache_misses(), 1, - extra2, extra3); -} - - -void StubCompiler::GenerateLoadGlobalFunctionPrototype(MacroAssembler* masm, - int index, - Register prototype) { - // Load the global or builtins object from the current context. - __ Ldr(prototype, GlobalObjectMemOperand()); - // Load the native context from the global or builtins object. - __ Ldr(prototype, - FieldMemOperand(prototype, GlobalObject::kNativeContextOffset)); - // Load the function from the native context. - __ Ldr(prototype, ContextMemOperand(prototype, index)); - // Load the initial map. The global functions all have initial maps. - __ Ldr(prototype, - FieldMemOperand(prototype, JSFunction::kPrototypeOrInitialMapOffset)); - // Load the prototype from the initial map. - __ Ldr(prototype, FieldMemOperand(prototype, Map::kPrototypeOffset)); -} - - -void StubCompiler::GenerateDirectLoadGlobalFunctionPrototype( - MacroAssembler* masm, - int index, - Register prototype, - Label* miss) { - Isolate* isolate = masm->isolate(); - // Get the global function with the given index. - Handle function( - JSFunction::cast(isolate->native_context()->get(index))); - - // Check we're still in the same context. - Register scratch = prototype; - __ Ldr(scratch, GlobalObjectMemOperand()); - __ Ldr(scratch, FieldMemOperand(scratch, GlobalObject::kNativeContextOffset)); - __ Ldr(scratch, ContextMemOperand(scratch, index)); - __ Cmp(scratch, Operand(function)); - __ B(ne, miss); - - // Load its initial map. The global functions all have initial maps. - __ Mov(prototype, Operand(Handle(function->initial_map()))); - // Load the prototype from the initial map. - __ Ldr(prototype, FieldMemOperand(prototype, Map::kPrototypeOffset)); -} - - -void StubCompiler::GenerateFastPropertyLoad(MacroAssembler* masm, - Register dst, - Register src, - bool inobject, - int index, - Representation representation) { - ASSERT(!FLAG_track_double_fields || !representation.IsDouble()); - USE(representation); - if (inobject) { - int offset = index * kPointerSize; - __ Ldr(dst, FieldMemOperand(src, offset)); - } else { - // Calculate the offset into the properties array. - int offset = index * kPointerSize + FixedArray::kHeaderSize; - __ Ldr(dst, FieldMemOperand(src, JSObject::kPropertiesOffset)); - __ Ldr(dst, FieldMemOperand(dst, offset)); - } -} - - -void StubCompiler::GenerateLoadArrayLength(MacroAssembler* masm, - Register receiver, - Register scratch, - Label* miss_label) { - ASSERT(!AreAliased(receiver, scratch)); - - // Check that the receiver isn't a smi. - __ JumpIfSmi(receiver, miss_label); - - // Check that the object is a JS array. - __ JumpIfNotObjectType(receiver, scratch, scratch, JS_ARRAY_TYPE, - miss_label); - - // Load length directly from the JS array. - __ Ldr(x0, FieldMemOperand(receiver, JSArray::kLengthOffset)); - __ Ret(); -} - - -// Generate code to check if an object is a string. If the object is a -// heap object, its map's instance type is left in the scratch1 register. -static void GenerateStringCheck(MacroAssembler* masm, - Register receiver, - Register scratch1, - Label* smi, - Label* non_string_object) { - // Check that the receiver isn't a smi. - __ JumpIfSmi(receiver, smi); - - // Get the object's instance type filed. - __ Ldr(scratch1, FieldMemOperand(receiver, HeapObject::kMapOffset)); - __ Ldrb(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset)); - // Check if the "not string" bit is set. - __ Tbnz(scratch1, MaskToBit(kNotStringTag), non_string_object); -} - - -// Generate code to load the length from a string object and return the length. -// If the receiver object is not a string or a wrapped string object the -// execution continues at the miss label. The register containing the -// receiver is not clobbered if the receiver is not a string. -void StubCompiler::GenerateLoadStringLength(MacroAssembler* masm, - Register receiver, - Register scratch1, - Register scratch2, - Label* miss) { - // Input registers can't alias because we don't want to clobber the - // receiver register if the object is not a string. - ASSERT(!AreAliased(receiver, scratch1, scratch2)); - - Label check_wrapper; - - // Check if the object is a string leaving the instance type in the - // scratch1 register. - GenerateStringCheck(masm, receiver, scratch1, miss, &check_wrapper); - - // Load length directly from the string. - __ Ldr(x0, FieldMemOperand(receiver, String::kLengthOffset)); - __ Ret(); - - // Check if the object is a JSValue wrapper. - __ Bind(&check_wrapper); - __ Cmp(scratch1, Operand(JS_VALUE_TYPE)); - __ B(ne, miss); - - // Unwrap the value and check if the wrapped value is a string. - __ Ldr(scratch1, FieldMemOperand(receiver, JSValue::kValueOffset)); - GenerateStringCheck(masm, scratch1, scratch2, miss, miss); - __ Ldr(x0, FieldMemOperand(scratch1, String::kLengthOffset)); - __ Ret(); -} - - -void StubCompiler::GenerateLoadFunctionPrototype(MacroAssembler* masm, - Register receiver, - Register scratch1, - Register scratch2, - Label* miss_label) { - __ TryGetFunctionPrototype(receiver, scratch1, scratch2, miss_label); - // TryGetFunctionPrototype can't put the result directly in x0 because the - // 3 inputs registers can't alias and we call this function from - // LoadIC::GenerateFunctionPrototype, where receiver is x0. So we explicitly - // move the result in x0. - __ Mov(x0, scratch1); - __ Ret(); -} - - -// 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 StubCompiler::GenerateCheckPropertyCell(MacroAssembler* masm, - Handle global, - Handle name, - Register scratch, - Label* miss) { - Handle cell = JSGlobalObject::EnsurePropertyCell(global, name); - ASSERT(cell->value()->IsTheHole()); - __ Mov(scratch, Operand(cell)); - __ Ldr(scratch, FieldMemOperand(scratch, Cell::kValueOffset)); - __ JumpIfNotRoot(scratch, Heap::kTheHoleValueRootIndex, miss); -} - - -void StoreStubCompiler::GenerateNegativeHolderLookup( - MacroAssembler* masm, - Handle holder, - Register holder_reg, - Handle name, - Label* miss) { - if (holder->IsJSGlobalObject()) { - GenerateCheckPropertyCell( - masm, Handle::cast(holder), name, scratch1(), miss); - } else if (!holder->HasFastProperties() && !holder->IsJSGlobalProxy()) { - GenerateDictionaryNegativeLookup( - masm, miss, holder_reg, name, scratch1(), scratch2()); - } -} - - -// Generate StoreTransition code, value is passed in x0 register. -// When leaving generated code after success, the receiver_reg and storage_reg -// may be clobbered. Upon branch to miss_label, the receiver and name registers -// have their original values. -void StoreStubCompiler::GenerateStoreTransition(MacroAssembler* masm, - Handle object, - LookupResult* lookup, - Handle transition, - Handle name, - Register receiver_reg, - Register storage_reg, - Register value_reg, - Register scratch1, - Register scratch2, - Register scratch3, - Label* miss_label, - Label* slow) { - Label exit; - - ASSERT(!AreAliased(receiver_reg, storage_reg, value_reg, - scratch1, scratch2, scratch3)); - - // We don't need scratch3. - scratch3 = NoReg; - - int descriptor = transition->LastAdded(); - DescriptorArray* descriptors = transition->instance_descriptors(); - PropertyDetails details = descriptors->GetDetails(descriptor); - Representation representation = details.representation(); - ASSERT(!representation.IsNone()); - - if (details.type() == CONSTANT) { - Handle constant(descriptors->GetValue(descriptor), masm->isolate()); - __ LoadObject(scratch1, constant); - __ Cmp(value_reg, scratch1); - __ B(ne, miss_label); - } else if (FLAG_track_fields && representation.IsSmi()) { - __ JumpIfNotSmi(value_reg, miss_label); - } else if (FLAG_track_heap_object_fields && representation.IsHeapObject()) { - __ JumpIfSmi(value_reg, miss_label); - } else if (FLAG_track_double_fields && representation.IsDouble()) { - Label do_store, heap_number; - __ AllocateHeapNumber(storage_reg, slow, scratch1, scratch2); - - // TODO(jbramley): Is fp_scratch the most appropriate FP scratch register? - // It's only used in Fcmp, but it's not really safe to use it like this. - __ JumpIfNotSmi(value_reg, &heap_number); - __ SmiUntagToDouble(fp_scratch, value_reg); - __ B(&do_store); - - __ Bind(&heap_number); - __ CheckMap(value_reg, scratch1, Heap::kHeapNumberMapRootIndex, - miss_label, DONT_DO_SMI_CHECK); - __ Ldr(fp_scratch, FieldMemOperand(value_reg, HeapNumber::kValueOffset)); - - __ Bind(&do_store); - __ Str(fp_scratch, FieldMemOperand(storage_reg, HeapNumber::kValueOffset)); - } - - // Stub never generated for non-global objects that require access checks. - ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded()); - - // Perform map transition for the receiver if necessary. - if ((details.type() == FIELD) && - (object->map()->unused_property_fields() == 0)) { - // The properties must be extended before we can store the value. - // We jump to a runtime call that extends the properties array. - __ Mov(scratch1, Operand(transition)); - __ Push(receiver_reg, scratch1, value_reg); - __ TailCallExternalReference( - ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage), - masm->isolate()), - 3, - 1); - return; - } - - // Update the map of the object. - __ Mov(scratch1, Operand(transition)); - __ Str(scratch1, FieldMemOperand(receiver_reg, HeapObject::kMapOffset)); - - // Update the write barrier for the map field. - __ RecordWriteField(receiver_reg, - HeapObject::kMapOffset, - scratch1, - scratch2, - kLRHasNotBeenSaved, - kDontSaveFPRegs, - OMIT_REMEMBERED_SET, - OMIT_SMI_CHECK); - - if (details.type() == CONSTANT) { - ASSERT(value_reg.is(x0)); - __ Ret(); - return; - } - - int index = transition->instance_descriptors()->GetFieldIndex( - transition->LastAdded()); - - // Adjust for the number of properties stored in the object. Even in the - // face of a transition we can use the old map here because the size of the - // object and the number of in-object properties is not going to change. - index -= object->map()->inobject_properties(); - - // TODO(verwaest): Share this code as a code stub. - SmiCheck smi_check = representation.IsTagged() - ? INLINE_SMI_CHECK : OMIT_SMI_CHECK; - if (index < 0) { - // Set the property straight into the object. - int offset = object->map()->instance_size() + (index * kPointerSize); - // TODO(jbramley): This construct appears in several places in this - // function. Try to clean it up, perhaps using a result_reg. - if (FLAG_track_double_fields && representation.IsDouble()) { - __ Str(storage_reg, FieldMemOperand(receiver_reg, offset)); - } else { - __ Str(value_reg, FieldMemOperand(receiver_reg, offset)); - } - - if (!FLAG_track_fields || !representation.IsSmi()) { - // Update the write barrier for the array address. - if (!FLAG_track_double_fields || !representation.IsDouble()) { - __ Mov(storage_reg, value_reg); - } - __ RecordWriteField(receiver_reg, - offset, - storage_reg, - scratch1, - kLRHasNotBeenSaved, - kDontSaveFPRegs, - EMIT_REMEMBERED_SET, - smi_check); - } - } else { - // Write to the properties array. - int offset = index * kPointerSize + FixedArray::kHeaderSize; - // Get the properties array - __ Ldr(scratch1, - FieldMemOperand(receiver_reg, JSObject::kPropertiesOffset)); - if (FLAG_track_double_fields && representation.IsDouble()) { - __ Str(storage_reg, FieldMemOperand(scratch1, offset)); - } else { - __ Str(value_reg, FieldMemOperand(scratch1, offset)); - } - - if (!FLAG_track_fields || !representation.IsSmi()) { - // Update the write barrier for the array address. - if (!FLAG_track_double_fields || !representation.IsDouble()) { - __ Mov(storage_reg, value_reg); - } - __ RecordWriteField(scratch1, - offset, - storage_reg, - receiver_reg, - kLRHasNotBeenSaved, - kDontSaveFPRegs, - EMIT_REMEMBERED_SET, - smi_check); - } - } - - __ Bind(&exit); - // Return the value (register x0). - ASSERT(value_reg.is(x0)); - __ Ret(); -} - - -// Generate StoreField code, value is passed in x0 register. -// When leaving generated code after success, the receiver_reg and name_reg may -// be clobbered. Upon branch to miss_label, the receiver and name registers have -// their original values. -void StoreStubCompiler::GenerateStoreField(MacroAssembler* masm, - Handle object, - LookupResult* lookup, - Register receiver_reg, - Register name_reg, - Register value_reg, - Register scratch1, - Register scratch2, - Label* miss_label) { - // x0 : value - Label exit; - - // Stub never generated for non-global objects that require access - // checks. - ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded()); - - int index = lookup->GetFieldIndex().field_index(); - - // Adjust for the number of properties stored in the object. Even in the - // face of a transition we can use the old map here because the size of the - // object and the number of in-object properties is not going to change. - index -= object->map()->inobject_properties(); - - Representation representation = lookup->representation(); - ASSERT(!representation.IsNone()); - if (FLAG_track_fields && representation.IsSmi()) { - __ JumpIfNotSmi(value_reg, miss_label); - } else if (FLAG_track_heap_object_fields && representation.IsHeapObject()) { - __ JumpIfSmi(value_reg, miss_label); - } else if (FLAG_track_double_fields && representation.IsDouble()) { - // Load the double storage. - if (index < 0) { - int offset = (index * kPointerSize) + object->map()->instance_size(); - __ Ldr(scratch1, FieldMemOperand(receiver_reg, offset)); - } else { - int offset = (index * kPointerSize) + FixedArray::kHeaderSize; - __ Ldr(scratch1, - FieldMemOperand(receiver_reg, JSObject::kPropertiesOffset)); - __ Ldr(scratch1, FieldMemOperand(scratch1, offset)); - } - - // Store the value into the storage. - Label do_store, heap_number; - // TODO(jbramley): Is fp_scratch the most appropriate FP scratch register? - // It's only used in Fcmp, but it's not really safe to use it like this. - __ JumpIfNotSmi(value_reg, &heap_number); - __ SmiUntagToDouble(fp_scratch, value_reg); - __ B(&do_store); - - __ Bind(&heap_number); - __ CheckMap(value_reg, scratch2, Heap::kHeapNumberMapRootIndex, - miss_label, DONT_DO_SMI_CHECK); - __ Ldr(fp_scratch, FieldMemOperand(value_reg, HeapNumber::kValueOffset)); - - __ Bind(&do_store); - __ Str(fp_scratch, FieldMemOperand(scratch1, HeapNumber::kValueOffset)); - - // Return the value (register x0). - ASSERT(value_reg.is(x0)); - __ Ret(); - return; - } - - // TODO(verwaest): Share this code as a code stub. - SmiCheck smi_check = representation.IsTagged() - ? INLINE_SMI_CHECK : OMIT_SMI_CHECK; - if (index < 0) { - // Set the property straight into the object. - int offset = object->map()->instance_size() + (index * kPointerSize); - __ Str(value_reg, FieldMemOperand(receiver_reg, offset)); - - if (!FLAG_track_fields || !representation.IsSmi()) { - // Skip updating write barrier if storing a smi. - __ JumpIfSmi(value_reg, &exit); - - // Update the write barrier for the array address. - // Pass the now unused name_reg as a scratch register. - __ Mov(name_reg, value_reg); - __ RecordWriteField(receiver_reg, - offset, - name_reg, - scratch1, - kLRHasNotBeenSaved, - kDontSaveFPRegs, - EMIT_REMEMBERED_SET, - smi_check); - } - } else { - // Write to the properties array. - int offset = index * kPointerSize + FixedArray::kHeaderSize; - // Get the properties array - __ Ldr(scratch1, - FieldMemOperand(receiver_reg, JSObject::kPropertiesOffset)); - __ Str(value_reg, FieldMemOperand(scratch1, offset)); - - if (!FLAG_track_fields || !representation.IsSmi()) { - // Skip updating write barrier if storing a smi. - __ JumpIfSmi(value_reg, &exit); - - // Update the write barrier for the array address. - // Ok to clobber receiver_reg and name_reg, since we return. - __ Mov(name_reg, value_reg); - __ RecordWriteField(scratch1, - offset, - name_reg, - receiver_reg, - kLRHasNotBeenSaved, - kDontSaveFPRegs, - EMIT_REMEMBERED_SET, - smi_check); - } - } - - __ Bind(&exit); - // Return the value (register x0). - ASSERT(value_reg.is(x0)); - __ Ret(); -} - - -void StoreStubCompiler::GenerateRestoreName(MacroAssembler* masm, - Label* label, - Handle name) { - if (!label->is_unused()) { - __ Bind(label); - __ Mov(this->name(), Operand(name)); - } -} - - -static void PushInterceptorArguments(MacroAssembler* masm, - Register receiver, - Register holder, - Register name, - Handle holder_obj) { - STATIC_ASSERT(StubCache::kInterceptorArgsNameIndex == 0); - STATIC_ASSERT(StubCache::kInterceptorArgsInfoIndex == 1); - STATIC_ASSERT(StubCache::kInterceptorArgsThisIndex == 2); - STATIC_ASSERT(StubCache::kInterceptorArgsHolderIndex == 3); - STATIC_ASSERT(StubCache::kInterceptorArgsLength == 4); - - __ Push(name); - Handle interceptor(holder_obj->GetNamedInterceptor()); - ASSERT(!masm->isolate()->heap()->InNewSpace(*interceptor)); - Register scratch = name; - __ Mov(scratch, Operand(interceptor)); - __ Push(scratch, receiver, holder); -} - - -static void CompileCallLoadPropertyWithInterceptor( - MacroAssembler* masm, - Register receiver, - Register holder, - Register name, - Handle holder_obj, - IC::UtilityId id) { - PushInterceptorArguments(masm, receiver, holder, name, holder_obj); - - __ CallExternalReference( - ExternalReference(IC_Utility(id), masm->isolate()), - StubCache::kInterceptorArgsLength); -} - - -// Generate call to api function. -void StubCompiler::GenerateFastApiCall(MacroAssembler* masm, - const CallOptimization& optimization, - Handle receiver_map, - Register receiver, - Register scratch, - bool is_store, - int argc, - Register* values) { - ASSERT(!AreAliased(receiver, scratch)); - __ Push(receiver); - // Write the arguments to stack frame. - for (int i = 0; i < argc; i++) { - // TODO(jbramley): Push these in as few Push() calls as possible. - Register arg = values[argc-1-i]; - ASSERT(!AreAliased(receiver, scratch, arg)); - __ Push(arg); - } - - ASSERT(optimization.is_simple_api_call()); - - // Abi for CallApiFunctionStub. - Register callee = x0; - Register call_data = x4; - Register holder = x2; - Register api_function_address = x1; - - // Put holder in place. - CallOptimization::HolderLookup holder_lookup; - Handle api_holder = - optimization.LookupHolderOfExpectedType(receiver_map, &holder_lookup); - switch (holder_lookup) { - case CallOptimization::kHolderIsReceiver: - __ Mov(holder, receiver); - break; - case CallOptimization::kHolderFound: - __ LoadObject(holder, api_holder); - break; - case CallOptimization::kHolderNotFound: - UNREACHABLE(); - break; - } - - Isolate* isolate = masm->isolate(); - Handle function = optimization.constant_function(); - Handle api_call_info = optimization.api_call_info(); - Handle call_data_obj(api_call_info->data(), isolate); - - // Put callee in place. - __ LoadObject(callee, function); - - bool call_data_undefined = false; - // Put call_data in place. - if (isolate->heap()->InNewSpace(*call_data_obj)) { - __ LoadObject(call_data, api_call_info); - __ Ldr(call_data, FieldMemOperand(call_data, CallHandlerInfo::kDataOffset)); - } else if (call_data_obj->IsUndefined()) { - call_data_undefined = true; - __ LoadRoot(call_data, Heap::kUndefinedValueRootIndex); - } else { - __ LoadObject(call_data, call_data_obj); - } - - // Put api_function_address in place. - Address function_address = v8::ToCData
(api_call_info->callback()); - ApiFunction fun(function_address); - ExternalReference ref = ExternalReference(&fun, - ExternalReference::DIRECT_API_CALL, - masm->isolate()); - __ Mov(api_function_address, Operand(ref)); - - // Jump to stub. - CallApiFunctionStub stub(is_store, call_data_undefined, argc); - __ TailCallStub(&stub); -} - - -void StubCompiler::GenerateTailCall(MacroAssembler* masm, Handle code) { - __ Jump(code, RelocInfo::CODE_TARGET); -} - - -#undef __ -#define __ ACCESS_MASM(masm()) - - -Register StubCompiler::CheckPrototypes(Handle type, - Register object_reg, - Handle holder, - Register holder_reg, - Register scratch1, - Register scratch2, - Handle name, - Label* miss, - PrototypeCheckType check) { - Handle receiver_map(IC::TypeToMap(*type, isolate())); - // Make sure that the type feedback oracle harvests the receiver map. - // TODO(svenpanne) Remove this hack when all ICs are reworked. - __ Mov(scratch1, Operand(receiver_map)); - - // object_reg and holder_reg registers can alias. - ASSERT(!AreAliased(object_reg, scratch1, scratch2)); - ASSERT(!AreAliased(holder_reg, scratch1, scratch2)); - - // Keep track of the current object in register reg. - Register reg = object_reg; - int depth = 0; - - Handle current = Handle::null(); - if (type->IsConstant()) { - current = Handle::cast(type->AsConstant()); - } - Handle prototype = Handle::null(); - Handle current_map = receiver_map; - 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; - - // Only global objects and objects that do not require access - // checks are allowed in stubs. - ASSERT(current_map->IsJSGlobalProxyMap() || - !current_map->is_access_check_needed()); - - prototype = handle(JSObject::cast(current_map->prototype())); - if (current_map->is_dictionary_map() && - !current_map->IsJSGlobalObjectMap() && - !current_map->IsJSGlobalProxyMap()) { - if (!name->IsUniqueName()) { - ASSERT(name->IsString()); - name = factory()->InternalizeString(Handle::cast(name)); - } - ASSERT(current.is_null() || - (current->property_dictionary()->FindEntry(*name) == - NameDictionary::kNotFound)); - - GenerateDictionaryNegativeLookup(masm(), miss, reg, name, - scratch1, scratch2); - - __ Ldr(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset)); - reg = holder_reg; // From now on the object will be in holder_reg. - __ Ldr(reg, FieldMemOperand(scratch1, Map::kPrototypeOffset)); - } else { - bool need_map = (depth != 1 || check == CHECK_ALL_MAPS) || - heap()->InNewSpace(*prototype); - Register map_reg = NoReg; - if (need_map) { - map_reg = scratch1; - __ Ldr(map_reg, FieldMemOperand(reg, HeapObject::kMapOffset)); - } - - if (depth != 1 || check == CHECK_ALL_MAPS) { - __ CheckMap(map_reg, current_map, miss, DONT_DO_SMI_CHECK); - } - - // Check access rights to the global object. This has to happen after - // the map check so that we know that the object is actually a global - // object. - if (current_map->IsJSGlobalProxyMap()) { - __ CheckAccessGlobalProxy(reg, scratch2, miss); - } else if (current_map->IsJSGlobalObjectMap()) { - GenerateCheckPropertyCell( - masm(), Handle::cast(current), name, - scratch2, miss); - } - - reg = holder_reg; // From now on the object will be in holder_reg. - - if (heap()->InNewSpace(*prototype)) { - // The prototype is in new space; we cannot store a reference to it - // in the code. Load it from the map. - __ Ldr(reg, FieldMemOperand(map_reg, Map::kPrototypeOffset)); - } else { - // The prototype is in old space; load it directly. - __ Mov(reg, Operand(prototype)); - } - } - - // Go to the next object in the prototype chain. - current = prototype; - current_map = handle(current->map()); - } - - // Log the check depth. - LOG(isolate(), IntEvent("check-maps-depth", depth + 1)); - - // Check the holder map. - if (depth != 0 || check == CHECK_ALL_MAPS) { - // Check the holder map. - __ CheckMap(reg, scratch1, current_map, miss, DONT_DO_SMI_CHECK); - } - - // Perform security check for access to the global object. - ASSERT(current_map->IsJSGlobalProxyMap() || - !current_map->is_access_check_needed()); - if (current_map->IsJSGlobalProxyMap()) { - __ CheckAccessGlobalProxy(reg, scratch1, miss); - } - - // Return the register containing the holder. - return reg; -} - - -void LoadStubCompiler::HandlerFrontendFooter(Handle name, Label* miss) { - if (!miss->is_unused()) { - Label success; - __ B(&success); - - __ Bind(miss); - TailCallBuiltin(masm(), MissBuiltin(kind())); - - __ Bind(&success); - } -} - - -void StoreStubCompiler::HandlerFrontendFooter(Handle name, Label* miss) { - if (!miss->is_unused()) { - Label success; - __ B(&success); - - GenerateRestoreName(masm(), miss, name); - TailCallBuiltin(masm(), MissBuiltin(kind())); - - __ Bind(&success); - } -} - - -Register LoadStubCompiler::CallbackHandlerFrontend(Handle type, - Register object_reg, - Handle holder, - Handle name, - Handle callback) { - Label miss; - - Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); - - // TODO(jbramely): HandlerFrontendHeader returns its result in scratch1(), so - // we can't use it below, but that isn't very obvious. Is there a better way - // of handling this? - - if (!holder->HasFastProperties() && !holder->IsJSGlobalObject()) { - ASSERT(!AreAliased(reg, scratch2(), scratch3(), scratch4())); - - // Load the properties dictionary. - Register dictionary = scratch4(); - __ Ldr(dictionary, FieldMemOperand(reg, JSObject::kPropertiesOffset)); - - // Probe the dictionary. - Label probe_done; - NameDictionaryLookupStub::GeneratePositiveLookup(masm(), - &miss, - &probe_done, - dictionary, - this->name(), - scratch2(), - scratch3()); - __ Bind(&probe_done); - - // If probing finds an entry in the dictionary, scratch3 contains the - // pointer into the dictionary. Check that the value is the callback. - Register pointer = scratch3(); - const int kElementsStartOffset = NameDictionary::kHeaderSize + - NameDictionary::kElementsStartIndex * kPointerSize; - const int kValueOffset = kElementsStartOffset + kPointerSize; - __ Ldr(scratch2(), FieldMemOperand(pointer, kValueOffset)); - __ Cmp(scratch2(), Operand(callback)); - __ B(ne, &miss); - } - - HandlerFrontendFooter(name, &miss); - return reg; -} - - -void LoadStubCompiler::GenerateLoadField(Register reg, - Handle holder, - PropertyIndex field, - Representation representation) { - __ Mov(receiver(), reg); - if (kind() == Code::LOAD_IC) { - LoadFieldStub stub(field.is_inobject(holder), - field.translate(holder), - representation); - GenerateTailCall(masm(), stub.GetCode(isolate())); - } else { - KeyedLoadFieldStub stub(field.is_inobject(holder), - field.translate(holder), - representation); - GenerateTailCall(masm(), stub.GetCode(isolate())); - } -} - - -void LoadStubCompiler::GenerateLoadConstant(Handle value) { - // Return the constant value. - __ LoadObject(x0, value); - __ Ret(); -} - - -void LoadStubCompiler::GenerateLoadCallback( - Register reg, - Handle callback) { - ASSERT(!AreAliased(scratch2(), scratch3(), scratch4(), reg)); - - // Build ExecutableAccessorInfo::args_ list on the stack and push property - // name below the exit frame to make GC aware of them and store pointers to - // them. - STATIC_ASSERT(PropertyCallbackArguments::kHolderIndex == 0); - STATIC_ASSERT(PropertyCallbackArguments::kIsolateIndex == 1); - STATIC_ASSERT(PropertyCallbackArguments::kReturnValueDefaultValueIndex == 2); - STATIC_ASSERT(PropertyCallbackArguments::kReturnValueOffset == 3); - STATIC_ASSERT(PropertyCallbackArguments::kDataIndex == 4); - STATIC_ASSERT(PropertyCallbackArguments::kThisIndex == 5); - STATIC_ASSERT(PropertyCallbackArguments::kArgsLength == 6); - - __ Push(receiver()); - - if (heap()->InNewSpace(callback->data())) { - __ Mov(scratch3(), Operand(callback)); - __ Ldr(scratch3(), FieldMemOperand(scratch3(), - ExecutableAccessorInfo::kDataOffset)); - } else { - __ Mov(scratch3(), Operand(Handle(callback->data(), isolate()))); - } - // TODO(jbramley): Find another scratch register and combine the pushes - // together. Can we use scratch1() here? - __ LoadRoot(scratch4(), Heap::kUndefinedValueRootIndex); - __ Push(scratch3(), scratch4()); - __ Mov(scratch3(), Operand(ExternalReference::isolate_address(isolate()))); - __ Push(scratch4(), scratch3(), reg, name()); - - Register args_addr = scratch2(); - __ Add(args_addr, __ StackPointer(), kPointerSize); - - // Stack at this point: - // sp[40] callback data - // sp[32] undefined - // sp[24] undefined - // sp[16] isolate - // args_addr -> sp[8] reg - // sp[0] name - - // Abi for CallApiGetter. - Register getter_address_reg = x2; - - // Set up the call. - Address getter_address = v8::ToCData
(callback->getter()); - ApiFunction fun(getter_address); - ExternalReference::Type type = ExternalReference::DIRECT_GETTER_CALL; - ExternalReference ref = ExternalReference(&fun, type, isolate()); - __ Mov(getter_address_reg, Operand(ref)); - - CallApiGetterStub stub; - __ TailCallStub(&stub); -} - - -void LoadStubCompiler::GenerateLoadInterceptor( - Register holder_reg, - Handle object, - Handle interceptor_holder, - LookupResult* lookup, - Handle name) { - ASSERT(!AreAliased(receiver(), this->name(), - scratch1(), scratch2(), scratch3())); - ASSERT(interceptor_holder->HasNamedInterceptor()); - ASSERT(!interceptor_holder->GetNamedInterceptor()->getter()->IsUndefined()); - - // So far the most popular follow ups for interceptor loads are FIELD - // and CALLBACKS, so inline only them, other cases may be added later. - bool compile_followup_inline = false; - if (lookup->IsFound() && lookup->IsCacheable()) { - if (lookup->IsField()) { - compile_followup_inline = true; - } else if (lookup->type() == CALLBACKS && - lookup->GetCallbackObject()->IsExecutableAccessorInfo()) { - ExecutableAccessorInfo* callback = - ExecutableAccessorInfo::cast(lookup->GetCallbackObject()); - compile_followup_inline = callback->getter() != NULL && - callback->IsCompatibleReceiver(*object); - } - } - - if (compile_followup_inline) { - // Compile the interceptor call, followed by inline code to load the - // property from further up the prototype chain if the call fails. - // Check that the maps haven't changed. - ASSERT(holder_reg.is(receiver()) || holder_reg.is(scratch1())); - - // Preserve the receiver register explicitly whenever it is different from - // the holder and it is needed should the interceptor return without any - // result. The CALLBACKS case needs the receiver to be passed into C++ code, - // the FIELD case might cause a miss during the prototype check. - bool must_perfrom_prototype_check = *interceptor_holder != lookup->holder(); - bool must_preserve_receiver_reg = !receiver().Is(holder_reg) && - (lookup->type() == CALLBACKS || must_perfrom_prototype_check); - - // Save necessary data before invoking an interceptor. - // Requires a frame to make GC aware of pushed pointers. - { - FrameScope frame_scope(masm(), StackFrame::INTERNAL); - if (must_preserve_receiver_reg) { - __ Push(receiver(), holder_reg, this->name()); - } else { - __ Push(holder_reg, this->name()); - } - // Invoke an interceptor. Note: map checks from receiver to - // interceptor's holder has been compiled before (see a caller - // of this method.) - CompileCallLoadPropertyWithInterceptor( - masm(), receiver(), holder_reg, this->name(), interceptor_holder, - IC::kLoadPropertyWithInterceptorOnly); - - // Check if interceptor provided a value for property. If it's - // the case, return immediately. - Label interceptor_failed; - __ JumpIfRoot(x0, - Heap::kNoInterceptorResultSentinelRootIndex, - &interceptor_failed); - frame_scope.GenerateLeaveFrame(); - __ Ret(); - - __ Bind(&interceptor_failed); - if (must_preserve_receiver_reg) { - __ Pop(this->name(), holder_reg, receiver()); - } else { - __ Pop(this->name(), holder_reg); - } - // Leave the internal frame. - } - GenerateLoadPostInterceptor(holder_reg, interceptor_holder, name, lookup); - } else { // !compile_followup_inline - // Call the runtime system to load the interceptor. - // Check that the maps haven't changed. - PushInterceptorArguments( - masm(), receiver(), holder_reg, this->name(), interceptor_holder); - - ExternalReference ref = - ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorForLoad), - isolate()); - __ TailCallExternalReference(ref, StubCache::kInterceptorArgsLength, 1); - } -} - - -void StubCompiler::GenerateBooleanCheck(Register object, Label* miss) { - Label success; - // Check that the object is a boolean. - // TODO(all): Optimize this like LCodeGen::DoDeferredTaggedToI. - __ JumpIfRoot(object, Heap::kTrueValueRootIndex, &success); - __ JumpIfNotRoot(object, Heap::kFalseValueRootIndex, miss); - __ Bind(&success); -} - - -Handle StoreStubCompiler::CompileStoreCallback( - Handle object, - Handle holder, - Handle name, - Handle callback) { - ASM_LOCATION("StoreStubCompiler::CompileStoreCallback"); - Register holder_reg = HandlerFrontend( - IC::CurrentTypeOf(object, isolate()), receiver(), holder, name); - - // Stub never generated for non-global objects that require access checks. - ASSERT(holder->IsJSGlobalProxy() || !holder->IsAccessCheckNeeded()); - - // TODO(jbramley): Make Push take more than four arguments and combine these - // two calls. - __ Push(receiver(), holder_reg); - __ Mov(scratch1(), Operand(callback)); - __ Mov(scratch2(), Operand(name)); - __ Push(scratch1(), scratch2(), value()); - - // Do tail-call to the runtime system. - ExternalReference store_callback_property = - ExternalReference(IC_Utility(IC::kStoreCallbackProperty), isolate()); - __ TailCallExternalReference(store_callback_property, 5, 1); - - // Return the generated code. - return GetCode(kind(), Code::FAST, name); -} - - -#undef __ -#define __ ACCESS_MASM(masm) - - -void StoreStubCompiler::GenerateStoreViaSetter( - MacroAssembler* masm, - Handle type, - Handle setter) { - // ----------- S t a t e ------------- - // -- x0 : value - // -- x1 : receiver - // -- x2 : name - // -- lr : return address - // ----------------------------------- - Register value = x0; - Register receiver = x1; - Label miss; - - { - FrameScope scope(masm, StackFrame::INTERNAL); - - // Save value register, so we can restore it later. - __ Push(value); - - if (!setter.is_null()) { - // Call the JavaScript setter with receiver and value on the stack. - if (IC::TypeToMap(*type, masm->isolate())->IsJSGlobalObjectMap()) { - // Swap in the global receiver. - __ Ldr(receiver, - FieldMemOperand( - receiver, JSGlobalObject::kGlobalReceiverOffset)); - } - __ Push(receiver, value); - ParameterCount actual(1); - ParameterCount expected(setter); - __ InvokeFunction(setter, expected, actual, - CALL_FUNCTION, NullCallWrapper()); - } 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(value); - - // Restore context register. - __ Ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); - } - __ Ret(); -} - - -#undef __ -#define __ ACCESS_MASM(masm()) - - -Handle StoreStubCompiler::CompileStoreInterceptor( - Handle object, - Handle name) { - Label miss; - - ASM_LOCATION("StoreStubCompiler::CompileStoreInterceptor"); - - __ Push(receiver(), this->name(), value()); - - // Do tail-call to the runtime system. - ExternalReference store_ic_property = - ExternalReference(IC_Utility(IC::kStoreInterceptorProperty), isolate()); - __ TailCallExternalReference(store_ic_property, 3, 1); - - // Return the generated code. - return GetCode(kind(), Code::FAST, name); -} - - -Handle LoadStubCompiler::CompileLoadNonexistent(Handle type, - Handle last, - Handle name) { - NonexistentHandlerFrontend(type, last, name); - - // Return undefined if maps of the full prototype chain are still the - // same and no global property with this name contains a value. - __ LoadRoot(x0, Heap::kUndefinedValueRootIndex); - __ Ret(); - - // Return the generated code. - return GetCode(kind(), Code::FAST, name); -} - - -// TODO(all): The so-called scratch registers are significant in some cases. For -// example, KeyedStoreStubCompiler::registers()[3] (x3) is actually used for -// KeyedStoreCompiler::transition_map(). We should verify which registers are -// actually scratch registers, and which are important. For now, we use the same -// assignments as ARM to remain on the safe side. - -Register* LoadStubCompiler::registers() { - // receiver, name, scratch1, scratch2, scratch3, scratch4. - static Register registers[] = { x0, x2, x3, x1, x4, x5 }; - return registers; -} - - -Register* KeyedLoadStubCompiler::registers() { - // receiver, name/key, scratch1, scratch2, scratch3, scratch4. - static Register registers[] = { x1, x0, x2, x3, x4, x5 }; - return registers; -} - - -Register* StoreStubCompiler::registers() { - // receiver, name, value, scratch1, scratch2, scratch3. - static Register registers[] = { x1, x2, x0, x3, x4, x5 }; - return registers; -} - - -Register* KeyedStoreStubCompiler::registers() { - // receiver, name, value, scratch1, scratch2, scratch3. - static Register registers[] = { x2, x1, x0, x3, x4, x5 }; - return registers; -} - - -#undef __ -#define __ ACCESS_MASM(masm) - -void LoadStubCompiler::GenerateLoadViaGetter(MacroAssembler* masm, - Handle type, - Register receiver, - Handle getter) { - { - FrameScope scope(masm, StackFrame::INTERNAL); - - if (!getter.is_null()) { - // Call the JavaScript getter with the receiver on the stack. - if (IC::TypeToMap(*type, masm->isolate())->IsJSGlobalObjectMap()) { - // Swap in the global receiver. - __ Ldr(receiver, - FieldMemOperand( - receiver, JSGlobalObject::kGlobalReceiverOffset)); - } - __ Push(receiver); - ParameterCount actual(0); - ParameterCount expected(getter); - __ InvokeFunction(getter, expected, actual, - CALL_FUNCTION, NullCallWrapper()); - } else { - // 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. - __ Ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); - } - __ Ret(); -} - - -#undef __ -#define __ ACCESS_MASM(masm()) - - -Handle LoadStubCompiler::CompileLoadGlobal( - Handle type, - Handle global, - Handle cell, - Handle name, - bool is_dont_delete) { - Label miss; - HandlerFrontendHeader(type, receiver(), global, name, &miss); - - // Get the value from the cell. - __ Mov(x3, Operand(cell)); - __ Ldr(x4, FieldMemOperand(x3, Cell::kValueOffset)); - - // Check for deleted property if property can actually be deleted. - if (!is_dont_delete) { - __ JumpIfRoot(x4, Heap::kTheHoleValueRootIndex, &miss); - } - - Counters* counters = isolate()->counters(); - __ IncrementCounter(counters->named_load_global_stub(), 1, x1, x3); - __ Mov(x0, x4); - __ Ret(); - - HandlerFrontendFooter(name, &miss); - - // Return the generated code. - return GetCode(kind(), Code::NORMAL, name); -} - - -Handle BaseLoadStoreStubCompiler::CompilePolymorphicIC( - TypeHandleList* types, - CodeHandleList* handlers, - Handle name, - Code::StubType type, - IcCheckType check) { - Label miss; - - if (check == PROPERTY && - (kind() == Code::KEYED_LOAD_IC || kind() == Code::KEYED_STORE_IC)) { - __ CompareAndBranch(this->name(), Operand(name), ne, &miss); - } - - Label number_case; - Label* smi_target = IncludesNumberType(types) ? &number_case : &miss; - __ JumpIfSmi(receiver(), smi_target); - - Register map_reg = scratch1(); - __ Ldr(map_reg, FieldMemOperand(receiver(), HeapObject::kMapOffset)); - int receiver_count = types->length(); - int number_of_handled_maps = 0; - for (int current = 0; current < receiver_count; ++current) { - Handle type = types->at(current); - Handle map = IC::TypeToMap(*type, isolate()); - if (!map->is_deprecated()) { - number_of_handled_maps++; - Label try_next; - __ Cmp(map_reg, Operand(map)); - __ B(ne, &try_next); - if (type->Is(HeapType::Number())) { - ASSERT(!number_case.is_unused()); - __ Bind(&number_case); - } - __ Jump(handlers->at(current), RelocInfo::CODE_TARGET); - __ Bind(&try_next); - } - } - ASSERT(number_of_handled_maps != 0); - - __ Bind(&miss); - TailCallBuiltin(masm(), MissBuiltin(kind())); - - // Return the generated code. - InlineCacheState state = - (number_of_handled_maps > 1) ? POLYMORPHIC : MONOMORPHIC; - return GetICCode(kind(), type, name, state); -} - - -Handle KeyedStoreStubCompiler::CompileStorePolymorphic( - MapHandleList* receiver_maps, - CodeHandleList* handler_stubs, - MapHandleList* transitioned_maps) { - Label miss; - - ASM_LOCATION("KeyedStoreStubCompiler::CompileStorePolymorphic"); - - __ JumpIfSmi(receiver(), &miss); - - int receiver_count = receiver_maps->length(); - __ Ldr(scratch1(), FieldMemOperand(receiver(), HeapObject::kMapOffset)); - for (int i = 0; i < receiver_count; i++) { - __ Cmp(scratch1(), Operand(receiver_maps->at(i))); - - Label skip; - __ B(&skip, ne); - if (!transitioned_maps->at(i).is_null()) { - // This argument is used by the handler stub. For example, see - // ElementsTransitionGenerator::GenerateMapChangeElementsTransition. - __ Mov(transition_map(), Operand(transitioned_maps->at(i))); - } - __ Jump(handler_stubs->at(i), RelocInfo::CODE_TARGET); - __ Bind(&skip); - } - - __ Bind(&miss); - TailCallBuiltin(masm(), MissBuiltin(kind())); - - return GetICCode( - kind(), Code::NORMAL, factory()->empty_string(), POLYMORPHIC); -} - - -#undef __ -#define __ ACCESS_MASM(masm) - -void KeyedLoadStubCompiler::GenerateLoadDictionaryElement( - MacroAssembler* masm) { - // ---------- S t a t e -------------- - // -- lr : return address - // -- x0 : key - // -- x1 : receiver - // ----------------------------------- - Label slow, miss; - - Register result = x0; - Register key = x0; - Register receiver = x1; - - __ JumpIfNotSmi(key, &miss); - __ Ldr(x4, FieldMemOperand(receiver, JSObject::kElementsOffset)); - __ LoadFromNumberDictionary(&slow, x4, key, result, x2, x3, x5, x6); - __ Ret(); - - __ Bind(&slow); - __ IncrementCounter( - masm->isolate()->counters()->keyed_load_external_array_slow(), 1, x2, x3); - TailCallBuiltin(masm, Builtins::kKeyedLoadIC_Slow); - - // Miss case, call the runtime. - __ Bind(&miss); - TailCallBuiltin(masm, Builtins::kKeyedLoadIC_Miss); -} - - -} } // namespace v8::internal - -#endif // V8_TARGET_ARCH_A64 diff --git a/deps/v8/src/a64/utils-a64.cc b/deps/v8/src/a64/utils-a64.cc deleted file mode 100644 index 7e710d770e..0000000000 --- a/deps/v8/src/a64/utils-a64.cc +++ /dev/null @@ -1,112 +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. - -#if V8_TARGET_ARCH_A64 - -#include "a64/utils-a64.h" - - -namespace v8 { -namespace internal { - -#define __ assm-> - - -int CountLeadingZeros(uint64_t value, int width) { - // TODO(jbramley): Optimize this for A64 hosts. - ASSERT((width == 32) || (width == 64)); - int count = 0; - uint64_t bit_test = 1UL << (width - 1); - while ((count < width) && ((bit_test & value) == 0)) { - count++; - bit_test >>= 1; - } - return count; -} - - -int CountLeadingSignBits(int64_t value, int width) { - // TODO(jbramley): Optimize this for A64 hosts. - ASSERT((width == 32) || (width == 64)); - if (value >= 0) { - return CountLeadingZeros(value, width) - 1; - } else { - return CountLeadingZeros(~value, width) - 1; - } -} - - -int CountTrailingZeros(uint64_t value, int width) { - // TODO(jbramley): Optimize this for A64 hosts. - ASSERT((width == 32) || (width == 64)); - int count = 0; - while ((count < width) && (((value >> count) & 1) == 0)) { - count++; - } - return count; -} - - -int CountSetBits(uint64_t value, int width) { - // TODO(jbramley): Would it be useful to allow other widths? The - // implementation already supports them. - ASSERT((width == 32) || (width == 64)); - - // Mask out unused bits to ensure that they are not counted. - value &= (0xffffffffffffffffUL >> (64-width)); - - // Add up the set bits. - // The algorithm works by adding pairs of bit fields together iteratively, - // where the size of each bit field doubles each time. - // An example for an 8-bit value: - // Bits: h g f e d c b a - // \ | \ | \ | \ | - // value = h+g f+e d+c b+a - // \ | \ | - // value = h+g+f+e d+c+b+a - // \ | - // value = h+g+f+e+d+c+b+a - value = ((value >> 1) & 0x5555555555555555) + (value & 0x5555555555555555); - value = ((value >> 2) & 0x3333333333333333) + (value & 0x3333333333333333); - value = ((value >> 4) & 0x0f0f0f0f0f0f0f0f) + (value & 0x0f0f0f0f0f0f0f0f); - value = ((value >> 8) & 0x00ff00ff00ff00ff) + (value & 0x00ff00ff00ff00ff); - value = ((value >> 16) & 0x0000ffff0000ffff) + (value & 0x0000ffff0000ffff); - value = ((value >> 32) & 0x00000000ffffffff) + (value & 0x00000000ffffffff); - - return value; -} - - -int MaskToBit(uint64_t mask) { - ASSERT(CountSetBits(mask, 64) == 1); - return CountTrailingZeros(mask, 64); -} - - -} } // namespace v8::internal - -#endif // V8_TARGET_ARCH_A64 diff --git a/deps/v8/src/a64/utils-a64.h b/deps/v8/src/a64/utils-a64.h deleted file mode 100644 index 16c51a9c8b..0000000000 --- a/deps/v8/src/a64/utils-a64.h +++ /dev/null @@ -1,109 +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. - -#ifndef V8_A64_UTILS_A64_H_ -#define V8_A64_UTILS_A64_H_ - -#include -#include "v8.h" -#include "a64/constants-a64.h" - -#define REGISTER_CODE_LIST(R) \ -R(0) R(1) R(2) R(3) R(4) R(5) R(6) R(7) \ -R(8) R(9) R(10) R(11) R(12) R(13) R(14) R(15) \ -R(16) R(17) R(18) R(19) R(20) R(21) R(22) R(23) \ -R(24) R(25) R(26) R(27) R(28) R(29) R(30) R(31) - -namespace v8 { -namespace internal { - -// Floating point representation. -static inline uint32_t float_to_rawbits(float value) { - uint32_t bits = 0; - memcpy(&bits, &value, 4); - return bits; -} - - -static inline uint64_t double_to_rawbits(double value) { - uint64_t bits = 0; - memcpy(&bits, &value, 8); - return bits; -} - - -static inline float rawbits_to_float(uint32_t bits) { - float value = 0.0; - memcpy(&value, &bits, 4); - return value; -} - - -static inline double rawbits_to_double(uint64_t bits) { - double value = 0.0; - memcpy(&value, &bits, 8); - return value; -} - - -// Bits counting. -int CountLeadingZeros(uint64_t value, int width); -int CountLeadingSignBits(int64_t value, int width); -int CountTrailingZeros(uint64_t value, int width); -int CountSetBits(uint64_t value, int width); -int MaskToBit(uint64_t mask); - - -// NaN tests. -inline bool IsSignallingNaN(double num) { - const uint64_t kFP64QuietNaNMask = 0x0008000000000000UL; - uint64_t raw = double_to_rawbits(num); - if (std::isnan(num) && ((raw & kFP64QuietNaNMask) == 0)) { - return true; - } - return false; -} - - -inline bool IsSignallingNaN(float num) { - const uint64_t kFP32QuietNaNMask = 0x00400000UL; - uint32_t raw = float_to_rawbits(num); - if (std::isnan(num) && ((raw & kFP32QuietNaNMask) == 0)) { - return true; - } - return false; -} - - -template -inline bool IsQuietNaN(T num) { - return std::isnan(num) && !IsSignallingNaN(num); -} - -} } // namespace v8::internal - -#endif // V8_A64_UTILS_A64_H_ diff --git a/deps/v8/src/allocation-tracker.cc b/deps/v8/src/allocation-tracker.cc index 83e1bb4b39..5ec6484601 100644 --- a/deps/v8/src/allocation-tracker.cc +++ b/deps/v8/src/allocation-tracker.cc @@ -267,7 +267,6 @@ AllocationTracker::UnresolvedLocation::~UnresolvedLocation() { void AllocationTracker::UnresolvedLocation::Resolve() { if (script_.is_null()) return; - HandleScope scope(script_->GetIsolate()); info_->line = GetScriptLineNumber(script_, start_position_); info_->column = GetScriptColumnNumber(script_, start_position_); } diff --git a/deps/v8/src/api.cc b/deps/v8/src/api.cc index 2c7db3be16..7a412df28d 100644 --- a/deps/v8/src/api.cc +++ b/deps/v8/src/api.cc @@ -6293,25 +6293,6 @@ void V8::AddCallCompletedCallback(CallCompletedCallback callback) { } -void V8::RunMicrotasks(Isolate* isolate) { - i::Isolate* i_isolate = reinterpret_cast(isolate); - i::HandleScope scope(i_isolate); - i::V8::RunMicrotasks(i_isolate); -} - - -void V8::EnqueueMicrotask(Isolate* isolate, Handle microtask) { - i::Isolate* i_isolate = reinterpret_cast(isolate); - ENTER_V8(i_isolate); - i::Execution::EnqueueMicrotask(i_isolate, Utils::OpenHandle(*microtask)); -} - - -void V8::SetAutorunMicrotasks(Isolate* isolate, bool autorun) { - reinterpret_cast(isolate)->set_autorun_microtasks(autorun); -} - - void V8::RemoveCallCompletedCallback(CallCompletedCallback callback) { i::V8::RemoveCallCompletedCallback(callback); } @@ -6971,13 +6952,6 @@ SnapshotObjectId HeapGraphNode::GetId() const { int HeapGraphNode::GetSelfSize() const { - size_t size = ToInternal(this)->self_size(); - CHECK(size <= static_cast(internal::kMaxInt)); - return static_cast(size); -} - - -size_t HeapGraphNode::GetShallowSize() const { return ToInternal(this)->self_size(); } diff --git a/deps/v8/src/arm/OWNERS b/deps/v8/src/arm/OWNERS deleted file mode 100644 index 906a5ce641..0000000000 --- a/deps/v8/src/arm/OWNERS +++ /dev/null @@ -1 +0,0 @@ -rmcilroy@chromium.org diff --git a/deps/v8/src/arm/code-stubs-arm.cc b/deps/v8/src/arm/code-stubs-arm.cc index 501b5c7dff..44de7aabc3 100644 --- a/deps/v8/src/arm/code-stubs-arm.cc +++ b/deps/v8/src/arm/code-stubs-arm.cc @@ -105,8 +105,8 @@ void FastCloneShallowObjectStub::InitializeInterfaceDescriptor( void CreateAllocationSiteStub::InitializeInterfaceDescriptor( Isolate* isolate, CodeStubInterfaceDescriptor* descriptor) { - static Register registers[] = { r2, r3 }; - descriptor->register_param_count_ = 2; + static Register registers[] = { r2 }; + descriptor->register_param_count_ = 1; descriptor->register_params_ = registers; descriptor->deoptimization_handler_ = NULL; } @@ -602,7 +602,6 @@ void DoubleToIStub::Generate(MacroAssembler* masm) { Label out_of_range, only_low, negate, done; Register input_reg = source(); Register result_reg = destination(); - ASSERT(is_truncating()); int double_offset = offset(); // Account for saved regs if input is sp. @@ -3005,102 +3004,82 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { static void GenerateRecordCallTarget(MacroAssembler* masm) { - // Cache the called function in a feedback vector slot. Cache states + // Cache the called function in a global property cell. Cache states // are uninitialized, monomorphic (indicated by a JSFunction), and // megamorphic. // r0 : number of arguments to the construct function // r1 : the function to call - // r2 : Feedback vector - // r3 : slot in feedback vector (Smi) - Label check_array, initialize_array, initialize_non_array, megamorphic, done; + // r2 : cache cell for call target + Label initialize, done, miss, megamorphic, not_array_function; - ASSERT_EQ(*TypeFeedbackInfo::MegamorphicSentinel(masm->isolate()), + ASSERT_EQ(*TypeFeedbackCells::MegamorphicSentinel(masm->isolate()), masm->isolate()->heap()->undefined_value()); - Heap::RootListIndex kMegamorphicRootIndex = Heap::kUndefinedValueRootIndex; - ASSERT_EQ(*TypeFeedbackInfo::UninitializedSentinel(masm->isolate()), + ASSERT_EQ(*TypeFeedbackCells::UninitializedSentinel(masm->isolate()), masm->isolate()->heap()->the_hole_value()); - Heap::RootListIndex kUninitializedRootIndex = Heap::kTheHoleValueRootIndex; - ASSERT_EQ(*TypeFeedbackInfo::PremonomorphicSentinel(masm->isolate()), - masm->isolate()->heap()->null_value()); - Heap::RootListIndex kPremonomorphicRootIndex = Heap::kNullValueRootIndex; - // Load the cache state into r4. - __ add(r4, r2, Operand::PointerOffsetFromSmiKey(r3)); - __ ldr(r4, FieldMemOperand(r4, FixedArray::kHeaderSize)); + // Load the cache state into r3. + __ ldr(r3, FieldMemOperand(r2, Cell::kValueOffset)); // A monomorphic cache hit or an already megamorphic state: invoke the // function without changing the state. - __ cmp(r4, r1); - __ b(eq, &done); - __ CompareRoot(r4, kMegamorphicRootIndex); + __ cmp(r3, r1); __ b(eq, &done); - // Check if we're dealing with the Array function or not. - __ LoadArrayFunction(r5); - __ cmp(r1, r5); - __ b(eq, &check_array); + // 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 cell either some other function or an + // AllocationSite. Do a map check on the object in ecx. + __ ldr(r5, FieldMemOperand(r3, 0)); + __ CompareRoot(r5, Heap::kAllocationSiteMapRootIndex); + __ b(ne, &miss); - // Non-array cache: Check the cache state. - __ CompareRoot(r4, kPremonomorphicRootIndex); - __ b(eq, &initialize_non_array); - __ CompareRoot(r4, kUninitializedRootIndex); + // Make sure the function is the Array() function + __ LoadArrayFunction(r3); + __ cmp(r1, r3); __ b(ne, &megamorphic); - - // Non-array cache: Uninitialized -> premonomorphic. The sentinel is an - // immortal immovable object (null) so no write-barrier is needed. - __ add(r4, r2, Operand::PointerOffsetFromSmiKey(r3)); - __ LoadRoot(ip, kPremonomorphicRootIndex); - __ str(ip, FieldMemOperand(r4, FixedArray::kHeaderSize)); __ jmp(&done); - // Array cache: Check the cache state to see if we're in a monomorphic - // state where the state object is an AllocationSite object. - __ bind(&check_array); - __ ldr(r5, FieldMemOperand(r4, 0)); - __ CompareRoot(r5, Heap::kAllocationSiteMapRootIndex); - __ b(eq, &done); - - // Array cache: Uninitialized or premonomorphic -> monomorphic. - __ CompareRoot(r4, kUninitializedRootIndex); - __ b(eq, &initialize_array); - __ CompareRoot(r4, kPremonomorphicRootIndex); - __ b(eq, &initialize_array); + __ bind(&miss); - // Both caches: Monomorphic -> megamorphic. The sentinel is an - // immortal immovable object (undefined) so no write-barrier is needed. + // A monomorphic miss (i.e, here the cache is not uninitialized) goes + // megamorphic. + __ CompareRoot(r3, Heap::kTheHoleValueRootIndex); + __ b(eq, &initialize); + // MegamorphicSentinel is an immortal immovable object (undefined) so no + // write-barrier is needed. __ bind(&megamorphic); - __ add(r4, r2, Operand::PointerOffsetFromSmiKey(r3)); - __ LoadRoot(ip, kMegamorphicRootIndex); - __ str(ip, FieldMemOperand(r4, FixedArray::kHeaderSize)); + __ LoadRoot(ip, Heap::kUndefinedValueRootIndex); + __ str(ip, FieldMemOperand(r2, Cell::kValueOffset)); __ jmp(&done); - // Array cache: Uninitialized or premonomorphic -> monomorphic. - __ bind(&initialize_array); + // 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 + __ LoadArrayFunction(r3); + __ cmp(r1, r3); + __ b(ne, ¬_array_function); + + // The target function is the Array constructor, + // Create an AllocationSite if we don't already have it, store it in the cell { FrameScope scope(masm, StackFrame::INTERNAL); // Arguments register must be smi-tagged to call out. __ SmiTag(r0); - __ Push(r3, r2, r1, r0); + __ Push(r2, r1, r0); CreateAllocationSiteStub create_stub; __ CallStub(&create_stub); - __ Pop(r3, r2, r1, r0); + __ Pop(r2, r1, r0); __ SmiUntag(r0); } __ b(&done); - // Non-array cache: Premonomorphic -> monomorphic. - __ bind(&initialize_non_array); - __ add(r4, r2, Operand::PointerOffsetFromSmiKey(r3)); - __ add(r4, r4, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); - __ str(r1, MemOperand(r4, 0)); - - __ Push(r4, r2, r1); - __ RecordWrite(r2, r4, r1, kLRHasNotBeenSaved, kDontSaveFPRegs, - EMIT_REMEMBERED_SET, OMIT_SMI_CHECK); - __ Pop(r4, r2, r1); + __ bind(¬_array_function); + __ str(r1, FieldMemOperand(r2, Cell::kValueOffset)); + // No need for a write barrier here - cells are rescanned. __ bind(&done); } @@ -3108,8 +3087,7 @@ static void GenerateRecordCallTarget(MacroAssembler* masm) { void CallFunctionStub::Generate(MacroAssembler* masm) { // r1 : the function to call - // r2 : feedback vector - // r3 : (only if r2 is not undefined) slot in feedback vector (Smi) + // r2 : cache cell for call target Label slow, non_function, wrap, cont; if (NeedsChecks()) { @@ -3118,7 +3096,7 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { __ JumpIfSmi(r1, &non_function); // Goto slow case if we do not have a function. - __ CompareObjectType(r1, r4, r4, JS_FUNCTION_TYPE); + __ CompareObjectType(r1, r3, r3, JS_FUNCTION_TYPE); __ b(ne, &slow); if (RecordCallTarget()) { @@ -3166,14 +3144,13 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { // If there is a call target cache, mark it megamorphic in the // non-function case. MegamorphicSentinel is an immortal immovable // object (undefined) so no write barrier is needed. - ASSERT_EQ(*TypeFeedbackInfo::MegamorphicSentinel(masm->isolate()), + ASSERT_EQ(*TypeFeedbackCells::MegamorphicSentinel(masm->isolate()), masm->isolate()->heap()->undefined_value()); - __ add(r5, r2, Operand::PointerOffsetFromSmiKey(r3)); __ LoadRoot(ip, Heap::kUndefinedValueRootIndex); - __ str(ip, FieldMemOperand(r5, FixedArray::kHeaderSize)); + __ str(ip, FieldMemOperand(r2, Cell::kValueOffset)); } // Check for function proxy. - __ cmp(r4, Operand(JS_FUNCTION_PROXY_TYPE)); + __ cmp(r3, Operand(JS_FUNCTION_PROXY_TYPE)); __ b(ne, &non_function); __ push(r1); // put proxy as additional argument __ mov(r0, Operand(argc_ + 1, RelocInfo::NONE32)); @@ -3213,14 +3190,13 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { void CallConstructStub::Generate(MacroAssembler* masm) { // r0 : number of arguments // r1 : the function to call - // r2 : feedback vector - // r3 : (only if r2 is not undefined) slot in feedback vector (Smi) + // r2 : cache cell for call target Label slow, non_function_call; // Check that the function is not a smi. __ JumpIfSmi(r1, &non_function_call); // Check that the function is a JSFunction. - __ CompareObjectType(r1, r4, r4, JS_FUNCTION_TYPE); + __ CompareObjectType(r1, r3, r3, JS_FUNCTION_TYPE); __ b(ne, &slow); if (RecordCallTarget()) { @@ -3228,7 +3204,7 @@ void CallConstructStub::Generate(MacroAssembler* masm) { } // Jump to the function-specific construct stub. - Register jmp_reg = r4; + Register jmp_reg = r3; __ ldr(jmp_reg, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset)); __ ldr(jmp_reg, FieldMemOperand(jmp_reg, SharedFunctionInfo::kConstructStubOffset)); @@ -3236,10 +3212,10 @@ void CallConstructStub::Generate(MacroAssembler* masm) { // r0: number of arguments // r1: called object - // r4: object type + // r3: object type Label do_call; __ bind(&slow); - __ cmp(r4, Operand(JS_FUNCTION_PROXY_TYPE)); + __ cmp(r3, Operand(JS_FUNCTION_PROXY_TYPE)); __ b(ne, &non_function_call); __ GetBuiltinFunction(r1, Builtins::CALL_FUNCTION_PROXY_AS_CONSTRUCTOR); __ jmp(&do_call); @@ -5199,7 +5175,7 @@ static void CreateArrayDispatchOneArgument(MacroAssembler* masm, __ 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 (only if we have an allocation site in the slot). + // Fix kind and retry (only if we have an allocation site in the cell). __ add(r3, r3, Operand(1)); if (FLAG_debug_code) { @@ -5307,8 +5283,7 @@ void ArrayConstructorStub::Generate(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- r0 : argc (only if argument_count_ == ANY) // -- r1 : constructor - // -- r2 : feedback vector (fixed array or undefined) - // -- r3 : slot index (if r2 is fixed array) + // -- r2 : type info cell // -- sp[0] : return address // -- sp[4] : last argument // ----------------------------------- @@ -5317,25 +5292,21 @@ void ArrayConstructorStub::Generate(MacroAssembler* masm) { // builtin Array functions which always have maps. // Initial map for the builtin Array function should be a map. - __ ldr(r4, FieldMemOperand(r1, JSFunction::kPrototypeOrInitialMapOffset)); + __ ldr(r3, FieldMemOperand(r1, JSFunction::kPrototypeOrInitialMapOffset)); // Will both indicate a NULL and a Smi. - __ tst(r4, Operand(kSmiTagMask)); + __ tst(r3, Operand(kSmiTagMask)); __ Assert(ne, kUnexpectedInitialMapForArrayFunction); - __ CompareObjectType(r4, r4, r5, MAP_TYPE); + __ CompareObjectType(r3, r3, r4, MAP_TYPE); __ Assert(eq, kUnexpectedInitialMapForArrayFunction); - // We should either have undefined in ebx or a valid fixed array. + // We should either have undefined in ebx or a valid cell Label okay_here; - Handle fixed_array_map = masm->isolate()->factory()->fixed_array_map(); + Handle cell_map = masm->isolate()->factory()->cell_map(); __ CompareRoot(r2, Heap::kUndefinedValueRootIndex); __ b(eq, &okay_here); - __ ldr(r4, FieldMemOperand(r2, 0)); - __ cmp(r4, Operand(fixed_array_map)); - __ Assert(eq, kExpectedFixedArrayInRegisterR2); - - // r3 should be a smi if we don't have undefined in r2 - __ AssertSmi(r3); - + __ ldr(r3, FieldMemOperand(r2, 0)); + __ cmp(r3, Operand(cell_map)); + __ Assert(eq, kExpectedPropertyCellInRegisterEbx); __ bind(&okay_here); } @@ -5343,10 +5314,9 @@ void ArrayConstructorStub::Generate(MacroAssembler* masm) { // Get the elements kind and case on that. __ CompareRoot(r2, Heap::kUndefinedValueRootIndex); __ b(eq, &no_info); - __ add(r2, r2, Operand::PointerOffsetFromSmiKey(r3)); - __ ldr(r2, FieldMemOperand(r2, FixedArray::kHeaderSize)); + __ ldr(r2, FieldMemOperand(r2, Cell::kValueOffset)); - // If the feedback vector is undefined, or contains anything other than an + // If the type cell is undefined, or contains anything other than an // AllocationSite, call an array constructor that doesn't use AllocationSites. __ ldr(r4, FieldMemOperand(r2, 0)); __ CompareRoot(r4, Heap::kAllocationSiteMapRootIndex); @@ -5459,7 +5429,7 @@ void CallApiFunctionStub::Generate(MacroAssembler* masm) { Register context = cp; int argc = ArgumentBits::decode(bit_field_); - bool is_store = IsStoreBits::decode(bit_field_); + bool restore_context = RestoreContextBits::decode(bit_field_); bool call_data_undefined = CallDataUndefinedBits::decode(bit_field_); typedef FunctionCallbackArguments FCA; @@ -5537,20 +5507,15 @@ void CallApiFunctionStub::Generate(MacroAssembler* masm) { AllowExternalCallThatCantCauseGC scope(masm); MemOperand context_restore_operand( fp, (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; - } - MemOperand return_value_operand(fp, return_value_offset * kPointerSize); + MemOperand return_value_operand(fp, + (2 + FCA::kReturnValueOffset) * kPointerSize); __ CallApiFunctionAndReturn(api_function_address, thunk_ref, kStackUnwindSpace, return_value_operand, - &context_restore_operand); + restore_context ? + &context_restore_operand : NULL); } diff --git a/deps/v8/src/arm/debug-arm.cc b/deps/v8/src/arm/debug-arm.cc index 9990bccdcf..efd11069b3 100644 --- a/deps/v8/src/arm/debug-arm.cc +++ b/deps/v8/src/arm/debug-arm.cc @@ -265,10 +265,9 @@ void Debug::GenerateCallFunctionStubRecordDebugBreak(MacroAssembler* masm) { // Register state for CallFunctionStub (from code-stubs-arm.cc). // ----------- S t a t e ------------- // -- r1 : function - // -- r2 : feedback array - // -- r3 : slot in feedback array + // -- r2 : cache cell for call target // ----------------------------------- - Generate_DebugBreakCallHelper(masm, r1.bit() | r2.bit() | r3.bit(), 0); + Generate_DebugBreakCallHelper(masm, r1.bit() | r2.bit(), 0); } @@ -287,10 +286,9 @@ void Debug::GenerateCallConstructStubRecordDebugBreak(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- r0 : number of arguments (not smi) // -- r1 : constructor function - // -- r2 : feedback array - // -- r3 : feedback slot (smi) + // -- r2 : cache cell for call target // ----------------------------------- - Generate_DebugBreakCallHelper(masm, r1.bit() | r2.bit() | r3.bit(), r0.bit()); + Generate_DebugBreakCallHelper(masm, r1.bit() | r2.bit(), r0.bit()); } diff --git a/deps/v8/src/arm/full-codegen-arm.cc b/deps/v8/src/arm/full-codegen-arm.cc index 2eb5ccf974..813e9492df 100644 --- a/deps/v8/src/arm/full-codegen-arm.cc +++ b/deps/v8/src/arm/full-codegen-arm.cc @@ -130,9 +130,6 @@ void FullCodeGenerator::Generate() { CompilationInfo* info = info_; handler_table_ = isolate()->factory()->NewFixedArray(function()->handler_count(), TENURED); - - InitializeFeedbackVector(); - profiling_counter_ = isolate()->factory()->NewCell( Handle(Smi::FromInt(FLAG_interrupt_budget), isolate())); SetFunctionPosition(function()); @@ -671,7 +668,7 @@ void FullCodeGenerator::DoTest(Expression* condition, Label* if_false, Label* fall_through) { Handle ic = ToBooleanStub::GetUninitialized(isolate()); - CallIC(ic, condition->test_id()); + CallIC(ic, NOT_CONTEXTUAL, condition->test_id()); __ tst(result_register(), result_register()); Split(ne, if_true, if_false, fall_through); } @@ -1032,7 +1029,7 @@ void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { // Record position before stub call for type feedback. SetSourcePosition(clause->position()); Handle ic = CompareIC::GetUninitialized(isolate(), Token::EQ_STRICT); - CallIC(ic, clause->CompareId()); + CallIC(ic, NOT_CONTEXTUAL, clause->CompareId()); patch_site.EmitPatchInfo(); Label skip; @@ -1077,7 +1074,6 @@ void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { Comment cmnt(masm_, "[ ForInStatement"); - int slot = stmt->ForInFeedbackSlot(); SetStatementPosition(stmt); Label loop, exit; @@ -1167,13 +1163,13 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { Label non_proxy; __ bind(&fixed_array); - Handle feedback = Handle( - Smi::FromInt(TypeFeedbackInfo::kForInFastCaseMarker), - isolate()); - StoreFeedbackVectorSlot(slot, feedback); - __ Move(r1, FeedbackVector()); - __ mov(r2, Operand(Smi::FromInt(TypeFeedbackInfo::kForInSlowCaseMarker))); - __ str(r2, FieldMemOperand(r1, FixedArray::OffsetOfElementAt(slot))); + Handle cell = isolate()->factory()->NewCell( + Handle(Smi::FromInt(TypeFeedbackCells::kForInFastCaseMarker), + isolate())); + RecordTypeFeedbackCell(stmt->ForInFeedbackId(), cell); + __ Move(r1, cell); + __ mov(r2, Operand(Smi::FromInt(TypeFeedbackCells::kForInSlowCaseMarker))); + __ str(r2, FieldMemOperand(r1, Cell::kValueOffset)); __ mov(r1, Operand(Smi::FromInt(1))); // Smi indicates slow check __ ldr(r2, MemOperand(sp, 0 * kPointerSize)); // Get enumerated object @@ -1482,7 +1478,7 @@ void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy) { // variables. switch (var->location()) { case Variable::UNALLOCATED: { - Comment cmnt(masm_, "[ Global variable"); + Comment cmnt(masm_, "Global variable"); // Use inline caching. Variable name is passed in r2 and the global // object (receiver) in r0. __ ldr(r0, GlobalObjectOperand()); @@ -1495,8 +1491,9 @@ void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy) { case Variable::PARAMETER: case Variable::LOCAL: case Variable::CONTEXT: { - Comment cmnt(masm_, var->IsContextSlot() ? "[ Context variable" - : "[ Stack variable"); + Comment cmnt(masm_, var->IsContextSlot() + ? "Context variable" + : "Stack variable"); if (var->binding_needs_init()) { // var->scope() may be NULL when the proxy is located in eval code and // refers to a potential outside binding. Currently those bindings are @@ -1559,12 +1556,12 @@ void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy) { } case Variable::LOOKUP: { - Comment cmnt(masm_, "[ Lookup variable"); Label done, slow; // Generate code for loading from variables potentially shadowed // by eval-introduced variables. EmitDynamicLookupFastCase(var, NOT_INSIDE_TYPEOF, &slow, &done); __ bind(&slow); + Comment cmnt(masm_, "Lookup variable"); __ mov(r1, Operand(var->name())); __ Push(cp, r1); // Context and name. __ CallRuntime(Runtime::kLoadContextSlot, 2); @@ -1695,7 +1692,7 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { VisitForAccumulatorValue(value); __ mov(r2, Operand(key->value())); __ ldr(r1, MemOperand(sp)); - CallStoreIC(key->LiteralFeedbackId()); + CallStoreIC(NOT_CONTEXTUAL, key->LiteralFeedbackId()); PrepareForBailoutForId(key->id(), NO_REGISTERS); } else { VisitForEffect(value); @@ -2097,7 +2094,7 @@ void FullCodeGenerator::VisitYield(Yield* expr) { __ ldr(r1, MemOperand(sp, kPointerSize)); __ ldr(r0, MemOperand(sp, 2 * kPointerSize)); Handle ic = isolate()->builtins()->KeyedLoadIC_Initialize(); - CallIC(ic, TypeFeedbackId::None()); + CallIC(ic, NOT_CONTEXTUAL, TypeFeedbackId::None()); __ mov(r1, r0); __ str(r1, MemOperand(sp, 2 * kPointerSize)); CallFunctionStub stub(1, CALL_AS_METHOD); @@ -2294,7 +2291,7 @@ void FullCodeGenerator::EmitKeyedPropertyLoad(Property* prop) { SetSourcePosition(prop->position()); // Call keyed load IC. It has arguments key and receiver in r0 and r1. Handle ic = isolate()->builtins()->KeyedLoadIC_Initialize(); - CallIC(ic, prop->PropertyFeedbackId()); + CallIC(ic, NOT_CONTEXTUAL, prop->PropertyFeedbackId()); } @@ -2321,7 +2318,8 @@ void FullCodeGenerator::EmitInlineSmiBinaryOp(BinaryOperation* expr, __ bind(&stub_call); BinaryOpICStub stub(op, mode); - CallIC(stub.GetCode(isolate()), expr->BinaryOperationFeedbackId()); + CallIC(stub.GetCode(isolate()), NOT_CONTEXTUAL, + expr->BinaryOperationFeedbackId()); patch_site.EmitPatchInfo(); __ jmp(&done); @@ -2398,7 +2396,8 @@ void FullCodeGenerator::EmitBinaryOp(BinaryOperation* expr, __ pop(r1); BinaryOpICStub stub(op, mode); JumpPatchSite patch_site(masm_); // unbound, signals no inlined smi code. - CallIC(stub.GetCode(isolate()), expr->BinaryOperationFeedbackId()); + CallIC(stub.GetCode(isolate()), NOT_CONTEXTUAL, + expr->BinaryOperationFeedbackId()); patch_site.EmitPatchInfo(); context()->Plug(r0); } @@ -2436,7 +2435,7 @@ void FullCodeGenerator::EmitAssignment(Expression* expr) { __ mov(r1, r0); __ pop(r0); // Restore value. __ mov(r2, Operand(prop->key()->AsLiteral()->value())); - CallStoreIC(); + CallStoreIC(NOT_CONTEXTUAL); break; } case KEYED_PROPERTY: { @@ -2456,60 +2455,41 @@ void FullCodeGenerator::EmitAssignment(Expression* expr) { } -void FullCodeGenerator::EmitStoreToStackLocalOrContextSlot( - Variable* var, MemOperand location) { - __ str(result_register(), location); - if (var->IsContextSlot()) { - // RecordWrite may destroy all its register arguments. - __ mov(r3, result_register()); - int offset = Context::SlotOffset(var->index()); - __ RecordWriteContextSlot( - r1, offset, r3, r2, kLRHasBeenSaved, kDontSaveFPRegs); - } -} - - -void FullCodeGenerator::EmitCallStoreContextSlot( - Handle name, LanguageMode mode) { - __ push(r0); // Value. - __ mov(r1, Operand(name)); - __ mov(r0, Operand(Smi::FromInt(mode))); - __ Push(cp, r1, r0); // Context, name, strict mode. - __ CallRuntime(Runtime::kStoreContextSlot, 4); -} - - void FullCodeGenerator::EmitVariableAssignment(Variable* var, Token::Value op) { if (var->IsUnallocated()) { // Global var, const, or let. __ mov(r2, Operand(var->name())); __ ldr(r1, GlobalObjectOperand()); - CallStoreIC(); - + CallStoreIC(CONTEXTUAL); } else if (op == Token::INIT_CONST) { // Const initializers need a write barrier. ASSERT(!var->IsParameter()); // No const parameters. - if (var->IsLookupSlot()) { + if (var->IsStackLocal()) { + __ ldr(r1, StackOperand(var)); + __ CompareRoot(r1, Heap::kTheHoleValueRootIndex); + __ str(result_register(), StackOperand(var), eq); + } else { + ASSERT(var->IsContextSlot() || var->IsLookupSlot()); + // Like var declarations, const declarations are hoisted to function + // scope. However, unlike var initializers, const initializers are + // able to drill a hole to that function context, even from inside a + // 'with' context. We thus bypass the normal static scope lookup for + // var->IsContextSlot(). __ push(r0); __ mov(r0, Operand(var->name())); __ Push(cp, r0); // Context and name. __ CallRuntime(Runtime::kInitializeConstContextSlot, 3); - } else { - ASSERT(var->IsStackAllocated() || var->IsContextSlot()); - Label skip; - MemOperand location = VarOperand(var, r1); - __ ldr(r2, location); - __ CompareRoot(r2, Heap::kTheHoleValueRootIndex); - __ b(ne, &skip); - EmitStoreToStackLocalOrContextSlot(var, location); - __ bind(&skip); } } else if (var->mode() == LET && op != Token::INIT_LET) { // Non-initializing assignment to let variable needs a write barrier. if (var->IsLookupSlot()) { - EmitCallStoreContextSlot(var->name(), language_mode()); + __ push(r0); // Value. + __ mov(r1, Operand(var->name())); + __ mov(r0, Operand(Smi::FromInt(language_mode()))); + __ Push(cp, r1, r0); // Context, name, strict mode. + __ CallRuntime(Runtime::kStoreContextSlot, 4); } else { ASSERT(var->IsStackAllocated() || var->IsContextSlot()); Label assign; @@ -2522,16 +2502,20 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, __ CallRuntime(Runtime::kThrowReferenceError, 1); // Perform the assignment. __ bind(&assign); - EmitStoreToStackLocalOrContextSlot(var, location); + __ str(result_register(), location); + if (var->IsContextSlot()) { + // RecordWrite may destroy all its register arguments. + __ mov(r3, result_register()); + int offset = Context::SlotOffset(var->index()); + __ RecordWriteContextSlot( + r1, offset, r3, r2, kLRHasBeenSaved, kDontSaveFPRegs); + } } } else if (!var->is_const_mode() || op == Token::INIT_CONST_HARMONY) { // Assignment to var or initializing assignment to let/const // in harmony mode. - if (var->IsLookupSlot()) { - EmitCallStoreContextSlot(var->name(), language_mode()); - } else { - ASSERT((var->IsStackAllocated() || var->IsContextSlot())); + if (var->IsStackAllocated() || var->IsContextSlot()) { MemOperand location = VarOperand(var, r1); if (generate_debug_code_ && op == Token::INIT_LET) { // Check for an uninitialized let binding. @@ -2539,7 +2523,21 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, __ CompareRoot(r2, Heap::kTheHoleValueRootIndex); __ Check(eq, kLetBindingReInitialization); } - EmitStoreToStackLocalOrContextSlot(var, location); + // Perform the assignment. + __ str(r0, location); + if (var->IsContextSlot()) { + __ mov(r3, r0); + int offset = Context::SlotOffset(var->index()); + __ RecordWriteContextSlot( + r1, offset, r3, r2, kLRHasBeenSaved, kDontSaveFPRegs); + } + } else { + ASSERT(var->IsLookupSlot()); + __ push(r0); // Value. + __ mov(r1, Operand(var->name())); + __ mov(r0, Operand(Smi::FromInt(language_mode()))); + __ Push(cp, r1, r0); // Context, name, strict mode. + __ CallRuntime(Runtime::kStoreContextSlot, 4); } } // Non-initializing assignments to consts are ignored. @@ -2557,7 +2555,7 @@ void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { __ mov(r2, Operand(prop->key()->AsLiteral()->value())); __ pop(r1); - CallStoreIC(expr->AssignmentFeedbackId()); + CallStoreIC(NOT_CONTEXTUAL, expr->AssignmentFeedbackId()); PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); context()->Plug(r0); @@ -2574,7 +2572,7 @@ void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) { Handle ic = is_classic_mode() ? isolate()->builtins()->KeyedStoreIC_Initialize() : isolate()->builtins()->KeyedStoreIC_Initialize_Strict(); - CallIC(ic, expr->AssignmentFeedbackId()); + CallIC(ic, NOT_CONTEXTUAL, expr->AssignmentFeedbackId()); PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); context()->Plug(r0); @@ -2601,10 +2599,12 @@ void FullCodeGenerator::VisitProperty(Property* expr) { void FullCodeGenerator::CallIC(Handle code, + ContextualMode mode, TypeFeedbackId ast_id) { ic_total_count_++; // All calls must have a predictable size in full-codegen code to ensure that // the debugger can patch them correctly. + ASSERT(mode != CONTEXTUAL || ast_id.IsNone()); __ Call(code, RelocInfo::CODE_TARGET, ast_id, al, NEVER_INLINE_TARGET_ADDRESS); } @@ -2716,15 +2716,15 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr) { SetSourcePosition(expr->position()); Handle uninitialized = - TypeFeedbackInfo::UninitializedSentinel(isolate()); - StoreFeedbackVectorSlot(expr->CallFeedbackSlot(), uninitialized); - __ Move(r2, FeedbackVector()); - __ mov(r3, Operand(Smi::FromInt(expr->CallFeedbackSlot()))); + TypeFeedbackCells::UninitializedSentinel(isolate()); + Handle cell = isolate()->factory()->NewCell(uninitialized); + RecordTypeFeedbackCell(expr->CallFeedbackId(), cell); + __ mov(r2, Operand(cell)); // Record call targets in unoptimized code. CallFunctionStub stub(arg_count, RECORD_CALL_TARGET); __ ldr(r1, MemOperand(sp, (arg_count + 1) * kPointerSize)); - __ CallStub(&stub); + __ CallStub(&stub, expr->CallFeedbackId()); RecordJSReturnSite(expr); // Restore context register. __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); @@ -2905,10 +2905,10 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) { // Record call targets in unoptimized code. Handle uninitialized = - TypeFeedbackInfo::UninitializedSentinel(isolate()); - StoreFeedbackVectorSlot(expr->CallNewFeedbackSlot(), uninitialized); - __ Move(r2, FeedbackVector()); - __ mov(r3, Operand(Smi::FromInt(expr->CallNewFeedbackSlot()))); + TypeFeedbackCells::UninitializedSentinel(isolate()); + Handle cell = isolate()->factory()->NewCell(uninitialized); + RecordTypeFeedbackCell(expr->CallNewFeedbackId(), cell); + __ mov(r2, Operand(cell)); CallConstructStub stub(RECORD_CALL_TARGET); __ Call(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL); @@ -4411,7 +4411,9 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { SetSourcePosition(expr->position()); BinaryOpICStub stub(Token::ADD, NO_OVERWRITE); - CallIC(stub.GetCode(isolate()), expr->CountBinOpFeedbackId()); + CallIC(stub.GetCode(isolate()), + NOT_CONTEXTUAL, + expr->CountBinOpFeedbackId()); patch_site.EmitPatchInfo(); __ bind(&done); @@ -4440,7 +4442,7 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { case NAMED_PROPERTY: { __ mov(r2, Operand(prop->key()->AsLiteral()->value())); __ pop(r1); - CallStoreIC(expr->CountStoreFeedbackId()); + CallStoreIC(NOT_CONTEXTUAL, expr->CountStoreFeedbackId()); PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); if (expr->is_postfix()) { if (!context()->IsEffect()) { @@ -4456,7 +4458,7 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { Handle ic = is_classic_mode() ? isolate()->builtins()->KeyedStoreIC_Initialize() : isolate()->builtins()->KeyedStoreIC_Initialize_Strict(); - CallIC(ic, expr->CountStoreFeedbackId()); + CallIC(ic, NOT_CONTEXTUAL, expr->CountStoreFeedbackId()); PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); if (expr->is_postfix()) { if (!context()->IsEffect()) { @@ -4476,7 +4478,7 @@ void FullCodeGenerator::VisitForTypeofValue(Expression* expr) { ASSERT(!context()->IsTest()); VariableProxy* proxy = expr->AsVariableProxy(); if (proxy != NULL && proxy->var()->IsUnallocated()) { - Comment cmnt(masm_, "[ Global variable"); + Comment cmnt(masm_, "Global variable"); __ ldr(r0, GlobalObjectOperand()); __ mov(r2, Operand(proxy->name())); // Use a regular load, not a contextual load, to avoid a reference @@ -4485,7 +4487,6 @@ void FullCodeGenerator::VisitForTypeofValue(Expression* expr) { PrepareForBailout(expr, TOS_REG); context()->Plug(r0); } else if (proxy != NULL && proxy->var()->IsLookupSlot()) { - Comment cmnt(masm_, "[ Lookup slot"); Label done, slow; // Generate code for loading from variables potentially shadowed @@ -4647,7 +4648,7 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { // Record position and call the compare IC. SetSourcePosition(expr->position()); Handle ic = CompareIC::GetUninitialized(isolate(), op); - CallIC(ic, expr->CompareOperationFeedbackId()); + CallIC(ic, NOT_CONTEXTUAL, expr->CompareOperationFeedbackId()); patch_site.EmitPatchInfo(); PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); __ cmp(r0, Operand::Zero()); @@ -4682,7 +4683,7 @@ void FullCodeGenerator::EmitLiteralCompareNil(CompareOperation* expr, Split(eq, if_true, if_false, fall_through); } else { Handle ic = CompareNilICStub::GetUninitialized(isolate(), nil); - CallIC(ic, expr->CompareOperationFeedbackId()); + CallIC(ic, NOT_CONTEXTUAL, expr->CompareOperationFeedbackId()); __ cmp(r0, Operand(0)); Split(ne, if_true, if_false, fall_through); } diff --git a/deps/v8/src/arm/ic-arm.cc b/deps/v8/src/arm/ic-arm.cc index 1af6cf87b8..d324a8c6b3 100644 --- a/deps/v8/src/arm/ic-arm.cc +++ b/deps/v8/src/arm/ic-arm.cc @@ -333,7 +333,8 @@ static void GenerateKeyNameCheck(MacroAssembler* masm, } -void LoadIC::GenerateMegamorphic(MacroAssembler* masm) { +void LoadIC::GenerateMegamorphic(MacroAssembler* masm, + ExtraICState extra_state) { // ----------- S t a t e ------------- // -- r2 : name // -- lr : return address @@ -341,7 +342,9 @@ void LoadIC::GenerateMegamorphic(MacroAssembler* masm) { // ----------------------------------- // Probe the stub cache. - Code::Flags flags = Code::ComputeHandlerFlags(Code::LOAD_IC); + Code::Flags flags = Code::ComputeFlags( + Code::HANDLER, MONOMORPHIC, extra_state, + Code::NORMAL, Code::LOAD_IC); masm->isolate()->stub_cache()->GenerateProbe( masm, flags, r0, r2, r3, r4, r5, r6); @@ -1159,7 +1162,8 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm, } -void StoreIC::GenerateMegamorphic(MacroAssembler* masm) { +void StoreIC::GenerateMegamorphic(MacroAssembler* masm, + ExtraICState extra_ic_state) { // ----------- S t a t e ------------- // -- r0 : value // -- r1 : receiver @@ -1168,7 +1172,9 @@ void StoreIC::GenerateMegamorphic(MacroAssembler* masm) { // ----------------------------------- // Get the receiver from the stack and probe the stub cache. - Code::Flags flags = Code::ComputeHandlerFlags(Code::STORE_IC); + Code::Flags flags = Code::ComputeFlags( + Code::HANDLER, MONOMORPHIC, extra_ic_state, + Code::NORMAL, Code::STORE_IC); masm->isolate()->stub_cache()->GenerateProbe( masm, flags, r1, r2, r3, r4, r5, r6); diff --git a/deps/v8/src/arm/lithium-arm.cc b/deps/v8/src/arm/lithium-arm.cc index fdf4ddfd80..38509a6ea8 100644 --- a/deps/v8/src/arm/lithium-arm.cc +++ b/deps/v8/src/arm/lithium-arm.cc @@ -840,6 +840,7 @@ void LChunkBuilder::DoBasicBlock(HBasicBlock* block, HBasicBlock* next_block) { void LChunkBuilder::VisitInstruction(HInstruction* current) { HInstruction* old_current = current_instruction_; current_instruction_ = current; + if (current->has_position()) position_ = current->position(); LInstruction* instr = NULL; if (current->CanReplaceWithDummyUses()) { @@ -1236,7 +1237,7 @@ LInstruction* LChunkBuilder::DoDiv(HDiv* instr) { ASSERT(instr->right()->representation().Equals(instr->representation())); if (instr->RightIsPowerOf2()) { ASSERT(!instr->CheckFlag(HValue::kCanBeDivByZero)); - LOperand* value = UseRegister(instr->left()); + LOperand* value = UseRegisterAtStart(instr->left()); LDivI* div = new(zone()) LDivI(value, UseConstant(instr->right()), NULL); return AssignEnvironment(DefineAsRegister(div)); } diff --git a/deps/v8/src/arm/lithium-arm.h b/deps/v8/src/arm/lithium-arm.h index 982ac2c5a3..29a176628e 100644 --- a/deps/v8/src/arm/lithium-arm.h +++ b/deps/v8/src/arm/lithium-arm.h @@ -2580,6 +2580,7 @@ class LChunkBuilder V8_FINAL : public LChunkBuilderBase { current_block_(NULL), next_block_(NULL), allocator_(allocator), + position_(RelocInfo::kNoPosition), instruction_pending_deoptimization_environment_(NULL), pending_deoptimization_ast_id_(BailoutId::None()) { } @@ -2716,6 +2717,7 @@ class LChunkBuilder V8_FINAL : public LChunkBuilderBase { HBasicBlock* current_block_; HBasicBlock* next_block_; LAllocator* allocator_; + int position_; LInstruction* instruction_pending_deoptimization_environment_; BailoutId pending_deoptimization_ast_id_; diff --git a/deps/v8/src/arm/lithium-codegen-arm.cc b/deps/v8/src/arm/lithium-codegen-arm.cc index 5ff3fa0764..05348bf018 100644 --- a/deps/v8/src/arm/lithium-codegen-arm.cc +++ b/deps/v8/src/arm/lithium-codegen-arm.cc @@ -277,8 +277,7 @@ bool LCodeGen::GenerateDeferredCode() { HValue* value = instructions_->at(code->instruction_index())->hydrogen_value(); - RecordAndWritePosition( - chunk()->graph()->SourcePositionToScriptPosition(value->position())); + RecordAndWritePosition(value->position()); Comment(";;; <@%d,#%d> " "-------------------- Deferred %s --------------------", @@ -907,7 +906,6 @@ void LCodeGen::PopulateDeoptimizationData(Handle code) { translations_.CreateByteArray(isolate()->factory()); data->SetTranslationByteArray(*translations); data->SetInlinedFunctionCount(Smi::FromInt(inlined_function_count_)); - data->SetOptimizationId(Smi::FromInt(info_->optimization_id())); Handle literals = factory()->NewFixedArray(deoptimization_literals_.length(), TENURED); @@ -1343,45 +1341,54 @@ void LCodeGen::EmitSignedIntegerDivisionByConstant( void LCodeGen::DoDivI(LDivI* instr) { if (!instr->is_flooring() && instr->hydrogen()->RightIsPowerOf2()) { - Register dividend = ToRegister(instr->left()); - HDiv* hdiv = instr->hydrogen(); - int32_t divisor = hdiv->right()->GetInteger32Constant(); - Register result = ToRegister(instr->result()); - ASSERT(!result.is(dividend)); - - // Check for (0 / -x) that will produce negative zero. - if (hdiv->left()->RangeCanInclude(0) && divisor < 0 && - hdiv->CheckFlag(HValue::kBailoutOnMinusZero)) { - __ cmp(dividend, Operand::Zero()); - DeoptimizeIf(eq, instr->environment()); - } - // Check for (kMinInt / -1). - if (hdiv->left()->RangeCanInclude(kMinInt) && divisor == -1 && - hdiv->CheckFlag(HValue::kCanOverflow)) { - __ cmp(dividend, Operand(kMinInt)); - DeoptimizeIf(eq, instr->environment()); - } - // Deoptimize if remainder will not be 0. - if (!hdiv->CheckFlag(HInstruction::kAllUsesTruncatingToInt32) && - Abs(divisor) != 1) { - __ tst(dividend, Operand(Abs(divisor) - 1)); - DeoptimizeIf(ne, instr->environment()); - } - if (divisor == -1) { // Nice shortcut, not needed for correctness. - __ rsb(result, dividend, Operand(0)); - return; + const Register dividend = ToRegister(instr->left()); + const Register result = ToRegister(instr->result()); + int32_t divisor = instr->hydrogen()->right()->GetInteger32Constant(); + int32_t test_value = 0; + int32_t power = 0; + + if (divisor > 0) { + test_value = divisor - 1; + power = WhichPowerOf2(divisor); + } else { + // Check for (0 / -x) that will produce negative zero. + if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { + __ cmp(dividend, Operand::Zero()); + DeoptimizeIf(eq, instr->environment()); + } + // Check for (kMinInt / -1). + if (divisor == -1 && instr->hydrogen()->CheckFlag(HValue::kCanOverflow)) { + __ cmp(dividend, Operand(kMinInt)); + DeoptimizeIf(eq, instr->environment()); + } + test_value = - divisor - 1; + power = WhichPowerOf2(-divisor); } - int32_t shift = WhichPowerOf2(Abs(divisor)); - if (shift == 0) { - __ mov(result, dividend); - } else if (shift == 1) { - __ add(result, dividend, Operand(dividend, LSR, 31)); + + if (test_value != 0) { + if (instr->hydrogen()->CheckFlag( + HInstruction::kAllUsesTruncatingToInt32)) { + __ sub(result, dividend, Operand::Zero(), SetCC); + __ rsb(result, result, Operand::Zero(), LeaveCC, lt); + __ mov(result, Operand(result, ASR, power)); + if (divisor > 0) __ rsb(result, result, Operand::Zero(), LeaveCC, lt); + if (divisor < 0) __ rsb(result, result, Operand::Zero(), LeaveCC, gt); + return; // Don't fall through to "__ rsb" below. + } else { + // Deoptimize if remainder is not 0. + __ tst(dividend, Operand(test_value)); + DeoptimizeIf(ne, instr->environment()); + __ mov(result, Operand(dividend, ASR, power)); + if (divisor < 0) __ rsb(result, result, Operand(0)); + } } else { - __ mov(result, Operand(dividend, ASR, 31)); - __ add(result, dividend, Operand(result, LSR, 32 - shift)); + if (divisor < 0) { + __ rsb(result, dividend, Operand(0)); + } else { + __ Move(result, dividend); + } } - if (shift > 0) __ mov(result, Operand(result, ASR, shift)); - if (divisor < 0) __ rsb(result, result, Operand(0)); + return; } @@ -4044,18 +4051,12 @@ void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) { } Handle transition = instr->transition(); - SmiCheck check_needed = - instr->hydrogen()->value()->IsHeapObject() - ? OMIT_SMI_CHECK : INLINE_SMI_CHECK; if (FLAG_track_heap_object_fields && representation.IsHeapObject()) { Register value = ToRegister(instr->value()); if (!instr->hydrogen()->value()->type().IsHeapObject()) { __ SmiTst(value); DeoptimizeIf(eq, instr->environment()); - - // We know that value is a smi now, so we can omit the check below. - check_needed = OMIT_SMI_CHECK; } } else if (representation.IsDouble()) { ASSERT(transition.is_null()); @@ -4085,6 +4086,9 @@ void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) { // Do the store. Register value = ToRegister(instr->value()); + SmiCheck check_needed = + instr->hydrogen()->value()->IsHeapObject() + ? OMIT_SMI_CHECK : INLINE_SMI_CHECK; if (access.IsInobject()) { MemOperand operand = FieldMemOperand(object, offset); __ Store(value, operand, representation); @@ -5245,7 +5249,11 @@ void LCodeGen::DoAllocate(LAllocate* instr) { if (instr->size()->IsConstantOperand()) { int32_t size = ToInteger32(LConstantOperand::cast(instr->size())); - __ Allocate(size, result, scratch, scratch2, deferred->entry(), flags); + if (size <= Page::kMaxRegularHeapObjectSize) { + __ Allocate(size, result, scratch, scratch2, deferred->entry(), flags); + } else { + __ jmp(deferred->entry()); + } } else { Register size = ToRegister(instr->size()); __ Allocate(size, diff --git a/deps/v8/src/arm/macro-assembler-arm.cc b/deps/v8/src/arm/macro-assembler-arm.cc index c377274efb..77c514ff54 100644 --- a/deps/v8/src/arm/macro-assembler-arm.cc +++ b/deps/v8/src/arm/macro-assembler-arm.cc @@ -2806,8 +2806,16 @@ void MacroAssembler::Check(Condition cond, BailoutReason reason) { void MacroAssembler::Abort(BailoutReason reason) { Label abort_start; bind(&abort_start); -#ifdef DEBUG + // We want to pass the msg string like a smi to avoid GC + // problems, however msg is not guaranteed to be aligned + // properly. Instead, we pass an aligned pointer that is + // a proper v8 smi, but also pass the alignment difference + // from the real pointer as a smi. const char* msg = GetBailoutReason(reason); + intptr_t p1 = reinterpret_cast(msg); + intptr_t p0 = (p1 & ~kSmiTagMask) + kSmiTag; + ASSERT(reinterpret_cast(p0)->IsSmi()); +#ifdef DEBUG if (msg != NULL) { RecordComment("Abort message: "); RecordComment(msg); @@ -2819,24 +2827,25 @@ void MacroAssembler::Abort(BailoutReason reason) { } #endif - mov(r0, Operand(Smi::FromInt(reason))); + mov(r0, Operand(p0)); + push(r0); + mov(r0, Operand(Smi::FromInt(p1 - p0))); push(r0); - // 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); - CallRuntime(Runtime::kAbort, 1); + CallRuntime(Runtime::kAbort, 2); } else { - CallRuntime(Runtime::kAbort, 1); + CallRuntime(Runtime::kAbort, 2); } // will not return here if (is_const_pool_blocked()) { // If the calling code cares about the exact number of // instructions generated, we insert padding here to keep the size // of the Abort macro constant. - static const int kExpectedAbortInstructions = 7; + static const int kExpectedAbortInstructions = 10; int abort_instructions = InstructionsGeneratedSince(&abort_start); ASSERT(abort_instructions <= kExpectedAbortInstructions); while (abort_instructions++ < kExpectedAbortInstructions) { diff --git a/deps/v8/src/arm/simulator-arm.h b/deps/v8/src/arm/simulator-arm.h index 24d7fe58c4..0af5162e93 100644 --- a/deps/v8/src/arm/simulator-arm.h +++ b/deps/v8/src/arm/simulator-arm.h @@ -207,10 +207,6 @@ class Simulator { void set_pc(int32_t value); int32_t get_pc() const; - Address get_sp() { - return reinterpret_cast
(static_cast(get_register(sp))); - } - // Accessor to the internal simulator stack area. uintptr_t StackLimit() const; diff --git a/deps/v8/src/arm/stub-cache-arm.cc b/deps/v8/src/arm/stub-cache-arm.cc index 3bc9554594..694a4ed68f 100644 --- a/deps/v8/src/arm/stub-cache-arm.cc +++ b/deps/v8/src/arm/stub-cache-arm.cc @@ -783,14 +783,13 @@ static void CompileCallLoadPropertyWithInterceptor( // Generate call to api function. -void StubCompiler::GenerateFastApiCall(MacroAssembler* masm, - const CallOptimization& optimization, - Handle receiver_map, - Register receiver, - Register scratch_in, - bool is_store, - int argc, - Register* values) { +static void GenerateFastApiCall(MacroAssembler* masm, + const CallOptimization& optimization, + Handle receiver_map, + Register receiver, + Register scratch_in, + int argc, + Register* values) { ASSERT(!receiver.is(scratch_in)); __ push(receiver); // Write the arguments to stack frame. @@ -855,7 +854,7 @@ void StubCompiler::GenerateFastApiCall(MacroAssembler* masm, __ mov(api_function_address, Operand(ref)); // Jump to stub. - CallApiFunctionStub stub(is_store, call_data_undefined, argc); + CallApiFunctionStub stub(true, call_data_undefined, argc); __ TailCallStub(&stub); } @@ -1076,6 +1075,15 @@ void LoadStubCompiler::GenerateLoadConstant(Handle value) { } +void LoadStubCompiler::GenerateLoadCallback( + const CallOptimization& call_optimization, + Handle receiver_map) { + GenerateFastApiCall( + masm(), call_optimization, receiver_map, + receiver(), scratch3(), 0, NULL); +} + + void LoadStubCompiler::GenerateLoadCallback( Register reg, Handle callback) { @@ -1252,6 +1260,24 @@ Handle StoreStubCompiler::CompileStoreCallback( } +Handle StoreStubCompiler::CompileStoreCallback( + Handle object, + Handle holder, + Handle name, + const CallOptimization& call_optimization) { + HandlerFrontend(IC::CurrentTypeOf(object, isolate()), + receiver(), holder, name); + + Register values[] = { value() }; + GenerateFastApiCall( + masm(), call_optimization, handle(object->map()), + receiver(), scratch3(), 1, values); + + // Return the generated code. + return GetCode(kind(), Code::FAST, name); +} + + #undef __ #define __ ACCESS_MASM(masm) @@ -1310,6 +1336,21 @@ void StoreStubCompiler::GenerateStoreViaSetter( Handle StoreStubCompiler::CompileStoreInterceptor( Handle object, Handle name) { + Label miss; + + // Check that the map of the object hasn't changed. + __ CheckMap(receiver(), scratch1(), Handle(object->map()), &miss, + DO_SMI_CHECK); + + // Perform global security token check if needed. + if (object->IsJSGlobalProxy()) { + __ CheckAccessGlobalProxy(receiver(), scratch1(), &miss); + } + + // Stub is never generated for non-global objects that require access + // checks. + ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded()); + __ Push(receiver(), this->name(), value()); // Do tail-call to the runtime system. @@ -1317,6 +1358,10 @@ Handle StoreStubCompiler::CompileStoreInterceptor( ExternalReference(IC_Utility(IC::kStoreInterceptorProperty), isolate()); __ TailCallExternalReference(store_ic_property, 3, 1); + // Handle store cache miss. + __ bind(&miss); + TailCallBuiltin(masm(), MissBuiltin(kind())); + // Return the generated code. return GetCode(kind(), Code::FAST, name); } diff --git a/deps/v8/src/arraybuffer.js b/deps/v8/src/arraybuffer.js index 6125f0f61c..cfaa8d7efc 100644 --- a/deps/v8/src/arraybuffer.js +++ b/deps/v8/src/arraybuffer.js @@ -57,17 +57,18 @@ function ArrayBufferSlice(start, end) { var relativeStart = TO_INTEGER(start); var first; + var byte_length = %ArrayBufferGetByteLength(this); if (relativeStart < 0) { - first = MathMax(this.byteLength + relativeStart, 0); + first = MathMax(byte_length + relativeStart, 0); } else { - first = MathMin(relativeStart, this.byteLength); + first = MathMin(relativeStart, byte_length); } - var relativeEnd = IS_UNDEFINED(end) ? this.byteLength : TO_INTEGER(end); + var relativeEnd = IS_UNDEFINED(end) ? byte_length : TO_INTEGER(end); var fin; if (relativeEnd < 0) { - fin = MathMax(this.byteLength + relativeEnd, 0); + fin = MathMax(byte_length + relativeEnd, 0); } else { - fin = MathMin(relativeEnd, this.byteLength); + fin = MathMin(relativeEnd, byte_length); } if (fin < first) { diff --git a/deps/v8/src/assembler.cc b/deps/v8/src/assembler.cc index 4b4c3d4daf..436d035c3e 100644 --- a/deps/v8/src/assembler.cc +++ b/deps/v8/src/assembler.cc @@ -59,8 +59,6 @@ #include "ia32/assembler-ia32-inl.h" #elif V8_TARGET_ARCH_X64 #include "x64/assembler-x64-inl.h" -#elif V8_TARGET_ARCH_A64 -#include "a64/assembler-a64-inl.h" #elif V8_TARGET_ARCH_ARM #include "arm/assembler-arm-inl.h" #elif V8_TARGET_ARCH_MIPS @@ -75,8 +73,6 @@ #include "ia32/regexp-macro-assembler-ia32.h" #elif V8_TARGET_ARCH_X64 #include "x64/regexp-macro-assembler-x64.h" -#elif V8_TARGET_ARCH_A64 -#include "a64/regexp-macro-assembler-a64.h" #elif V8_TARGET_ARCH_ARM #include "arm/regexp-macro-assembler-arm.h" #elif V8_TARGET_ARCH_MIPS @@ -126,6 +122,7 @@ AssemblerBase::AssemblerBase(Isolate* isolate, void* buffer, int buffer_size) if (FLAG_mask_constants_with_cookie && isolate != NULL) { jit_cookie_ = isolate->random_number_generator()->NextInt(); } + if (buffer == NULL) { // Do our own buffer management. if (buffer_size <= kMinimalBufferSize) { @@ -1339,8 +1336,6 @@ ExternalReference ExternalReference::re_check_stack_guard_state( function = FUNCTION_ADDR(RegExpMacroAssemblerX64::CheckStackGuardState); #elif V8_TARGET_ARCH_IA32 function = FUNCTION_ADDR(RegExpMacroAssemblerIA32::CheckStackGuardState); -#elif V8_TARGET_ARCH_A64 - function = FUNCTION_ADDR(RegExpMacroAssemblerA64::CheckStackGuardState); #elif V8_TARGET_ARCH_ARM function = FUNCTION_ADDR(RegExpMacroAssemblerARM::CheckStackGuardState); #elif V8_TARGET_ARCH_MIPS diff --git a/deps/v8/src/assembler.h b/deps/v8/src/assembler.h index 89b0e5a622..ce7d9f5b7d 100644 --- a/deps/v8/src/assembler.h +++ b/deps/v8/src/assembler.h @@ -1002,6 +1002,32 @@ class PreservePositionScope BASE_EMBEDDED { // ----------------------------------------------------------------------------- // Utility functions +inline bool is_intn(int x, int n) { + return -(1 << (n-1)) <= x && x < (1 << (n-1)); +} + +inline bool is_int8(int x) { return is_intn(x, 8); } +inline bool is_int16(int x) { return is_intn(x, 16); } +inline bool is_int18(int x) { return is_intn(x, 18); } +inline bool is_int24(int x) { return is_intn(x, 24); } + +inline bool is_uintn(int x, int n) { + return (x & -(1 << n)) == 0; +} + +inline bool is_uint2(int x) { return is_uintn(x, 2); } +inline bool is_uint3(int x) { return is_uintn(x, 3); } +inline bool is_uint4(int x) { return is_uintn(x, 4); } +inline bool is_uint5(int x) { return is_uintn(x, 5); } +inline bool is_uint6(int x) { return is_uintn(x, 6); } +inline bool is_uint8(int x) { return is_uintn(x, 8); } +inline bool is_uint10(int x) { return is_uintn(x, 10); } +inline bool is_uint12(int x) { return is_uintn(x, 12); } +inline bool is_uint16(int x) { return is_uintn(x, 16); } +inline bool is_uint24(int x) { return is_uintn(x, 24); } +inline bool is_uint26(int x) { return is_uintn(x, 26); } +inline bool is_uint28(int x) { return is_uintn(x, 28); } + inline int NumberOfBitsSet(uint32_t x) { unsigned int num_bits_set; for (num_bits_set = 0; x; x >>= 1) { diff --git a/deps/v8/src/ast.cc b/deps/v8/src/ast.cc index 6b2f48f017..1a9919b5aa 100644 --- a/deps/v8/src/ast.cc +++ b/deps/v8/src/ast.cc @@ -593,17 +593,6 @@ void Expression::RecordToBooleanTypeFeedback(TypeFeedbackOracle* oracle) { } -int Call::ComputeFeedbackSlotCount(Isolate* isolate) { - CallType call_type = GetCallType(isolate); - if (call_type == LOOKUP_SLOT_CALL || call_type == OTHER_CALL) { - // Call only uses a slot in some cases. - return 1; - } - - return 0; -} - - Call::CallType Call::GetCallType(Isolate* isolate) const { VariableProxy* proxy = expression()->AsVariableProxy(); if (proxy != NULL) { @@ -644,10 +633,10 @@ bool Call::ComputeGlobalTarget(Handle global, void CallNew::RecordTypeFeedback(TypeFeedbackOracle* oracle) { allocation_site_ = - oracle->GetCallNewAllocationSite(CallNewFeedbackSlot()); - is_monomorphic_ = oracle->CallNewIsMonomorphic(CallNewFeedbackSlot()); + oracle->GetCallNewAllocationSite(CallNewFeedbackId()); + is_monomorphic_ = oracle->CallNewIsMonomorphic(CallNewFeedbackId()); if (is_monomorphic_) { - target_ = oracle->GetCallNewTarget(CallNewFeedbackSlot()); + target_ = oracle->GetCallNewTarget(CallNewFeedbackId()); if (!allocation_site_.is_null()) { elements_kind_ = allocation_site_->GetElementsKind(); } @@ -1050,11 +1039,6 @@ CaseClause::CaseClause(Zone* zone, void AstConstructionVisitor::Visit##NodeType(NodeType* node) { \ increase_node_count(); \ } -#define REGULAR_NODE_WITH_FEEDBACK_SLOTS(NodeType) \ - void AstConstructionVisitor::Visit##NodeType(NodeType* node) { \ - increase_node_count(); \ - add_slot_node(node); \ - } #define DONT_OPTIMIZE_NODE(NodeType) \ void AstConstructionVisitor::Visit##NodeType(NodeType* node) { \ increase_node_count(); \ @@ -1067,12 +1051,6 @@ CaseClause::CaseClause(Zone* zone, increase_node_count(); \ add_flag(kDontSelfOptimize); \ } -#define DONT_SELFOPTIMIZE_NODE_WITH_FEEDBACK_SLOTS(NodeType) \ - void AstConstructionVisitor::Visit##NodeType(NodeType* node) { \ - increase_node_count(); \ - add_slot_node(node); \ - add_flag(kDontSelfOptimize); \ - } #define DONT_CACHE_NODE(NodeType) \ void AstConstructionVisitor::Visit##NodeType(NodeType* node) { \ increase_node_count(); \ @@ -1107,8 +1085,8 @@ REGULAR_NODE(CountOperation) REGULAR_NODE(BinaryOperation) REGULAR_NODE(CompareOperation) REGULAR_NODE(ThisFunction) -REGULAR_NODE_WITH_FEEDBACK_SLOTS(Call) -REGULAR_NODE_WITH_FEEDBACK_SLOTS(CallNew) +REGULAR_NODE(Call) +REGULAR_NODE(CallNew) // In theory, for VariableProxy we'd have to add: // if (node->var()->IsLookupSlot()) add_flag(kDontInline); // But node->var() is usually not bound yet at VariableProxy creation time, and @@ -1133,12 +1111,11 @@ DONT_OPTIMIZE_NODE(NativeFunctionLiteral) DONT_SELFOPTIMIZE_NODE(DoWhileStatement) DONT_SELFOPTIMIZE_NODE(WhileStatement) DONT_SELFOPTIMIZE_NODE(ForStatement) -DONT_SELFOPTIMIZE_NODE_WITH_FEEDBACK_SLOTS(ForInStatement) +DONT_SELFOPTIMIZE_NODE(ForInStatement) DONT_SELFOPTIMIZE_NODE(ForOfStatement) DONT_CACHE_NODE(ModuleLiteral) - void AstConstructionVisitor::VisitCallRuntime(CallRuntime* node) { increase_node_count(); if (node->is_jsruntime()) { diff --git a/deps/v8/src/ast.h b/deps/v8/src/ast.h index aacc5e4fc8..2b33820f9e 100644 --- a/deps/v8/src/ast.h +++ b/deps/v8/src/ast.h @@ -32,7 +32,6 @@ #include "assembler.h" #include "factory.h" -#include "feedback-slots.h" #include "isolate.h" #include "jsregexp.h" #include "list-inl.h" @@ -182,7 +181,7 @@ class AstProperties V8_FINAL BASE_EMBEDDED { public: class Flags : public EnumSet {}; - AstProperties() : node_count_(0) {} + AstProperties() : node_count_(0) { } Flags* flags() { return &flags_; } int node_count() { return node_count_; } @@ -915,8 +914,7 @@ class ForEachStatement : public IterationStatement { }; -class ForInStatement V8_FINAL : public ForEachStatement, - public FeedbackSlotInterface { +class ForInStatement V8_FINAL : public ForEachStatement { public: DECLARE_NODE_TYPE(ForInStatement) @@ -924,16 +922,7 @@ class ForInStatement V8_FINAL : public ForEachStatement, return subject(); } - // Type feedback information. - virtual ComputablePhase GetComputablePhase() { return DURING_PARSE; } - virtual int ComputeFeedbackSlotCount(Isolate* isolate) { return 1; } - virtual void SetFirstFeedbackSlot(int slot) { for_in_feedback_slot_ = slot; } - - int ForInFeedbackSlot() { - ASSERT(for_in_feedback_slot_ != kInvalidFeedbackSlot); - return for_in_feedback_slot_; - } - + TypeFeedbackId ForInFeedbackId() const { return reuse(PrepareId()); } enum ForInType { FAST_FOR_IN, SLOW_FOR_IN }; ForInType for_in_type() const { return for_in_type_; } void set_for_in_type(ForInType type) { for_in_type_ = type; } @@ -947,13 +936,11 @@ class ForInStatement V8_FINAL : public ForEachStatement, ForInStatement(Zone* zone, ZoneStringList* labels, int pos) : ForEachStatement(zone, labels, pos), for_in_type_(SLOW_FOR_IN), - for_in_feedback_slot_(kInvalidFeedbackSlot), body_id_(GetNextId(zone)), prepare_id_(GetNextId(zone)) { } ForInType for_in_type_; - int for_in_feedback_slot_; const BailoutId body_id_; const BailoutId prepare_id_; }; @@ -1746,7 +1733,7 @@ class Property V8_FINAL : public Expression { }; -class Call V8_FINAL : public Expression, public FeedbackSlotInterface { +class Call V8_FINAL : public Expression { public: DECLARE_NODE_TYPE(Call) @@ -1754,16 +1741,7 @@ class Call V8_FINAL : public Expression, public FeedbackSlotInterface { ZoneList* arguments() const { return arguments_; } // Type feedback information. - virtual ComputablePhase GetComputablePhase() { return AFTER_SCOPING; } - virtual int ComputeFeedbackSlotCount(Isolate* isolate); - virtual void SetFirstFeedbackSlot(int slot) { - call_feedback_slot_ = slot; - } - - bool HasCallFeedbackSlot() const { - return call_feedback_slot_ != kInvalidFeedbackSlot; - } - int CallFeedbackSlot() const { return call_feedback_slot_; } + TypeFeedbackId CallFeedbackId() const { return reuse(id()); } virtual SmallMapList* GetReceiverTypes() V8_OVERRIDE { if (expression()->IsProperty()) { @@ -1812,7 +1790,6 @@ class Call V8_FINAL : public Expression, public FeedbackSlotInterface { : Expression(zone, pos), expression_(expression), arguments_(arguments), - call_feedback_slot_(kInvalidFeedbackSlot), return_id_(GetNextId(zone)) { if (expression->IsProperty()) { expression->AsProperty()->mark_for_call(); @@ -1825,13 +1802,12 @@ class Call V8_FINAL : public Expression, public FeedbackSlotInterface { Handle target_; Handle cell_; - int call_feedback_slot_; const BailoutId return_id_; }; -class CallNew V8_FINAL : public Expression, public FeedbackSlotInterface { +class CallNew V8_FINAL : public Expression { public: DECLARE_NODE_TYPE(CallNew) @@ -1839,17 +1815,6 @@ class CallNew V8_FINAL : public Expression, public FeedbackSlotInterface { ZoneList* arguments() const { return arguments_; } // Type feedback information. - virtual ComputablePhase GetComputablePhase() { return DURING_PARSE; } - virtual int ComputeFeedbackSlotCount(Isolate* isolate) { return 1; } - virtual void SetFirstFeedbackSlot(int slot) { - callnew_feedback_slot_ = slot; - } - - int CallNewFeedbackSlot() { - ASSERT(callnew_feedback_slot_ != kInvalidFeedbackSlot); - return callnew_feedback_slot_; - } - TypeFeedbackId CallNewFeedbackId() const { return reuse(id()); } void RecordTypeFeedback(TypeFeedbackOracle* oracle); virtual bool IsMonomorphic() V8_OVERRIDE { return is_monomorphic_; } @@ -1859,8 +1824,6 @@ class CallNew V8_FINAL : public Expression, public FeedbackSlotInterface { return allocation_site_; } - static int feedback_slots() { return 1; } - BailoutId ReturnId() const { return return_id_; } protected: @@ -1873,7 +1836,6 @@ class CallNew V8_FINAL : public Expression, public FeedbackSlotInterface { arguments_(arguments), is_monomorphic_(false), elements_kind_(GetInitialFastElementsKind()), - callnew_feedback_slot_(kInvalidFeedbackSlot), return_id_(GetNextId(zone)) { } private: @@ -1884,7 +1846,6 @@ class CallNew V8_FINAL : public Expression, public FeedbackSlotInterface { Handle target_; ElementsKind elements_kind_; Handle allocation_site_; - int callnew_feedback_slot_; const BailoutId return_id_; }; @@ -2371,15 +2332,7 @@ class FunctionLiteral V8_FINAL : public Expression { void set_ast_properties(AstProperties* ast_properties) { ast_properties_ = *ast_properties; } - void set_slot_processor(DeferredFeedbackSlotProcessor* slot_processor) { - slot_processor_ = *slot_processor; - } - void ProcessFeedbackSlots(Isolate* isolate) { - slot_processor_.ProcessFeedbackSlots(isolate); - } - int slot_count() { - return slot_processor_.slot_count(); - } + bool dont_optimize() { return dont_optimize_reason_ != kNoReason; } BailoutReason dont_optimize_reason() { return dont_optimize_reason_; } void set_dont_optimize_reason(BailoutReason reason) { @@ -2429,7 +2382,6 @@ class FunctionLiteral V8_FINAL : public Expression { ZoneList* body_; Handle inferred_name_; AstProperties ast_properties_; - DeferredFeedbackSlotProcessor slot_processor_; BailoutReason dont_optimize_reason_; int materialized_literal_count_; @@ -2904,13 +2856,10 @@ private: \ class AstConstructionVisitor BASE_EMBEDDED { public: - explicit AstConstructionVisitor(Zone* zone) - : dont_optimize_reason_(kNoReason), - zone_(zone) { } + AstConstructionVisitor() : dont_optimize_reason_(kNoReason) { } AstProperties* ast_properties() { return &properties_; } BailoutReason dont_optimize_reason() { return dont_optimize_reason_; } - DeferredFeedbackSlotProcessor* slot_processor() { return &slot_processor_; } private: template friend class AstNodeFactory; @@ -2927,21 +2876,13 @@ class AstConstructionVisitor BASE_EMBEDDED { dont_optimize_reason_ = reason; } - void add_slot_node(FeedbackSlotInterface* slot_node) { - slot_processor_.add_slot_node(zone_, slot_node); - } - AstProperties properties_; - DeferredFeedbackSlotProcessor slot_processor_; BailoutReason dont_optimize_reason_; - Zone* zone_; }; class AstNullVisitor BASE_EMBEDDED { public: - explicit AstNullVisitor(Zone* zone) {} - // Node visitors. #define DEF_VISIT(type) \ void Visit##type(type* node) {} @@ -2957,9 +2898,7 @@ class AstNullVisitor BASE_EMBEDDED { template class AstNodeFactory V8_FINAL BASE_EMBEDDED { public: - explicit AstNodeFactory(Zone* zone) - : zone_(zone), - visitor_(zone) { } + explicit AstNodeFactory(Zone* zone) : zone_(zone) { } Visitor* visitor() { return &visitor_; } diff --git a/deps/v8/src/atomicops.h b/deps/v8/src/atomicops.h index d7d4df6763..789721edfc 100644 --- a/deps/v8/src/atomicops.h +++ b/deps/v8/src/atomicops.h @@ -159,8 +159,6 @@ Atomic64 Release_Load(volatile const Atomic64* ptr); #include "atomicops_internals_x86_macosx.h" #elif defined(__GNUC__) && (V8_HOST_ARCH_IA32 || V8_HOST_ARCH_X64) #include "atomicops_internals_x86_gcc.h" -#elif defined(__GNUC__) && V8_HOST_ARCH_A64 -#include "atomicops_internals_a64_gcc.h" #elif defined(__GNUC__) && V8_HOST_ARCH_ARM #include "atomicops_internals_arm_gcc.h" #elif defined(__GNUC__) && V8_HOST_ARCH_MIPS diff --git a/deps/v8/src/atomicops_internals_a64_gcc.h b/deps/v8/src/atomicops_internals_a64_gcc.h deleted file mode 100644 index 074da5841e..0000000000 --- a/deps/v8/src/atomicops_internals_a64_gcc.h +++ /dev/null @@ -1,416 +0,0 @@ -// Copyright 2012 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. - -// This file is an internal atomic implementation, use atomicops.h instead. - -#ifndef V8_ATOMICOPS_INTERNALS_ARM_GCC_H_ -#define V8_ATOMICOPS_INTERNALS_ARM_GCC_H_ - -namespace v8 { -namespace internal { - -inline void MemoryBarrier() { /* Not used. */ } - -inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, - Atomic32 old_value, - Atomic32 new_value) { - Atomic32 prev; - int32_t temp; - - __asm__ __volatile__ ( // NOLINT - "0: \n\t" - "ldxr %w[prev], [%[ptr]] \n\t" // Load the previous value. - "cmp %w[prev], %w[old_value] \n\t" - "bne 1f \n\t" - "stxr %w[temp], %w[new_value], [%[ptr]]\n\t" // Try to store the new value. - "cbnz %w[temp], 0b \n\t" // Retry if it did not work. - "1: \n\t" - "clrex \n\t" // In case we didn't swap. - : [prev]"=&r" (prev), - [temp]"=&r" (temp) - : [ptr]"r" (ptr), - [old_value]"r" (old_value), - [new_value]"r" (new_value) - : "memory", "cc" - ); // NOLINT - - return prev; -} - -inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, - Atomic32 new_value) { - Atomic32 result; - int32_t temp; - - __asm__ __volatile__ ( // NOLINT - "0: \n\t" - "ldxr %w[result], [%[ptr]] \n\t" // Load the previous value. - "stxr %w[temp], %w[new_value], [%[ptr]]\n\t" // Try to store the new value. - "cbnz %w[temp], 0b \n\t" // Retry if it did not work. - : [result]"=&r" (result), - [temp]"=&r" (temp) - : [ptr]"r" (ptr), - [new_value]"r" (new_value) - : "memory" - ); // NOLINT - - return result; -} - -inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, - Atomic32 increment) { - Atomic32 result; - int32_t temp; - - __asm__ __volatile__ ( // NOLINT - "0: \n\t" - "ldxr %w[result], [%[ptr]] \n\t" // Load the previous value. - "add %w[result], %w[result], %w[increment]\n\t" - "stxr %w[temp], %w[result], [%[ptr]] \n\t" // Try to store the result. - "cbnz %w[temp], 0b \n\t" // Retry on failure. - : [result]"=&r" (result), - [temp]"=&r" (temp) - : [ptr]"r" (ptr), - [increment]"r" (increment) - : "memory" - ); // NOLINT - - return result; -} - -inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, - Atomic32 increment) { - Atomic32 result; - int32_t temp; - - __asm__ __volatile__ ( // NOLINT - "dmb ish \n\t" // Data memory barrier. - "0: \n\t" - "ldxr %w[result], [%[ptr]] \n\t" // Load the previous value. - "add %w[result], %w[result], %w[increment]\n\t" - "stxr %w[temp], %w[result], [%[ptr]] \n\t" // Try to store the result. - "cbnz %w[temp], 0b \n\t" // Retry on failure. - "dmb ish \n\t" // Data memory barrier. - : [result]"=&r" (result), - [temp]"=&r" (temp) - : [ptr]"r" (ptr), - [increment]"r" (increment) - : "memory" - ); // NOLINT - - return result; -} - -inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr, - Atomic32 old_value, - Atomic32 new_value) { - Atomic32 prev; - int32_t temp; - - __asm__ __volatile__ ( // NOLINT - "0: \n\t" - "ldxr %w[prev], [%[ptr]] \n\t" // Load the previous value. - "cmp %w[prev], %w[old_value] \n\t" - "bne 1f \n\t" - "stxr %w[temp], %w[new_value], [%[ptr]]\n\t" // Try to store the new value. - "cbnz %w[temp], 0b \n\t" // Retry if it did not work. - "dmb ish \n\t" // Data memory barrier. - "1: \n\t" - // If the compare failed the 'dmb' is unnecessary, but we still need a - // 'clrex'. - "clrex \n\t" - : [prev]"=&r" (prev), - [temp]"=&r" (temp) - : [ptr]"r" (ptr), - [old_value]"r" (old_value), - [new_value]"r" (new_value) - : "memory", "cc" - ); // NOLINT - - return prev; -} - -inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr, - Atomic32 old_value, - Atomic32 new_value) { - Atomic32 prev; - int32_t temp; - - __asm__ __volatile__ ( // NOLINT - "dmb ish \n\t" // Data memory barrier. - "0: \n\t" - "ldxr %w[prev], [%[ptr]] \n\t" // Load the previous value. - "cmp %w[prev], %w[old_value] \n\t" - "bne 1f \n\t" - "stxr %w[temp], %w[new_value], [%[ptr]]\n\t" // Try to store the new value. - "cbnz %w[temp], 0b \n\t" // Retry if it did not work. - "1: \n\t" - // If the compare failed the we still need a 'clrex'. - "clrex \n\t" - : [prev]"=&r" (prev), - [temp]"=&r" (temp) - : [ptr]"r" (ptr), - [old_value]"r" (old_value), - [new_value]"r" (new_value) - : "memory", "cc" - ); // NOLINT - - return prev; -} - -inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) { - *ptr = value; -} - -inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) { - *ptr = value; - __asm__ __volatile__ ( // NOLINT - "dmb ish \n\t" // Data memory barrier. - ::: "memory" // Prevent gcc from reordering before the store above. - ); // NOLINT -} - -inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) { - __asm__ __volatile__ ( // NOLINT - "dmb ish \n\t" // Data memory barrier. - ::: "memory" // Prevent gcc from reordering after the store below. - ); // NOLINT - *ptr = value; -} - -inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) { - return *ptr; -} - -inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) { - Atomic32 value = *ptr; - __asm__ __volatile__ ( // NOLINT - "dmb ish \n\t" // Data memory barrier. - ::: "memory" // Prevent gcc from reordering before the load above. - ); // NOLINT - return value; -} - -inline Atomic32 Release_Load(volatile const Atomic32* ptr) { - __asm__ __volatile__ ( // NOLINT - "dmb ish \n\t" // Data memory barrier. - ::: "memory" // Prevent gcc from reordering after the load below. - ); // NOLINT - return *ptr; -} - -// 64-bit versions of the operations. -// See the 32-bit versions for comments. - -inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr, - Atomic64 old_value, - Atomic64 new_value) { - Atomic64 prev; - int32_t temp; - - __asm__ __volatile__ ( // NOLINT - "0: \n\t" - "ldxr %[prev], [%[ptr]] \n\t" - "cmp %[prev], %[old_value] \n\t" - "bne 1f \n\t" - "stxr %w[temp], %[new_value], [%[ptr]] \n\t" - "cbnz %w[temp], 0b \n\t" - "1: \n\t" - "clrex \n\t" - : [prev]"=&r" (prev), - [temp]"=&r" (temp) - : [ptr]"r" (ptr), - [old_value]"r" (old_value), - [new_value]"r" (new_value) - : "memory", "cc" - ); // NOLINT - - return prev; -} - -inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr, - Atomic64 new_value) { - Atomic64 result; - int32_t temp; - - __asm__ __volatile__ ( // NOLINT - "0: \n\t" - "ldxr %[result], [%[ptr]] \n\t" - "stxr %w[temp], %[new_value], [%[ptr]] \n\t" - "cbnz %w[temp], 0b \n\t" - : [result]"=&r" (result), - [temp]"=&r" (temp) - : [ptr]"r" (ptr), - [new_value]"r" (new_value) - : "memory" - ); // NOLINT - - return result; -} - -inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr, - Atomic64 increment) { - Atomic64 result; - int32_t temp; - - __asm__ __volatile__ ( // NOLINT - "0: \n\t" - "ldxr %[result], [%[ptr]] \n\t" - "add %[result], %[result], %[increment] \n\t" - "stxr %w[temp], %[result], [%[ptr]] \n\t" - "cbnz %w[temp], 0b \n\t" - : [result]"=&r" (result), - [temp]"=&r" (temp) - : [ptr]"r" (ptr), - [increment]"r" (increment) - : "memory" - ); // NOLINT - - return result; -} - -inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr, - Atomic64 increment) { - Atomic64 result; - int32_t temp; - - __asm__ __volatile__ ( // NOLINT - "dmb ish \n\t" - "0: \n\t" - "ldxr %[result], [%[ptr]] \n\t" - "add %[result], %[result], %[increment] \n\t" - "stxr %w[temp], %[result], [%[ptr]] \n\t" - "cbnz %w[temp], 0b \n\t" - "dmb ish \n\t" - : [result]"=&r" (result), - [temp]"=&r" (temp) - : [ptr]"r" (ptr), - [increment]"r" (increment) - : "memory" - ); // NOLINT - - return result; -} - -inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr, - Atomic64 old_value, - Atomic64 new_value) { - Atomic64 prev; - int32_t temp; - - __asm__ __volatile__ ( // NOLINT - "0: \n\t" - "ldxr %[prev], [%[ptr]] \n\t" - "cmp %[prev], %[old_value] \n\t" - "bne 1f \n\t" - "stxr %w[temp], %[new_value], [%[ptr]] \n\t" - "cbnz %w[temp], 0b \n\t" - "dmb ish \n\t" - "1: \n\t" - "clrex \n\t" - : [prev]"=&r" (prev), - [temp]"=&r" (temp) - : [ptr]"r" (ptr), - [old_value]"r" (old_value), - [new_value]"r" (new_value) - : "memory", "cc" - ); // NOLINT - - return prev; -} - -inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr, - Atomic64 old_value, - Atomic64 new_value) { - Atomic64 prev; - int32_t temp; - - __asm__ __volatile__ ( // NOLINT - "dmb ish \n\t" - "0: \n\t" - "ldxr %[prev], [%[ptr]] \n\t" - "cmp %[prev], %[old_value] \n\t" - "bne 1f \n\t" - "stxr %w[temp], %[new_value], [%[ptr]] \n\t" - "cbnz %w[temp], 0b \n\t" - "1: \n\t" - "clrex \n\t" - : [prev]"=&r" (prev), - [temp]"=&r" (temp) - : [ptr]"r" (ptr), - [old_value]"r" (old_value), - [new_value]"r" (new_value) - : "memory", "cc" - ); // NOLINT - - return prev; -} - -inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) { - *ptr = value; -} - -inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) { - *ptr = value; - __asm__ __volatile__ ( // NOLINT - "dmb ish \n\t" - ::: "memory" - ); // NOLINT -} - -inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) { - __asm__ __volatile__ ( // NOLINT - "dmb ish \n\t" - ::: "memory" - ); // NOLINT - *ptr = value; -} - -inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) { - return *ptr; -} - -inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) { - Atomic64 value = *ptr; - __asm__ __volatile__ ( // NOLINT - "dmb ish \n\t" - ::: "memory" - ); // NOLINT - return value; -} - -inline Atomic64 Release_Load(volatile const Atomic64* ptr) { - __asm__ __volatile__ ( // NOLINT - "dmb ish \n\t" - ::: "memory" - ); // NOLINT - return *ptr; -} - -} } // namespace v8::internal - -#endif // V8_ATOMICOPS_INTERNALS_ARM_GCC_H_ diff --git a/deps/v8/src/bootstrapper.cc b/deps/v8/src/bootstrapper.cc index d11ff34754..ef802ba987 100644 --- a/deps/v8/src/bootstrapper.cc +++ b/deps/v8/src/bootstrapper.cc @@ -1582,9 +1582,6 @@ void Genesis::InstallNativeFunctions() { void Genesis::InstallExperimentalNativeFunctions() { INSTALL_NATIVE(JSFunction, "RunMicrotasks", run_microtasks); - INSTALL_NATIVE(JSFunction, "EnqueueExternalMicrotask", - enqueue_external_microtask); - if (FLAG_harmony_proxies) { INSTALL_NATIVE(JSFunction, "DerivedHasTrap", derived_has_trap); INSTALL_NATIVE(JSFunction, "DerivedGetTrap", derived_get_trap); @@ -2569,9 +2566,7 @@ class NoTrackDoubleFieldsForSerializerScope { } } ~NoTrackDoubleFieldsForSerializerScope() { - if (Serializer::enabled()) { - FLAG_track_double_fields = flag_; - } + FLAG_track_double_fields = flag_; } private: diff --git a/deps/v8/src/builtins.cc b/deps/v8/src/builtins.cc index b9ff9e1344..e68890fcb2 100644 --- a/deps/v8/src/builtins.cc +++ b/deps/v8/src/builtins.cc @@ -1599,7 +1599,9 @@ void Builtins::InitBuiltinFunctionTable() { functions->c_code = NULL; \ functions->s_name = #aname; \ functions->name = k##aname; \ - functions->flags = Code::ComputeHandlerFlags(Code::kind); \ + functions->flags = Code::ComputeFlags( \ + Code::HANDLER, MONOMORPHIC, kNoExtraICState, \ + Code::NORMAL, Code::kind); \ functions->extra_args = NO_EXTRA_ARGUMENTS; \ ++functions; @@ -1625,9 +1627,7 @@ void Builtins::SetUp(Isolate* isolate, bool create_heap_objects) { // For now we generate builtin adaptor code into a stack-allocated // buffer, before copying it into individual code objects. Be careful // with alignment, some platforms don't like unaligned code. - // TODO(jbramley): I had to increase the size of this buffer from 8KB because - // we can generate a lot of debug code on A64. - union { int force_alignment; byte buffer[16*KB]; } u; + union { int force_alignment; byte buffer[8*KB]; } u; // Traverse the list of builtins and generate an adaptor in a // separate code object for each one. diff --git a/deps/v8/src/char-predicates.h b/deps/v8/src/char-predicates.h index f52feda6c1..767ad6513a 100644 --- a/deps/v8/src/char-predicates.h +++ b/deps/v8/src/char-predicates.h @@ -66,27 +66,6 @@ struct IdentifierPart { } }; - -// WhiteSpace according to ECMA-262 5.1, 7.2. -struct WhiteSpace { - static inline bool Is(uc32 c) { - return c == 0x0009 || // - c == 0x000B || // - c == 0x000C || // - c == 0xFEFF || // - // \u0020 and \u00A0 are included in unibrow::WhiteSpace. - unibrow::WhiteSpace::Is(c); - } -}; - - -// WhiteSpace and LineTerminator according to ECMA-262 5.1, 7.2 and 7.3. -struct WhiteSpaceOrLineTerminator { - static inline bool Is(uc32 c) { - return WhiteSpace::Is(c) || unibrow::LineTerminator::Is(c); - } -}; - } } // namespace v8::internal #endif // V8_CHAR_PREDICATES_H_ diff --git a/deps/v8/src/checks.h b/deps/v8/src/checks.h index 57f1852618..f7b145fc8a 100644 --- a/deps/v8/src/checks.h +++ b/deps/v8/src/checks.h @@ -34,7 +34,6 @@ extern "C" void V8_Fatal(const char* file, int line, const char* format, ...); - // The FATAL, UNREACHABLE and UNIMPLEMENTED macros are useful during // development, but they should not be relied on in the final product. #ifdef DEBUG @@ -52,23 +51,6 @@ extern "C" void V8_Fatal(const char* file, int line, const char* format, ...); #define UNREACHABLE() ((void) 0) #endif -// Simulator specific helpers. -#if defined(USE_SIMULATOR) && defined(V8_TARGET_ARCH_A64) - // TODO(all): If possible automatically prepend an indicator like - // UNIMPLEMENTED or LOCATION. - #define ASM_UNIMPLEMENTED(message) \ - __ Debug(message, __LINE__, NO_PARAM) - #define ASM_UNIMPLEMENTED_BREAK(message) \ - __ Debug(message, __LINE__, \ - FLAG_ignore_asm_unimplemented_break ? NO_PARAM : BREAK) - #define ASM_LOCATION(message) \ - __ Debug("LOCATION: " message, __LINE__, NO_PARAM) -#else - #define ASM_UNIMPLEMENTED(message) - #define ASM_UNIMPLEMENTED_BREAK(message) - #define ASM_LOCATION(message) -#endif - // The CHECK macro checks that the given condition is true; if not, it // prints a message to stderr and aborts. diff --git a/deps/v8/src/code-stubs-hydrogen.cc b/deps/v8/src/code-stubs-hydrogen.cc index b7247eb6bf..638f9e5a1d 100644 --- a/deps/v8/src/code-stubs-hydrogen.cc +++ b/deps/v8/src/code-stubs-hydrogen.cc @@ -81,11 +81,6 @@ class CodeStubGraphBuilderBase : public HGraphBuilder { HContext* context() { return context_; } Isolate* isolate() { return info_.isolate(); } - HLoadNamedField* BuildLoadNamedField(HValue* object, - Representation representation, - int offset, - bool is_inobject); - enum ArgumentClass { NONE, SINGLE, @@ -252,7 +247,8 @@ Handle HydrogenCodeStub::GenerateLightweightMissCode(Isolate* isolate) { GetCodeKind(), GetICState(), GetExtraICState(), - GetStubType()); + GetStubType(), + GetStubFlags()); Handle new_object = factory->NewCode( desc, flags, masm.CodeObject(), NeedsImmovableCode()); return new_object; @@ -299,8 +295,7 @@ HValue* CodeStubGraphBuilder::BuildCodeStub() { // Check if the parameter is already a SMI or heap number. IfBuilder if_number(this); if_number.If(value); - if_number.OrIf(value, isolate()->factory()->heap_number_map(), - top_info()); + if_number.OrIf(value, isolate()->factory()->heap_number_map()); if_number.Then(); // Return the number. @@ -363,8 +358,7 @@ HValue* CodeStubGraphBuilder::BuildCodeStub() { HValue* elements = AddLoadElements(boilerplate); IfBuilder if_fixed_cow(this); - if_fixed_cow.If(elements, factory->fixed_cow_array_map(), - top_info()); + if_fixed_cow.If(elements, factory->fixed_cow_array_map()); if_fixed_cow.Then(); push_value = BuildCloneShallowArray(boilerplate, allocation_site, @@ -375,7 +369,7 @@ HValue* CodeStubGraphBuilder::BuildCodeStub() { if_fixed_cow.Else(); IfBuilder if_fixed(this); - if_fixed.If(elements, factory->fixed_array_map(), top_info()); + if_fixed.If(elements, factory->fixed_array_map()); if_fixed.Then(); push_value = BuildCloneShallowArray(boilerplate, allocation_site, @@ -536,11 +530,15 @@ HValue* CodeStubGraphBuilder::BuildCodeStub() { Add(site_list, HObjectAccess::ForAllocationSiteList(), object); - HInstruction* feedback_vector = GetParameter(0); - HInstruction* slot = GetParameter(1); - Add(feedback_vector, slot, object, FAST_ELEMENTS, - INITIALIZING_STORE); - return feedback_vector; + // We use a hammer (SkipWriteBarrier()) to indicate that we know the input + // cell is really a Cell, and so no write barrier is needed. + // TODO(mvstanton): Add a debug_code check to verify the input cell is really + // a cell. (perhaps with a new instruction, HAssert). + HInstruction* cell = GetParameter(0); + HObjectAccess access = HObjectAccess::ForCellValue(); + store = Add(cell, access, object); + store->SkipWriteBarrier(); + return cell; } @@ -554,7 +552,7 @@ HValue* CodeStubGraphBuilder::BuildCodeStub() { HInstruction* load = BuildUncheckedMonomorphicElementAccess( GetParameter(0), GetParameter(1), NULL, casted_stub()->is_js_array(), casted_stub()->elements_kind(), - LOAD, NEVER_RETURN_HOLE, STANDARD_STORE); + false, NEVER_RETURN_HOLE, STANDARD_STORE); return load; } @@ -564,32 +562,14 @@ Handle KeyedLoadFastElementStub::GenerateCode(Isolate* isolate) { } -HLoadNamedField* CodeStubGraphBuilderBase::BuildLoadNamedField( - HValue* object, - Representation representation, - int offset, - bool is_inobject) { - HObjectAccess access = is_inobject - ? HObjectAccess::ForObservableJSObjectOffset(offset, representation) - : HObjectAccess::ForBackingStoreOffset(offset, representation); - if (representation.IsDouble()) { - // Load the heap number. - object = Add( - object, static_cast(NULL), - access.WithRepresentation(Representation::Tagged())); - // Load the double value from it. - access = HObjectAccess::ForHeapNumberValue(); - } - return Add(object, static_cast(NULL), access); -} - - template<> HValue* CodeStubGraphBuilder::BuildCodeStub() { - return BuildLoadNamedField(GetParameter(0), - casted_stub()->representation(), - casted_stub()->offset(), - casted_stub()->is_inobject()); + Representation rep = casted_stub()->representation(); + int offset = casted_stub()->offset(); + HObjectAccess access = casted_stub()->is_inobject() ? + HObjectAccess::ForObservableJSObjectOffset(offset, rep) : + HObjectAccess::ForBackingStoreOffset(offset, rep); + return AddLoadNamedField(GetParameter(0), access); } @@ -600,10 +580,12 @@ Handle LoadFieldStub::GenerateCode(Isolate* isolate) { template<> HValue* CodeStubGraphBuilder::BuildCodeStub() { - return BuildLoadNamedField(GetParameter(0), - casted_stub()->representation(), - casted_stub()->offset(), - casted_stub()->is_inobject()); + Representation rep = casted_stub()->representation(); + int offset = casted_stub()->offset(); + HObjectAccess access = casted_stub()->is_inobject() ? + HObjectAccess::ForObservableJSObjectOffset(offset, rep) : + HObjectAccess::ForBackingStoreOffset(offset, rep); + return AddLoadNamedField(GetParameter(0), access); } @@ -617,7 +599,7 @@ HValue* CodeStubGraphBuilder::BuildCodeStub() { BuildUncheckedMonomorphicElementAccess( GetParameter(0), GetParameter(1), GetParameter(2), casted_stub()->is_js_array(), casted_stub()->elements_kind(), - STORE, NEVER_RETURN_HOLE, casted_stub()->store_mode()); + true, NEVER_RETURN_HOLE, casted_stub()->store_mode()); return GetParameter(2); } @@ -1051,16 +1033,13 @@ HValue* CodeStubGraphBuilder::BuildCodeInitializedStub() { Handle placeholder_cell = isolate()->factory()->NewPropertyCell(placeholer_value); + HParameter* receiver = GetParameter(0); HParameter* value = GetParameter(2); - if (stub->check_global()) { - // Check that the map of the global has not changed: use a placeholder map - // that will be replaced later with the global object's map. - Handle placeholder_map = isolate()->factory()->meta_map(); - HValue* global = Add( - StoreGlobalStub::global_placeholder(isolate())); - Add(global, placeholder_map, top_info()); - } + // Check that the map of the global has not changed: use a placeholder map + // that will be replaced later with the global object's map. + Handle placeholder_map = isolate()->factory()->meta_map(); + Add(receiver, placeholder_map, top_info()); HValue* cell = Add(placeholder_cell); HObjectAccess access(HObjectAccess::ForCellPayload(isolate())); @@ -1117,7 +1096,7 @@ HValue* CodeStubGraphBuilder::BuildCodeStub() { BuildUncheckedMonomorphicElementAccess(object, key, value, casted_stub()->is_jsarray(), casted_stub()->to_kind(), - STORE, ALLOW_RETURN_HOLE, + true, ALLOW_RETURN_HOLE, casted_stub()->store_mode()); } diff --git a/deps/v8/src/code-stubs.cc b/deps/v8/src/code-stubs.cc index be14cf6e87..d86bc70dcf 100644 --- a/deps/v8/src/code-stubs.cc +++ b/deps/v8/src/code-stubs.cc @@ -119,7 +119,8 @@ Handle PlatformCodeStub::GenerateCode(Isolate* isolate) { GetCodeKind(), GetICState(), GetExtraICState(), - GetStubType()); + GetStubType(), + GetStubFlags()); Handle new_object = factory->NewCode( desc, flags, masm.CodeObject(), NeedsImmovableCode()); return new_object; diff --git a/deps/v8/src/code-stubs.h b/deps/v8/src/code-stubs.h index 07e34be578..a7283ba642 100644 --- a/deps/v8/src/code-stubs.h +++ b/deps/v8/src/code-stubs.h @@ -101,7 +101,7 @@ namespace internal { V(KeyedLoadField) // List of code stubs only used on ARM platforms. -#if defined(V8_TARGET_ARCH_ARM) || defined(V8_TARGET_ARCH_A64) +#if V8_TARGET_ARCH_ARM #define CODE_STUB_LIST_ARM(V) \ V(GetProperty) \ V(SetProperty) \ @@ -188,6 +188,9 @@ class CodeStub BASE_EMBEDDED { virtual Code::StubType GetStubType() { return Code::NORMAL; } + virtual int GetStubFlags() { + return -1; + } virtual void PrintName(StringStream* stream); @@ -439,8 +442,6 @@ class RuntimeCallHelper { #include "ia32/code-stubs-ia32.h" #elif V8_TARGET_ARCH_X64 #include "x64/code-stubs-x64.h" -#elif V8_TARGET_ARCH_A64 -#include "a64/code-stubs-a64.h" #elif V8_TARGET_ARCH_ARM #include "arm/code-stubs-arm.h" #elif V8_TARGET_ARCH_MIPS @@ -882,7 +883,7 @@ class HICStub: public HydrogenCodeStub { class HandlerStub: public HICStub { public: virtual Code::Kind GetCodeKind() const { return Code::HANDLER; } - virtual ExtraICState GetExtraICState() { return kind(); } + virtual int GetStubFlags() { return kind(); } protected: HandlerStub() : HICStub() { } @@ -954,27 +955,19 @@ class LoadFieldStub: public HandlerStub { class StoreGlobalStub : public HandlerStub { public: - explicit StoreGlobalStub(bool is_constant, bool check_global) { - bit_field_ = IsConstantBits::encode(is_constant) | - CheckGlobalBits::encode(check_global); - } - - static Handle global_placeholder(Isolate* isolate) { - return isolate->factory()->uninitialized_value(); + explicit StoreGlobalStub(bool is_constant) { + bit_field_ = IsConstantBits::encode(is_constant); } Handle GetCodeCopyFromTemplate(Isolate* isolate, - GlobalObject* global, + Map* receiver_map, PropertyCell* cell) { Handle code = CodeStub::GetCodeCopyFromTemplate(isolate); - if (check_global()) { - // Replace the placeholder cell and global object map with the actual - // global cell and receiver map. - code->ReplaceNthObject(1, global_placeholder(isolate)->map(), global); - code->ReplaceNthObject(1, isolate->heap()->meta_map(), global->map()); - } + // Replace the placeholder cell and global object map with the actual global + // cell and receiver map. Map* cell_map = isolate->heap()->global_property_cell_map(); code->ReplaceNthObject(1, cell_map, cell); + code->ReplaceNthObject(1, isolate->heap()->meta_map(), receiver_map); return code; } @@ -986,12 +979,11 @@ class StoreGlobalStub : public HandlerStub { Isolate* isolate, CodeStubInterfaceDescriptor* descriptor); - bool is_constant() const { + virtual ExtraICState GetExtraICState() { return bit_field_; } + + bool is_constant() { return IsConstantBits::decode(bit_field_); } - bool check_global() const { - return CheckGlobalBits::decode(bit_field_); - } void set_is_constant(bool value) { bit_field_ = IsConstantBits::update(bit_field_, value); } @@ -1004,11 +996,13 @@ class StoreGlobalStub : public HandlerStub { } private: + virtual int NotMissMinorKey() { return GetExtraICState(); } Major MajorKey() { return StoreGlobal; } class IsConstantBits: public BitField {}; class RepresentationBits: public BitField {}; - class CheckGlobalBits: public BitField {}; + + int bit_field_; DISALLOW_COPY_AND_ASSIGN(StoreGlobalStub); }; @@ -1016,14 +1010,13 @@ class StoreGlobalStub : public HandlerStub { class CallApiFunctionStub : public PlatformCodeStub { public: - CallApiFunctionStub(bool is_store, + CallApiFunctionStub(bool restore_context, bool call_data_undefined, int argc) { bit_field_ = - IsStoreBits::encode(is_store) | + RestoreContextBits::encode(restore_context) | CallDataUndefinedBits::encode(call_data_undefined) | ArgumentBits::encode(argc); - ASSERT(!is_store || argc == 1); } private: @@ -1031,7 +1024,7 @@ class CallApiFunctionStub : public PlatformCodeStub { virtual Major MajorKey() V8_OVERRIDE { return CallApiFunction; } virtual int MinorKey() V8_OVERRIDE { return bit_field_; } - class IsStoreBits: public BitField {}; + class RestoreContextBits: public BitField {}; class CallDataUndefinedBits: public BitField {}; class ArgumentBits: public BitField {}; @@ -1873,21 +1866,23 @@ class DoubleToIStub : public PlatformCodeStub { int offset, bool is_truncating, bool skip_fastpath = false) : bit_field_(0) { - bit_field_ = SourceRegisterBits::encode(source.code()) | - DestinationRegisterBits::encode(destination.code()) | + bit_field_ = SourceRegisterBits::encode(source.code_) | + DestinationRegisterBits::encode(destination.code_) | OffsetBits::encode(offset) | IsTruncatingBits::encode(is_truncating) | SkipFastPathBits::encode(skip_fastpath) | SSEBits::encode(CpuFeatures::IsSafeForSnapshot(SSE2) ? - CpuFeatures::IsSafeForSnapshot(SSE3) ? 2 : 1 : 0); + CpuFeatures::IsSafeForSnapshot(SSE3) ? 2 : 1 : 0); } Register source() { - return Register::from_code(SourceRegisterBits::decode(bit_field_)); + Register result = { SourceRegisterBits::decode(bit_field_) }; + return result; } Register destination() { - return Register::from_code(DestinationRegisterBits::decode(bit_field_)); + Register result = { DestinationRegisterBits::decode(bit_field_) }; + return result; } bool is_truncating() { diff --git a/deps/v8/src/codegen.cc b/deps/v8/src/codegen.cc index f6c36682de..13ce2218df 100644 --- a/deps/v8/src/codegen.cc +++ b/deps/v8/src/codegen.cc @@ -165,8 +165,6 @@ void CodeGenerator::PrintCode(Handle code, CompilationInfo* info) { function->debug_name()->ToCString().get(), tracing_scope.file()); } PrintF(tracing_scope.file(), "--- Optimized code ---\n"); - PrintF(tracing_scope.file(), - "optimization_id = %d\n", info->optimization_id()); } else { PrintF(tracing_scope.file(), "--- Code ---\n"); } diff --git a/deps/v8/src/codegen.h b/deps/v8/src/codegen.h index be76de8aea..8bd4302662 100644 --- a/deps/v8/src/codegen.h +++ b/deps/v8/src/codegen.h @@ -72,8 +72,6 @@ enum TypeofState { INSIDE_TYPEOF, NOT_INSIDE_TYPEOF }; #include "ia32/codegen-ia32.h" #elif V8_TARGET_ARCH_X64 #include "x64/codegen-x64.h" -#elif V8_TARGET_ARCH_A64 -#include "a64/codegen-a64.h" #elif V8_TARGET_ARCH_ARM #include "arm/codegen-arm.h" #elif V8_TARGET_ARCH_MIPS diff --git a/deps/v8/src/compiler.cc b/deps/v8/src/compiler.cc index d466778069..b9e13c1661 100644 --- a/deps/v8/src/compiler.cc +++ b/deps/v8/src/compiler.cc @@ -60,8 +60,7 @@ CompilationInfo::CompilationInfo(Handle