diff -Nur qtwebengine-everywhere-src-5.10.0/examples/webengine/customdialogs/customdialogs.pro qtwebengine-everywhere-src-5.10.0-no-sse2/examples/webengine/customdialogs/customdialogs.pro --- qtwebengine-everywhere-src-5.10.0/examples/webengine/customdialogs/customdialogs.pro 2017-11-29 09:42:29.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/examples/webengine/customdialogs/customdialogs.pro 2017-12-30 00:42:17.069800971 +0100 @@ -1,5 +1,7 @@ QT += webengine +QMAKE_LFLAGS += -Wl,-rpath-link,$$OUT_PWD/../../../src/core/release + HEADERS += \ server.h diff -Nur qtwebengine-everywhere-src-5.10.0/examples/webengine/minimal/minimal.pro qtwebengine-everywhere-src-5.10.0-no-sse2/examples/webengine/minimal/minimal.pro --- qtwebengine-everywhere-src-5.10.0/examples/webengine/minimal/minimal.pro 2017-11-29 09:42:29.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/examples/webengine/minimal/minimal.pro 2017-12-30 00:42:00.003067640 +0100 @@ -2,6 +2,8 @@ QT += webengine +QMAKE_LFLAGS += -Wl,-rpath-link,$$OUT_PWD/../../../src/core/release + SOURCES += main.cpp RESOURCES += qml.qrc diff -Nur qtwebengine-everywhere-src-5.10.0/examples/webengine/quicknanobrowser/quicknanobrowser.pro qtwebengine-everywhere-src-5.10.0-no-sse2/examples/webengine/quicknanobrowser/quicknanobrowser.pro --- qtwebengine-everywhere-src-5.10.0/examples/webengine/quicknanobrowser/quicknanobrowser.pro 2017-11-29 09:42:29.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/examples/webengine/quicknanobrowser/quicknanobrowser.pro 2017-12-30 00:42:06.126971953 +0100 @@ -20,5 +20,7 @@ QT += widgets # QApplication is required to get native styling with QtQuickControls } +QMAKE_LFLAGS += -Wl,-rpath-link,$$OUT_PWD/../../../src/core/release + target.path = $$[QT_INSTALL_EXAMPLES]/webengine/quicknanobrowser INSTALLS += target diff -Nur qtwebengine-everywhere-src-5.10.0/examples/webengine/recipebrowser/recipebrowser.pro qtwebengine-everywhere-src-5.10.0-no-sse2/examples/webengine/recipebrowser/recipebrowser.pro --- qtwebengine-everywhere-src-5.10.0/examples/webengine/recipebrowser/recipebrowser.pro 2017-11-29 09:42:29.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/examples/webengine/recipebrowser/recipebrowser.pro 2017-12-30 00:42:11.598886454 +0100 @@ -2,6 +2,8 @@ QT += quick qml quickcontrols2 webengine +QMAKE_LFLAGS += -Wl,-rpath-link,$$OUT_PWD/../../../src/core/release + cross_compile { posix|qnx|linux: DEFINES += QTWEBENGINE_RECIPE_BROWSER_EMBEDDED } diff -Nur qtwebengine-everywhere-src-5.10.0/examples/webenginewidgets/contentmanipulation/contentmanipulation.pro qtwebengine-everywhere-src-5.10.0-no-sse2/examples/webenginewidgets/contentmanipulation/contentmanipulation.pro --- qtwebengine-everywhere-src-5.10.0/examples/webenginewidgets/contentmanipulation/contentmanipulation.pro 2017-11-29 09:42:29.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/examples/webenginewidgets/contentmanipulation/contentmanipulation.pro 2017-12-30 00:41:54.676150874 +0100 @@ -1,5 +1,7 @@ QT += webenginewidgets +QMAKE_LFLAGS += -Wl,-rpath-link,$$OUT_PWD/../../../src/core/release + HEADERS = mainwindow.h SOURCES = main.cpp \ mainwindow.cpp diff -Nur qtwebengine-everywhere-src-5.10.0/examples/webenginewidgets/cookiebrowser/cookiebrowser.pro qtwebengine-everywhere-src-5.10.0-no-sse2/examples/webenginewidgets/cookiebrowser/cookiebrowser.pro --- qtwebengine-everywhere-src-5.10.0/examples/webenginewidgets/cookiebrowser/cookiebrowser.pro 2017-11-29 09:42:29.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/examples/webenginewidgets/cookiebrowser/cookiebrowser.pro 2017-12-30 00:39:17.808601947 +0100 @@ -3,6 +3,8 @@ TEMPLATE = app CONFIG += c++11 +QMAKE_LFLAGS += -Wl,-rpath-link,$$OUT_PWD/../../../src/core/release + SOURCES += \ main.cpp\ mainwindow.cpp diff -Nur qtwebengine-everywhere-src-5.10.0/examples/webenginewidgets/html2pdf/html2pdf.pro qtwebengine-everywhere-src-5.10.0-no-sse2/examples/webenginewidgets/html2pdf/html2pdf.pro --- qtwebengine-everywhere-src-5.10.0/examples/webenginewidgets/html2pdf/html2pdf.pro 2017-11-29 09:42:29.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/examples/webenginewidgets/html2pdf/html2pdf.pro 2017-12-30 00:39:29.007426964 +0100 @@ -2,6 +2,8 @@ QT += webenginewidgets +QMAKE_LFLAGS += -Wl,-rpath-link,$$OUT_PWD/../../../src/core/release + SOURCES += html2pdf.cpp target.path = $$[QT_INSTALL_EXAMPLES]/webenginewidgets/html2pdf diff -Nur qtwebengine-everywhere-src-5.10.0/examples/webenginewidgets/maps/maps.pro qtwebengine-everywhere-src-5.10.0-no-sse2/examples/webenginewidgets/maps/maps.pro --- qtwebengine-everywhere-src-5.10.0/examples/webenginewidgets/maps/maps.pro 2017-11-29 09:42:29.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/examples/webenginewidgets/maps/maps.pro 2017-12-30 00:39:43.928193826 +0100 @@ -2,6 +2,8 @@ QT += webenginewidgets +QMAKE_LFLAGS += -Wl,-rpath-link,$$OUT_PWD/../../../src/core/release + HEADERS += \ mainwindow.h diff -Nur qtwebengine-everywhere-src-5.10.0/examples/webenginewidgets/markdowneditor/markdowneditor.pro qtwebengine-everywhere-src-5.10.0-no-sse2/examples/webenginewidgets/markdowneditor/markdowneditor.pro --- qtwebengine-everywhere-src-5.10.0/examples/webenginewidgets/markdowneditor/markdowneditor.pro 2017-11-29 09:42:29.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/examples/webenginewidgets/markdowneditor/markdowneditor.pro 2017-12-30 00:41:37.256423059 +0100 @@ -3,6 +3,8 @@ QT += webenginewidgets webchannel CONFIG += c++11 +QMAKE_LFLAGS += -Wl,-rpath-link,$$OUT_PWD/../../../src/core/release + HEADERS += \ mainwindow.h \ previewpage.h \ diff -Nur qtwebengine-everywhere-src-5.10.0/examples/webenginewidgets/minimal/minimal.pro qtwebengine-everywhere-src-5.10.0-no-sse2/examples/webenginewidgets/minimal/minimal.pro --- qtwebengine-everywhere-src-5.10.0/examples/webenginewidgets/minimal/minimal.pro 2017-11-29 09:42:29.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/examples/webenginewidgets/minimal/minimal.pro 2017-12-30 00:39:11.992692822 +0100 @@ -2,6 +2,8 @@ QT += webenginewidgets +QMAKE_LFLAGS += -Wl,-rpath-link,$$OUT_PWD/../../../src/core/release + SOURCES += main.cpp target.path = $$[QT_INSTALL_EXAMPLES]/webenginewidgets/minimal diff -Nur qtwebengine-everywhere-src-5.10.0/examples/webenginewidgets/simplebrowser/simplebrowser.pro qtwebengine-everywhere-src-5.10.0-no-sse2/examples/webenginewidgets/simplebrowser/simplebrowser.pro --- qtwebengine-everywhere-src-5.10.0/examples/webenginewidgets/simplebrowser/simplebrowser.pro 2017-11-29 09:42:29.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/examples/webenginewidgets/simplebrowser/simplebrowser.pro 2017-12-30 00:39:37.644292012 +0100 @@ -3,6 +3,8 @@ QT += webenginewidgets CONFIG += c++11 +QMAKE_LFLAGS += -Wl,-rpath-link,$$OUT_PWD/../../../src/core/release + HEADERS += \ browser.h \ browserwindow.h \ diff -Nur qtwebengine-everywhere-src-5.10.0/examples/webenginewidgets/spellchecker/spellchecker.pro qtwebengine-everywhere-src-5.10.0-no-sse2/examples/webenginewidgets/spellchecker/spellchecker.pro --- qtwebengine-everywhere-src-5.10.0/examples/webenginewidgets/spellchecker/spellchecker.pro 2017-11-29 09:42:29.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/examples/webenginewidgets/spellchecker/spellchecker.pro 2017-12-30 00:41:43.565324482 +0100 @@ -9,6 +9,8 @@ error("Spellcheck example can not be built when using native OS dictionaries.") } +QMAKE_LFLAGS += -Wl,-rpath-link,$$OUT_PWD/../../../src/core/release + HEADERS += \ webview.h diff -Nur qtwebengine-everywhere-src-5.10.0/examples/webenginewidgets/stylesheetbrowser/stylesheetbrowser.pro qtwebengine-everywhere-src-5.10.0-no-sse2/examples/webenginewidgets/stylesheetbrowser/stylesheetbrowser.pro --- qtwebengine-everywhere-src-5.10.0/examples/webenginewidgets/stylesheetbrowser/stylesheetbrowser.pro 2017-11-29 09:42:29.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/examples/webenginewidgets/stylesheetbrowser/stylesheetbrowser.pro 2017-12-30 00:41:49.055238701 +0100 @@ -3,6 +3,8 @@ QT += webenginewidgets CONFIG += c++11 +QMAKE_LFLAGS += -Wl,-rpath-link,$$OUT_PWD/../../../src/core/release + HEADERS += \ mainwindow.h \ stylesheetdialog.h diff -Nur qtwebengine-everywhere-src-5.10.0/examples/webenginewidgets/videoplayer/videoplayer.pro qtwebengine-everywhere-src-5.10.0-no-sse2/examples/webenginewidgets/videoplayer/videoplayer.pro --- qtwebengine-everywhere-src-5.10.0/examples/webenginewidgets/videoplayer/videoplayer.pro 2017-11-29 09:42:29.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/examples/webenginewidgets/videoplayer/videoplayer.pro 2017-12-30 00:39:23.960505823 +0100 @@ -2,6 +2,8 @@ QT += webenginewidgets +QMAKE_LFLAGS += -Wl,-rpath-link,$$OUT_PWD/../../../src/core/release + HEADERS += \ mainwindow.h \ fullscreenwindow.h \ diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/build/config/compiler/BUILD.gn qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/build/config/compiler/BUILD.gn --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/build/config/compiler/BUILD.gn 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/build/config/compiler/BUILD.gn 2017-12-25 12:49:16.315356585 +0100 @@ -600,13 +600,6 @@ } else if (current_cpu == "x86") { cflags += [ "-m32" ] ldflags += [ "-m32" ] - if (!is_nacl) { - cflags += [ - "-msse2", - "-mfpmath=sse", - "-mmmx", - ] - } } else if (current_cpu == "arm") { if (is_clang && !is_android && !is_nacl) { cflags += [ "--target=arm-linux-gnueabihf" ] diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/build/config/v8_target_cpu.gni qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/build/config/v8_target_cpu.gni --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/build/config/v8_target_cpu.gni 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/build/config/v8_target_cpu.gni 2017-12-25 12:49:16.315356585 +0100 @@ -59,3 +59,11 @@ # It should never be explicitly set by the user. v8_current_cpu = v8_target_cpu } + +if (v8_current_cpu == "x86") { + # If we are not building for the x86_sse2 toolchain, we actually want to build + # the "x87" backend instead. + if (current_toolchain != "//build/toolchain/linux:x86_sse2") { + v8_current_cpu = "x87" + } +} diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/build/toolchain/gcc_toolchain.gni qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/build/toolchain/gcc_toolchain.gni --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/build/toolchain/gcc_toolchain.gni 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/build/toolchain/gcc_toolchain.gni 2017-12-25 12:49:16.454354623 +0100 @@ -266,6 +266,10 @@ enable_linker_map = defined(invoker.enable_linker_map) && invoker.enable_linker_map && generate_linker_map + if (defined(invoker.shlib_subdir)) { + shlib_subdir = invoker.shlib_subdir + } + # These library switches can apply to all tools below. lib_switch = "-l" lib_dir_switch = "-L" diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/build/toolchain/linux/BUILD.gn qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/build/toolchain/linux/BUILD.gn --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/build/toolchain/linux/BUILD.gn 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/build/toolchain/linux/BUILD.gn 2017-12-25 12:49:16.454354623 +0100 @@ -110,6 +110,26 @@ } } +gcc_toolchain("x86_sse2") { + cc = "gcc" + cxx = "g++" + + readelf = "readelf" + nm = "nm" + ar = "ar" + ld = cxx + + extra_cflags = "-msse2 -mfpmath=sse" + extra_cxxflags = "-msse2 -mfpmath=sse" + shlib_subdir = "lib/sse2" + + toolchain_args = { + current_cpu = "x86" + current_os = "linux" + is_clang = false + } +} + clang_toolchain("clang_x64") { # Output linker map files for binary size analysis. enable_linker_map = true diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/cc/base/math_util.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/cc/base/math_util.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/cc/base/math_util.cc 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/cc/base/math_util.cc 2017-12-26 23:04:53.301868189 +0100 @@ -7,7 +7,7 @@ #include #include #include -#if defined(ARCH_CPU_X86_FAMILY) +#ifdef __SSE__ #include #endif @@ -810,7 +810,7 @@ } ScopedSubnormalFloatDisabler::ScopedSubnormalFloatDisabler() { -#if defined(ARCH_CPU_X86_FAMILY) +#ifdef __SSE__ // Turn on "subnormals are zero" and "flush to zero" CSR flags. orig_state_ = _mm_getcsr(); _mm_setcsr(orig_state_ | 0x8040); @@ -818,7 +818,7 @@ } ScopedSubnormalFloatDisabler::~ScopedSubnormalFloatDisabler() { -#if defined(ARCH_CPU_X86_FAMILY) +#ifdef __SSE__ _mm_setcsr(orig_state_); #endif } diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/cc/base/math_util.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/cc/base/math_util.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/cc/base/math_util.h 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/cc/base/math_util.h 2017-12-26 23:04:53.301868189 +0100 @@ -11,7 +11,6 @@ #include #include "base/logging.h" -#include "build/build_config.h" #include "cc/base/base_export.h" #include "ui/gfx/geometry/box_f.h" #include "ui/gfx/geometry/point3_f.h" @@ -331,7 +330,7 @@ ~ScopedSubnormalFloatDisabler(); private: -#if defined(ARCH_CPU_X86_FAMILY) +#ifdef __SSE__ unsigned int orig_state_; #endif DISALLOW_COPY_AND_ASSIGN(ScopedSubnormalFloatDisabler); diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/cc/BUILD.gn qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/cc/BUILD.gn --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/cc/BUILD.gn 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/cc/BUILD.gn 2017-12-25 13:16:20.896994372 +0100 @@ -445,13 +445,6 @@ "trees/tree_synchronizer.h", ] - if (current_cpu == "x86" || current_cpu == "x64") { - sources += [ - "raster/texture_compressor_etc1_sse.cc", - "raster/texture_compressor_etc1_sse.h", - ] - } - # TODO(khushalsagar): Remove once crbug.com/683263 is fixed. configs = [ "//build/config/compiler:no_size_t_to_int_warning" ] @@ -463,6 +456,7 @@ deps = [ "//base", "//base/third_party/dynamic_annotations", + "//cc:cc_opts", "//cc/paint", "//components/viz/common", "//gpu", @@ -493,6 +487,36 @@ } } +source_set("cc_opts") { + public_deps = [ + "//cc:cc_opts_sse", + ] +} + +source_set("cc_opts_sse") { + if (current_cpu == "x86" || current_cpu == "x64") { + deps = [ + "//base", + ] + + defines = [ "CC_IMPLEMENTATION=1" ] + + if (!is_debug && (is_win || is_android)) { + configs -= [ "//build/config/compiler:optimize" ] + configs += [ "//build/config/compiler:optimize_max" ] + } + + sources = [ + "raster/texture_compressor.h", + "raster/texture_compressor_etc1.h", + "raster/texture_compressor_etc1_sse.cc", + "raster/texture_compressor_etc1_sse.h", + ] + + cflags = [ "-msse2" ] + } +} + cc_static_library("test_support") { testonly = true sources = [ diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/content/renderer/BUILD.gn qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/content/renderer/BUILD.gn --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/content/renderer/BUILD.gn 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/content/renderer/BUILD.gn 2017-12-25 12:49:16.454354623 +0100 @@ -514,6 +514,13 @@ "//ui/surface", "//v8", ] + + if (current_cpu == "x86") { + deps += [ + "//v8(//build/toolchain/linux:x86_sse2)", + ] + } + allow_circular_includes_from = [] if (use_aura && !use_qt) { diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/media/base/BUILD.gn qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/media/base/BUILD.gn --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/media/base/BUILD.gn 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/media/base/BUILD.gn 2017-12-25 13:30:14.473844548 +0100 @@ -344,6 +344,12 @@ defines += [ "DISABLE_USER_INPUT_MONITOR" ] } + if (current_cpu == "x86" || current_cpu == "x64") { + deps += [ + ":media_sse", + ] + } + if (is_linux || is_win) { sources += [ "keyboard_event_counter.cc", @@ -366,6 +372,21 @@ ] } +if (current_cpu == "x86" || current_cpu == "x64") { + source_set("media_sse") { + sources = [ + "sinc_resampler_sse.cc", + ] + configs += [ + "//media:media_config", + "//media:media_implementation", + ] + if (!is_win) { + cflags = [ "-msse" ] + } + } +} + if (is_android) { java_cpp_enum("java_enums") { sources = [ diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/media/base/media.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/media/base/media.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/media/base/media.cc 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/media/base/media.cc 2017-12-25 13:32:19.234052101 +0100 @@ -10,6 +10,8 @@ #include "base/metrics/field_trial.h" #include "base/trace_event/trace_event.h" #include "media/base/media_switches.h" +#include "media/base/sinc_resampler.h" +#include "media/base/vector_math.h" #include "third_party/libyuv/include/libyuv.h" #if defined(OS_ANDROID) @@ -30,6 +32,9 @@ TRACE_EVENT_WARMUP_CATEGORY("audio"); TRACE_EVENT_WARMUP_CATEGORY("media"); + // Perform initialization of libraries which require runtime CPU detection. + vector_math::Initialize(); + SincResampler::InitializeCPUSpecificFeatures(); libyuv::InitCpuFlags(); #if !defined(MEDIA_DISABLE_FFMPEG) diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/media/base/sinc_resampler.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/media/base/sinc_resampler.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/media/base/sinc_resampler.cc 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/media/base/sinc_resampler.cc 2017-12-25 12:57:58.624478849 +0100 @@ -81,17 +81,12 @@ #include #include +#include "base/cpu.h" #include "base/logging.h" #include "build/build_config.h" -#if defined(ARCH_CPU_X86_FAMILY) -#include -#define CONVOLVE_FUNC Convolve_SSE -#elif defined(ARCH_CPU_ARM_FAMILY) && defined(USE_NEON) +#if defined(ARCH_CPU_ARM_FAMILY) && defined(USE_NEON) #include -#define CONVOLVE_FUNC Convolve_NEON -#else -#define CONVOLVE_FUNC Convolve_C #endif namespace media { @@ -112,10 +107,41 @@ return sinc_scale_factor; } +#undef CONVOLVE_FUNC + static int CalculateChunkSize(int block_size_, double io_ratio) { return block_size_ / io_ratio; } +// If we know the minimum architecture at compile time, avoid CPU detection. +// Force NaCl code to use C routines since (at present) nothing there uses these +// methods and plumbing the -msse built library is non-trivial. +#if defined(ARCH_CPU_X86_FAMILY) && !defined(OS_NACL) +#if defined(__SSE__) +#define CONVOLVE_FUNC Convolve_SSE +void SincResampler::InitializeCPUSpecificFeatures() {} +#else +// X86 CPU detection required. Functions will be set by +// InitializeCPUSpecificFeatures(). +#define CONVOLVE_FUNC g_convolve_proc_ + +typedef float (*ConvolveProc)(const float*, const float*, const float*, double); +static ConvolveProc g_convolve_proc_ = NULL; + +void SincResampler::InitializeCPUSpecificFeatures() { + CHECK(!g_convolve_proc_); + g_convolve_proc_ = base::CPU().has_sse() ? Convolve_SSE : Convolve_C; +} +#endif +#elif defined(ARCH_CPU_ARM_FAMILY) && defined(USE_NEON) +#define CONVOLVE_FUNC Convolve_NEON +void SincResampler::InitializeCPUSpecificFeatures() {} +#else +// Unknown architecture. +#define CONVOLVE_FUNC Convolve_C +void SincResampler::InitializeCPUSpecificFeatures() {} +#endif + SincResampler::SincResampler(double io_sample_rate_ratio, int request_frames, const ReadCB& read_cb) @@ -328,46 +354,7 @@ kernel_interpolation_factor * sum2); } -#if defined(ARCH_CPU_X86_FAMILY) -float SincResampler::Convolve_SSE(const float* input_ptr, const float* k1, - const float* k2, - double kernel_interpolation_factor) { - __m128 m_input; - __m128 m_sums1 = _mm_setzero_ps(); - __m128 m_sums2 = _mm_setzero_ps(); - - // Based on |input_ptr| alignment, we need to use loadu or load. Unrolling - // these loops hurt performance in local testing. - if (reinterpret_cast(input_ptr) & 0x0F) { - for (int i = 0; i < kKernelSize; i += 4) { - m_input = _mm_loadu_ps(input_ptr + i); - m_sums1 = _mm_add_ps(m_sums1, _mm_mul_ps(m_input, _mm_load_ps(k1 + i))); - m_sums2 = _mm_add_ps(m_sums2, _mm_mul_ps(m_input, _mm_load_ps(k2 + i))); - } - } else { - for (int i = 0; i < kKernelSize; i += 4) { - m_input = _mm_load_ps(input_ptr + i); - m_sums1 = _mm_add_ps(m_sums1, _mm_mul_ps(m_input, _mm_load_ps(k1 + i))); - m_sums2 = _mm_add_ps(m_sums2, _mm_mul_ps(m_input, _mm_load_ps(k2 + i))); - } - } - - // Linearly interpolate the two "convolutions". - m_sums1 = _mm_mul_ps(m_sums1, _mm_set_ps1( - static_cast(1.0 - kernel_interpolation_factor))); - m_sums2 = _mm_mul_ps(m_sums2, _mm_set_ps1( - static_cast(kernel_interpolation_factor))); - m_sums1 = _mm_add_ps(m_sums1, m_sums2); - - // Sum components together. - float result; - m_sums2 = _mm_add_ps(_mm_movehl_ps(m_sums1, m_sums1), m_sums1); - _mm_store_ss(&result, _mm_add_ss(m_sums2, _mm_shuffle_ps( - m_sums2, m_sums2, 1))); - - return result; -} -#elif defined(ARCH_CPU_ARM_FAMILY) && defined(USE_NEON) +#if defined(ARCH_CPU_ARM_FAMILY) && defined(USE_NEON) float SincResampler::Convolve_NEON(const float* input_ptr, const float* k1, const float* k2, double kernel_interpolation_factor) { diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/media/base/sinc_resampler.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/media/base/sinc_resampler.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/media/base/sinc_resampler.h 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/media/base/sinc_resampler.h 2017-12-25 12:57:58.798476686 +0100 @@ -36,6 +36,10 @@ kKernelStorageSize = kKernelSize * (kKernelOffsetCount + 1), }; + // Selects runtime specific CPU features like SSE. Must be called before + // using SincResampler. + static void InitializeCPUSpecificFeatures(); + // Callback type for providing more data into the resampler. Expects |frames| // of data to be rendered into |destination|; zero padded if not enough frames // are available to satisfy the request. diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/media/base/sinc_resampler_perftest.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/media/base/sinc_resampler_perftest.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/media/base/sinc_resampler_perftest.cc 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/media/base/sinc_resampler_perftest.cc 2017-12-25 12:57:58.798476686 +0100 @@ -4,6 +4,7 @@ #include "base/bind.h" #include "base/bind_helpers.h" +#include "base/cpu.h" #include "base/time/time.h" #include "build/build_config.h" #include "media/base/sinc_resampler.h" @@ -61,6 +62,9 @@ &resampler, SincResampler::Convolve_C, true, "unoptimized_aligned"); #if defined(CONVOLVE_FUNC) +#if defined(ARCH_CPU_X86_FAMILY) + ASSERT_TRUE(base::CPU().has_sse()); +#endif RunConvolveBenchmark( &resampler, SincResampler::CONVOLVE_FUNC, true, "optimized_aligned"); RunConvolveBenchmark( diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/media/base/sinc_resampler_sse.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/media/base/sinc_resampler_sse.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/media/base/sinc_resampler_sse.cc 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/media/base/sinc_resampler_sse.cc 2017-07-01 03:36:35.000000000 +0200 @@ -0,0 +1,50 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/base/sinc_resampler.h" + +#include + +namespace media { + +float SincResampler::Convolve_SSE(const float* input_ptr, const float* k1, + const float* k2, + double kernel_interpolation_factor) { + __m128 m_input; + __m128 m_sums1 = _mm_setzero_ps(); + __m128 m_sums2 = _mm_setzero_ps(); + + // Based on |input_ptr| alignment, we need to use loadu or load. Unrolling + // these loops hurt performance in local testing. + if (reinterpret_cast(input_ptr) & 0x0F) { + for (int i = 0; i < kKernelSize; i += 4) { + m_input = _mm_loadu_ps(input_ptr + i); + m_sums1 = _mm_add_ps(m_sums1, _mm_mul_ps(m_input, _mm_load_ps(k1 + i))); + m_sums2 = _mm_add_ps(m_sums2, _mm_mul_ps(m_input, _mm_load_ps(k2 + i))); + } + } else { + for (int i = 0; i < kKernelSize; i += 4) { + m_input = _mm_load_ps(input_ptr + i); + m_sums1 = _mm_add_ps(m_sums1, _mm_mul_ps(m_input, _mm_load_ps(k1 + i))); + m_sums2 = _mm_add_ps(m_sums2, _mm_mul_ps(m_input, _mm_load_ps(k2 + i))); + } + } + + // Linearly interpolate the two "convolutions". + m_sums1 = _mm_mul_ps(m_sums1, _mm_set_ps1( + static_cast(1.0 - kernel_interpolation_factor))); + m_sums2 = _mm_mul_ps(m_sums2, _mm_set_ps1( + static_cast(kernel_interpolation_factor))); + m_sums1 = _mm_add_ps(m_sums1, m_sums2); + + // Sum components together. + float result; + m_sums2 = _mm_add_ps(_mm_movehl_ps(m_sums1, m_sums1), m_sums1); + _mm_store_ss(&result, _mm_add_ss(m_sums2, _mm_shuffle_ps( + m_sums2, m_sums2, 1))); + + return result; +} + +} // namespace media diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/media/base/sinc_resampler_unittest.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/media/base/sinc_resampler_unittest.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/media/base/sinc_resampler_unittest.cc 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/media/base/sinc_resampler_unittest.cc 2017-12-25 12:57:58.798476686 +0100 @@ -10,6 +10,7 @@ #include "base/bind.h" #include "base/bind_helpers.h" +#include "base/cpu.h" #include "base/macros.h" #include "base/strings/string_number_conversions.h" #include "base/time/time.h" @@ -166,6 +167,10 @@ static const double kKernelInterpolationFactor = 0.5; TEST(SincResamplerTest, Convolve) { +#if defined(ARCH_CPU_X86_FAMILY) + ASSERT_TRUE(base::CPU().has_sse()); +#endif + // Initialize a dummy resampler. MockSource mock_source; SincResampler resampler( diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/media/base/vector_math.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/media/base/vector_math.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/media/base/vector_math.cc 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/media/base/vector_math.cc 2017-12-25 12:57:58.799476673 +0100 @@ -7,12 +7,17 @@ #include +#include "base/cpu.h" #include "base/logging.h" #include "build/build_config.h" +namespace media { +namespace vector_math { + +// If we know the minimum architecture at compile time, avoid CPU detection. // NaCl does not allow intrinsics. #if defined(ARCH_CPU_X86_FAMILY) && !defined(OS_NACL) -#include +#if defined(__SSE__) // Don't use custom SSE versions where the auto-vectorized C version performs // better, which is anywhere clang is used. // TODO(pcc): Linux currently uses ThinLTO which has broken auto-vectorization @@ -25,20 +30,52 @@ #define FMUL_FUNC FMUL_C #endif #define EWMAAndMaxPower_FUNC EWMAAndMaxPower_SSE +void Initialize() {} +#else +// X86 CPU detection required. Functions will be set by Initialize(). +#if !defined(__clang__) +#define FMAC_FUNC g_fmac_proc_ +#define FMUL_FUNC g_fmul_proc_ +#else +#define FMAC_FUNC FMAC_C +#define FMUL_FUNC FMUL_C +#endif +#define EWMAAndMaxPower_FUNC g_ewma_power_proc_ + +#if !defined(__clang__) +typedef void (*MathProc)(const float src[], float scale, int len, float dest[]); +static MathProc g_fmac_proc_ = NULL; +static MathProc g_fmul_proc_ = NULL; +#endif +typedef std::pair (*EWMAAndMaxPowerProc)( + float initial_value, const float src[], int len, float smoothing_factor); +static EWMAAndMaxPowerProc g_ewma_power_proc_ = NULL; + +void Initialize() { + CHECK(!g_fmac_proc_); + CHECK(!g_fmul_proc_); + CHECK(!g_ewma_power_proc_); + const bool kUseSSE = base::CPU().has_sse(); +#if !defined(__clang__) + g_fmac_proc_ = kUseSSE ? FMAC_SSE : FMAC_C; + g_fmul_proc_ = kUseSSE ? FMUL_SSE : FMUL_C; +#endif + g_ewma_power_proc_ = kUseSSE ? EWMAAndMaxPower_SSE : EWMAAndMaxPower_C; +} +#endif #elif defined(ARCH_CPU_ARM_FAMILY) && defined(USE_NEON) #include #define FMAC_FUNC FMAC_NEON #define FMUL_FUNC FMUL_NEON #define EWMAAndMaxPower_FUNC EWMAAndMaxPower_NEON +void Initialize() {} #else #define FMAC_FUNC FMAC_C #define FMUL_FUNC FMUL_C #define EWMAAndMaxPower_FUNC EWMAAndMaxPower_C +void Initialize() {} #endif -namespace media { -namespace vector_math { - void FMAC(const float src[], float scale, int len, float dest[]) { // Ensure |src| and |dest| are 16-byte aligned. DCHECK_EQ(0u, reinterpret_cast(src) & (kRequiredAlignment - 1)); @@ -91,111 +128,6 @@ return result; } -#if defined(ARCH_CPU_X86_FAMILY) && !defined(OS_NACL) -void FMUL_SSE(const float src[], float scale, int len, float dest[]) { - const int rem = len % 4; - const int last_index = len - rem; - __m128 m_scale = _mm_set_ps1(scale); - for (int i = 0; i < last_index; i += 4) - _mm_store_ps(dest + i, _mm_mul_ps(_mm_load_ps(src + i), m_scale)); - - // Handle any remaining values that wouldn't fit in an SSE pass. - for (int i = last_index; i < len; ++i) - dest[i] = src[i] * scale; -} - -void FMAC_SSE(const float src[], float scale, int len, float dest[]) { - const int rem = len % 4; - const int last_index = len - rem; - __m128 m_scale = _mm_set_ps1(scale); - for (int i = 0; i < last_index; i += 4) { - _mm_store_ps(dest + i, _mm_add_ps(_mm_load_ps(dest + i), - _mm_mul_ps(_mm_load_ps(src + i), m_scale))); - } - - // Handle any remaining values that wouldn't fit in an SSE pass. - for (int i = last_index; i < len; ++i) - dest[i] += src[i] * scale; -} - -// Convenience macro to extract float 0 through 3 from the vector |a|. This is -// needed because compilers other than clang don't support access via -// operator[](). -#define EXTRACT_FLOAT(a, i) \ - (i == 0 ? \ - _mm_cvtss_f32(a) : \ - _mm_cvtss_f32(_mm_shuffle_ps(a, a, i))) - -std::pair EWMAAndMaxPower_SSE( - float initial_value, const float src[], int len, float smoothing_factor) { - // When the recurrence is unrolled, we see that we can split it into 4 - // separate lanes of evaluation: - // - // y[n] = a(S[n]^2) + (1-a)(y[n-1]) - // = a(S[n]^2) + (1-a)^1(aS[n-1]^2) + (1-a)^2(aS[n-2]^2) + ... - // = z[n] + (1-a)^1(z[n-1]) + (1-a)^2(z[n-2]) + (1-a)^3(z[n-3]) - // - // where z[n] = a(S[n]^2) + (1-a)^4(z[n-4]) + (1-a)^8(z[n-8]) + ... - // - // Thus, the strategy here is to compute z[n], z[n-1], z[n-2], and z[n-3] in - // each of the 4 lanes, and then combine them to give y[n]. - - const int rem = len % 4; - const int last_index = len - rem; - - const __m128 smoothing_factor_x4 = _mm_set_ps1(smoothing_factor); - const float weight_prev = 1.0f - smoothing_factor; - const __m128 weight_prev_x4 = _mm_set_ps1(weight_prev); - const __m128 weight_prev_squared_x4 = - _mm_mul_ps(weight_prev_x4, weight_prev_x4); - const __m128 weight_prev_4th_x4 = - _mm_mul_ps(weight_prev_squared_x4, weight_prev_squared_x4); - - // Compute z[n], z[n-1], z[n-2], and z[n-3] in parallel in lanes 3, 2, 1 and - // 0, respectively. - __m128 max_x4 = _mm_setzero_ps(); - __m128 ewma_x4 = _mm_setr_ps(0.0f, 0.0f, 0.0f, initial_value); - int i; - for (i = 0; i < last_index; i += 4) { - ewma_x4 = _mm_mul_ps(ewma_x4, weight_prev_4th_x4); - const __m128 sample_x4 = _mm_load_ps(src + i); - const __m128 sample_squared_x4 = _mm_mul_ps(sample_x4, sample_x4); - max_x4 = _mm_max_ps(max_x4, sample_squared_x4); - // Note: The compiler optimizes this to a single multiply-and-accumulate - // instruction: - ewma_x4 = _mm_add_ps(ewma_x4, - _mm_mul_ps(sample_squared_x4, smoothing_factor_x4)); - } - - // y[n] = z[n] + (1-a)^1(z[n-1]) + (1-a)^2(z[n-2]) + (1-a)^3(z[n-3]) - float ewma = EXTRACT_FLOAT(ewma_x4, 3); - ewma_x4 = _mm_mul_ps(ewma_x4, weight_prev_x4); - ewma += EXTRACT_FLOAT(ewma_x4, 2); - ewma_x4 = _mm_mul_ps(ewma_x4, weight_prev_x4); - ewma += EXTRACT_FLOAT(ewma_x4, 1); - ewma_x4 = _mm_mul_ss(ewma_x4, weight_prev_x4); - ewma += EXTRACT_FLOAT(ewma_x4, 0); - - // Fold the maximums together to get the overall maximum. - max_x4 = _mm_max_ps(max_x4, - _mm_shuffle_ps(max_x4, max_x4, _MM_SHUFFLE(3, 3, 1, 1))); - max_x4 = _mm_max_ss(max_x4, _mm_shuffle_ps(max_x4, max_x4, 2)); - - std::pair result(ewma, EXTRACT_FLOAT(max_x4, 0)); - - // Handle remaining values at the end of |src|. - for (; i < len; ++i) { - result.first *= weight_prev; - const float sample = src[i]; - const float sample_squared = sample * sample; - result.first += sample_squared * smoothing_factor; - result.second = std::max(result.second, sample_squared); - } - - return result; -} -#endif - #if defined(ARCH_CPU_ARM_FAMILY) && defined(USE_NEON) void FMAC_NEON(const float src[], float scale, int len, float dest[]) { const int rem = len % 4; diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/media/base/vector_math.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/media/base/vector_math.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/media/base/vector_math.h 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/media/base/vector_math.h 2017-12-25 12:57:58.799476673 +0100 @@ -15,6 +15,11 @@ // Required alignment for inputs and outputs to all vector math functions enum { kRequiredAlignment = 16 }; +// Selects runtime specific optimizations such as SSE. Must be called prior to +// calling FMAC() or FMUL(). Called during media library initialization; most +// users should never have to call this. +MEDIA_EXPORT void Initialize(); + // Multiply each element of |src| (up to |len|) by |scale| and add to |dest|. // |src| and |dest| must be aligned by kRequiredAlignment. MEDIA_EXPORT void FMAC(const float src[], float scale, int len, float dest[]); diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/media/base/vector_math_perftest.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/media/base/vector_math_perftest.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/media/base/vector_math_perftest.cc 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/media/base/vector_math_perftest.cc 2017-12-25 12:57:58.800476661 +0100 @@ -5,6 +5,7 @@ #include #include "base/macros.h" +#include "base/cpu.h" #include "base/memory/aligned_memory.h" #include "base/time/time.h" #include "build/build_config.h" @@ -82,15 +83,11 @@ DISALLOW_COPY_AND_ASSIGN(VectorMathPerfTest); }; -// Define platform dependent function names for SIMD optimized methods. +// Define platform independent function name for FMAC* perf tests. #if defined(ARCH_CPU_X86_FAMILY) #define FMAC_FUNC FMAC_SSE -#define FMUL_FUNC FMUL_SSE -#define EWMAAndMaxPower_FUNC EWMAAndMaxPower_SSE #elif defined(ARCH_CPU_ARM_FAMILY) && defined(USE_NEON) #define FMAC_FUNC FMAC_NEON -#define FMUL_FUNC FMUL_NEON -#define EWMAAndMaxPower_FUNC EWMAAndMaxPower_NEON #endif // Benchmark for each optimized vector_math::FMAC() method. @@ -99,6 +96,9 @@ RunBenchmark( vector_math::FMAC_C, true, "vector_math_fmac", "unoptimized"); #if defined(FMAC_FUNC) +#if defined(ARCH_CPU_X86_FAMILY) + ASSERT_TRUE(base::CPU().has_sse()); +#endif // Benchmark FMAC_FUNC() with unaligned size. ASSERT_NE((kVectorSize - 1) % (vector_math::kRequiredAlignment / sizeof(float)), 0U); @@ -112,12 +112,24 @@ #endif } +#undef FMAC_FUNC + +// Define platform independent function name for FMULBenchmark* tests. +#if defined(ARCH_CPU_X86_FAMILY) +#define FMUL_FUNC FMUL_SSE +#elif defined(ARCH_CPU_ARM_FAMILY) && defined(USE_NEON) +#define FMUL_FUNC FMUL_NEON +#endif + // Benchmark for each optimized vector_math::FMUL() method. TEST_F(VectorMathPerfTest, FMUL) { // Benchmark FMUL_C(). RunBenchmark( vector_math::FMUL_C, true, "vector_math_fmul", "unoptimized"); #if defined(FMUL_FUNC) +#if defined(ARCH_CPU_X86_FAMILY) + ASSERT_TRUE(base::CPU().has_sse()); +#endif // Benchmark FMUL_FUNC() with unaligned size. ASSERT_NE((kVectorSize - 1) % (vector_math::kRequiredAlignment / sizeof(float)), 0U); @@ -131,6 +143,14 @@ #endif } +#undef FMUL_FUNC + +#if defined(ARCH_CPU_X86_FAMILY) +#define EWMAAndMaxPower_FUNC EWMAAndMaxPower_SSE +#elif defined(ARCH_CPU_ARM_FAMILY) && defined(USE_NEON) +#define EWMAAndMaxPower_FUNC EWMAAndMaxPower_NEON +#endif + // Benchmark for each optimized vector_math::EWMAAndMaxPower() method. TEST_F(VectorMathPerfTest, EWMAAndMaxPower) { // Benchmark EWMAAndMaxPower_C(). @@ -139,6 +159,9 @@ "vector_math_ewma_and_max_power", "unoptimized"); #if defined(EWMAAndMaxPower_FUNC) +#if defined(ARCH_CPU_X86_FAMILY) + ASSERT_TRUE(base::CPU().has_sse()); +#endif // Benchmark EWMAAndMaxPower_FUNC() with unaligned size. ASSERT_NE((kVectorSize - 1) % (vector_math::kRequiredAlignment / sizeof(float)), 0U); @@ -156,4 +179,6 @@ #endif } +#undef EWMAAndMaxPower_FUNC + } // namespace media diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/media/base/vector_math_sse.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/media/base/vector_math_sse.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/media/base/vector_math_sse.cc 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/media/base/vector_math_sse.cc 2017-07-01 03:36:35.000000000 +0200 @@ -0,0 +1,118 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/base/vector_math_testing.h" + +#include + +#include // NOLINT + +namespace media { +namespace vector_math { + +void FMUL_SSE(const float src[], float scale, int len, float dest[]) { + const int rem = len % 4; + const int last_index = len - rem; + __m128 m_scale = _mm_set_ps1(scale); + for (int i = 0; i < last_index; i += 4) + _mm_store_ps(dest + i, _mm_mul_ps(_mm_load_ps(src + i), m_scale)); + + // Handle any remaining values that wouldn't fit in an SSE pass. + for (int i = last_index; i < len; ++i) + dest[i] = src[i] * scale; +} + +void FMAC_SSE(const float src[], float scale, int len, float dest[]) { + const int rem = len % 4; + const int last_index = len - rem; + __m128 m_scale = _mm_set_ps1(scale); + for (int i = 0; i < last_index; i += 4) { + _mm_store_ps(dest + i, _mm_add_ps(_mm_load_ps(dest + i), + _mm_mul_ps(_mm_load_ps(src + i), m_scale))); + } + + // Handle any remaining values that wouldn't fit in an SSE pass. + for (int i = last_index; i < len; ++i) + dest[i] += src[i] * scale; +} + +// Convenience macro to extract float 0 through 3 from the vector |a|. This is +// needed because compilers other than clang don't support access via +// operator[](). +#define EXTRACT_FLOAT(a, i) \ + (i == 0 ? \ + _mm_cvtss_f32(a) : \ + _mm_cvtss_f32(_mm_shuffle_ps(a, a, i))) + +std::pair EWMAAndMaxPower_SSE( + float initial_value, const float src[], int len, float smoothing_factor) { + // When the recurrence is unrolled, we see that we can split it into 4 + // separate lanes of evaluation: + // + // y[n] = a(S[n]^2) + (1-a)(y[n-1]) + // = a(S[n]^2) + (1-a)^1(aS[n-1]^2) + (1-a)^2(aS[n-2]^2) + ... + // = z[n] + (1-a)^1(z[n-1]) + (1-a)^2(z[n-2]) + (1-a)^3(z[n-3]) + // + // where z[n] = a(S[n]^2) + (1-a)^4(z[n-4]) + (1-a)^8(z[n-8]) + ... + // + // Thus, the strategy here is to compute z[n], z[n-1], z[n-2], and z[n-3] in + // each of the 4 lanes, and then combine them to give y[n]. + + const int rem = len % 4; + const int last_index = len - rem; + + const __m128 smoothing_factor_x4 = _mm_set_ps1(smoothing_factor); + const float weight_prev = 1.0f - smoothing_factor; + const __m128 weight_prev_x4 = _mm_set_ps1(weight_prev); + const __m128 weight_prev_squared_x4 = + _mm_mul_ps(weight_prev_x4, weight_prev_x4); + const __m128 weight_prev_4th_x4 = + _mm_mul_ps(weight_prev_squared_x4, weight_prev_squared_x4); + + // Compute z[n], z[n-1], z[n-2], and z[n-3] in parallel in lanes 3, 2, 1 and + // 0, respectively. + __m128 max_x4 = _mm_setzero_ps(); + __m128 ewma_x4 = _mm_setr_ps(0.0f, 0.0f, 0.0f, initial_value); + int i; + for (i = 0; i < last_index; i += 4) { + ewma_x4 = _mm_mul_ps(ewma_x4, weight_prev_4th_x4); + const __m128 sample_x4 = _mm_load_ps(src + i); + const __m128 sample_squared_x4 = _mm_mul_ps(sample_x4, sample_x4); + max_x4 = _mm_max_ps(max_x4, sample_squared_x4); + // Note: The compiler optimizes this to a single multiply-and-accumulate + // instruction: + ewma_x4 = _mm_add_ps(ewma_x4, + _mm_mul_ps(sample_squared_x4, smoothing_factor_x4)); + } + + // y[n] = z[n] + (1-a)^1(z[n-1]) + (1-a)^2(z[n-2]) + (1-a)^3(z[n-3]) + float ewma = EXTRACT_FLOAT(ewma_x4, 3); + ewma_x4 = _mm_mul_ps(ewma_x4, weight_prev_x4); + ewma += EXTRACT_FLOAT(ewma_x4, 2); + ewma_x4 = _mm_mul_ps(ewma_x4, weight_prev_x4); + ewma += EXTRACT_FLOAT(ewma_x4, 1); + ewma_x4 = _mm_mul_ss(ewma_x4, weight_prev_x4); + ewma += EXTRACT_FLOAT(ewma_x4, 0); + + // Fold the maximums together to get the overall maximum. + max_x4 = _mm_max_ps(max_x4, + _mm_shuffle_ps(max_x4, max_x4, _MM_SHUFFLE(3, 3, 1, 1))); + max_x4 = _mm_max_ss(max_x4, _mm_shuffle_ps(max_x4, max_x4, 2)); + + std::pair result(ewma, EXTRACT_FLOAT(max_x4, 0)); + + // Handle remaining values at the end of |src|. + for (; i < len; ++i) { + result.first *= weight_prev; + const float sample = src[i]; + const float sample_squared = sample * sample; + result.first += sample_squared * smoothing_factor; + result.second = std::max(result.second, sample_squared); + } + + return result; +} + +} // namespace vector_math +} // namespace media diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/media/base/vector_math_testing.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/media/base/vector_math_testing.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/media/base/vector_math_testing.h 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/media/base/vector_math_testing.h 2017-12-25 12:57:58.800476661 +0100 @@ -19,7 +19,7 @@ MEDIA_EXPORT std::pair EWMAAndMaxPower_C( float initial_value, const float src[], int len, float smoothing_factor); -#if defined(ARCH_CPU_X86_FAMILY) && !defined(OS_NACL) +#if defined(ARCH_CPU_X86_FAMILY) MEDIA_EXPORT void FMAC_SSE(const float src[], float scale, int len, float dest[]); MEDIA_EXPORT void FMUL_SSE(const float src[], float scale, int len, diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/media/base/vector_math_unittest.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/media/base/vector_math_unittest.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/media/base/vector_math_unittest.cc 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/media/base/vector_math_unittest.cc 2017-12-25 12:57:58.800476661 +0100 @@ -9,6 +9,7 @@ #include #include "base/macros.h" +#include "base/cpu.h" #include "base/memory/aligned_memory.h" #include "base/strings/string_number_conversions.h" #include "base/strings/stringize_macros.h" @@ -78,6 +79,7 @@ #if defined(ARCH_CPU_X86_FAMILY) { + ASSERT_TRUE(base::CPU().has_sse()); SCOPED_TRACE("FMAC_SSE"); FillTestVectors(kInputFillValue, kOutputFillValue); vector_math::FMAC_SSE( @@ -119,6 +121,7 @@ #if defined(ARCH_CPU_X86_FAMILY) { + ASSERT_TRUE(base::CPU().has_sse()); SCOPED_TRACE("FMUL_SSE"); FillTestVectors(kInputFillValue, kOutputFillValue); vector_math::FMUL_SSE( @@ -227,6 +230,7 @@ #if defined(ARCH_CPU_X86_FAMILY) { + ASSERT_TRUE(base::CPU().has_sse()); SCOPED_TRACE("EWMAAndMaxPower_SSE"); const std::pair& result = vector_math::EWMAAndMaxPower_SSE( initial_value_, data_.get(), data_len_, smoothing_factor_); diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/media/BUILD.gn qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/media/BUILD.gn --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/media/BUILD.gn 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/media/BUILD.gn 2017-12-25 13:38:30.438718953 +0100 @@ -534,6 +534,26 @@ "//base", "//ui/gfx/geometry", ] + if (current_cpu == "x86" || current_cpu == "x64") { + deps += [ + ":shared_memory_support_sse", + ] + } +} + +if (current_cpu == "x86" || current_cpu == "x64") { + source_set("shared_memory_support_sse") { + sources = [ + "base/vector_math_sse.cc", + ] + configs += [ + "//media:media_config", + "//media:media_implementation", + ] + if (!is_win) { + cflags = [ "-msse" ] + } + } } # TODO(watk): Refactor tests that could be made to run on Android. See diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/media/filters/wsola_internals.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/media/filters/wsola_internals.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/media/filters/wsola_internals.cc 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/media/filters/wsola_internals.cc 2017-12-26 23:00:39.631753174 +0100 @@ -15,7 +15,7 @@ #include "base/logging.h" #include "media/base/audio_bus.h" -#if defined(ARCH_CPU_X86_FAMILY) +#if defined(ARCH_CPU_X86_FAMILY) && defined(__SSE__) #define USE_SIMD 1 #include #elif defined(ARCH_CPU_ARM_FAMILY) && defined(USE_NEON) diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/skia/BUILD.gn qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/skia/BUILD.gn --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/skia/BUILD.gn 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/skia/BUILD.gn 2017-12-25 13:45:09.341998517 +0100 @@ -257,17 +257,6 @@ "ext/platform_canvas.h", ] } - if (!is_ios && (current_cpu == "x86" || current_cpu == "x64")) { - sources += [ - "ext/convolver_SSE2.cc", - "ext/convolver_SSE2.h", - ] - } else if (current_cpu == "mipsel" && mips_dsp_rev >= 2) { - sources += [ - "ext/convolver_mips_dspr2.cc", - "ext/convolver_mips_dspr2.h", - ] - } if (!is_fuchsia) { sources -= [ @@ -522,6 +511,31 @@ } } if (current_cpu == "x86" || current_cpu == "x64") { + source_set("skia_opts_sse2") { + sources = skia_opts.sse2_sources + + [ + # Chrome-specific. + "ext/convolver_SSE2.cc", + "ext/convolver_SSE2.h", + ] + sources -= [ + # Detection code must not be built with -msse2 + "//third_party/skia/src/opts/opts_check_x86.cpp", + ] + if (!is_win || is_clang) { + cflags = [ "-msse2" ] + } + if (is_win) { + defines = [ "SK_CPU_SSE_LEVEL=20" ] + } + visibility = [ ":skia_opts" ] + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ + ":skia_config", + ":skia_library_config", + "//build/config/compiler:no_chromium_code", + ] + } source_set("skia_opts_sse3") { sources = skia_opts.ssse3_sources if (!is_win || is_clang) { @@ -626,10 +640,13 @@ ] if (current_cpu == "x86" || current_cpu == "x64") { - sources = skia_opts.sse2_sources + sources = [ + "//third_party/skia/src/opts/opts_check_x86.cpp", + ] deps += [ ":skia_opts_avx", ":skia_opts_hsw", + ":skia_opts_sse2", ":skia_opts_sse3", ":skia_opts_sse41", ":skia_opts_sse42", @@ -664,6 +681,13 @@ if (mips_dsp_rev >= 1) { sources = skia_opts.mips_dsp_sources + if (mips_dsp_rev >= 2) { + sources += [ + # Chrome-specific. + "ext/convolver_mips_dspr2.cc", + "ext/convolver_mips_dspr2.h", + ] + } } else { sources = skia_opts.none_sources } diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/skia/ext/convolver.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/skia/ext/convolver.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/skia/ext/convolver.cc 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/skia/ext/convolver.cc 2017-12-25 13:05:23.911940902 +0100 @@ -362,10 +362,13 @@ void SetupSIMD(ConvolveProcs *procs) { #ifdef SIMD_SSE2 - procs->extra_horizontal_reads = 3; - procs->convolve_vertically = &ConvolveVertically_SSE2; - procs->convolve_4rows_horizontally = &Convolve4RowsHorizontally_SSE2; - procs->convolve_horizontally = &ConvolveHorizontally_SSE2; + base::CPU cpu; + if (cpu.has_sse2()) { + procs->extra_horizontal_reads = 3; + procs->convolve_vertically = &ConvolveVertically_SSE2; + procs->convolve_4rows_horizontally = &Convolve4RowsHorizontally_SSE2; + procs->convolve_horizontally = &ConvolveHorizontally_SSE2; + } #elif defined SIMD_MIPS_DSPR2 procs->extra_horizontal_reads = 3; procs->convolve_vertically = &ConvolveVertically_mips_dspr2; diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/skia/ext/convolver.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/skia/ext/convolver.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/skia/ext/convolver.h 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/skia/ext/convolver.h 2017-12-25 13:05:23.951940405 +0100 @@ -11,6 +11,7 @@ #include #include "build/build_config.h" +#include "base/cpu.h" #include "third_party/skia/include/core/SkSize.h" #include "third_party/skia/include/core/SkTypes.h" diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/angle/BUILD.gn qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/angle/BUILD.gn --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/angle/BUILD.gn 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/angle/BUILD.gn 2017-12-25 13:05:23.951940405 +0100 @@ -192,6 +192,26 @@ public_deps = [ ":angle_common", ] + + if (current_cpu == "x86") { + deps = [ + ":angle_image_util_x86_sse2", + ] + } +} + +source_set("angle_image_util_x86_sse2") { + configs -= angle_undefine_configs + configs += [ ":internal_config" ] + + deps = [ + ":angle_common", + ] + + sources = [ + "src/image_util/loadimage_SSE2.cpp", + ] + cflags = [ "-msse2", "-mfpmath=sse" ] } config("angle_gpu_info_util_config") { diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/angle/src/common/mathutil.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/angle/src/common/mathutil.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/angle/src/common/mathutil.h 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/angle/src/common/mathutil.h 2017-12-25 13:05:23.951940405 +0100 @@ -124,9 +124,42 @@ } } -inline bool supportsSSE2() +#if defined(ANGLE_USE_SSE) && !defined(__x86_64__) && !defined(__SSE2__) && !defined(_MSC_VER) + +// From the base/cpu.cc in Chromium, to avoid depending on Chromium headers + +#if defined(__pic__) && defined(__i386__) + +static inline void __cpuid(int cpu_info[4], int info_type) { + __asm__ volatile ( + "mov %%ebx, %%edi\n" + "cpuid\n" + "xchg %%edi, %%ebx\n" + : "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3]) + : "a"(info_type) + ); +} + +#else + +static inline void __cpuid(int cpu_info[4], int info_type) { + __asm__ volatile ( + "cpuid\n" + : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3]) + : "a"(info_type) + ); +} + +#endif + +#endif + +static inline bool supportsSSE2() { #if defined(ANGLE_USE_SSE) +#if defined(__x86_64__) || defined(__SSE2__) + return true; +#else static bool checked = false; static bool supports = false; @@ -135,7 +168,6 @@ return supports; } -#if defined(ANGLE_PLATFORM_WINDOWS) && !defined(_M_ARM) { int info[4]; __cpuid(info, 0); @@ -147,9 +179,9 @@ supports = (info[3] >> 26) & 1; } } -#endif // defined(ANGLE_PLATFORM_WINDOWS) && !defined(_M_ARM) checked = true; return supports; +#endif // defined(x86_64) || defined(__SSE2__) #else // defined(ANGLE_USE_SSE) return false; #endif diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/angle/src/common/platform.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/angle/src/common/platform.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/angle/src/common/platform.h 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/angle/src/common/platform.h 2017-12-25 13:05:23.951940405 +0100 @@ -87,7 +87,9 @@ #include #define ANGLE_USE_SSE #elif defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__)) +#if defined(__x86_64__) || defined(__SSE2__) #include +#endif #define ANGLE_USE_SSE #endif diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/angle/src/image_util/loadimage.cpp qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/angle/src/image_util/loadimage.cpp --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/angle/src/image_util/loadimage.cpp 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/angle/src/image_util/loadimage.cpp 2017-12-25 13:05:23.952940392 +0100 @@ -12,9 +12,17 @@ #include "common/platform.h" #include "image_util/imageformats.h" +#if defined(BUILD_ONLY_THE_SSE2_PARTS) && !defined(__SSE2__) +#error SSE2 parts must be built with -msse2 +#endif + namespace angle { +#ifdef BUILD_ONLY_THE_SSE2_PARTS +namespace SSE2 { +#endif + void LoadA8ToRGBA8(size_t width, size_t height, size_t depth, @@ -28,6 +36,11 @@ #if defined(ANGLE_USE_SSE) if (gl::supportsSSE2()) { +#if !defined(__x86_64__) && !defined(__SSE2__) + angle::SSE2::LoadA8ToRGBA8(width, height, depth, input, inputRowPitch, + inputDepthPitch, output, outputRowPitch, + outputDepthPitch); +#else __m128i zeroWide = _mm_setzero_si128(); for (size_t z = 0; z < depth; z++) @@ -68,6 +81,7 @@ } } } +#endif return; } @@ -89,6 +103,8 @@ } } +#ifndef BUILD_ONLY_THE_SSE2_PARTS + void LoadA8ToBGRA8(size_t width, size_t height, size_t depth, @@ -584,6 +600,8 @@ } } +#endif + void LoadRGBA8ToBGRA8(size_t width, size_t height, size_t depth, @@ -597,6 +615,11 @@ #if defined(ANGLE_USE_SSE) if (gl::supportsSSE2()) { +#if !defined(__x86_64__) && !defined(__SSE2__) + angle::SSE2::LoadRGBA8ToBGRA8(width, height, depth, input, + inputRowPitch, inputDepthPitch, output, + outputRowPitch, outputDepthPitch); +#else __m128i brMask = _mm_set1_epi32(0x00ff00ff); for (size_t z = 0; z < depth; z++) @@ -641,6 +664,7 @@ } } } +#endif return; } @@ -663,6 +687,8 @@ } } +#ifndef BUILD_ONLY_THE_SSE2_PARTS + void LoadRGBA8ToBGRA4(size_t width, size_t height, size_t depth, @@ -1320,4 +1346,10 @@ } } +#endif + +#ifdef BUILD_ONLY_THE_SSE2_PARTS +} // namespace SSE2 +#endif + } // namespace angle diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/angle/src/image_util/loadimage.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/angle/src/image_util/loadimage.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/angle/src/image_util/loadimage.h 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/angle/src/image_util/loadimage.h 2017-12-25 13:05:24.018939571 +0100 @@ -651,6 +651,32 @@ size_t outputRowPitch, size_t outputDepthPitch); +#if defined(__i386__) +namespace SSE2 { + +void LoadA8ToRGBA8(size_t width, + size_t height, + size_t depth, + const uint8_t *input, + size_t inputRowPitch, + size_t inputDepthPitch, + uint8_t *output, + size_t outputRowPitch, + size_t outputDepthPitch); + +void LoadRGBA8ToBGRA8(size_t width, + size_t height, + size_t depth, + const uint8_t *input, + size_t inputRowPitch, + size_t inputDepthPitch, + uint8_t *output, + size_t outputRowPitch, + size_t outputDepthPitch); + +} +#endif // defined(__i386__) + } // namespace angle #include "loadimage.inl" diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/angle/src/image_util/loadimage_SSE2.cpp qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/angle/src/image_util/loadimage_SSE2.cpp --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/angle/src/image_util/loadimage_SSE2.cpp 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/angle/src/image_util/loadimage_SSE2.cpp 2017-12-25 13:05:24.018939571 +0100 @@ -0,0 +1,2 @@ +#define BUILD_ONLY_THE_SSE2_PARTS +#include "loadimage.cpp" diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/qcms/BUILD.gn qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/qcms/BUILD.gn --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/qcms/BUILD.gn 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/qcms/BUILD.gn 2017-12-25 13:51:32.804702578 +0100 @@ -34,8 +34,8 @@ defines = [] if (current_cpu == "x86" || current_cpu == "x64") { - defines += [ "SSE2_ENABLE" ] - sources += [ "src/transform-sse2.c" ] + defines += [ "SSE2_ENABLE" ] # runtime detection + deps = [ ":qcms_sse2" ] } if (use_libfuzzer) { @@ -99,3 +99,15 @@ public_configs = [ ":qcms_config" ] } } + +source_set("qcms_sse2") { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + public_configs = [ ":qcms_config" ] + + if (current_cpu == "x86" || current_cpu == "x64") { + defines = [ "SSE2_ENABLE" ] + sources = [ "src/transform-sse2.c" ] + cflags = [ "-msse2" ] + } +} diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/WebKit/Source/modules/webaudio/AudioParamTimeline.cpp qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/WebKit/Source/modules/webaudio/AudioParamTimeline.cpp --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/WebKit/Source/modules/webaudio/AudioParamTimeline.cpp 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/WebKit/Source/modules/webaudio/AudioParamTimeline.cpp 2017-12-25 14:03:37.424681528 +0100 @@ -35,7 +35,7 @@ #include "platform/wtf/MathExtras.h" #include "platform/wtf/PtrUtil.h" -#if defined(ARCH_CPU_X86_FAMILY) +#if (defined(ARCH_CPU_X86) && defined(__SSE2__)) || defined(ARCH_CPU_X86_64) #include #endif @@ -1290,7 +1290,7 @@ size_t current_frame, float value, unsigned write_index) { -#if defined(ARCH_CPU_X86_FAMILY) +#if (defined(ARCH_CPU_X86) && defined(__SSE2__)) || defined(ARCH_CPU_X86_64) auto number_of_values = current_state.number_of_values; #endif auto fill_to_frame = current_state.fill_to_frame; @@ -1303,7 +1303,7 @@ double delta_time = time2 - time1; float k = delta_time > 0 ? 1 / delta_time : 0; const float value_delta = value2 - value1; -#if defined(ARCH_CPU_X86_FAMILY) +#if (defined(ARCH_CPU_X86) && defined(__SSE2__)) || defined(ARCH_CPU_X86_64) if (fill_to_frame > write_index) { // Minimize in-loop operations. Calculate starting value and increment. // Next step: value += inc. @@ -1431,7 +1431,7 @@ size_t current_frame, float value, unsigned write_index) { -#if defined(ARCH_CPU_X86_FAMILY) +#if (defined(ARCH_CPU_X86) && defined(__SSE2__)) || defined(ARCH_CPU_X86_64) auto number_of_values = current_state.number_of_values; #endif auto fill_to_frame = current_state.fill_to_frame; @@ -1482,7 +1482,7 @@ for (; write_index < fill_to_frame; ++write_index) values[write_index] = target; } else { -#if defined(ARCH_CPU_X86_FAMILY) +#if (defined(ARCH_CPU_X86) && defined(__SSE2__)) || defined(ARCH_CPU_X86_64) if (fill_to_frame > write_index) { // Resolve recursion by expanding constants to achieve a 4-step // loop unrolling. @@ -1616,7 +1616,7 @@ // Oversampled curve data can be provided if sharp discontinuities are // desired. unsigned k = 0; -#if defined(ARCH_CPU_X86_FAMILY) +#if (defined(ARCH_CPU_X86) && defined(__SSE2__)) || defined(ARCH_CPU_X86_64) if (fill_to_frame > write_index) { const __m128 v_curve_virtual_index = _mm_set_ps1(curve_virtual_index); const __m128 v_curve_points_per_frame = _mm_set_ps1(curve_points_per_frame); diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/WebKit/Source/platform/audio/DirectConvolver.cpp qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/WebKit/Source/platform/audio/DirectConvolver.cpp --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/WebKit/Source/platform/audio/DirectConvolver.cpp 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/WebKit/Source/platform/audio/DirectConvolver.cpp 2017-12-27 00:16:35.571877993 +0100 @@ -26,6 +26,9 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +// include this first to get it before the CPU() function-like macro +#include "base/cpu.h" + #include "platform/audio/DirectConvolver.h" #include "build/build_config.h" @@ -35,21 +38,48 @@ #include #endif -#if defined(ARCH_CPU_X86_FAMILY) && !defined(OS_MACOSX) +#if ((defined(ARCH_CPU_X86) && defined(__SSE2__)) || defined(ARCH_CPU_X86_64)) \ + && !defined(OS_MACOSX) #include #endif +#if defined(BUILD_ONLY_THE_SSE2_PARTS) && !defined(__SSE2__) +#error SSE2 parts must be built with -msse2 +#endif + namespace blink { using namespace VectorMath; +#ifndef BUILD_ONLY_THE_SSE2_PARTS + DirectConvolver::DirectConvolver(size_t input_block_size) - : input_block_size_(input_block_size), buffer_(input_block_size * 2) {} + : input_block_size_(input_block_size), buffer_(input_block_size * 2) { +#ifdef ARCH_CPU_X86 + base::CPU cpu; + m_haveSSE2 = cpu.has_sse2(); +#endif +} + +#endif +#ifdef BUILD_ONLY_THE_SSE2_PARTS +void DirectConvolver::m_ProcessSSE2(AudioFloatArray* convolution_kernel, + const float* source_p, + float* dest_p, + size_t frames_to_process) { +#else void DirectConvolver::Process(AudioFloatArray* convolution_kernel, const float* source_p, float* dest_p, size_t frames_to_process) { +#endif +#if defined(ARCH_CPU_X86) && !defined(__SSE2__) + if (m_haveSSE2) { + m_ProcessSSE2(convolution_kernel, source_p, dest_p, frames_to_process); + return; + } +#endif DCHECK_EQ(frames_to_process, input_block_size_); if (frames_to_process != input_block_size_) return; @@ -83,7 +113,7 @@ #endif // ARCH_CPU_X86 #else size_t i = 0; -#if defined(ARCH_CPU_X86_FAMILY) +#if (defined(ARCH_CPU_X86) && defined(__SSE2__)) || defined(ARCH_CPU_X86_64) // Convolution using SSE2. Currently only do this if both |kernelSize| and // |framesToProcess| are multiples of 4. If not, use the straightforward loop // below. @@ -397,7 +427,7 @@ } dest_p[i++] = sum; } -#if defined(ARCH_CPU_X86_FAMILY) +#if (defined(ARCH_CPU_X86) && defined(__SSE2__)) || defined(ARCH_CPU_X86_64) } #endif #endif // OS_MACOSX @@ -406,8 +436,12 @@ memcpy(buffer_.Data(), input_p, sizeof(float) * frames_to_process); } +#ifndef BUILD_ONLY_THE_SSE2_PARTS + void DirectConvolver::Reset() { buffer_.Zero(); } +#endif + } // namespace blink diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/WebKit/Source/platform/audio/DirectConvolver.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/WebKit/Source/platform/audio/DirectConvolver.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/WebKit/Source/platform/audio/DirectConvolver.h 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/WebKit/Source/platform/audio/DirectConvolver.h 2017-12-25 14:39:21.094641400 +0100 @@ -29,6 +29,7 @@ #ifndef DirectConvolver_h #define DirectConvolver_h +#include "build/build_config.h" #include "platform/PlatformExport.h" #include "platform/audio/AudioArray.h" #include "platform/wtf/Allocator.h" @@ -54,6 +55,14 @@ size_t input_block_size_; AudioFloatArray buffer_; + +#ifdef ARCH_CPU_X86 + bool m_haveSSE2; + void m_ProcessSSE2(AudioFloatArray* convolution_kernel, + const float* source_p, + float* dest_p, + size_t frames_to_process); +#endif }; } // namespace blink diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/WebKit/Source/platform/audio/DirectConvolverSSE2.cpp qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/WebKit/Source/platform/audio/DirectConvolverSSE2.cpp --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/WebKit/Source/platform/audio/DirectConvolverSSE2.cpp 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/WebKit/Source/platform/audio/DirectConvolverSSE2.cpp 2017-12-25 13:05:24.021939534 +0100 @@ -0,0 +1,2 @@ +#define BUILD_ONLY_THE_SSE2_PARTS +#include "DirectConvolver.cpp" diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/WebKit/Source/platform/audio/SincResampler.cpp qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/WebKit/Source/platform/audio/SincResampler.cpp --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/WebKit/Source/platform/audio/SincResampler.cpp 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/WebKit/Source/platform/audio/SincResampler.cpp 2017-12-25 14:41:03.697194334 +0100 @@ -26,16 +26,23 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +// include this first to get it before the CPU() function-like macro +#include "base/cpu.h" + #include "platform/audio/SincResampler.h" #include "build/build_config.h" #include "platform/audio/AudioBus.h" #include "platform/wtf/MathExtras.h" -#if defined(ARCH_CPU_X86_FAMILY) +#if (defined(ARCH_CPU_X86) && defined(__SSE2__)) || defined(ARCH_CPU_X86_64) #include #endif +#if defined(BUILD_ONLY_THE_SSE2_PARTS) && !defined(__SSE2__) +#error SSE2 parts must be built with -msse2 +#endif + // Input buffer layout, dividing the total buffer into regions (r0 - r5): // // |----------------|-----------------------------------------|----------------| @@ -67,6 +74,8 @@ namespace blink { +#ifndef BUILD_ONLY_THE_SSE2_PARTS + SincResampler::SincResampler(double scale_factor, unsigned kernel_size, unsigned number_of_kernel_offsets) @@ -82,6 +91,10 @@ source_frames_available_(0), source_provider_(nullptr), is_buffer_primed_(false) { +#ifdef ARCH_CPU_X86 + base::CPU cpu; + m_haveSSE2 = cpu.has_sse2(); +#endif InitializeKernel(); } @@ -205,9 +218,23 @@ } } +#endif + +#ifdef BUILD_ONLY_THE_SSE2_PARTS +void SincResampler::m_ProcessSSE2(AudioSourceProvider* source_provider, + float* destination, + size_t frames_to_process) { +#else void SincResampler::Process(AudioSourceProvider* source_provider, float* destination, size_t frames_to_process) { +#endif +#if defined(ARCH_CPU_X86) && !defined(__SSE2__) + if (m_haveSSE2) { + m_ProcessSSE2(source_provider, destination, frames_to_process); + return; + } +#endif bool is_good = source_provider && block_size_ > kernel_size_ && input_buffer_.size() >= block_size_ + kernel_size_ && !(kernel_size_ % 2); @@ -276,7 +303,7 @@ { float input; -#if defined(ARCH_CPU_X86_FAMILY) +#if (defined(ARCH_CPU_X86) && defined(__SSE2__)) || defined(ARCH_CPU_X86_64) // If the sourceP address is not 16-byte aligned, the first several // frames (at most three) should be processed seperately. while ((reinterpret_cast(input_p) & 0x0F) && n) { diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/WebKit/Source/platform/audio/SincResampler.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/WebKit/Source/platform/audio/SincResampler.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/WebKit/Source/platform/audio/SincResampler.h 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/WebKit/Source/platform/audio/SincResampler.h 2017-12-25 14:40:36.454578552 +0100 @@ -29,6 +29,7 @@ #ifndef SincResampler_h #define SincResampler_h +#include "build/build_config.h" #include "platform/PlatformExport.h" #include "platform/audio/AudioArray.h" #include "platform/audio/AudioSourceProvider.h" @@ -96,6 +97,14 @@ // The buffer is primed once at the very beginning of processing. bool is_buffer_primed_; + +#ifdef ARCH_CPU_X86 + private: + bool m_haveSSE2; + void m_ProcessSSE2(AudioSourceProvider*, + float* destination, + size_t frames_to_process); +#endif }; } // namespace blink diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/WebKit/Source/platform/audio/SincResamplerSSE2.cpp qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/WebKit/Source/platform/audio/SincResamplerSSE2.cpp --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/WebKit/Source/platform/audio/SincResamplerSSE2.cpp 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/WebKit/Source/platform/audio/SincResamplerSSE2.cpp 2017-12-25 13:05:24.022939522 +0100 @@ -0,0 +1,2 @@ +#define BUILD_ONLY_THE_SSE2_PARTS +#include "SincResampler.cpp" diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/WebKit/Source/platform/audio/VectorMath.cpp qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/WebKit/Source/platform/audio/VectorMath.cpp --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/WebKit/Source/platform/audio/VectorMath.cpp 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/WebKit/Source/platform/audio/VectorMath.cpp 2017-12-25 14:48:07.515216969 +0100 @@ -23,6 +23,9 @@ * DAMAGE. */ +// include this first to get it before the CPU() function-like macro +#include "base/cpu.h" + #include "platform/audio/VectorMath.h" #include @@ -35,10 +38,14 @@ #include #endif -#if defined(ARCH_CPU_X86_FAMILY) +#if (defined(ARCH_CPU_X86) && defined(__SSE2__)) || defined(ARCH_CPU_X86_64) #include #endif +#if defined(BUILD_ONLY_THE_SSE2_PARTS) && !defined(__SSE2__) +#error SSE2 parts must be built with -msse2 +#endif + #if WTF_CPU_ARM_NEON #include #endif @@ -170,15 +177,30 @@ } #else +#ifdef BUILD_ONLY_THE_SSE2_PARTS +namespace SSE2 { +#endif + +#if defined(ARCH_CPU_X86) && !defined(__SSE2__) +static base::CPU cpu; +#endif + void Vsma(const float* source_p, int source_stride, const float* scale, float* dest_p, int dest_stride, size_t frames_to_process) { +#if defined(ARCH_CPU_X86) && !defined(__SSE2__) + if (cpu.has_sse2()) { + blink::VectorMath::SSE2::Vsma(source_p, source_stride, scale, dest_p, + dest_stride, frames_to_process); + return; + } +#endif int n = frames_to_process; -#if defined(ARCH_CPU_X86_FAMILY) +#if (defined(ARCH_CPU_X86) && defined(__SSE2__)) || defined(ARCH_CPU_X86_64) if ((source_stride == 1) && (dest_stride == 1)) { float k = *scale; @@ -274,9 +296,16 @@ float* dest_p, int dest_stride, size_t frames_to_process) { +#if defined(ARCH_CPU_X86) && !defined(__SSE2__) + if (cpu.has_sse2()) { + blink::VectorMath::SSE2::Vsmul(source_p, source_stride, scale, dest_p, + dest_stride, frames_to_process); + return; + } +#endif int n = frames_to_process; -#if defined(ARCH_CPU_X86_FAMILY) +#if (defined(ARCH_CPU_X86) && defined(__SSE2__)) || defined(ARCH_CPU_X86_64) if ((source_stride == 1) && (dest_stride == 1)) { float k = *scale; @@ -365,7 +394,7 @@ source_p += source_stride; dest_p += dest_stride; } -#if defined(ARCH_CPU_X86_FAMILY) +#if (defined(ARCH_CPU_X86) && defined(__SSE2__)) || defined(ARCH_CPU_X86_64) } #endif } @@ -377,9 +406,17 @@ float* dest_p, int dest_stride, size_t frames_to_process) { +#if defined(ARCH_CPU_X86) && !defined(__SSE2__) + if (cpu.has_sse2()) { + blink::VectorMath::SSE2::Vadd(source1p, source_stride1, source2p, + source_stride2, dest_p, dest_stride, + frames_to_process); + return; + } +#endif int n = frames_to_process; -#if defined(ARCH_CPU_X86_FAMILY) +#if (defined(ARCH_CPU_X86) && defined(__SSE2__)) || defined(ARCH_CPU_X86_64) if ((source_stride1 == 1) && (source_stride2 == 1) && (dest_stride == 1)) { // If the sourceP address is not 16-byte aligned, the first several frames // (at most three) should be processed separately. @@ -506,7 +543,7 @@ source2p += source_stride2; dest_p += dest_stride; } -#if defined(ARCH_CPU_X86_FAMILY) +#if (defined(ARCH_CPU_X86) && defined(__SSE2__)) || defined(ARCH_CPU_X86_64) } #endif } @@ -518,9 +555,17 @@ float* dest_p, int dest_stride, size_t frames_to_process) { +#if defined(ARCH_CPU_X86) && !defined(__SSE2__) + if (cpu.has_sse2()) { + blink::VectorMath::SSE2::Vmul(source1p, source_stride1, source2p, + source_stride2, dest_p, dest_stride, + frames_to_process); + return; + } +#endif int n = frames_to_process; -#if defined(ARCH_CPU_X86_FAMILY) +#if (defined(ARCH_CPU_X86) && defined(__SSE2__)) || defined(ARCH_CPU_X86_64) if ((source_stride1 == 1) && (source_stride2 == 1) && (dest_stride == 1)) { // If the source1P address is not 16-byte aligned, the first several frames // (at most three) should be processed separately. @@ -619,8 +664,15 @@ float* real_dest_p, float* imag_dest_p, size_t frames_to_process) { +#if defined(ARCH_CPU_X86) && !defined(__SSE2__) + if (cpu.has_sse2()) { + blink::VectorMath::SSE2::Zvmul(real1p, imag1p, real2p, imag2p, real_dest_p, + imag_dest_p, frames_to_process); + return; + } +#endif unsigned i = 0; -#if defined(ARCH_CPU_X86_FAMILY) +#if (defined(ARCH_CPU_X86) && defined(__SSE2__)) || defined(ARCH_CPU_X86_64) // Only use the SSE optimization in the very common case that all addresses // are 16-byte aligned. Otherwise, fall through to the scalar code below. if (!(reinterpret_cast(real1p) & 0x0F) && @@ -676,10 +728,17 @@ int source_stride, float* sum_p, size_t frames_to_process) { +#if defined(ARCH_CPU_X86) && !defined(__SSE2__) + if (cpu.has_sse2()) { + blink::VectorMath::SSE2::Vsvesq(source_p, source_stride, sum_p, + frames_to_process); + return; + } +#endif int n = frames_to_process; float sum = 0; -#if defined(ARCH_CPU_X86_FAMILY) +#if (defined(ARCH_CPU_X86) && defined(__SSE2__)) || defined(ARCH_CPU_X86_64) if (source_stride == 1) { // If the sourceP address is not 16-byte aligned, the first several frames // (at most three) should be processed separately. @@ -745,10 +804,17 @@ int source_stride, float* max_p, size_t frames_to_process) { +#if defined(ARCH_CPU_X86) && !defined(__SSE2__) + if (cpu.has_sse2()) { + blink::VectorMath::SSE2::Vmaxmgv(source_p, source_stride, max_p, + frames_to_process); + return; + } +#endif int n = frames_to_process; float max = 0; -#if defined(ARCH_CPU_X86_FAMILY) +#if (defined(ARCH_CPU_X86) && defined(__SSE2__)) || defined(ARCH_CPU_X86_64) if (source_stride == 1) { // If the sourceP address is not 16-byte aligned, the first several frames // (at most three) should be processed separately. @@ -837,6 +903,8 @@ *max_p = max; } +#ifndef BUILD_ONLY_THE_SSE2_PARTS + void Vclip(const float* source_p, int source_stride, const float* low_threshold_p, @@ -894,6 +962,12 @@ } } +#endif + +#ifdef BUILD_ONLY_THE_SSE2_PARTS +} // namespace SSE2 +#endif + #endif // defined(OS_MACOSX) } // namespace VectorMath diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/WebKit/Source/platform/audio/VectorMath.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/WebKit/Source/platform/audio/VectorMath.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/WebKit/Source/platform/audio/VectorMath.h 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/WebKit/Source/platform/audio/VectorMath.h 2017-12-25 14:51:17.547536826 +0100 @@ -27,6 +27,7 @@ #define VectorMath_h #include +#include "build/build_config.h" #include "platform/PlatformExport.h" #include "platform/wtf/build_config.h" @@ -97,6 +98,62 @@ int dest_stride, size_t frames_to_process); +#ifdef ARCH_CPU_X86 +namespace SSE2 { +// Vector scalar multiply and then add. +PLATFORM_EXPORT void Vsma(const float* source_p, + int source_stride, + const float* scale, + float* dest_p, + int dest_stride, + size_t frames_to_process); + +PLATFORM_EXPORT void Vsmul(const float* source_p, + int source_stride, + const float* scale, + float* dest_p, + int dest_stride, + size_t frames_to_process); +PLATFORM_EXPORT void Vadd(const float* source1p, + int source_stride1, + const float* source2p, + int source_stride2, + float* dest_p, + int dest_stride, + size_t frames_to_process); + +// Finds the maximum magnitude of a float vector. +PLATFORM_EXPORT void Vmaxmgv(const float* source_p, + int source_stride, + float* max_p, + size_t frames_to_process); + +// Sums the squares of a float vector's elements. +PLATFORM_EXPORT void Vsvesq(const float* source_p, + int source_stride, + float* sum_p, + size_t frames_to_process); + +// For an element-by-element multiply of two float vectors. +PLATFORM_EXPORT void Vmul(const float* source1p, + int source_stride1, + const float* source2p, + int source_stride2, + float* dest_p, + int dest_stride, + size_t frames_to_process); + +// Multiplies two complex vectors. +PLATFORM_EXPORT void Zvmul(const float* real1p, + const float* imag1p, + const float* real2p, + const float* imag2p, + float* real_dest_p, + float* imag_dest_p, + size_t frames_to_process); +} +#endif + } // namespace VectorMath } // namespace blink diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/WebKit/Source/platform/audio/VectorMathSSE2.cpp qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/WebKit/Source/platform/audio/VectorMathSSE2.cpp --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/WebKit/Source/platform/audio/VectorMathSSE2.cpp 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/WebKit/Source/platform/audio/VectorMathSSE2.cpp 2017-12-25 13:05:24.024939497 +0100 @@ -0,0 +1,2 @@ +#define BUILD_ONLY_THE_SSE2_PARTS +#include "VectorMath.cpp" diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/WebKit/Source/platform/BUILD.gn qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/WebKit/Source/platform/BUILD.gn --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/WebKit/Source/platform/BUILD.gn 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/WebKit/Source/platform/BUILD.gn 2017-12-26 01:28:56.946048732 +0100 @@ -1693,6 +1693,10 @@ deps += [ ":blink_x86_sse" ] } + if (current_cpu == "x86") { + deps += [ ":blink_x86_sse2" ] + } + if (use_webaudio_ffmpeg) { include_dirs += [ "//third_party/ffmpeg" ] deps += [ "//third_party/ffmpeg" ] @@ -2139,6 +2143,23 @@ } } +if (current_cpu == "x86") { + source_set("blink_x86_sse2") { + sources = [ + "audio/DirectConvolverSSE2.cpp", + "audio/SincResamplerSSE2.cpp", + "audio/VectorMathSSE2.cpp", + ] + cflags = [ "-msse2", "-mfpmath=sse" ] + configs += [ + # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. + "//build/config/compiler:no_size_t_to_int_warning", + "//third_party/WebKit/Source:config", + "//third_party/WebKit/Source:non_test_config", + ] + } +} + # This source set is used for fuzzers that need an environment similar to unit # tests. source_set("blink_fuzzer_test_support") { diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/WebKit/Source/platform/graphics/cpu/x86/WebGLImageConversionSSE.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/WebKit/Source/platform/graphics/cpu/x86/WebGLImageConversionSSE.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/WebKit/Source/platform/graphics/cpu/x86/WebGLImageConversionSSE.h 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/WebKit/Source/platform/graphics/cpu/x86/WebGLImageConversionSSE.h 2017-12-25 17:01:28.182182131 +0100 @@ -7,7 +7,7 @@ #include "build/build_config.h" -#if defined(ARCH_CPU_X86_FAMILY) +#if (defined(ARCH_CPU_X86) && defined(__SSE2__)) || defined(ARCH_CPU_X86_64) #include namespace blink { diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/WebKit/Source/platform/graphics/gpu/WebGLImageConversion.cpp qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/WebKit/Source/platform/graphics/gpu/WebGLImageConversion.cpp --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/WebKit/Source/platform/graphics/gpu/WebGLImageConversion.cpp 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/WebKit/Source/platform/graphics/gpu/WebGLImageConversion.cpp 2017-12-25 17:03:30.477435712 +0100 @@ -444,7 +444,7 @@ const uint32_t* source32 = reinterpret_cast_ptr(source); uint32_t* destination32 = reinterpret_cast_ptr(destination); -#if defined(ARCH_CPU_X86_FAMILY) +#if (defined(ARCH_CPU_X86) && defined(__SSE2__)) || defined(ARCH_CPU_X86_64) SIMD::UnpackOneRowOfBGRA8LittleToRGBA8(source32, destination32, pixels_per_row); #endif @@ -472,7 +472,7 @@ const uint16_t* source, uint8_t* destination, unsigned pixels_per_row) { -#if defined(ARCH_CPU_X86_FAMILY) +#if (defined(ARCH_CPU_X86) && defined(__SSE2__)) || defined(ARCH_CPU_X86_64) SIMD::UnpackOneRowOfRGBA5551LittleToRGBA8(source, destination, pixels_per_row); #endif @@ -502,7 +502,7 @@ const uint16_t* source, uint8_t* destination, unsigned pixels_per_row) { -#if defined(ARCH_CPU_X86_FAMILY) +#if (defined(ARCH_CPU_X86) && defined(__SSE2__)) || defined(ARCH_CPU_X86_64) SIMD::UnpackOneRowOfRGBA4444LittleToRGBA8(source, destination, pixels_per_row); #endif @@ -718,7 +718,7 @@ uint8_t>(const uint8_t* source, uint8_t* destination, unsigned pixels_per_row) { -#if defined(ARCH_CPU_X86_FAMILY) +#if (defined(ARCH_CPU_X86) && defined(__SSE2__)) || defined(ARCH_CPU_X86_64) SIMD::PackOneRowOfRGBA8LittleToR8(source, destination, pixels_per_row); #endif #if HAVE(MIPS_MSA_INTRINSICS) @@ -775,7 +775,7 @@ uint8_t>(const uint8_t* source, uint8_t* destination, unsigned pixels_per_row) { -#if defined(ARCH_CPU_X86_FAMILY) +#if (defined(ARCH_CPU_X86) && defined(__SSE2__)) || defined(ARCH_CPU_X86_64) SIMD::PackOneRowOfRGBA8LittleToRA8(source, destination, pixels_per_row); #endif #if HAVE(MIPS_MSA_INTRINSICS) @@ -887,7 +887,7 @@ uint8_t>(const uint8_t* source, uint8_t* destination, unsigned pixels_per_row) { -#if defined(ARCH_CPU_X86_FAMILY) +#if (defined(ARCH_CPU_X86) && defined(__SSE2__)) || defined(ARCH_CPU_X86_64) SIMD::PackOneRowOfRGBA8LittleToRGBA8(source, destination, pixels_per_row); #endif #if HAVE(MIPS_MSA_INTRINSICS) diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/webrtc/common_audio/real_fourier.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/webrtc/common_audio/real_fourier.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/webrtc/common_audio/real_fourier.cc 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/webrtc/common_audio/real_fourier.cc 2017-12-25 17:05:49.957443890 +0100 @@ -14,6 +14,7 @@ #include "webrtc/common_audio/real_fourier_openmax.h" #include "webrtc/common_audio/signal_processing/include/signal_processing_library.h" #include "webrtc/rtc_base/checks.h" +#include "webrtc/system_wrappers/include/cpu_features_wrapper.h" namespace webrtc { @@ -23,7 +24,15 @@ std::unique_ptr RealFourier::Create(int fft_order) { #if defined(RTC_USE_OPENMAX_DL) +#if defined(WEBRTC_ARCH_X86_FAMILY) && !defined(__SSE2__) + // x86 CPU detection required. + if (WebRtc_GetCPUInfo(kSSE2)) + return std::unique_ptr(new RealFourierOpenmax(fft_order)); + else + return std::unique_ptr(new RealFourierOoura(fft_order)); +#else return std::unique_ptr(new RealFourierOpenmax(fft_order)); +#endif #else return std::unique_ptr(new RealFourierOoura(fft_order)); #endif diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/adaptive_fir_filter.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/adaptive_fir_filter.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/adaptive_fir_filter.cc 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/adaptive_fir_filter.cc 2017-12-29 20:06:31.389287437 +0100 @@ -14,7 +14,7 @@ #include #endif #include "webrtc/typedefs.h" -#if defined(WEBRTC_ARCH_X86_FAMILY) +#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__)) #include #endif #include @@ -59,7 +59,7 @@ } #endif -#if defined(WEBRTC_ARCH_X86_FAMILY) +#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__)) // Computes and stores the frequency response of the filter. void UpdateFrequencyResponse_SSE2( rtc::ArrayView H, @@ -111,7 +111,7 @@ } #endif -#if defined(WEBRTC_ARCH_X86_FAMILY) +#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__)) // Computes and stores the echo return loss estimate of the filter, which is the // sum of the partition frequency responses. void UpdateErlEstimator_SSE2( @@ -204,7 +204,7 @@ } #endif -#if defined(WEBRTC_ARCH_X86_FAMILY) +#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__)) // Adapts the filter partitions. (SSE2 variant) void AdaptPartitions_SSE2(const RenderBuffer& render_buffer, const FftData& G, @@ -345,7 +345,7 @@ } #endif -#if defined(WEBRTC_ARCH_X86_FAMILY) +#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__)) // Produces the filter output (SSE2 variant). void ApplyFilter_SSE2(const RenderBuffer& render_buffer, rtc::ArrayView H, @@ -445,7 +445,7 @@ FftData* S) const { RTC_DCHECK(S); switch (optimization_) { -#if defined(WEBRTC_ARCH_X86_FAMILY) +#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__)) case Aec3Optimization::kSse2: aec3::ApplyFilter_SSE2(render_buffer, H_, S); break; @@ -464,7 +464,7 @@ const FftData& G) { // Adapt the filter. switch (optimization_) { -#if defined(WEBRTC_ARCH_X86_FAMILY) +#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__)) case Aec3Optimization::kSse2: aec3::AdaptPartitions_SSE2(render_buffer, G, H_); break; @@ -483,7 +483,7 @@ // Update the frequency response and echo return loss for the filter. switch (optimization_) { -#if defined(WEBRTC_ARCH_X86_FAMILY) +#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__)) case Aec3Optimization::kSse2: aec3::UpdateFrequencyResponse_SSE2(H_, &H2_); aec3::UpdateErlEstimator_SSE2(H2_, &erl_); diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/adaptive_fir_filter.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/adaptive_fir_filter.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/adaptive_fir_filter.h 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/adaptive_fir_filter.h 2017-12-29 20:07:57.631963762 +0100 @@ -34,7 +34,7 @@ rtc::ArrayView H, std::vector>* H2); #endif -#if defined(WEBRTC_ARCH_X86_FAMILY) +#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__)) void UpdateFrequencyResponse_SSE2( rtc::ArrayView H, std::vector>* H2); @@ -50,7 +50,7 @@ const std::vector>& H2, std::array* erl); #endif -#if defined(WEBRTC_ARCH_X86_FAMILY) +#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__)) void UpdateErlEstimator_SSE2( const std::vector>& H2, std::array* erl); @@ -65,7 +65,7 @@ const FftData& G, rtc::ArrayView H); #endif -#if defined(WEBRTC_ARCH_X86_FAMILY) +#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__)) void AdaptPartitions_SSE2(const RenderBuffer& render_buffer, const FftData& G, rtc::ArrayView H); @@ -80,7 +80,7 @@ rtc::ArrayView H, FftData* S); #endif -#if defined(WEBRTC_ARCH_X86_FAMILY) +#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__)) void ApplyFilter_SSE2(const RenderBuffer& render_buffer, rtc::ArrayView H, FftData* S); diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/adaptive_fir_filter_unittest.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/adaptive_fir_filter_unittest.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/adaptive_fir_filter_unittest.cc 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/adaptive_fir_filter_unittest.cc 2017-12-29 20:07:08.465718378 +0100 @@ -15,7 +15,7 @@ #include #include #include "webrtc/typedefs.h" -#if defined(WEBRTC_ARCH_X86_FAMILY) +#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__)) #include #endif #include "webrtc/modules/audio_processing/aec3/aec3_fft.h" @@ -147,7 +147,7 @@ #endif -#if defined(WEBRTC_ARCH_X86_FAMILY) +#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__)) // Verifies that the optimized methods for filter adaptation are bitexact to // their reference counterparts. TEST(AdaptiveFirFilter, FilterAdaptationSse2Optimizations) { diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/aec3_common.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/aec3_common.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/aec3_common.cc 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/aec3_common.cc 2017-12-29 20:08:45.048236004 +0100 @@ -16,10 +16,8 @@ namespace webrtc { Aec3Optimization DetectOptimization() { -#if defined(WEBRTC_ARCH_X86_FAMILY) - if (WebRtc_GetCPUInfo(kSSE2) != 0) { - return Aec3Optimization::kSse2; - } +#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__)) + return Aec3Optimization::kSse2; #endif #if defined(WEBRTC_HAS_NEON) diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/comfort_noise_generator.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/comfort_noise_generator.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/comfort_noise_generator.cc 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/comfort_noise_generator.cc 2017-12-29 20:07:45.343152374 +0100 @@ -11,7 +11,7 @@ #include "webrtc/modules/audio_processing/aec3/comfort_noise_generator.h" #include "webrtc/typedefs.h" -#if defined(WEBRTC_ARCH_X86_FAMILY) +#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__)) #include #endif #include @@ -38,7 +38,7 @@ namespace aec3 { -#if defined(WEBRTC_ARCH_X86_FAMILY) +#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__)) void EstimateComfortNoise_SSE2(const std::array& N2, uint32_t* seed, @@ -204,7 +204,7 @@ N2_initial_ ? *N2_initial_ : N2_; switch (optimization_) { -#if defined(WEBRTC_ARCH_X86_FAMILY) +#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__)) case Aec3Optimization::kSse2: aec3::EstimateComfortNoise_SSE2(N2, &seed_, lower_band_noise, upper_band_noise); diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/comfort_noise_generator.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/comfort_noise_generator.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/comfort_noise_generator.h 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/comfort_noise_generator.h 2017-12-29 20:06:55.919910934 +0100 @@ -21,7 +21,7 @@ namespace webrtc { namespace aec3 { -#if defined(WEBRTC_ARCH_X86_FAMILY) +#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__)) void EstimateComfortNoise_SSE2(const std::array& N2, uint32_t* seed, diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/comfort_noise_generator_unittest.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/comfort_noise_generator_unittest.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/comfort_noise_generator_unittest.cc 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/comfort_noise_generator_unittest.cc 2017-12-29 20:06:11.867587061 +0100 @@ -50,7 +50,7 @@ #endif -#if defined(WEBRTC_ARCH_X86_FAMILY) +#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__)) // Verifies that the optimized methods are bitexact to their reference // counterparts. TEST(ComfortNoiseGenerator, TestOptimizations) { diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/fft_data.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/fft_data.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/fft_data.h 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/fft_data.h 2017-12-29 20:06:23.495408594 +0100 @@ -12,7 +12,7 @@ #define WEBRTC_MODULES_AUDIO_PROCESSING_AEC3_FFT_DATA_H_ #include "webrtc/typedefs.h" -#if defined(WEBRTC_ARCH_X86_FAMILY) +#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__)) #include #endif #include @@ -43,7 +43,7 @@ std::array* power_spectrum) const { RTC_DCHECK(power_spectrum); switch (optimization) { -#if defined(WEBRTC_ARCH_X86_FAMILY) +#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__)) case Aec3Optimization::kSse2: { constexpr int kNumFourBinBands = kFftLengthBy2 / 4; constexpr int kLimit = kNumFourBinBands * 4; diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/fft_data_unittest.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/fft_data_unittest.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/fft_data_unittest.cc 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/fft_data_unittest.cc 2017-12-29 20:06:45.873065136 +0100 @@ -16,7 +16,7 @@ namespace webrtc { -#if defined(WEBRTC_ARCH_X86_FAMILY) +#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__)) // Verifies that the optimized methods are bitexact to their reference // counterparts. TEST(FftData, TestOptimizations) { diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/matched_filter.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/matched_filter.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/matched_filter.cc 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/matched_filter.cc 2017-12-29 20:05:54.793849113 +0100 @@ -13,7 +13,7 @@ #include #endif #include "webrtc/typedefs.h" -#if defined(WEBRTC_ARCH_X86_FAMILY) +#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__)) #include #endif #include @@ -133,7 +133,7 @@ #endif -#if defined(WEBRTC_ARCH_X86_FAMILY) +#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__)) void MatchedFilterCore_SSE2(size_t x_start_index, float x2_sum_threshold, @@ -331,7 +331,7 @@ render_buffer.buffer.size(); switch (optimization_) { -#if defined(WEBRTC_ARCH_X86_FAMILY) +#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__)) case Aec3Optimization::kSse2: aec3::MatchedFilterCore_SSE2(x_start_index, x2_sum_threshold, render_buffer.buffer, y, filters_[n], diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/matched_filter.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/matched_filter.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/matched_filter.h 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/matched_filter.h 2017-12-29 20:08:03.879867867 +0100 @@ -36,7 +36,7 @@ #endif -#if defined(WEBRTC_ARCH_X86_FAMILY) +#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__)) // Filter core for the matched filter that is optimized for SSE2. void MatchedFilterCore_SSE2(size_t x_start_index, diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/matched_filter_unittest.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/matched_filter_unittest.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/matched_filter_unittest.cc 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/matched_filter_unittest.cc 2017-12-29 20:07:38.935250724 +0100 @@ -11,7 +11,7 @@ #include "webrtc/modules/audio_processing/aec3/matched_filter.h" #include "webrtc/typedefs.h" -#if defined(WEBRTC_ARCH_X86_FAMILY) +#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__)) #include #endif #include @@ -80,7 +80,7 @@ } #endif -#if defined(WEBRTC_ARCH_X86_FAMILY) +#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__)) // Verifies that the optimized methods for SSE2 are bitexact to their reference // counterparts. TEST(MatchedFilter, TestSse2Optimizations) { diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/suppression_gain.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/suppression_gain.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/suppression_gain.cc 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/suppression_gain.cc 2017-12-29 20:07:51.472058305 +0100 @@ -11,7 +11,7 @@ #include "webrtc/modules/audio_processing/aec3/suppression_gain.h" #include "webrtc/typedefs.h" -#if defined(WEBRTC_ARCH_X86_FAMILY) +#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__)) #include #endif #include diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/vector_math.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/vector_math.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/vector_math.h 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/vector_math.h 2017-12-29 20:07:15.035617541 +0100 @@ -15,7 +15,7 @@ #if defined(WEBRTC_HAS_NEON) #include #endif -#if defined(WEBRTC_ARCH_X86_FAMILY) +#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__)) #include #endif #include @@ -39,7 +39,7 @@ // Elementwise square root. void Sqrt(rtc::ArrayView x) { switch (optimization_) { -#if defined(WEBRTC_ARCH_X86_FAMILY) +#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__)) case Aec3Optimization::kSse2: { const int x_size = static_cast(x.size()); const int vector_limit = x_size >> 2; @@ -113,7 +113,7 @@ RTC_DCHECK_EQ(z.size(), x.size()); RTC_DCHECK_EQ(z.size(), y.size()); switch (optimization_) { -#if defined(WEBRTC_ARCH_X86_FAMILY) +#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__)) case Aec3Optimization::kSse2: { const int x_size = static_cast(x.size()); const int vector_limit = x_size >> 2; @@ -159,7 +159,7 @@ void Accumulate(rtc::ArrayView x, rtc::ArrayView z) { RTC_DCHECK_EQ(z.size(), x.size()); switch (optimization_) { -#if defined(WEBRTC_ARCH_X86_FAMILY) +#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__)) case Aec3Optimization::kSse2: { const int x_size = static_cast(x.size()); const int vector_limit = x_size >> 2; diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/vector_math_unittest.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/vector_math_unittest.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/vector_math_unittest.cc 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/third_party/webrtc/modules/audio_processing/aec3/vector_math_unittest.cc 2017-12-29 20:06:38.812173509 +0100 @@ -77,7 +77,7 @@ } #endif -#if defined(WEBRTC_ARCH_X86_FAMILY) +#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__)) TEST(VectorMath, Sqrt) { if (WebRtc_GetCPUInfo(kSSE2) != 0) { diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/BUILD.gn qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/BUILD.gn --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/BUILD.gn 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/BUILD.gn 2017-12-26 01:32:06.842303361 +0100 @@ -116,9 +116,9 @@ v8_experimental_extra_library_files = [ "//test/cctest/test-experimental-extra.js" ] - v8_enable_gdbjit = - ((v8_current_cpu == "x86" || v8_current_cpu == "x64") && - (is_linux || is_mac)) || (v8_current_cpu == "ppc64" && is_linux) + v8_enable_gdbjit = ((v8_current_cpu == "x86" || v8_current_cpu == "x64" || + v8_current_cpu == "x87") && (is_linux || is_mac)) || + (v8_current_cpu == "ppc64" && is_linux) # Temporary flag to allow embedders to update their microtasks scopes # while rolling in a new version of V8. @@ -161,7 +161,7 @@ include_dirs = [ "." ] - if (is_component_build) { + if (is_component_build || v8_build_shared) { defines = [ "BUILDING_V8_SHARED" ] } } @@ -175,14 +175,14 @@ # This config should be applied to code using the libplatform. config("libplatform_config") { include_dirs = [ "include" ] - if (is_component_build) { + if (is_component_build || v8_build_shared) { defines = [ "USING_V8_PLATFORM_SHARED" ] } } # This config should be applied to code using the libbase. config("libbase_config") { - if (is_component_build) { + if (is_component_build || v8_build_shared) { defines = [ "USING_V8_BASE_SHARED" ] } libs = [] @@ -199,7 +199,7 @@ # This config should only be applied to code using V8 and not any V8 code # itself. config("external_config") { - if (is_component_build) { + if (is_component_build || v8_build_shared) { defines = [ "USING_V8_SHARED" ] } include_dirs = [ @@ -434,6 +434,9 @@ cflags += [ "/arch:SSE2" ] } } + if (v8_current_cpu == "x87") { + defines += [ "V8_TARGET_ARCH_X87" ] + } if (v8_current_cpu == "x64") { defines += [ "V8_TARGET_ARCH_X64" ] if (is_win) { @@ -443,6 +446,9 @@ ldflags += [ "/STACK:2097152" ] } } + if (v8_current_cpu == "x87") { + defines += [ "V8_TARGET_ARCH_X87" ] + } if (is_android && v8_android_log_stdout) { defines += [ "V8_ANDROID_LOG_STDOUT" ] } @@ -1040,6 +1046,11 @@ ### gcmole(arch:s390) ### "src/builtins/s390/builtins-s390.cc", ] + } else if (v8_current_cpu == "x87") { + sources += [ + ### gcmole(arch:x87) ### + "src/builtins/x87/builtins-x87.cc", + ] } if (!v8_enable_i18n_support) { @@ -2309,6 +2320,37 @@ "src/s390/simulator-s390.cc", "src/s390/simulator-s390.h", ] + } else if (v8_current_cpu == "x87") { + sources += [ ### gcmole(arch:x87) ### + "src/compiler/x87/code-generator-x87.cc", + "src/compiler/x87/instruction-codes-x87.h", + "src/compiler/x87/instruction-scheduler-x87.cc", + "src/compiler/x87/instruction-selector-x87.cc", + "src/debug/x87/debug-x87.cc", + "src/full-codegen/x87/full-codegen-x87.cc", + "src/ic/x87/access-compiler-x87.cc", + "src/ic/x87/handler-compiler-x87.cc", + "src/ic/x87/ic-x87.cc", + "src/regexp/x87/regexp-macro-assembler-x87.cc", + "src/regexp/x87/regexp-macro-assembler-x87.h", + "src/x87/assembler-x87-inl.h", + "src/x87/assembler-x87.cc", + "src/x87/assembler-x87.h", + "src/x87/code-stubs-x87.cc", + "src/x87/code-stubs-x87.h", + "src/x87/codegen-x87.cc", + "src/x87/codegen-x87.h", + "src/x87/cpu-x87.cc", + "src/x87/deoptimizer-x87.cc", + "src/x87/disasm-x87.cc", + "src/x87/frames-x87.cc", + "src/x87/frames-x87.h", + "src/x87/interface-descriptors-x87.cc", + "src/x87/macro-assembler-x87.cc", + "src/x87/macro-assembler-x87.h", + "src/x87/simulator-x87.cc", + "src/x87/simulator-x87.h", + ] } configs = [ ":internal_config" ] @@ -2420,7 +2462,7 @@ defines = [] - if (is_component_build) { + if (is_component_build || v8_build_shared) { defines = [ "BUILDING_V8_BASE_SHARED" ] } @@ -2530,7 +2572,7 @@ configs = [ ":internal_config_base" ] - if (is_component_build) { + if (is_component_build || v8_build_shared) { defines = [ "BUILDING_V8_PLATFORM_SHARED" ] } @@ -2676,7 +2718,37 @@ ] } -if (is_component_build) { +if (v8_build_shared) { + shared_library("v8") { + sources = [ + "src/v8dll-main.cc", + ] + + public_deps = [ + ":v8_base", + ":v8_maybe_snapshot", + ] + + configs += [ ":internal_config" ] + + public_configs = [ ":external_config" ] + } + + group("v8_for_testing") { + testonly = true + + public_deps = [ + ":v8_base", + ":v8_maybe_snapshot", + ] + + if (v8_use_snapshot) { + public_deps += [ ":v8_builtins_generators" ] + } + + public_configs = [ ":external_config" ] + } +} else if (is_component_build) { v8_component("v8") { sources = [ "src/v8dll-main.cc", diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/gni/v8.gni qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/gni/v8.gni --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/gni/v8.gni 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/gni/v8.gni 2017-12-25 18:02:06.095884970 +0100 @@ -42,6 +42,9 @@ # add a dependency on the ICU library. v8_enable_i18n_support = true + # Whether to build V8 as a shared library + v8_build_shared = false + # Use static libraries instead of source_sets. v8_static_library = false } @@ -56,6 +59,11 @@ v8_enable_backtrace = is_debug && !v8_optimized_debug } +if (v8_current_cpu == "x86" || v8_current_cpu == "x87") { + # build V8 shared on x86 so we can swap x87 vs. SSE2 builds + v8_build_shared = true +} + # Points to // in v8 stand-alone or to //v8/ in chromium. We need absolute # paths for all configs in templates as they are shared in different # subdirectories. diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/gypfiles/standalone.gypi qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/gypfiles/standalone.gypi --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/gypfiles/standalone.gypi 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/gypfiles/standalone.gypi 2017-12-25 17:42:57.200465867 +0100 @@ -262,14 +262,14 @@ # goma doesn't support PDB yet. 'fastbuild%': 1, }], - ['((v8_target_arch=="ia32" or v8_target_arch=="x64") and \ + ['((v8_target_arch=="ia32" or v8_target_arch=="x64" or v8_target_arch=="x87") and \ (OS=="linux" or OS=="mac")) or (v8_target_arch=="ppc64" and OS=="linux")', { 'v8_enable_gdbjit%': 1, }, { 'v8_enable_gdbjit%': 0, }], ['(OS=="linux" or OS=="mac") and (target_arch=="ia32" or target_arch=="x64") and \ - v8_target_arch!="x32"', { + (v8_target_arch!="x87" and v8_target_arch!="x32")', { 'clang%': 1, }, { 'clang%': 0, @@ -1207,7 +1207,7 @@ '-L<(android_libcpp_libs)/arm64-v8a', ], }], - ['target_arch=="ia32"', { + ['target_arch=="ia32" or target_arch=="x87"', { # The x86 toolchain currently has problems with stack-protector. 'cflags!': [ '-fstack-protector', diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/gypfiles/toolchain.gypi qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/gypfiles/toolchain.gypi --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/gypfiles/toolchain.gypi 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/gypfiles/toolchain.gypi 2017-12-25 17:42:57.200465867 +0100 @@ -144,7 +144,7 @@ 'host_cxx_is_biarch%': 0, }, }], - ['target_arch=="ia32" or target_arch=="x64" or \ + ['target_arch=="ia32" or target_arch=="x64" or target_arch=="x87" or \ target_arch=="ppc" or target_arch=="ppc64" or target_arch=="s390" or \ target_arch=="s390x" or clang==1', { 'variables': { @@ -342,6 +342,12 @@ 'V8_TARGET_ARCH_IA32', ], }], # v8_target_arch=="ia32" + ['v8_target_arch=="x87"', { + 'defines': [ + 'V8_TARGET_ARCH_X87', + ], + 'cflags': ['-march=i586'], + }], # v8_target_arch=="x87" ['v8_target_arch=="mips" or v8_target_arch=="mipsel" \ or v8_target_arch=="mips64" or v8_target_arch=="mips64el"', { 'target_conditions': [ @@ -1000,8 +1006,9 @@ ['(OS=="linux" or OS=="freebsd" or OS=="openbsd" or OS=="solaris" \ or OS=="netbsd" or OS=="mac" or OS=="android" or OS=="qnx") and \ (v8_target_arch=="arm" or v8_target_arch=="ia32" or \ - v8_target_arch=="mips" or v8_target_arch=="mipsel" or \ - v8_target_arch=="ppc" or v8_target_arch=="s390")', { + v8_target_arch=="x87" or v8_target_arch=="mips" or \ + v8_target_arch=="mipsel" or v8_target_arch=="ppc" or \ + v8_target_arch=="s390")', { 'target_conditions': [ ['_toolset=="host"', { 'conditions': [ diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/Makefile qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/Makefile --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/Makefile 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/Makefile 2017-12-25 17:42:57.200465867 +0100 @@ -255,13 +255,14 @@ # Architectures and modes to be compiled. Consider these to be internal # variables, don't override them (use the targets instead). -ARCHES = ia32 x64 arm arm64 mips mipsel mips64 mips64el ppc ppc64 s390 s390x -ARCHES32 = ia32 arm mips mipsel ppc s390 +ARCHES = ia32 x64 arm arm64 mips mipsel mips64 mips64el x87 ppc ppc64 s390 \ + s390x +ARCHES32 = ia32 arm mips mipsel x87 ppc s390 DEFAULT_ARCHES = ia32 x64 arm MODES = release debug optdebug DEFAULT_MODES = release debug ANDROID_ARCHES = android_ia32 android_x64 android_arm android_arm64 \ - android_mipsel + android_mipsel android_x87 # List of files that trigger Makefile regeneration: GYPFILES = third_party/icu/icu.gypi third_party/icu/icu.gyp \ diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/assembler.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/assembler.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/assembler.cc 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/assembler.cc 2017-12-25 17:42:57.201465852 +0100 @@ -85,6 +85,8 @@ #include "src/regexp/mips64/regexp-macro-assembler-mips64.h" // NOLINT #elif V8_TARGET_ARCH_S390 #include "src/regexp/s390/regexp-macro-assembler-s390.h" // NOLINT +#elif V8_TARGET_ARCH_X87 +#include "src/regexp/x87/regexp-macro-assembler-x87.h" // NOLINT #else // Unknown architecture. #error "Unknown architecture." #endif // Target architecture. @@ -1318,6 +1320,8 @@ function = FUNCTION_ADDR(RegExpMacroAssemblerMIPS::CheckStackGuardState); #elif V8_TARGET_ARCH_S390 function = FUNCTION_ADDR(RegExpMacroAssemblerS390::CheckStackGuardState); +#elif V8_TARGET_ARCH_X87 + function = FUNCTION_ADDR(RegExpMacroAssemblerX87::CheckStackGuardState); #else UNREACHABLE(); #endif diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/assembler-inl.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/assembler-inl.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/assembler-inl.h 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/assembler-inl.h 2017-12-25 17:42:57.201465852 +0100 @@ -23,6 +23,8 @@ #include "src/mips64/assembler-mips64-inl.h" #elif V8_TARGET_ARCH_S390 #include "src/s390/assembler-s390-inl.h" +#elif V8_TARGET_ARCH_X87 +#include "src/x87/assembler-x87-inl.h" #else #error Unknown architecture. #endif diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/base/build_config.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/base/build_config.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/base/build_config.h 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/base/build_config.h 2017-12-25 17:42:57.201465852 +0100 @@ -76,9 +76,9 @@ // Target architecture detection. This may be set externally. If not, detect // in the same way as the host architecture, that is, target the native // environment as presented by the compiler. -#if !V8_TARGET_ARCH_X64 && !V8_TARGET_ARCH_IA32 && !V8_TARGET_ARCH_ARM && \ - !V8_TARGET_ARCH_ARM64 && !V8_TARGET_ARCH_MIPS && !V8_TARGET_ARCH_MIPS64 && \ - !V8_TARGET_ARCH_PPC && !V8_TARGET_ARCH_S390 +#if !V8_TARGET_ARCH_X64 && !V8_TARGET_ARCH_IA32 && !V8_TARGET_ARCH_X87 && \ + !V8_TARGET_ARCH_ARM && !V8_TARGET_ARCH_ARM64 && !V8_TARGET_ARCH_MIPS && \ + !V8_TARGET_ARCH_MIPS64 && !V8_TARGET_ARCH_PPC && !V8_TARGET_ARCH_S390 #if defined(_M_X64) || defined(__x86_64__) #define V8_TARGET_ARCH_X64 1 #elif defined(_M_IX86) || defined(__i386__) @@ -129,6 +129,8 @@ #else #define V8_TARGET_ARCH_32_BIT 1 #endif +#elif V8_TARGET_ARCH_X87 +#define V8_TARGET_ARCH_32_BIT 1 #else #error Unknown target architecture pointer size #endif @@ -179,6 +181,8 @@ #else #define V8_TARGET_LITTLE_ENDIAN 1 #endif +#elif V8_TARGET_ARCH_X87 +#define V8_TARGET_LITTLE_ENDIAN 1 #elif __BIG_ENDIAN__ // FOR PPCGR on AIX #define V8_TARGET_BIG_ENDIAN 1 #elif V8_TARGET_ARCH_PPC_LE @@ -195,7 +199,8 @@ #error Unknown target architecture endianness #endif -#if defined(V8_TARGET_ARCH_IA32) || defined(V8_TARGET_ARCH_X64) +#if defined(V8_TARGET_ARCH_IA32) || defined(V8_TARGET_ARCH_X64) || \ + defined(V8_TARGET_ARCH_X87) #define V8_TARGET_ARCH_STORES_RETURN_ADDRESS_ON_STACK 1 #else #define V8_TARGET_ARCH_STORES_RETURN_ADDRESS_ON_STACK 0 diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/builtins/x87/builtins-x87.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/builtins/x87/builtins-x87.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/builtins/x87/builtins-x87.cc 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/builtins/x87/builtins-x87.cc 2017-12-29 01:00:28.359896864 +0100 @@ -0,0 +1,3008 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#if V8_TARGET_ARCH_X87 + +#include "src/code-factory.h" +#include "src/codegen.h" +#include "src/deoptimizer.h" +#include "src/full-codegen/full-codegen.h" +#include "src/x87/frames-x87.h" + +namespace v8 { +namespace internal { + +#define __ ACCESS_MASM(masm) + +void Builtins::Generate_Adaptor(MacroAssembler* masm, Address address, + ExitFrameType exit_frame_type) { + // ----------- S t a t e ------------- + // -- eax : number of arguments excluding receiver + // -- edi : target + // -- edx : new.target + // -- esp[0] : return address + // -- esp[4] : last argument + // -- ... + // -- esp[4 * argc] : first argument + // -- esp[4 * (argc +1)] : receiver + // ----------------------------------- + __ AssertFunction(edi); + + // Make sure we operate in the context of the called function (for example + // ConstructStubs implemented in C++ will be run in the context of the caller + // instead of the callee, due to the way that [[Construct]] is defined for + // ordinary functions). + __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); + + // JumpToExternalReference expects eax to contain the number of arguments + // including the receiver and the extra arguments. + const int num_extra_args = 3; + __ add(eax, Immediate(num_extra_args + 1)); + + // Insert extra arguments. + __ PopReturnAddressTo(ecx); + __ SmiTag(eax); + __ Push(eax); + __ SmiUntag(eax); + __ Push(edi); + __ Push(edx); + __ PushReturnAddressFrom(ecx); + + __ JumpToExternalReference(ExternalReference(address, masm->isolate()), + exit_frame_type == BUILTIN_EXIT); +} + +static void GenerateTailCallToReturnedCode(MacroAssembler* masm, + Runtime::FunctionId function_id) { + // ----------- S t a t e ------------- + // -- eax : argument count (preserved for callee) + // -- edx : new target (preserved for callee) + // -- edi : target function (preserved for callee) + // ----------------------------------- + { + FrameScope scope(masm, StackFrame::INTERNAL); + // Push the number of arguments to the callee. + __ SmiTag(eax); + __ push(eax); + // Push a copy of the target function and the new target. + __ push(edi); + __ push(edx); + // Function is also the parameter to the runtime call. + __ push(edi); + + __ CallRuntime(function_id, 1); + __ mov(ebx, eax); + + // Restore target function and new target. + __ pop(edx); + __ pop(edi); + __ pop(eax); + __ SmiUntag(eax); + } + + __ lea(ebx, FieldOperand(ebx, Code::kHeaderSize)); + __ jmp(ebx); +} + +static void GenerateTailCallToSharedCode(MacroAssembler* masm) { + __ mov(ebx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); + __ mov(ebx, FieldOperand(ebx, SharedFunctionInfo::kCodeOffset)); + __ lea(ebx, FieldOperand(ebx, Code::kHeaderSize)); + __ jmp(ebx); +} + +namespace { + +void Generate_JSBuiltinsConstructStubHelper(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- eax: number of arguments + // -- edi: constructor function + // -- edx: new target + // -- esi: context + // ----------------------------------- + + // Enter a construct frame. + { + FrameScope scope(masm, StackFrame::CONSTRUCT); + + // Preserve the incoming parameters on the stack. + __ SmiTag(eax); + __ push(esi); + __ push(eax); + __ SmiUntag(eax); + + // The receiver for the builtin/api call. + __ PushRoot(Heap::kTheHoleValueRootIndex); + + // Set up pointer to last argument. + __ lea(ebx, Operand(ebp, StandardFrameConstants::kCallerSPOffset)); + + // Copy arguments and receiver to the expression stack. + Label loop, entry; + __ mov(ecx, eax); + // ----------- S t a t e ------------- + // -- eax: number of arguments (untagged) + // -- edi: constructor function + // -- edx: new target + // -- ebx: pointer to last argument + // -- ecx: counter + // -- sp[0*kPointerSize]: the hole (receiver) + // -- sp[1*kPointerSize]: number of arguments (tagged) + // -- sp[2*kPointerSize]: context + // ----------------------------------- + __ jmp(&entry); + __ bind(&loop); + __ push(Operand(ebx, ecx, times_4, 0)); + __ bind(&entry); + __ dec(ecx); + __ j(greater_equal, &loop); + + // Call the function. + // eax: number of arguments (untagged) + // edi: constructor function + // edx: new target + ParameterCount actual(eax); + __ InvokeFunction(edi, edx, actual, CALL_FUNCTION, + CheckDebugStepCallWrapper()); + + // Restore context from the frame. + __ mov(esi, Operand(ebp, ConstructFrameConstants::kContextOffset)); + // Restore smi-tagged arguments count from the frame. + __ mov(ebx, Operand(ebp, ConstructFrameConstants::kLengthOffset)); + // Leave construct frame. + } + + // Remove caller arguments from the stack and return. + STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0); + __ pop(ecx); + __ lea(esp, Operand(esp, ebx, times_2, 1 * kPointerSize)); // 1 ~ receiver + __ push(ecx); + __ ret(0); +} + +// The construct stub for ES5 constructor functions and ES6 class constructors. +void Generate_JSConstructStubGeneric(MacroAssembler* masm, + bool restrict_constructor_return) { + // ----------- S t a t e ------------- + // -- eax: number of arguments (untagged) + // -- edi: constructor function + // -- edx: new target + // -- esi: context + // -- sp[...]: constructor arguments + // ----------------------------------- + + // Enter a construct frame. + { + FrameScope scope(masm, StackFrame::CONSTRUCT); + Label post_instantiation_deopt_entry, not_create_implicit_receiver; + + // Preserve the incoming parameters on the stack. + __ mov(ecx, eax); + __ SmiTag(ecx); + __ Push(esi); + __ Push(ecx); + __ Push(edi); + __ Push(edx); + + // ----------- S t a t e ------------- + // -- sp[0*kPointerSize]: new target + // -- edi and sp[1*kPointerSize]: constructor function + // -- sp[2*kPointerSize]: argument count + // -- sp[3*kPointerSize]: context + // ----------------------------------- + + __ mov(ebx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); + __ test(FieldOperand(ebx, SharedFunctionInfo::kCompilerHintsOffset), + Immediate(SharedFunctionInfo::kDerivedConstructorMask)); + __ j(not_zero, ¬_create_implicit_receiver); + + // If not derived class constructor: Allocate the new receiver object. + __ IncrementCounter(masm->isolate()->counters()->constructed_objects(), 1); + __ Call(masm->isolate()->builtins()->FastNewObject(), + RelocInfo::CODE_TARGET); + __ jmp(&post_instantiation_deopt_entry, Label::kNear); + + // Else: use TheHoleValue as receiver for constructor call + __ bind(¬_create_implicit_receiver); + __ LoadRoot(eax, Heap::kTheHoleValueRootIndex); + + // ----------- S t a t e ------------- + // -- eax: implicit receiver + // -- Slot 3 / sp[0*kPointerSize]: new target + // -- Slot 2 / sp[1*kPointerSize]: constructor function + // -- Slot 1 / sp[2*kPointerSize]: number of arguments (tagged) + // -- Slot 0 / sp[3*kPointerSize]: context + // ----------------------------------- + // Deoptimizer enters here. + masm->isolate()->heap()->SetConstructStubCreateDeoptPCOffset( + masm->pc_offset()); + __ bind(&post_instantiation_deopt_entry); + + // Restore new target. + __ Pop(edx); + + // Push the allocated receiver to the stack. We need two copies + // because we may have to return the original one and the calling + // conventions dictate that the called function pops the receiver. + __ Push(eax); + __ Push(eax); + + // ----------- S t a t e ------------- + // -- edx: new target + // -- sp[0*kPointerSize]: implicit receiver + // -- sp[1*kPointerSize]: implicit receiver + // -- sp[2*kPointerSize]: constructor function + // -- sp[3*kPointerSize]: number of arguments (tagged) + // -- sp[4*kPointerSize]: context + // ----------------------------------- + + // Restore constructor function and argument count. + __ mov(edi, Operand(ebp, ConstructFrameConstants::kConstructorOffset)); + __ mov(eax, Operand(ebp, ConstructFrameConstants::kLengthOffset)); + __ SmiUntag(eax); + + // Set up pointer to last argument. + __ lea(ebx, Operand(ebp, StandardFrameConstants::kCallerSPOffset)); + + // Copy arguments and receiver to the expression stack. + Label loop, entry; + __ mov(ecx, eax); + // ----------- S t a t e ------------- + // -- eax: number of arguments (untagged) + // -- edx: new target + // -- ebx: pointer to last argument + // -- ecx: counter (tagged) + // -- sp[0*kPointerSize]: implicit receiver + // -- sp[1*kPointerSize]: implicit receiver + // -- edi and sp[2*kPointerSize]: constructor function + // -- sp[3*kPointerSize]: number of arguments (tagged) + // -- sp[4*kPointerSize]: context + // ----------------------------------- + __ jmp(&entry, Label::kNear); + __ bind(&loop); + __ Push(Operand(ebx, ecx, times_pointer_size, 0)); + __ bind(&entry); + __ dec(ecx); + __ j(greater_equal, &loop); + + // Call the function. + ParameterCount actual(eax); + __ InvokeFunction(edi, edx, actual, CALL_FUNCTION, + CheckDebugStepCallWrapper()); + + // ----------- S t a t e ------------- + // -- eax: constructor result + // -- sp[0*kPointerSize]: implicit receiver + // -- sp[1*kPointerSize]: constructor function + // -- sp[2*kPointerSize]: number of arguments + // -- sp[3*kPointerSize]: context + // ----------------------------------- + + // Store offset of return address for deoptimizer. + masm->isolate()->heap()->SetConstructStubInvokeDeoptPCOffset( + masm->pc_offset()); + + // Restore context from the frame. + __ mov(esi, Operand(ebp, ConstructFrameConstants::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, do_throw, other_result, leave_frame; + + // If the result is undefined, we jump out to using the implicit receiver. + __ JumpIfRoot(eax, Heap::kUndefinedValueRootIndex, &use_receiver, + Label::kNear); + + // Otherwise we do a smi check and fall through to check if the return value + // is a valid receiver. + + // If the result is a smi, it is *not* an object in the ECMA sense. + __ JumpIfSmi(eax, &other_result, Label::kNear); + + // If the type of the result (stored in its map) is less than + // FIRST_JS_RECEIVER_TYPE, it is not an object in the ECMA sense. + STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE); + __ CmpObjectType(eax, FIRST_JS_RECEIVER_TYPE, ecx); + __ j(above_equal, &leave_frame, Label::kNear); + + // The result is now neither undefined nor an object. + __ bind(&other_result); + __ mov(ebx, Operand(ebp, ConstructFrameConstants::kConstructorOffset)); + __ mov(ebx, FieldOperand(ebx, JSFunction::kSharedFunctionInfoOffset)); + __ test(FieldOperand(ebx, SharedFunctionInfo::kCompilerHintsOffset), + Immediate(SharedFunctionInfo::kClassConstructorMask)); + + if (restrict_constructor_return) { + // Throw if constructor function is a class constructor + __ j(Condition::zero, &use_receiver, Label::kNear); + } else { + __ j(not_zero, &use_receiver, Label::kNear); + __ CallRuntime( + Runtime::kIncrementUseCounterConstructorReturnNonUndefinedPrimitive); + __ jmp(&use_receiver, Label::kNear); + } + + __ bind(&do_throw); + __ CallRuntime(Runtime::kThrowConstructorReturnedNonObject); + + // Throw away the result of the constructor invocation and use the + // on-stack receiver as the result. + __ bind(&use_receiver); + __ mov(eax, Operand(esp, 0 * kPointerSize)); + __ JumpIfRoot(eax, Heap::kTheHoleValueRootIndex, &do_throw); + + __ bind(&leave_frame); + // Restore smi-tagged arguments count from the frame. + __ mov(ebx, Operand(ebp, ConstructFrameConstants::kLengthOffset)); + // Leave construct frame. + } + // Remove caller arguments from the stack and return. + STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0); + __ pop(ecx); + __ lea(esp, Operand(esp, ebx, times_2, 1 * kPointerSize)); // 1 ~ receiver + __ push(ecx); + __ ret(0); +} +} // namespace + +void Builtins::Generate_JSConstructStubGenericRestrictedReturn( + MacroAssembler* masm) { + return Generate_JSConstructStubGeneric(masm, true); +} +void Builtins::Generate_JSConstructStubGenericUnrestrictedReturn( + MacroAssembler* masm) { + return Generate_JSConstructStubGeneric(masm, false); +} +void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) { + Generate_JSBuiltinsConstructStubHelper(masm); +} +void Builtins::Generate_JSBuiltinsConstructStub(MacroAssembler* masm) { + Generate_JSBuiltinsConstructStubHelper(masm); +} + +void Builtins::Generate_ConstructedNonConstructable(MacroAssembler* masm) { + FrameScope scope(masm, StackFrame::INTERNAL); + __ push(edi); + __ CallRuntime(Runtime::kThrowConstructedNonConstructable); +} + +enum IsTagged { kEaxIsSmiTagged, kEaxIsUntaggedInt }; + +// Clobbers ecx, edx, edi; preserves all other registers. +static void Generate_CheckStackOverflow(MacroAssembler* masm, + IsTagged eax_is_tagged) { + // eax : the number of items to be pushed to the stack + // + // Check the stack for overflow. We are not trying to catch + // interruptions (e.g. debug break and preemption) here, so the "real stack + // limit" is checked. + Label okay; + ExternalReference real_stack_limit = + ExternalReference::address_of_real_stack_limit(masm->isolate()); + __ mov(edi, Operand::StaticVariable(real_stack_limit)); + // Make ecx the space we have left. The stack might already be overflowed + // here which will cause ecx to become negative. + __ mov(ecx, esp); + __ sub(ecx, edi); + // Make edx the space we need for the array when it is unrolled onto the + // stack. + __ mov(edx, eax); + int smi_tag = eax_is_tagged == kEaxIsSmiTagged ? kSmiTagSize : 0; + __ shl(edx, kPointerSizeLog2 - smi_tag); + // Check if the arguments will overflow the stack. + __ cmp(ecx, edx); + __ j(greater, &okay); // Signed comparison. + + // Out of stack space. + __ CallRuntime(Runtime::kThrowStackOverflow); + + __ bind(&okay); +} + +static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, + bool is_construct) { + ProfileEntryHookStub::MaybeCallEntryHook(masm); + + { + FrameScope scope(masm, StackFrame::INTERNAL); + + // Setup the context (we need to use the caller context from the isolate). + ExternalReference context_address(IsolateAddressId::kContextAddress, + masm->isolate()); + __ mov(esi, Operand::StaticVariable(context_address)); + + // Load the previous frame pointer (ebx) to access C arguments + __ mov(ebx, Operand(ebp, 0)); + + // Push the function and the receiver onto the stack. + __ push(Operand(ebx, EntryFrameConstants::kFunctionArgOffset)); + __ push(Operand(ebx, EntryFrameConstants::kReceiverArgOffset)); + + // Load the number of arguments and setup pointer to the arguments. + __ mov(eax, Operand(ebx, EntryFrameConstants::kArgcOffset)); + __ mov(ebx, Operand(ebx, EntryFrameConstants::kArgvOffset)); + + // Check if we have enough stack space to push all arguments. + // Expects argument count in eax. Clobbers ecx, edx, edi. + Generate_CheckStackOverflow(masm, kEaxIsUntaggedInt); + + // Copy arguments to the stack in a loop. + Label loop, entry; + __ Move(ecx, Immediate(0)); + __ jmp(&entry, Label::kNear); + __ bind(&loop); + __ mov(edx, Operand(ebx, ecx, times_4, 0)); // push parameter from argv + __ push(Operand(edx, 0)); // dereference handle + __ inc(ecx); + __ bind(&entry); + __ cmp(ecx, eax); + __ j(not_equal, &loop); + + // Load the previous frame pointer (ebx) to access C arguments + __ mov(ebx, Operand(ebp, 0)); + + // Get the new.target and function from the frame. + __ mov(edx, Operand(ebx, EntryFrameConstants::kNewTargetArgOffset)); + __ mov(edi, Operand(ebx, EntryFrameConstants::kFunctionArgOffset)); + + // Invoke the code. + Handle builtin = is_construct + ? masm->isolate()->builtins()->Construct() + : masm->isolate()->builtins()->Call(); + __ Call(builtin, RelocInfo::CODE_TARGET); + + // Exit the internal frame. Notice that this also removes the empty. + // context and the function left on the stack by the code + // invocation. + } + __ ret(kPointerSize); // Remove receiver. +} + +void Builtins::Generate_JSEntryTrampoline(MacroAssembler* masm) { + Generate_JSEntryTrampolineHelper(masm, false); +} + +void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) { + Generate_JSEntryTrampolineHelper(masm, true); +} + +// static +void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- eax : the value to pass to the generator + // -- ebx : the JSGeneratorObject to resume + // -- edx : the resume mode (tagged) + // -- esp[0] : return address + // ----------------------------------- + __ AssertGeneratorObject(ebx); + + // Store input value into generator object. + __ mov(FieldOperand(ebx, JSGeneratorObject::kInputOrDebugPosOffset), eax); + __ RecordWriteField(ebx, JSGeneratorObject::kInputOrDebugPosOffset, eax, ecx, + kDontSaveFPRegs); + + // Store resume mode into generator object. + __ mov(FieldOperand(ebx, JSGeneratorObject::kResumeModeOffset), edx); + + // Load suspended function and context. + __ mov(edi, FieldOperand(ebx, JSGeneratorObject::kFunctionOffset)); + __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); + + // Flood function if we are stepping. + Label prepare_step_in_if_stepping, prepare_step_in_suspended_generator; + Label stepping_prepared; + ExternalReference debug_hook = + ExternalReference::debug_hook_on_function_call_address(masm->isolate()); + __ cmpb(Operand::StaticVariable(debug_hook), Immediate(0)); + __ j(not_equal, &prepare_step_in_if_stepping); + + // Flood function if we need to continue stepping in the suspended generator. + ExternalReference debug_suspended_generator = + ExternalReference::debug_suspended_generator_address(masm->isolate()); + __ cmp(ebx, Operand::StaticVariable(debug_suspended_generator)); + __ j(equal, &prepare_step_in_suspended_generator); + __ bind(&stepping_prepared); + + // Pop return address. + __ PopReturnAddressTo(eax); + + // Push receiver. + __ Push(FieldOperand(ebx, JSGeneratorObject::kReceiverOffset)); + + // ----------- S t a t e ------------- + // -- eax : return address + // -- ebx : the JSGeneratorObject to resume + // -- edx : the resume mode (tagged) + // -- edi : generator function + // -- esi : generator context + // -- esp[0] : generator receiver + // ----------------------------------- + + // Push holes for arguments to generator function. Since the parser forced + // context allocation for any variables in generators, the actual argument + // values have already been copied into the context and these dummy values + // will never be used. + __ mov(ecx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); + __ mov(ecx, + FieldOperand(ecx, SharedFunctionInfo::kFormalParameterCountOffset)); + { + Label done_loop, loop; + __ bind(&loop); + __ sub(ecx, Immediate(1)); + __ j(carry, &done_loop, Label::kNear); + __ PushRoot(Heap::kTheHoleValueRootIndex); + __ jmp(&loop); + __ bind(&done_loop); + } + + // Underlying function needs to have bytecode available. + if (FLAG_debug_code) { + __ mov(ecx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); + __ mov(ecx, FieldOperand(ecx, SharedFunctionInfo::kFunctionDataOffset)); + __ CmpObjectType(ecx, BYTECODE_ARRAY_TYPE, ecx); + __ Assert(equal, kMissingBytecodeArray); + } + + // Resume (Ignition/TurboFan) generator object. + { + __ PushReturnAddressFrom(eax); + __ mov(eax, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); + __ mov(eax, + FieldOperand(eax, SharedFunctionInfo::kFormalParameterCountOffset)); + // We abuse new.target both to indicate that this is a resume call and to + // pass in the generator object. In ordinary calls, new.target is always + // undefined because generator functions are non-constructable. + __ mov(edx, ebx); + __ jmp(FieldOperand(edi, JSFunction::kCodeEntryOffset)); + } + + __ bind(&prepare_step_in_if_stepping); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ Push(ebx); + __ Push(edx); + __ Push(edi); + __ CallRuntime(Runtime::kDebugOnFunctionCall); + __ Pop(edx); + __ Pop(ebx); + __ mov(edi, FieldOperand(ebx, JSGeneratorObject::kFunctionOffset)); + } + __ jmp(&stepping_prepared); + + __ bind(&prepare_step_in_suspended_generator); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ Push(ebx); + __ Push(edx); + __ CallRuntime(Runtime::kDebugPrepareStepInSuspendedGenerator); + __ Pop(edx); + __ Pop(ebx); + __ mov(edi, FieldOperand(ebx, JSGeneratorObject::kFunctionOffset)); + } + __ jmp(&stepping_prepared); +} + +static void ReplaceClosureEntryWithOptimizedCode( + MacroAssembler* masm, Register optimized_code_entry, Register closure, + Register scratch1, Register scratch2, Register scratch3) { + Register native_context = scratch1; + + // Store the optimized code in the closure. + __ lea(optimized_code_entry, + FieldOperand(optimized_code_entry, Code::kHeaderSize)); + __ mov(FieldOperand(closure, JSFunction::kCodeEntryOffset), + optimized_code_entry); + __ RecordWriteCodeEntryField(closure, optimized_code_entry, scratch2); + + // Link the closure into the optimized function list. + __ mov(native_context, NativeContextOperand()); + __ mov(scratch3, + ContextOperand(native_context, Context::OPTIMIZED_FUNCTIONS_LIST)); + __ mov(FieldOperand(closure, JSFunction::kNextFunctionLinkOffset), scratch3); + __ RecordWriteField(closure, JSFunction::kNextFunctionLinkOffset, scratch3, + scratch2, kDontSaveFPRegs, EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); + const int function_list_offset = + Context::SlotOffset(Context::OPTIMIZED_FUNCTIONS_LIST); + __ mov(ContextOperand(native_context, Context::OPTIMIZED_FUNCTIONS_LIST), + closure); + // Save closure before the write barrier. + __ mov(scratch3, closure); + __ RecordWriteContextSlot(native_context, function_list_offset, closure, + scratch2, kDontSaveFPRegs); + __ mov(closure, scratch3); +} + +static void LeaveInterpreterFrame(MacroAssembler* masm, Register scratch1, + Register scratch2) { + Register args_count = scratch1; + Register return_pc = scratch2; + + // Get the arguments + reciever count. + __ mov(args_count, + Operand(ebp, InterpreterFrameConstants::kBytecodeArrayFromFp)); + __ mov(args_count, + FieldOperand(args_count, BytecodeArray::kParameterSizeOffset)); + + // Leave the frame (also dropping the register file). + __ leave(); + + // Drop receiver + arguments. + __ pop(return_pc); + __ add(esp, args_count); + __ push(return_pc); +} + +// Tail-call |function_id| if |smi_entry| == |marker| +static void TailCallRuntimeIfMarkerEquals(MacroAssembler* masm, + Register smi_entry, + OptimizationMarker marker, + Runtime::FunctionId function_id) { + Label no_match; + __ cmp(smi_entry, Immediate(Smi::FromEnum(marker))); + __ j(not_equal, &no_match, Label::kNear); + GenerateTailCallToReturnedCode(masm, function_id); + __ bind(&no_match); +} + +static void MaybeTailCallOptimizedCodeSlot(MacroAssembler* masm, + Register feedback_vector, + Register scratch) { + // ----------- S t a t e ------------- + // -- eax : argument count (preserved for callee if needed, and caller) + // -- edx : new target (preserved for callee if needed, and caller) + // -- edi : target function (preserved for callee if needed, and caller) + // -- feedback vector (preserved for caller if needed) + // ----------------------------------- + DCHECK(!AreAliased(feedback_vector, eax, edx, edi, scratch)); + + Label optimized_code_slot_is_cell, fallthrough; + + Register closure = edi; + Register optimized_code_entry = scratch; + + const int kOptimizedCodeCellOffset = + FeedbackVector::kOptimizedCodeIndex * kPointerSize + + FeedbackVector::kHeaderSize; + __ mov(optimized_code_entry, + FieldOperand(feedback_vector, kOptimizedCodeCellOffset)); + + // Check if the code entry is a Smi. If yes, we interpret it as an + // optimisation marker. Otherwise, interpret is as a weak cell to a code + // object. + __ JumpIfNotSmi(optimized_code_entry, &optimized_code_slot_is_cell); + + { + // Optimized code slot is an optimization marker. + + // Fall through if no optimization trigger. + __ cmp(optimized_code_entry, + Immediate(Smi::FromEnum(OptimizationMarker::kNone))); + __ j(equal, &fallthrough); + + TailCallRuntimeIfMarkerEquals(masm, optimized_code_entry, + OptimizationMarker::kCompileOptimized, + Runtime::kCompileOptimized_NotConcurrent); + TailCallRuntimeIfMarkerEquals( + masm, optimized_code_entry, + OptimizationMarker::kCompileOptimizedConcurrent, + Runtime::kCompileOptimized_Concurrent); + + { + // Otherwise, the marker is InOptimizationQueue. + if (FLAG_debug_code) { + __ cmp( + optimized_code_entry, + Immediate(Smi::FromEnum(OptimizationMarker::kInOptimizationQueue))); + __ Assert(equal, kExpectedOptimizationSentinel); + } + + // 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. + ExternalReference stack_limit = + ExternalReference::address_of_stack_limit(masm->isolate()); + __ cmp(esp, Operand::StaticVariable(stack_limit)); + __ j(above_equal, &fallthrough); + GenerateTailCallToReturnedCode(masm, Runtime::kTryInstallOptimizedCode); + } + } + + { + // Optimized code slot is a WeakCell. + __ bind(&optimized_code_slot_is_cell); + + __ mov(optimized_code_entry, + FieldOperand(optimized_code_entry, WeakCell::kValueOffset)); + __ JumpIfSmi(optimized_code_entry, &fallthrough); + + // Check if the optimized code is marked for deopt. If it is, bailout to a + // given label. + Label found_deoptimized_code; + __ test(FieldOperand(optimized_code_entry, Code::kKindSpecificFlags1Offset), + Immediate(1 << Code::kMarkedForDeoptimizationBit)); + __ j(not_zero, &found_deoptimized_code); + + // Optimized code is good, get it into the closure and link the closure into + // the optimized functions list, then tail call the optimized code. + __ push(eax); + __ push(edx); + // The feedback vector is no longer used, so re-use it as a scratch + // register. + ReplaceClosureEntryWithOptimizedCode(masm, optimized_code_entry, closure, + edx, eax, feedback_vector); + __ pop(edx); + __ pop(eax); + __ jmp(optimized_code_entry); + + // Optimized code slot contains deoptimized code, evict it and re-enter the + // closure's code. + __ bind(&found_deoptimized_code); + GenerateTailCallToReturnedCode(masm, Runtime::kEvictOptimizedCodeSlot); + } + + // Fall-through if the optimized code cell is clear and there is no + // optimization marker. + __ bind(&fallthrough); +} + +// Generate code for entering a JS function with the interpreter. +// On entry to the function the receiver and arguments have been pushed on the +// stack left to right. The actual argument count matches the formal parameter +// count expected by the function. +// +// The live registers are: +// o edi: the JS function object being called +// o edx: the new target +// o esi: our context +// o ebp: the caller's frame pointer +// o esp: stack pointer (pointing to return address) +// +// The function builds an interpreter frame. See InterpreterFrameConstants in +// frames.h for its layout. +void Builtins::Generate_InterpreterEntryTrampoline(MacroAssembler* masm) { + ProfileEntryHookStub::MaybeCallEntryHook(masm); + + Register closure = edi; + Register feedback_vector = ebx; + + // Load the feedback vector from the closure. + __ mov(feedback_vector, + FieldOperand(closure, JSFunction::kFeedbackVectorOffset)); + __ mov(feedback_vector, FieldOperand(feedback_vector, Cell::kValueOffset)); + // Read off the optimized code slot in the feedback vector, and if there + // is optimized code or an optimization marker, call that instead. + MaybeTailCallOptimizedCodeSlot(masm, feedback_vector, ecx); + + // Open a frame scope to indicate that there is a frame on the stack. The + // MANUAL indicates that the scope shouldn't actually generate code to set + // up the frame (that is done below). + FrameScope frame_scope(masm, StackFrame::MANUAL); + __ push(ebp); // Caller's frame pointer. + __ mov(ebp, esp); + __ push(esi); // Callee's context. + __ push(edi); // Callee's JS function. + __ push(edx); // Callee's new target. + + // Get the bytecode array from the function object (or from the DebugInfo if + // it is present) and load it into kInterpreterBytecodeArrayRegister. + Label maybe_load_debug_bytecode_array, bytecode_array_loaded; + __ mov(eax, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); + __ mov(kInterpreterBytecodeArrayRegister, + FieldOperand(eax, SharedFunctionInfo::kFunctionDataOffset)); + __ JumpIfNotSmi(FieldOperand(eax, SharedFunctionInfo::kDebugInfoOffset), + &maybe_load_debug_bytecode_array); + __ bind(&bytecode_array_loaded); + + // Check whether we should continue to use the interpreter. + // TODO(rmcilroy) Remove self healing once liveedit only has to deal with + // Ignition bytecode. + Label switch_to_different_code_kind; + __ Move(ecx, masm->CodeObject()); // Self-reference to this code. + __ cmp(ecx, FieldOperand(eax, SharedFunctionInfo::kCodeOffset)); + __ j(not_equal, &switch_to_different_code_kind); + + // Increment invocation count for the function. + __ add(FieldOperand(feedback_vector, + FeedbackVector::kInvocationCountIndex * kPointerSize + + FeedbackVector::kHeaderSize), + Immediate(Smi::FromInt(1))); + + // Check function data field is actually a BytecodeArray object. + if (FLAG_debug_code) { + __ AssertNotSmi(kInterpreterBytecodeArrayRegister); + __ CmpObjectType(kInterpreterBytecodeArrayRegister, BYTECODE_ARRAY_TYPE, + eax); + __ Assert(equal, kFunctionDataShouldBeBytecodeArrayOnInterpreterEntry); + } + + // Reset code age. + __ mov_b(FieldOperand(kInterpreterBytecodeArrayRegister, + BytecodeArray::kBytecodeAgeOffset), + Immediate(BytecodeArray::kNoAgeBytecodeAge)); + + // Push bytecode array. + __ push(kInterpreterBytecodeArrayRegister); + // Push Smi tagged initial bytecode array offset. + __ push(Immediate(Smi::FromInt(BytecodeArray::kHeaderSize - kHeapObjectTag))); + + // Allocate the local and temporary register file on the stack. + { + // Load frame size from the BytecodeArray object. + __ mov(ebx, FieldOperand(kInterpreterBytecodeArrayRegister, + BytecodeArray::kFrameSizeOffset)); + + // Do a stack check to ensure we don't go over the limit. + Label ok; + __ mov(ecx, esp); + __ sub(ecx, ebx); + ExternalReference stack_limit = + ExternalReference::address_of_real_stack_limit(masm->isolate()); + __ cmp(ecx, Operand::StaticVariable(stack_limit)); + __ j(above_equal, &ok); + __ CallRuntime(Runtime::kThrowStackOverflow); + __ bind(&ok); + + // If ok, push undefined as the initial value for all register file entries. + Label loop_header; + Label loop_check; + __ mov(eax, Immediate(masm->isolate()->factory()->undefined_value())); + __ jmp(&loop_check); + __ bind(&loop_header); + // TODO(rmcilroy): Consider doing more than one push per loop iteration. + __ push(eax); + // Continue loop if not done. + __ bind(&loop_check); + __ sub(ebx, Immediate(kPointerSize)); + __ j(greater_equal, &loop_header); + } + + // Load accumulator, bytecode offset and dispatch table into registers. + __ LoadRoot(kInterpreterAccumulatorRegister, Heap::kUndefinedValueRootIndex); + __ mov(kInterpreterBytecodeOffsetRegister, + Immediate(BytecodeArray::kHeaderSize - kHeapObjectTag)); + __ mov(kInterpreterDispatchTableRegister, + Immediate(ExternalReference::interpreter_dispatch_table_address( + masm->isolate()))); + + // Dispatch to the first bytecode handler for the function. + __ movzx_b(ebx, Operand(kInterpreterBytecodeArrayRegister, + kInterpreterBytecodeOffsetRegister, times_1, 0)); + __ mov(ebx, Operand(kInterpreterDispatchTableRegister, ebx, + times_pointer_size, 0)); + __ call(ebx); + masm->isolate()->heap()->SetInterpreterEntryReturnPCOffset(masm->pc_offset()); + + // The return value is in eax. + LeaveInterpreterFrame(masm, ebx, ecx); + __ ret(0); + + // Load debug copy of the bytecode array if it exists. + // kInterpreterBytecodeArrayRegister is already loaded with + // SharedFunctionInfo::kFunctionDataOffset. + __ bind(&maybe_load_debug_bytecode_array); + __ push(ebx); // feedback_vector == ebx, so save it. + __ mov(ecx, FieldOperand(eax, SharedFunctionInfo::kDebugInfoOffset)); + __ mov(ebx, FieldOperand(ecx, DebugInfo::kFlagsOffset)); + __ SmiUntag(ebx); + __ test(ebx, Immediate(DebugInfo::kHasBreakInfo)); + __ pop(ebx); + __ j(zero, &bytecode_array_loaded); + __ mov(kInterpreterBytecodeArrayRegister, + FieldOperand(ecx, DebugInfo::kDebugBytecodeArrayOffset)); + __ jmp(&bytecode_array_loaded); + + // If the shared code is no longer this entry trampoline, then the underlying + // function has been switched to a different kind of code and we heal the + // closure by switching the code entry field over to the new code as well. + __ bind(&switch_to_different_code_kind); + __ pop(edx); // Callee's new target. + __ pop(edi); // Callee's JS function. + __ pop(esi); // Callee's context. + __ leave(); // Leave the frame so we can tail call. + __ mov(ecx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); + __ mov(ecx, FieldOperand(ecx, SharedFunctionInfo::kCodeOffset)); + __ lea(ecx, FieldOperand(ecx, Code::kHeaderSize)); + __ mov(FieldOperand(edi, JSFunction::kCodeEntryOffset), ecx); + __ RecordWriteCodeEntryField(edi, ecx, ebx); + __ jmp(ecx); +} + +static void Generate_StackOverflowCheck(MacroAssembler* masm, Register num_args, + Register scratch1, Register scratch2, + Label* stack_overflow, + bool include_receiver = false) { + // Check the stack for overflow. We are not trying to catch + // interruptions (e.g. debug break and preemption) here, so the "real stack + // limit" is checked. + ExternalReference real_stack_limit = + ExternalReference::address_of_real_stack_limit(masm->isolate()); + __ mov(scratch1, Operand::StaticVariable(real_stack_limit)); + // Make scratch2 the space we have left. The stack might already be overflowed + // here which will cause scratch2 to become negative. + __ mov(scratch2, esp); + __ sub(scratch2, scratch1); + // Make scratch1 the space we need for the array when it is unrolled onto the + // stack. + __ mov(scratch1, num_args); + if (include_receiver) { + __ add(scratch1, Immediate(1)); + } + __ shl(scratch1, kPointerSizeLog2); + // Check if the arguments will overflow the stack. + __ cmp(scratch2, scratch1); + __ j(less_equal, stack_overflow); // Signed comparison. +} + +static void Generate_InterpreterPushArgs(MacroAssembler* masm, + Register array_limit, + Register start_address) { + // ----------- S t a t e ------------- + // -- start_address : Pointer to the last argument in the args array. + // -- array_limit : Pointer to one before the first argument in the + // args array. + // ----------------------------------- + Label loop_header, loop_check; + __ jmp(&loop_check); + __ bind(&loop_header); + __ Push(Operand(start_address, 0)); + __ sub(start_address, Immediate(kPointerSize)); + __ bind(&loop_check); + __ cmp(start_address, array_limit); + __ j(greater, &loop_header, Label::kNear); +} + +// static +void Builtins::Generate_InterpreterPushArgsThenCallImpl( + MacroAssembler* masm, ConvertReceiverMode receiver_mode, + InterpreterPushArgsMode mode) { + // ----------- S t a t e ------------- + // -- eax : the number of arguments (not including the receiver) + // -- ebx : the address of the first argument to be pushed. Subsequent + // arguments should be consecutive above this, in the same order as + // they are to be pushed onto the stack. + // -- edi : the target to call (can be any Object). + // ----------------------------------- + Label stack_overflow; + // Compute the expected number of arguments. + __ mov(ecx, eax); + __ add(ecx, Immediate(1)); // Add one for receiver. + + // Add a stack check before pushing the arguments. We need an extra register + // to perform a stack check. So push it onto the stack temporarily. This + // might cause stack overflow, but it will be detected by the check. + __ Push(edi); + Generate_StackOverflowCheck(masm, ecx, edx, edi, &stack_overflow); + __ Pop(edi); + + // Pop return address to allow tail-call after pushing arguments. + __ Pop(edx); + + // Push "undefined" as the receiver arg if we need to. + if (receiver_mode == ConvertReceiverMode::kNullOrUndefined) { + __ PushRoot(Heap::kUndefinedValueRootIndex); + __ sub(ecx, Immediate(1)); // Subtract one for receiver. + } + + // Find the address of the last argument. + __ shl(ecx, kPointerSizeLog2); + __ neg(ecx); + __ add(ecx, ebx); + Generate_InterpreterPushArgs(masm, ecx, ebx); + + if (mode == InterpreterPushArgsMode::kWithFinalSpread) { + __ Pop(ebx); // Pass the spread in a register + __ sub(eax, Immediate(1)); // Subtract one for spread + } + + // Call the target. + __ Push(edx); // Re-push return address. + + if (mode == InterpreterPushArgsMode::kJSFunction) { + __ Jump( + masm->isolate()->builtins()->CallFunction(ConvertReceiverMode::kAny), + RelocInfo::CODE_TARGET); + } else if (mode == InterpreterPushArgsMode::kWithFinalSpread) { + __ Jump(masm->isolate()->builtins()->CallWithSpread(), + RelocInfo::CODE_TARGET); + } else { + __ Jump(masm->isolate()->builtins()->Call(ConvertReceiverMode::kAny), + RelocInfo::CODE_TARGET); + } + + __ bind(&stack_overflow); + { + // Pop the temporary registers, so that return address is on top of stack. + __ Pop(edi); + + __ TailCallRuntime(Runtime::kThrowStackOverflow); + + // This should be unreachable. + __ int3(); + } +} + +namespace { + +// This function modified start_addr, and only reads the contents of num_args +// register. scratch1 and scratch2 are used as temporary registers. Their +// original values are restored after the use. +void Generate_InterpreterPushZeroAndArgsAndReturnAddress( + MacroAssembler* masm, Register num_args, Register start_addr, + Register scratch1, Register scratch2, int num_slots_above_ret_addr, + Label* stack_overflow) { + // We have to move return address and the temporary registers above it + // before we can copy arguments onto the stack. To achieve this: + // Step 1: Increment the stack pointer by num_args + 1 (for receiver). + // Step 2: Move the return address and values above it to the top of stack. + // Step 3: Copy the arguments into the correct locations. + // current stack =====> required stack layout + // | | | scratch1 | (2) <-- esp(1) + // | | | .... | (2) + // | | | scratch-n | (2) + // | | | return addr | (2) + // | | | arg N | (3) + // | scratch1 | <-- esp | .... | + // | .... | | arg 1 | + // | scratch-n | | arg 0 | + // | return addr | | receiver slot | + + // Check for stack overflow before we increment the stack pointer. + Generate_StackOverflowCheck(masm, num_args, scratch1, scratch2, + stack_overflow, true); + +// Step 1 - Update the stack pointer. scratch1 already contains the required +// increment to the stack. i.e. num_args + 1 stack slots. This is computed in +// the Generate_StackOverflowCheck. + +#ifdef _MSC_VER + // TODO(mythria): Move it to macro assembler. + // In windows, we cannot increment the stack size by more than one page + // (mimimum page size is 4KB) without accessing at least one byte on the + // page. Check this: + // https://msdn.microsoft.com/en-us/library/aa227153(v=vs.60).aspx. + const int page_size = 4 * 1024; + Label check_offset, update_stack_pointer; + __ bind(&check_offset); + __ cmp(scratch1, page_size); + __ j(less, &update_stack_pointer); + __ sub(esp, Immediate(page_size)); + // Just to touch the page, before we increment further. + __ mov(Operand(esp, 0), Immediate(0)); + __ sub(scratch1, Immediate(page_size)); + __ jmp(&check_offset); + __ bind(&update_stack_pointer); +#endif + + __ sub(esp, scratch1); + + // Step 2 move return_address and slots above it to the correct locations. + // Move from top to bottom, otherwise we may overwrite when num_args = 0 or 1, + // basically when the source and destination overlap. We at least need one + // extra slot for receiver, so no extra checks are required to avoid copy. + for (int i = 0; i < num_slots_above_ret_addr + 1; i++) { + __ mov(scratch1, + Operand(esp, num_args, times_pointer_size, (i + 1) * kPointerSize)); + __ mov(Operand(esp, i * kPointerSize), scratch1); + } + + // Step 3 copy arguments to correct locations. + // Slot meant for receiver contains return address. Reset it so that + // we will not incorrectly interpret return address as an object. + __ mov(Operand(esp, num_args, times_pointer_size, + (num_slots_above_ret_addr + 1) * kPointerSize), + Immediate(0)); + __ mov(scratch1, num_args); + + Label loop_header, loop_check; + __ jmp(&loop_check); + __ bind(&loop_header); + __ mov(scratch2, Operand(start_addr, 0)); + __ mov(Operand(esp, scratch1, times_pointer_size, + num_slots_above_ret_addr * kPointerSize), + scratch2); + __ sub(start_addr, Immediate(kPointerSize)); + __ sub(scratch1, Immediate(1)); + __ bind(&loop_check); + __ cmp(scratch1, Immediate(0)); + __ j(greater, &loop_header, Label::kNear); +} + +} // end anonymous namespace + +// static +void Builtins::Generate_InterpreterPushArgsThenConstructImpl( + MacroAssembler* masm, InterpreterPushArgsMode mode) { + // ----------- S t a t e ------------- + // -- eax : the number of arguments (not including the receiver) + // -- edx : the new target + // -- edi : the constructor + // -- ebx : allocation site feedback (if available or undefined) + // -- ecx : the address of the first argument to be pushed. Subsequent + // arguments should be consecutive above this, in the same order as + // they are to be pushed onto the stack. + // ----------------------------------- + Label stack_overflow; + // We need two scratch registers. Push edi and edx onto stack. + __ Push(edi); + __ Push(edx); + + // Push arguments and move return address to the top of stack. + // The eax register is readonly. The ecx register will be modified. The edx + // and edi registers will be modified but restored to their original values. + Generate_InterpreterPushZeroAndArgsAndReturnAddress(masm, eax, ecx, edx, edi, + 2, &stack_overflow); + + // Restore edi and edx + __ Pop(edx); + __ Pop(edi); + + if (mode == InterpreterPushArgsMode::kWithFinalSpread) { + __ PopReturnAddressTo(ecx); + __ Pop(ebx); // Pass the spread in a register + __ PushReturnAddressFrom(ecx); + __ sub(eax, Immediate(1)); // Subtract one for spread + } else { + __ AssertUndefinedOrAllocationSite(ebx); + } + + if (mode == InterpreterPushArgsMode::kJSFunction) { + // Tail call to the function-specific construct stub (still in the caller + // context at this point). + __ AssertFunction(edi); + + __ mov(ecx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); + __ mov(ecx, FieldOperand(ecx, SharedFunctionInfo::kConstructStubOffset)); + __ lea(ecx, FieldOperand(ecx, Code::kHeaderSize)); + __ jmp(ecx); + } else if (mode == InterpreterPushArgsMode::kWithFinalSpread) { + // Call the constructor with unmodified eax, edi, edx values. + __ Jump(masm->isolate()->builtins()->ConstructWithSpread(), + RelocInfo::CODE_TARGET); + } else { + DCHECK_EQ(InterpreterPushArgsMode::kOther, mode); + // Call the constructor with unmodified eax, edi, edx values. + __ Jump(masm->isolate()->builtins()->Construct(), RelocInfo::CODE_TARGET); + } + + __ bind(&stack_overflow); + { + // Pop the temporary registers, so that return address is on top of stack. + __ Pop(edx); + __ Pop(edi); + + __ TailCallRuntime(Runtime::kThrowStackOverflow); + + // This should be unreachable. + __ int3(); + } +} + +// static +void Builtins::Generate_InterpreterPushArgsThenConstructArray( + MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- eax : the number of arguments (not including the receiver) + // -- edx : the target to call checked to be Array function. + // -- ebx : the allocation site feedback + // -- ecx : the address of the first argument to be pushed. Subsequent + // arguments should be consecutive above this, in the same order as + // they are to be pushed onto the stack. + // ----------------------------------- + Label stack_overflow; + // We need two scratch registers. Register edi is available, push edx onto + // stack. + __ Push(edx); + + // Push arguments and move return address to the top of stack. + // The eax register is readonly. The ecx register will be modified. The edx + // and edi registers will be modified but restored to their original values. + Generate_InterpreterPushZeroAndArgsAndReturnAddress(masm, eax, ecx, edx, edi, + 1, &stack_overflow); + + // Restore edx. + __ Pop(edx); + + // Array constructor expects constructor in edi. It is same as edx here. + __ Move(edi, edx); + + ArrayConstructorStub stub(masm->isolate()); + __ TailCallStub(&stub); + + __ bind(&stack_overflow); + { + // Pop the temporary registers, so that return address is on top of stack. + __ Pop(edx); + + __ TailCallRuntime(Runtime::kThrowStackOverflow); + + // This should be unreachable. + __ int3(); + } +} + +static void Generate_InterpreterEnterBytecode(MacroAssembler* masm) { + // Set the return address to the correct point in the interpreter entry + // trampoline. + Smi* interpreter_entry_return_pc_offset( + masm->isolate()->heap()->interpreter_entry_return_pc_offset()); + DCHECK_NE(interpreter_entry_return_pc_offset, Smi::kZero); + __ Move(ebx, masm->isolate()->builtins()->InterpreterEntryTrampoline()); + __ add(ebx, Immediate(interpreter_entry_return_pc_offset->value() + + Code::kHeaderSize - kHeapObjectTag)); + __ push(ebx); + + // Initialize the dispatch table register. + __ mov(kInterpreterDispatchTableRegister, + Immediate(ExternalReference::interpreter_dispatch_table_address( + masm->isolate()))); + + // Get the bytecode array pointer from the frame. + __ mov(kInterpreterBytecodeArrayRegister, + Operand(ebp, InterpreterFrameConstants::kBytecodeArrayFromFp)); + + if (FLAG_debug_code) { + // Check function data field is actually a BytecodeArray object. + __ AssertNotSmi(kInterpreterBytecodeArrayRegister); + __ CmpObjectType(kInterpreterBytecodeArrayRegister, BYTECODE_ARRAY_TYPE, + ebx); + __ Assert(equal, kFunctionDataShouldBeBytecodeArrayOnInterpreterEntry); + } + + // Get the target bytecode offset from the frame. + __ mov(kInterpreterBytecodeOffsetRegister, + Operand(ebp, InterpreterFrameConstants::kBytecodeOffsetFromFp)); + __ SmiUntag(kInterpreterBytecodeOffsetRegister); + + // Dispatch to the target bytecode. + __ movzx_b(ebx, Operand(kInterpreterBytecodeArrayRegister, + kInterpreterBytecodeOffsetRegister, times_1, 0)); + __ mov(ebx, Operand(kInterpreterDispatchTableRegister, ebx, + times_pointer_size, 0)); + __ jmp(ebx); +} + +void Builtins::Generate_InterpreterEnterBytecodeAdvance(MacroAssembler* masm) { + // Advance the current bytecode offset stored within the given interpreter + // stack frame. This simulates what all bytecode handlers do upon completion + // of the underlying operation. + __ mov(ebx, Operand(ebp, InterpreterFrameConstants::kBytecodeArrayFromFp)); + __ mov(edx, Operand(ebp, InterpreterFrameConstants::kBytecodeOffsetFromFp)); + __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ Push(kInterpreterAccumulatorRegister); + __ Push(ebx); // First argument is the bytecode array. + __ Push(edx); // Second argument is the bytecode offset. + __ CallRuntime(Runtime::kInterpreterAdvanceBytecodeOffset); + __ Move(edx, eax); // Result is the new bytecode offset. + __ Pop(kInterpreterAccumulatorRegister); + } + __ mov(Operand(ebp, InterpreterFrameConstants::kBytecodeOffsetFromFp), edx); + + Generate_InterpreterEnterBytecode(masm); +} + +void Builtins::Generate_InterpreterEnterBytecodeDispatch(MacroAssembler* masm) { + Generate_InterpreterEnterBytecode(masm); +} + +void Builtins::Generate_CheckOptimizationMarker(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- rax : argument count (preserved for callee) + // -- rdx : new target (preserved for callee) + // -- rdi : target function (preserved for callee) + // ----------------------------------- + Register closure = edi; + + // Get the feedback vector. + Register feedback_vector = ebx; + __ mov(feedback_vector, + FieldOperand(closure, JSFunction::kFeedbackVectorOffset)); + __ mov(feedback_vector, FieldOperand(feedback_vector, Cell::kValueOffset)); + + // The feedback vector must be defined. + if (FLAG_debug_code) { + __ CompareRoot(feedback_vector, Heap::kUndefinedValueRootIndex); + __ Assert(not_equal, BailoutReason::kExpectedFeedbackVector); + } + + // Is there an optimization marker or optimized code in the feedback vector? + MaybeTailCallOptimizedCodeSlot(masm, feedback_vector, ecx); + + // Otherwise, tail call the SFI code. + GenerateTailCallToSharedCode(masm); +} + +void Builtins::Generate_CompileLazy(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- eax : argument count (preserved for callee) + // -- edx : new target (preserved for callee) + // -- edi : target function (preserved for callee) + // ----------------------------------- + // First lookup code, maybe we don't need to compile! + Label gotta_call_runtime; + + Register closure = edi; + Register feedback_vector = ebx; + + // Do we have a valid feedback vector? + __ mov(feedback_vector, + FieldOperand(closure, JSFunction::kFeedbackVectorOffset)); + __ mov(feedback_vector, FieldOperand(feedback_vector, Cell::kValueOffset)); + __ JumpIfRoot(feedback_vector, Heap::kUndefinedValueRootIndex, + &gotta_call_runtime); + + // Is there an optimization marker or optimized code in the feedback vector? + MaybeTailCallOptimizedCodeSlot(masm, feedback_vector, ecx); + + // We found no optimized code. + Register entry = ecx; + __ mov(entry, FieldOperand(closure, JSFunction::kSharedFunctionInfoOffset)); + + // If SFI points to anything other than CompileLazy, install that. + __ mov(entry, FieldOperand(entry, SharedFunctionInfo::kCodeOffset)); + __ Move(ebx, masm->CodeObject()); + __ cmp(entry, ebx); + __ j(equal, &gotta_call_runtime); + + // Install the SFI's code entry. + __ lea(entry, FieldOperand(entry, Code::kHeaderSize)); + __ mov(FieldOperand(closure, JSFunction::kCodeEntryOffset), entry); + __ RecordWriteCodeEntryField(closure, entry, ebx); + __ jmp(entry); + + __ bind(&gotta_call_runtime); + GenerateTailCallToReturnedCode(masm, Runtime::kCompileLazy); +} + +void Builtins::Generate_InstantiateAsmJs(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- eax : argument count (preserved for callee) + // -- edx : new target (preserved for callee) + // -- edi : target function (preserved for callee) + // ----------------------------------- + Label failed; + { + FrameScope scope(masm, StackFrame::INTERNAL); + // Preserve argument count for later compare. + __ mov(ecx, eax); + // Push the number of arguments to the callee. + __ SmiTag(eax); + __ push(eax); + // Push a copy of the target function and the new target. + __ push(edi); + __ push(edx); + + // The function. + __ push(edi); + // Copy arguments from caller (stdlib, foreign, heap). + Label args_done; + for (int j = 0; j < 4; ++j) { + Label over; + if (j < 3) { + __ cmp(ecx, Immediate(j)); + __ j(not_equal, &over, Label::kNear); + } + for (int i = j - 1; i >= 0; --i) { + __ Push(Operand( + ebp, StandardFrameConstants::kCallerSPOffset + i * kPointerSize)); + } + for (int i = 0; i < 3 - j; ++i) { + __ PushRoot(Heap::kUndefinedValueRootIndex); + } + if (j < 3) { + __ jmp(&args_done, Label::kNear); + __ bind(&over); + } + } + __ bind(&args_done); + + // Call runtime, on success unwind frame, and parent frame. + __ CallRuntime(Runtime::kInstantiateAsmJs, 4); + // A smi 0 is returned on failure, an object on success. + __ JumpIfSmi(eax, &failed, Label::kNear); + + __ Drop(2); + __ Pop(ecx); + __ SmiUntag(ecx); + scope.GenerateLeaveFrame(); + + __ PopReturnAddressTo(ebx); + __ inc(ecx); + __ lea(esp, Operand(esp, ecx, times_pointer_size, 0)); + __ PushReturnAddressFrom(ebx); + __ ret(0); + + __ bind(&failed); + // Restore target function and new target. + __ pop(edx); + __ pop(edi); + __ pop(eax); + __ SmiUntag(eax); + } + // On failure, tail call back to regular js. + GenerateTailCallToReturnedCode(masm, Runtime::kCompileLazy); +} + +static void GenerateMakeCodeYoungAgainCommon(MacroAssembler* masm) { + // For now, we are relying on the fact that make_code_young doesn't do any + // garbage collection which allows us to save/restore the registers without + // worrying about which of them contain pointers. We also don't build an + // internal frame to make the code faster, since we shouldn't have to do stack + // crawls in MakeCodeYoung. This seems a bit fragile. + + // Re-execute the code that was patched back to the young age when + // the stub returns. + __ sub(Operand(esp, 0), Immediate(5)); + __ pushad(); + __ mov(eax, Operand(esp, 8 * kPointerSize)); + { + FrameScope scope(masm, StackFrame::MANUAL); + __ PrepareCallCFunction(2, ebx); + __ mov(Operand(esp, 1 * kPointerSize), + Immediate(ExternalReference::isolate_address(masm->isolate()))); + __ mov(Operand(esp, 0), eax); + __ CallCFunction( + ExternalReference::get_make_code_young_function(masm->isolate()), 2); + } + __ popad(); + __ ret(0); +} + +#define DEFINE_CODE_AGE_BUILTIN_GENERATOR(C) \ + void Builtins::Generate_Make##C##CodeYoungAgain(MacroAssembler* masm) { \ + GenerateMakeCodeYoungAgainCommon(masm); \ + } +CODE_AGE_LIST(DEFINE_CODE_AGE_BUILTIN_GENERATOR) +#undef DEFINE_CODE_AGE_BUILTIN_GENERATOR + +void Builtins::Generate_MarkCodeAsExecutedOnce(MacroAssembler* masm) { + // For now, as in GenerateMakeCodeYoungAgainCommon, we are relying on the fact + // that make_code_young doesn't do any garbage collection which allows us to + // save/restore the registers without worrying about which of them contain + // pointers. + __ pushad(); + __ mov(eax, Operand(esp, 8 * kPointerSize)); + __ sub(eax, Immediate(Assembler::kCallInstructionLength)); + { // NOLINT + FrameScope scope(masm, StackFrame::MANUAL); + __ PrepareCallCFunction(2, ebx); + __ mov(Operand(esp, 1 * kPointerSize), + Immediate(ExternalReference::isolate_address(masm->isolate()))); + __ mov(Operand(esp, 0), eax); + __ CallCFunction( + ExternalReference::get_mark_code_as_executed_function(masm->isolate()), + 2); + } + __ popad(); + + // Perform prologue operations usually performed by the young code stub. + __ pop(eax); // Pop return address into scratch register. + __ push(ebp); // Caller's frame pointer. + __ mov(ebp, esp); + __ push(esi); // Callee's context. + __ push(edi); // Callee's JS Function. + __ push(eax); // Push return address after frame prologue. + + // Jump to point after the code-age stub. + __ ret(0); +} + +void Builtins::Generate_MarkCodeAsExecutedTwice(MacroAssembler* masm) { + GenerateMakeCodeYoungAgainCommon(masm); +} + +void Builtins::Generate_MarkCodeAsToBeExecutedOnce(MacroAssembler* masm) { + Generate_MarkCodeAsExecutedOnce(masm); +} + +void Builtins::Generate_NotifyBuiltinContinuation(MacroAssembler* masm) { + // Enter an internal frame. + { + FrameScope scope(masm, StackFrame::INTERNAL); + // Preserve possible return result from lazy deopt. + __ push(eax); + __ CallRuntime(Runtime::kNotifyStubFailure, false); + __ pop(eax); + // Tear down internal frame. + } + + __ pop(MemOperand(esp, 0)); // Ignore state offset + __ ret(0); // Return to ContinueToBuiltin stub still on stack. +} + +namespace { +void Generate_ContinueToBuiltinHelper(MacroAssembler* masm, + bool java_script_builtin, + bool with_result) { + const RegisterConfiguration* config(RegisterConfiguration::Turbofan()); + int allocatable_register_count = config->num_allocatable_general_registers(); + if (with_result) { + // Overwrite the hole inserted by the deoptimizer with the return value from + // the LAZY deopt point. + __ mov(Operand(esp, + config->num_allocatable_general_registers() * kPointerSize + + BuiltinContinuationFrameConstants::kFixedFrameSize), + eax); + } + for (int i = allocatable_register_count - 1; i >= 0; --i) { + int code = config->GetAllocatableGeneralCode(i); + __ pop(Register::from_code(code)); + if (java_script_builtin && code == kJavaScriptCallArgCountRegister.code()) { + __ SmiUntag(Register::from_code(code)); + } + } + __ mov( + ebp, + Operand(esp, BuiltinContinuationFrameConstants::kFixedFrameSizeFromFp)); + const int offsetToPC = + BuiltinContinuationFrameConstants::kFixedFrameSizeFromFp - kPointerSize; + __ pop(Operand(esp, offsetToPC)); + __ Drop(offsetToPC / kPointerSize); + __ add(Operand(esp, 0), Immediate(Code::kHeaderSize - kHeapObjectTag)); + __ ret(0); +} +} // namespace + +void Builtins::Generate_ContinueToCodeStubBuiltin(MacroAssembler* masm) { + Generate_ContinueToBuiltinHelper(masm, false, false); +} + +void Builtins::Generate_ContinueToCodeStubBuiltinWithResult( + MacroAssembler* masm) { + Generate_ContinueToBuiltinHelper(masm, false, true); +} + +void Builtins::Generate_ContinueToJavaScriptBuiltin(MacroAssembler* masm) { + Generate_ContinueToBuiltinHelper(masm, true, false); +} + +void Builtins::Generate_ContinueToJavaScriptBuiltinWithResult( + MacroAssembler* masm) { + Generate_ContinueToBuiltinHelper(masm, true, true); +} + +static void Generate_NotifyDeoptimizedHelper(MacroAssembler* masm, + Deoptimizer::BailoutType type) { + { + FrameScope scope(masm, StackFrame::INTERNAL); + + // Pass deoptimization type to the runtime system. + __ push(Immediate(Smi::FromInt(static_cast(type)))); + __ CallRuntime(Runtime::kNotifyDeoptimized); + + // Tear down internal frame. + } + + // Get the full codegen state from the stack and untag it. + __ mov(ecx, Operand(esp, 1 * kPointerSize)); + __ SmiUntag(ecx); + + // Switch on the state. + Label not_no_registers, not_tos_eax; + __ cmp(ecx, static_cast(Deoptimizer::BailoutState::NO_REGISTERS)); + __ j(not_equal, ¬_no_registers, Label::kNear); + __ ret(1 * kPointerSize); // Remove state. + + __ bind(¬_no_registers); + DCHECK_EQ(kInterpreterAccumulatorRegister.code(), eax.code()); + __ mov(eax, Operand(esp, 2 * kPointerSize)); + __ cmp(ecx, static_cast(Deoptimizer::BailoutState::TOS_REGISTER)); + __ j(not_equal, ¬_tos_eax, Label::kNear); + __ ret(2 * kPointerSize); // Remove state, eax. + + __ bind(¬_tos_eax); + __ Abort(kNoCasesLeft); +} + +void Builtins::Generate_NotifyDeoptimized(MacroAssembler* masm) { + Generate_NotifyDeoptimizedHelper(masm, Deoptimizer::EAGER); +} + +void Builtins::Generate_NotifySoftDeoptimized(MacroAssembler* masm) { + Generate_NotifyDeoptimizedHelper(masm, Deoptimizer::SOFT); +} + +void Builtins::Generate_NotifyLazyDeoptimized(MacroAssembler* masm) { + Generate_NotifyDeoptimizedHelper(masm, Deoptimizer::LAZY); +} + +// static +void Builtins::Generate_FunctionPrototypeApply(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- eax : argc + // -- esp[0] : return address + // -- esp[4] : argArray + // -- esp[8] : thisArg + // -- esp[12] : receiver + // ----------------------------------- + + // 1. Load receiver into edi, argArray into ebx (if present), remove all + // arguments from the stack (including the receiver), and push thisArg (if + // present) instead. + { + Label no_arg_array, no_this_arg; + __ LoadRoot(edx, Heap::kUndefinedValueRootIndex); + __ mov(ebx, edx); + __ mov(edi, Operand(esp, eax, times_pointer_size, kPointerSize)); + __ test(eax, eax); + __ j(zero, &no_this_arg, Label::kNear); + { + __ mov(edx, Operand(esp, eax, times_pointer_size, 0)); + __ cmp(eax, Immediate(1)); + __ j(equal, &no_arg_array, Label::kNear); + __ mov(ebx, Operand(esp, eax, times_pointer_size, -kPointerSize)); + __ bind(&no_arg_array); + } + __ bind(&no_this_arg); + __ PopReturnAddressTo(ecx); + __ lea(esp, Operand(esp, eax, times_pointer_size, kPointerSize)); + __ Push(edx); + __ PushReturnAddressFrom(ecx); + } + + // ----------- S t a t e ------------- + // -- ebx : argArray + // -- edi : receiver + // -- esp[0] : return address + // -- esp[4] : thisArg + // ----------------------------------- + + // 2. We don't need to check explicitly for callable receiver here, + // since that's the first thing the Call/CallWithArrayLike builtins + // will do. + + // 3. Tail call with no arguments if argArray is null or undefined. + Label no_arguments; + __ JumpIfRoot(ebx, Heap::kNullValueRootIndex, &no_arguments, Label::kNear); + __ JumpIfRoot(ebx, Heap::kUndefinedValueRootIndex, &no_arguments, + Label::kNear); + + // 4a. Apply the receiver to the given argArray. + __ Jump(masm->isolate()->builtins()->CallWithArrayLike(), + RelocInfo::CODE_TARGET); + + // 4b. The argArray is either null or undefined, so we tail call without any + // arguments to the receiver. + __ bind(&no_arguments); + { + __ Set(eax, 0); + __ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); + } +} + +// static +void Builtins::Generate_FunctionPrototypeCall(MacroAssembler* masm) { + // Stack Layout: + // esp[0] : Return address + // esp[8] : Argument n + // esp[16] : Argument n-1 + // ... + // esp[8 * n] : Argument 1 + // esp[8 * (n + 1)] : Receiver (callable to call) + // + // eax contains the number of arguments, n, not counting the receiver. + // + // 1. Make sure we have at least one argument. + { + Label done; + __ test(eax, eax); + __ j(not_zero, &done, Label::kNear); + __ PopReturnAddressTo(ebx); + __ PushRoot(Heap::kUndefinedValueRootIndex); + __ PushReturnAddressFrom(ebx); + __ inc(eax); + __ bind(&done); + } + + // 2. Get the callable to call (passed as receiver) from the stack. + __ mov(edi, Operand(esp, eax, times_pointer_size, kPointerSize)); + + // 3. Shift arguments and return address one slot down on the stack + // (overwriting the original receiver). Adjust argument count to make + // the original first argument the new receiver. + { + Label loop; + __ mov(ecx, eax); + __ bind(&loop); + __ mov(ebx, Operand(esp, ecx, times_pointer_size, 0)); + __ mov(Operand(esp, ecx, times_pointer_size, kPointerSize), ebx); + __ dec(ecx); + __ j(not_sign, &loop); // While non-negative (to copy return address). + __ pop(ebx); // Discard copy of return address. + __ dec(eax); // One fewer argument (first argument is new receiver). + } + + // 4. Call the callable. + __ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); +} + +void Builtins::Generate_ReflectApply(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- eax : argc + // -- esp[0] : return address + // -- esp[4] : argumentsList + // -- esp[8] : thisArgument + // -- esp[12] : target + // -- esp[16] : receiver + // ----------------------------------- + + // 1. Load target into edi (if present), argumentsList into ebx (if present), + // remove all arguments from the stack (including the receiver), and push + // thisArgument (if present) instead. + { + Label done; + __ LoadRoot(edi, Heap::kUndefinedValueRootIndex); + __ mov(edx, edi); + __ mov(ebx, edi); + __ cmp(eax, Immediate(1)); + __ j(below, &done, Label::kNear); + __ mov(edi, Operand(esp, eax, times_pointer_size, -0 * kPointerSize)); + __ j(equal, &done, Label::kNear); + __ mov(edx, Operand(esp, eax, times_pointer_size, -1 * kPointerSize)); + __ cmp(eax, Immediate(3)); + __ j(below, &done, Label::kNear); + __ mov(ebx, Operand(esp, eax, times_pointer_size, -2 * kPointerSize)); + __ bind(&done); + __ PopReturnAddressTo(ecx); + __ lea(esp, Operand(esp, eax, times_pointer_size, kPointerSize)); + __ Push(edx); + __ PushReturnAddressFrom(ecx); + } + + // ----------- S t a t e ------------- + // -- ebx : argumentsList + // -- edi : target + // -- esp[0] : return address + // -- esp[4] : thisArgument + // ----------------------------------- + + // 2. We don't need to check explicitly for callable target here, + // since that's the first thing the Call/CallWithArrayLike builtins + // will do. + + // 3. Apply the target to the given argumentsList. + __ Jump(masm->isolate()->builtins()->CallWithArrayLike(), + RelocInfo::CODE_TARGET); +} + +void Builtins::Generate_ReflectConstruct(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- eax : argc + // -- esp[0] : return address + // -- esp[4] : new.target (optional) + // -- esp[8] : argumentsList + // -- esp[12] : target + // -- esp[16] : receiver + // ----------------------------------- + + // 1. Load target into edi (if present), argumentsList into ebx (if present), + // new.target into edx (if present, otherwise use target), remove all + // arguments from the stack (including the receiver), and push thisArgument + // (if present) instead. + { + Label done; + __ LoadRoot(edi, Heap::kUndefinedValueRootIndex); + __ mov(edx, edi); + __ mov(ebx, edi); + __ cmp(eax, Immediate(1)); + __ j(below, &done, Label::kNear); + __ mov(edi, Operand(esp, eax, times_pointer_size, -0 * kPointerSize)); + __ mov(edx, edi); + __ j(equal, &done, Label::kNear); + __ mov(ebx, Operand(esp, eax, times_pointer_size, -1 * kPointerSize)); + __ cmp(eax, Immediate(3)); + __ j(below, &done, Label::kNear); + __ mov(edx, Operand(esp, eax, times_pointer_size, -2 * kPointerSize)); + __ bind(&done); + __ PopReturnAddressTo(ecx); + __ lea(esp, Operand(esp, eax, times_pointer_size, kPointerSize)); + __ PushRoot(Heap::kUndefinedValueRootIndex); + __ PushReturnAddressFrom(ecx); + } + + // ----------- S t a t e ------------- + // -- ebx : argumentsList + // -- edx : new.target + // -- edi : target + // -- esp[0] : return address + // -- esp[4] : receiver (undefined) + // ----------------------------------- + + // 2. We don't need to check explicitly for constructor target here, + // since that's the first thing the Construct/ConstructWithArrayLike + // builtins will do. + + // 3. We don't need to check explicitly for constructor new.target here, + // since that's the second thing the Construct/ConstructWithArrayLike + // builtins will do. + + // 4. Construct the target with the given new.target and argumentsList. + __ Jump(masm->isolate()->builtins()->ConstructWithArrayLike(), + RelocInfo::CODE_TARGET); +} + +void Builtins::Generate_InternalArrayCode(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- eax : argc + // -- esp[0] : return address + // -- esp[4] : last argument + // ----------------------------------- + Label generic_array_code; + + // Get the InternalArray function. + __ LoadGlobalFunction(Context::INTERNAL_ARRAY_FUNCTION_INDEX, edi); + + if (FLAG_debug_code) { + // Initial map for the builtin InternalArray function should be a map. + __ mov(ebx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset)); + // Will both indicate a NULL and a Smi. + __ test(ebx, Immediate(kSmiTagMask)); + __ Assert(not_zero, kUnexpectedInitialMapForInternalArrayFunction); + __ CmpObjectType(ebx, MAP_TYPE, ecx); + __ Assert(equal, kUnexpectedInitialMapForInternalArrayFunction); + } + + // Run the native code for the InternalArray function called as a normal + // function. + // tail call a stub + InternalArrayConstructorStub stub(masm->isolate()); + __ TailCallStub(&stub); +} + +void Builtins::Generate_ArrayCode(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- eax : argc + // -- esp[0] : return address + // -- esp[4] : last argument + // ----------------------------------- + Label generic_array_code; + + // Get the Array function. + __ LoadGlobalFunction(Context::ARRAY_FUNCTION_INDEX, edi); + __ mov(edx, edi); + + if (FLAG_debug_code) { + // Initial map for the builtin Array function should be a map. + __ mov(ebx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset)); + // Will both indicate a NULL and a Smi. + __ test(ebx, Immediate(kSmiTagMask)); + __ Assert(not_zero, kUnexpectedInitialMapForArrayFunction); + __ CmpObjectType(ebx, MAP_TYPE, ecx); + __ Assert(equal, kUnexpectedInitialMapForArrayFunction); + } + + // Run the native code for the Array function called as a normal function. + // tail call a stub + __ mov(ebx, masm->isolate()->factory()->undefined_value()); + ArrayConstructorStub stub(masm->isolate()); + __ TailCallStub(&stub); +} + +// static +void Builtins::Generate_NumberConstructor(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- eax : number of arguments + // -- edi : constructor function + // -- esi : context + // -- esp[0] : return address + // -- esp[(argc - n) * 4] : arg[n] (zero-based) + // -- esp[(argc + 1) * 4] : receiver + // ----------------------------------- + + // 1. Load the first argument into ebx. + Label no_arguments; + { + __ test(eax, eax); + __ j(zero, &no_arguments, Label::kNear); + __ mov(ebx, Operand(esp, eax, times_pointer_size, 0)); + } + + // 2a. Convert the first argument to a number. + { + FrameScope scope(masm, StackFrame::MANUAL); + __ SmiTag(eax); + __ EnterBuiltinFrame(esi, edi, eax); + __ mov(eax, ebx); + __ Call(masm->isolate()->builtins()->ToNumber(), RelocInfo::CODE_TARGET); + __ LeaveBuiltinFrame(esi, edi, ebx); // Argc popped to ebx. + __ SmiUntag(ebx); + } + + { + // Drop all arguments including the receiver. + __ PopReturnAddressTo(ecx); + __ lea(esp, Operand(esp, ebx, times_pointer_size, kPointerSize)); + __ PushReturnAddressFrom(ecx); + __ Ret(); + } + + // 2b. No arguments, return +0 (already in eax). + __ bind(&no_arguments); + __ ret(1 * kPointerSize); +} + +// static +void Builtins::Generate_NumberConstructor_ConstructStub(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- eax : number of arguments + // -- edi : constructor function + // -- edx : new target + // -- esi : context + // -- esp[0] : return address + // -- esp[(argc - n) * 4] : arg[n] (zero-based) + // -- esp[(argc + 1) * 4] : receiver + // ----------------------------------- + + // 1. Make sure we operate in the context of the called function. + __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); + + // Store argc in r8. + __ mov(ecx, eax); + __ SmiTag(ecx); + + // 2. Load the first argument into ebx. + { + Label no_arguments, done; + __ test(eax, eax); + __ j(zero, &no_arguments, Label::kNear); + __ mov(ebx, Operand(esp, eax, times_pointer_size, 0)); + __ jmp(&done, Label::kNear); + __ bind(&no_arguments); + __ Move(ebx, Smi::kZero); + __ bind(&done); + } + + // 3. Make sure ebx is a number. + { + Label done_convert; + __ JumpIfSmi(ebx, &done_convert); + __ CompareRoot(FieldOperand(ebx, HeapObject::kMapOffset), + Heap::kHeapNumberMapRootIndex); + __ j(equal, &done_convert); + { + FrameScope scope(masm, StackFrame::MANUAL); + __ EnterBuiltinFrame(esi, edi, ecx); + __ Push(edx); + __ Move(eax, ebx); + __ Call(masm->isolate()->builtins()->ToNumber(), RelocInfo::CODE_TARGET); + __ Move(ebx, eax); + __ Pop(edx); + __ LeaveBuiltinFrame(esi, edi, ecx); + } + __ bind(&done_convert); + } + + // 4. Check if new target and constructor differ. + Label drop_frame_and_ret, done_alloc, new_object; + __ cmp(edx, edi); + __ j(not_equal, &new_object); + + // 5. Allocate a JSValue wrapper for the number. + __ AllocateJSValue(eax, edi, ebx, esi, &done_alloc); + __ jmp(&drop_frame_and_ret); + + __ bind(&done_alloc); + __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); // Restore esi. + + // 6. Fallback to the runtime to create new object. + __ bind(&new_object); + { + FrameScope scope(masm, StackFrame::MANUAL); + __ EnterBuiltinFrame(esi, edi, ecx); + __ Push(ebx); // the first argument + __ Call(masm->isolate()->builtins()->FastNewObject(), + RelocInfo::CODE_TARGET); + __ Pop(FieldOperand(eax, JSValue::kValueOffset)); + __ LeaveBuiltinFrame(esi, edi, ecx); + } + + __ bind(&drop_frame_and_ret); + { + // Drop all arguments including the receiver. + __ PopReturnAddressTo(esi); + __ SmiUntag(ecx); + __ lea(esp, Operand(esp, ecx, times_pointer_size, kPointerSize)); + __ PushReturnAddressFrom(esi); + __ Ret(); + } +} + +// static +void Builtins::Generate_StringConstructor(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- eax : number of arguments + // -- edi : constructor function + // -- esi : context + // -- esp[0] : return address + // -- esp[(argc - n) * 4] : arg[n] (zero-based) + // -- esp[(argc + 1) * 4] : receiver + // ----------------------------------- + + // 1. Load the first argument into eax. + Label no_arguments; + { + __ mov(ebx, eax); // Store argc in ebx. + __ test(eax, eax); + __ j(zero, &no_arguments, Label::kNear); + __ mov(eax, Operand(esp, eax, times_pointer_size, 0)); + } + + // 2a. At least one argument, return eax if it's a string, otherwise + // dispatch to appropriate conversion. + Label drop_frame_and_ret, to_string, symbol_descriptive_string; + { + __ JumpIfSmi(eax, &to_string, Label::kNear); + STATIC_ASSERT(FIRST_NONSTRING_TYPE == SYMBOL_TYPE); + __ CmpObjectType(eax, FIRST_NONSTRING_TYPE, edx); + __ j(above, &to_string, Label::kNear); + __ j(equal, &symbol_descriptive_string, Label::kNear); + __ jmp(&drop_frame_and_ret, Label::kNear); + } + + // 2b. No arguments, return the empty string (and pop the receiver). + __ bind(&no_arguments); + { + __ LoadRoot(eax, Heap::kempty_stringRootIndex); + __ ret(1 * kPointerSize); + } + + // 3a. Convert eax to a string. + __ bind(&to_string); + { + FrameScope scope(masm, StackFrame::MANUAL); + __ SmiTag(ebx); + __ EnterBuiltinFrame(esi, edi, ebx); + __ Call(masm->isolate()->builtins()->ToString(), RelocInfo::CODE_TARGET); + __ LeaveBuiltinFrame(esi, edi, ebx); + __ SmiUntag(ebx); + } + __ jmp(&drop_frame_and_ret, Label::kNear); + + // 3b. Convert symbol in eax to a string. + __ bind(&symbol_descriptive_string); + { + __ PopReturnAddressTo(ecx); + __ lea(esp, Operand(esp, ebx, times_pointer_size, kPointerSize)); + __ Push(eax); + __ PushReturnAddressFrom(ecx); + __ TailCallRuntime(Runtime::kSymbolDescriptiveString); + } + + __ bind(&drop_frame_and_ret); + { + // Drop all arguments including the receiver. + __ PopReturnAddressTo(ecx); + __ lea(esp, Operand(esp, ebx, times_pointer_size, kPointerSize)); + __ PushReturnAddressFrom(ecx); + __ Ret(); + } +} + +// static +void Builtins::Generate_StringConstructor_ConstructStub(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- eax : number of arguments + // -- edi : constructor function + // -- edx : new target + // -- esi : context + // -- esp[0] : return address + // -- esp[(argc - n) * 4] : arg[n] (zero-based) + // -- esp[(argc + 1) * 4] : receiver + // ----------------------------------- + + // 1. Make sure we operate in the context of the called function. + __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); + + __ mov(ebx, eax); + + // 2. Load the first argument into eax. + { + Label no_arguments, done; + __ test(ebx, ebx); + __ j(zero, &no_arguments, Label::kNear); + __ mov(eax, Operand(esp, ebx, times_pointer_size, 0)); + __ jmp(&done, Label::kNear); + __ bind(&no_arguments); + __ LoadRoot(eax, Heap::kempty_stringRootIndex); + __ bind(&done); + } + + // 3. Make sure eax is a string. + { + Label convert, done_convert; + __ JumpIfSmi(eax, &convert, Label::kNear); + __ CmpObjectType(eax, FIRST_NONSTRING_TYPE, ecx); + __ j(below, &done_convert); + __ bind(&convert); + { + FrameScope scope(masm, StackFrame::MANUAL); + __ SmiTag(ebx); + __ EnterBuiltinFrame(esi, edi, ebx); + __ Push(edx); + __ Call(masm->isolate()->builtins()->ToString(), RelocInfo::CODE_TARGET); + __ Pop(edx); + __ LeaveBuiltinFrame(esi, edi, ebx); + __ SmiUntag(ebx); + } + __ bind(&done_convert); + } + + // 4. Check if new target and constructor differ. + Label drop_frame_and_ret, done_alloc, new_object; + __ cmp(edx, edi); + __ j(not_equal, &new_object); + + // 5. Allocate a JSValue wrapper for the string. + // AllocateJSValue can't handle src == dst register. Reuse esi and restore it + // as needed after the call. + __ mov(esi, eax); + __ AllocateJSValue(eax, edi, esi, ecx, &done_alloc); + __ jmp(&drop_frame_and_ret); + + __ bind(&done_alloc); + { + // Restore eax to the first argument and esi to the context. + __ mov(eax, esi); + __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); + } + + // 6. Fallback to the runtime to create new object. + __ bind(&new_object); + { + FrameScope scope(masm, StackFrame::MANUAL); + __ SmiTag(ebx); + __ EnterBuiltinFrame(esi, edi, ebx); + __ Push(eax); // the first argument + __ Call(masm->isolate()->builtins()->FastNewObject(), + RelocInfo::CODE_TARGET); + __ Pop(FieldOperand(eax, JSValue::kValueOffset)); + __ LeaveBuiltinFrame(esi, edi, ebx); + __ SmiUntag(ebx); + } + + __ bind(&drop_frame_and_ret); + { + // Drop all arguments including the receiver. + __ PopReturnAddressTo(ecx); + __ lea(esp, Operand(esp, ebx, times_pointer_size, kPointerSize)); + __ PushReturnAddressFrom(ecx); + __ Ret(); + } +} + +static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) { + __ push(ebp); + __ mov(ebp, esp); + + // Store the arguments adaptor context sentinel. + __ push(Immediate(StackFrame::TypeToMarker(StackFrame::ARGUMENTS_ADAPTOR))); + + // Push the function on the stack. + __ push(edi); + + // Preserve the number of arguments on the stack. Must preserve eax, + // ebx and ecx because these registers are used when copying the + // arguments and the receiver. + STATIC_ASSERT(kSmiTagSize == 1); + __ lea(edi, Operand(eax, eax, times_1, kSmiTag)); + __ push(edi); +} + +static void LeaveArgumentsAdaptorFrame(MacroAssembler* masm) { + // Retrieve the number of arguments from the stack. + __ mov(ebx, Operand(ebp, ArgumentsAdaptorFrameConstants::kLengthOffset)); + + // Leave the frame. + __ leave(); + + // Remove caller arguments from the stack. + STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0); + __ pop(ecx); + __ lea(esp, Operand(esp, ebx, times_2, 1 * kPointerSize)); // 1 ~ receiver + __ push(ecx); +} + +// static +void Builtins::Generate_CallOrConstructVarargs(MacroAssembler* masm, + Handle code) { + // ----------- S t a t e ------------- + // -- edi : target + // -- eax : number of parameters on the stack (not including the receiver) + // -- ebx : arguments list (a FixedArray) + // -- ecx : len (number of elements to from args) + // -- edx : new.target (checked to be constructor or undefined) + // -- esp[0] : return address. + // ----------------------------------- + __ AssertFixedArray(ebx); + + // Save edx/edi/eax to stX0/stX1/stX2. + __ push(edx); + __ push(edi); + __ push(eax); + __ fld_s(MemOperand(esp, 0)); + __ fld_s(MemOperand(esp, 4)); + __ fld_s(MemOperand(esp, 8)); + __ lea(esp, Operand(esp, 3 * kFloatSize)); + + // Check for stack overflow. + { + // Check the stack for overflow. We are not trying to catch interruptions + // (i.e. debug break and preemption) here, so check the "real stack limit". + Label done; + ExternalReference real_stack_limit = + ExternalReference::address_of_real_stack_limit(masm->isolate()); + __ mov(edx, Operand::StaticVariable(real_stack_limit)); + // Make edx the space we have left. The stack might already be overflowed + // here which will cause edx to become negative. + __ neg(edx); + __ add(edx, esp); + __ sar(edx, kPointerSizeLog2); + // Check if the arguments will overflow the stack. + __ cmp(edx, ecx); + __ j(greater, &done, Label::kNear); // Signed comparison. + __ TailCallRuntime(Runtime::kThrowStackOverflow); + __ bind(&done); + } + + // Push additional arguments onto the stack. + { + __ PopReturnAddressTo(edx); + __ Move(eax, Immediate(0)); + Label done, push, loop; + __ bind(&loop); + __ cmp(eax, ecx); + __ j(equal, &done, Label::kNear); + // Turn the hole into undefined as we go. + __ mov(edi, + FieldOperand(ebx, eax, times_pointer_size, FixedArray::kHeaderSize)); + __ CompareRoot(edi, Heap::kTheHoleValueRootIndex); + __ j(not_equal, &push, Label::kNear); + __ LoadRoot(edi, Heap::kUndefinedValueRootIndex); + __ bind(&push); + __ Push(edi); + __ inc(eax); + __ jmp(&loop); + __ bind(&done); + __ PushReturnAddressFrom(edx); + } + + // Restore edx/edi/eax from stX0/stX1/stX2. + __ lea(esp, Operand(esp, -3 * kFloatSize)); + __ fstp_s(MemOperand(esp, 0)); + __ fstp_s(MemOperand(esp, 4)); + __ fstp_s(MemOperand(esp, 8)); + __ pop(edx); + __ pop(edi); + __ pop(eax); + + // Compute the actual parameter count. + __ add(eax, ecx); + + // Tail-call to the actual Call or Construct builtin. + __ Jump(code, RelocInfo::CODE_TARGET); +} + +// static +void Builtins::Generate_CallOrConstructForwardVarargs(MacroAssembler* masm, + Handle code) { + // ----------- S t a t e ------------- + // -- eax : the number of arguments (not including the receiver) + // -- edi : the target to call (can be any Object) + // -- edx : the new target (for [[Construct]] calls) + // -- ecx : start index (to support rest parameters) + // ----------------------------------- + + // Preserve new.target (in case of [[Construct]]). + __ push(edx); + __ fld_s(MemOperand(esp, 0)); + __ lea(esp, Operand(esp, kFloatSize)); + + // Check if we have an arguments adaptor frame below the function frame. + Label arguments_adaptor, arguments_done; + __ mov(ebx, Operand(ebp, StandardFrameConstants::kCallerFPOffset)); + __ cmp(Operand(ebx, CommonFrameConstants::kContextOrFrameTypeOffset), + Immediate(StackFrame::TypeToMarker(StackFrame::ARGUMENTS_ADAPTOR))); + __ j(equal, &arguments_adaptor, Label::kNear); + { + __ mov(edx, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); + __ mov(edx, FieldOperand(edx, JSFunction::kSharedFunctionInfoOffset)); + __ mov(edx, + FieldOperand(edx, SharedFunctionInfo::kFormalParameterCountOffset)); + __ mov(ebx, ebp); + } + __ jmp(&arguments_done, Label::kNear); + __ bind(&arguments_adaptor); + { + // Just load the length from the ArgumentsAdaptorFrame. + __ mov(edx, Operand(ebx, ArgumentsAdaptorFrameConstants::kLengthOffset)); + __ SmiUntag(edx); + } + __ bind(&arguments_done); + + Label stack_done; + __ sub(edx, ecx); + __ j(less_equal, &stack_done); + { + // Check for stack overflow. + { + // Check the stack for overflow. We are not trying to catch interruptions + // (i.e. debug break and preemption) here, so check the "real stack + // limit". + Label done; + __ LoadRoot(ecx, Heap::kRealStackLimitRootIndex); + // Make ecx the space we have left. The stack might already be + // overflowed here which will cause ecx to become negative. + __ neg(ecx); + __ add(ecx, esp); + __ sar(ecx, kPointerSizeLog2); + // Check if the arguments will overflow the stack. + __ cmp(ecx, edx); + __ j(greater, &done, Label::kNear); // Signed comparison. + __ TailCallRuntime(Runtime::kThrowStackOverflow); + __ bind(&done); + } + + // Forward the arguments from the caller frame. + { + Label loop; + __ add(eax, edx); + __ PopReturnAddressTo(ecx); + __ bind(&loop); + { + __ Push(Operand(ebx, edx, times_pointer_size, 1 * kPointerSize)); + __ dec(edx); + __ j(not_zero, &loop); + } + __ PushReturnAddressFrom(ecx); + } + } + __ bind(&stack_done); + + // Restore new.target (in case of [[Construct]]). + __ lea(esp, Operand(esp, -kFloatSize)); + __ fstp_s(MemOperand(esp, 0)); + __ pop(edx); + + // Tail-call to the {code} handler. + __ Jump(code, RelocInfo::CODE_TARGET); +} + +// static +void Builtins::Generate_CallFunction(MacroAssembler* masm, + ConvertReceiverMode mode) { + // ----------- S t a t e ------------- + // -- eax : the number of arguments (not including the receiver) + // -- edi : the function to call (checked to be a JSFunction) + // ----------------------------------- + __ AssertFunction(edi); + + // See ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList) + // Check that the function is not a "classConstructor". + Label class_constructor; + __ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); + __ test(FieldOperand(edx, SharedFunctionInfo::kCompilerHintsOffset), + Immediate(SharedFunctionInfo::kClassConstructorMask)); + __ j(not_zero, &class_constructor); + + // Enter the context of the function; ToObject has to run in the function + // context, and we also need to take the global proxy from the function + // context in case of conversion. + __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); + // We need to convert the receiver for non-native sloppy mode functions. + Label done_convert; + __ test(FieldOperand(edx, SharedFunctionInfo::kCompilerHintsOffset), + Immediate(SharedFunctionInfo::IsNativeBit::kMask | + SharedFunctionInfo::IsStrictBit::kMask)); + __ j(not_zero, &done_convert); + { + // ----------- S t a t e ------------- + // -- eax : the number of arguments (not including the receiver) + // -- edx : the shared function info. + // -- edi : the function to call (checked to be a JSFunction) + // -- esi : the function context. + // ----------------------------------- + + if (mode == ConvertReceiverMode::kNullOrUndefined) { + // Patch receiver to global proxy. + __ LoadGlobalProxy(ecx); + } else { + Label convert_to_object, convert_receiver; + __ mov(ecx, Operand(esp, eax, times_pointer_size, kPointerSize)); + __ JumpIfSmi(ecx, &convert_to_object, Label::kNear); + STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE); + __ CmpObjectType(ecx, FIRST_JS_RECEIVER_TYPE, ebx); + __ j(above_equal, &done_convert); + if (mode != ConvertReceiverMode::kNotNullOrUndefined) { + Label convert_global_proxy; + __ JumpIfRoot(ecx, Heap::kUndefinedValueRootIndex, + &convert_global_proxy, Label::kNear); + __ JumpIfNotRoot(ecx, Heap::kNullValueRootIndex, &convert_to_object, + Label::kNear); + __ bind(&convert_global_proxy); + { + // Patch receiver to global proxy. + __ LoadGlobalProxy(ecx); + } + __ jmp(&convert_receiver); + } + __ bind(&convert_to_object); + { + // Convert receiver using ToObject. + // TODO(bmeurer): Inline the allocation here to avoid building the frame + // in the fast case? (fall back to AllocateInNewSpace?) + FrameScope scope(masm, StackFrame::INTERNAL); + __ SmiTag(eax); + __ Push(eax); + __ Push(edi); + __ mov(eax, ecx); + __ Push(esi); + __ Call(masm->isolate()->builtins()->ToObject(), + RelocInfo::CODE_TARGET); + __ Pop(esi); + __ mov(ecx, eax); + __ Pop(edi); + __ Pop(eax); + __ SmiUntag(eax); + } + __ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); + __ bind(&convert_receiver); + } + __ mov(Operand(esp, eax, times_pointer_size, kPointerSize), ecx); + } + __ bind(&done_convert); + + // ----------- S t a t e ------------- + // -- eax : the number of arguments (not including the receiver) + // -- edx : the shared function info. + // -- edi : the function to call (checked to be a JSFunction) + // -- esi : the function context. + // ----------------------------------- + + __ mov(ebx, + FieldOperand(edx, SharedFunctionInfo::kFormalParameterCountOffset)); + ParameterCount actual(eax); + ParameterCount expected(ebx); + __ InvokeFunctionCode(edi, no_reg, expected, actual, JUMP_FUNCTION, + CheckDebugStepCallWrapper()); + // The function is a "classConstructor", need to raise an exception. + __ bind(&class_constructor); + { + FrameScope frame(masm, StackFrame::INTERNAL); + __ push(edi); + __ CallRuntime(Runtime::kThrowConstructorNonCallableError); + } +} + +namespace { + +void Generate_PushBoundArguments(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- eax : the number of arguments (not including the receiver) + // -- edx : new.target (only in case of [[Construct]]) + // -- edi : target (checked to be a JSBoundFunction) + // ----------------------------------- + + // Load [[BoundArguments]] into ecx and length of that into ebx. + Label no_bound_arguments; + __ mov(ecx, FieldOperand(edi, JSBoundFunction::kBoundArgumentsOffset)); + __ mov(ebx, FieldOperand(ecx, FixedArray::kLengthOffset)); + __ SmiUntag(ebx); + __ test(ebx, ebx); + __ j(zero, &no_bound_arguments); + { + // ----------- S t a t e ------------- + // -- eax : the number of arguments (not including the receiver) + // -- edx : new.target (only in case of [[Construct]]) + // -- edi : target (checked to be a JSBoundFunction) + // -- ecx : the [[BoundArguments]] (implemented as FixedArray) + // -- ebx : the number of [[BoundArguments]] + // ----------------------------------- + + // Reserve stack space for the [[BoundArguments]]. + { + Label done; + __ lea(ecx, Operand(ebx, times_pointer_size, 0)); + __ sub(esp, ecx); + // Check the stack for overflow. We are not trying to catch interruptions + // (i.e. debug break and preemption) here, so check the "real stack + // limit". + __ CompareRoot(esp, ecx, Heap::kRealStackLimitRootIndex); + __ j(greater, &done, Label::kNear); // Signed comparison. + // Restore the stack pointer. + __ lea(esp, Operand(esp, ebx, times_pointer_size, 0)); + { + FrameScope scope(masm, StackFrame::MANUAL); + __ EnterFrame(StackFrame::INTERNAL); + __ CallRuntime(Runtime::kThrowStackOverflow); + } + __ bind(&done); + } + + // Adjust effective number of arguments to include return address. + __ inc(eax); + + // Relocate arguments and return address down the stack. + { + Label loop; + __ Set(ecx, 0); + __ lea(ebx, Operand(esp, ebx, times_pointer_size, 0)); + __ bind(&loop); + __ fld_s(Operand(ebx, ecx, times_pointer_size, 0)); + __ fstp_s(Operand(esp, ecx, times_pointer_size, 0)); + __ inc(ecx); + __ cmp(ecx, eax); + __ j(less, &loop); + } + + // Copy [[BoundArguments]] to the stack (below the arguments). + { + Label loop; + __ mov(ecx, FieldOperand(edi, JSBoundFunction::kBoundArgumentsOffset)); + __ mov(ebx, FieldOperand(ecx, FixedArray::kLengthOffset)); + __ SmiUntag(ebx); + __ bind(&loop); + __ dec(ebx); + __ fld_s( + FieldOperand(ecx, ebx, times_pointer_size, FixedArray::kHeaderSize)); + __ fstp_s(Operand(esp, eax, times_pointer_size, 0)); + __ lea(eax, Operand(eax, 1)); + __ j(greater, &loop); + } + + // Adjust effective number of arguments (eax contains the number of + // arguments from the call plus return address plus the number of + // [[BoundArguments]]), so we need to subtract one for the return address. + __ dec(eax); + } + __ bind(&no_bound_arguments); +} + +} // namespace + +// static +void Builtins::Generate_CallBoundFunctionImpl(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- eax : the number of arguments (not including the receiver) + // -- edi : the function to call (checked to be a JSBoundFunction) + // ----------------------------------- + __ AssertBoundFunction(edi); + + // Patch the receiver to [[BoundThis]]. + __ mov(ebx, FieldOperand(edi, JSBoundFunction::kBoundThisOffset)); + __ mov(Operand(esp, eax, times_pointer_size, kPointerSize), ebx); + + // Push the [[BoundArguments]] onto the stack. + Generate_PushBoundArguments(masm); + + // Call the [[BoundTargetFunction]] via the Call builtin. + __ mov(edi, FieldOperand(edi, JSBoundFunction::kBoundTargetFunctionOffset)); + __ mov(ecx, Operand::StaticVariable(ExternalReference( + Builtins::kCall_ReceiverIsAny, masm->isolate()))); + __ lea(ecx, FieldOperand(ecx, Code::kHeaderSize)); + __ jmp(ecx); +} + +// static +void Builtins::Generate_Call(MacroAssembler* masm, ConvertReceiverMode mode) { + // ----------- S t a t e ------------- + // -- eax : the number of arguments (not including the receiver) + // -- edi : the target to call (can be any Object). + // ----------------------------------- + + Label non_callable, non_function, non_smi; + __ JumpIfSmi(edi, &non_callable); + __ bind(&non_smi); + __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx); + __ j(equal, masm->isolate()->builtins()->CallFunction(mode), + RelocInfo::CODE_TARGET); + __ CmpInstanceType(ecx, JS_BOUND_FUNCTION_TYPE); + __ j(equal, masm->isolate()->builtins()->CallBoundFunction(), + RelocInfo::CODE_TARGET); + + // Check if target is a proxy and call CallProxy external builtin + __ test_b(FieldOperand(ecx, Map::kBitFieldOffset), + Immediate(1 << Map::kIsCallable)); + __ j(zero, &non_callable); + + // Call CallProxy external builtin + __ CmpInstanceType(ecx, JS_PROXY_TYPE); + __ j(not_equal, &non_function); + + __ mov(ecx, Operand::StaticVariable( + ExternalReference(Builtins::kCallProxy, masm->isolate()))); + __ lea(ecx, FieldOperand(ecx, Code::kHeaderSize)); + __ jmp(ecx); + + // 2. Call to something else, which might have a [[Call]] internal method (if + // not we raise an exception). + __ bind(&non_function); + // Overwrite the original receiver with the (original) target. + __ mov(Operand(esp, eax, times_pointer_size, kPointerSize), edi); + // Let the "call_as_function_delegate" take care of the rest. + __ LoadGlobalFunction(Context::CALL_AS_FUNCTION_DELEGATE_INDEX, edi); + __ Jump(masm->isolate()->builtins()->CallFunction( + ConvertReceiverMode::kNotNullOrUndefined), + RelocInfo::CODE_TARGET); + + // 3. Call to something that is not callable. + __ bind(&non_callable); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ Push(edi); + __ CallRuntime(Runtime::kThrowCalledNonCallable); + } +} + +// static +void Builtins::Generate_ConstructFunction(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- eax : the number of arguments (not including the receiver) + // -- edx : the new target (checked to be a constructor) + // -- edi : the constructor to call (checked to be a JSFunction) + // ----------------------------------- + __ AssertFunction(edi); + + // Calling convention for function specific ConstructStubs require + // ebx to contain either an AllocationSite or undefined. + __ LoadRoot(ebx, Heap::kUndefinedValueRootIndex); + + // Tail call to the function-specific construct stub (still in the caller + // context at this point). + __ mov(ecx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); + __ mov(ecx, FieldOperand(ecx, SharedFunctionInfo::kConstructStubOffset)); + __ lea(ecx, FieldOperand(ecx, Code::kHeaderSize)); + __ jmp(ecx); +} + +// static +void Builtins::Generate_ConstructBoundFunction(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- eax : the number of arguments (not including the receiver) + // -- edx : the new target (checked to be a constructor) + // -- edi : the constructor to call (checked to be a JSBoundFunction) + // ----------------------------------- + __ AssertBoundFunction(edi); + + // Push the [[BoundArguments]] onto the stack. + Generate_PushBoundArguments(masm); + + // Patch new.target to [[BoundTargetFunction]] if new.target equals target. + { + Label done; + __ cmp(edi, edx); + __ j(not_equal, &done, Label::kNear); + __ mov(edx, FieldOperand(edi, JSBoundFunction::kBoundTargetFunctionOffset)); + __ bind(&done); + } + + // Construct the [[BoundTargetFunction]] via the Construct builtin. + __ mov(edi, FieldOperand(edi, JSBoundFunction::kBoundTargetFunctionOffset)); + __ mov(ecx, Operand::StaticVariable( + ExternalReference(Builtins::kConstruct, masm->isolate()))); + __ lea(ecx, FieldOperand(ecx, Code::kHeaderSize)); + __ jmp(ecx); +} + +// static +void Builtins::Generate_ConstructProxy(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- eax : the number of arguments (not including the receiver) + // -- edi : the constructor to call (checked to be a JSProxy) + // -- edx : the new target (either the same as the constructor or + // the JSFunction on which new was invoked initially) + // ----------------------------------- + + // Call into the Runtime for Proxy [[Construct]]. + __ PopReturnAddressTo(ecx); + __ Push(edi); + __ Push(edx); + __ PushReturnAddressFrom(ecx); + // Include the pushed new_target, constructor and the receiver. + __ add(eax, Immediate(3)); + // Tail-call to the runtime. + __ JumpToExternalReference( + ExternalReference(Runtime::kJSProxyConstruct, masm->isolate())); +} + +// static +void Builtins::Generate_Construct(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- eax : the number of arguments (not including the receiver) + // -- edx : the new target (either the same as the constructor or + // the JSFunction on which new was invoked initially) + // -- edi : the constructor to call (can be any Object) + // ----------------------------------- + + // Check if target is a Smi. + Label non_constructor; + __ JumpIfSmi(edi, &non_constructor, Label::kNear); + + // Dispatch based on instance type. + __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx); + __ j(equal, masm->isolate()->builtins()->ConstructFunction(), + RelocInfo::CODE_TARGET); + + // Check if target has a [[Construct]] internal method. + __ test_b(FieldOperand(ecx, Map::kBitFieldOffset), + Immediate(1 << Map::kIsConstructor)); + __ j(zero, &non_constructor, Label::kNear); + + // Only dispatch to bound functions after checking whether they are + // constructors. + __ CmpInstanceType(ecx, JS_BOUND_FUNCTION_TYPE); + __ j(equal, masm->isolate()->builtins()->ConstructBoundFunction(), + RelocInfo::CODE_TARGET); + + // Only dispatch to proxies after checking whether they are constructors. + __ CmpInstanceType(ecx, JS_PROXY_TYPE); + __ j(equal, masm->isolate()->builtins()->ConstructProxy(), + RelocInfo::CODE_TARGET); + + // Called Construct on an exotic Object with a [[Construct]] internal method. + { + // Overwrite the original receiver with the (original) target. + __ mov(Operand(esp, eax, times_pointer_size, kPointerSize), edi); + // Let the "call_as_constructor_delegate" take care of the rest. + __ LoadGlobalFunction(Context::CALL_AS_CONSTRUCTOR_DELEGATE_INDEX, edi); + __ Jump(masm->isolate()->builtins()->CallFunction(), + RelocInfo::CODE_TARGET); + } + + // Called Construct on an Object that doesn't have a [[Construct]] internal + // method. + __ bind(&non_constructor); + __ Jump(masm->isolate()->builtins()->ConstructedNonConstructable(), + RelocInfo::CODE_TARGET); +} + +// static +void Builtins::Generate_AllocateInNewSpace(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- edx : requested object size (untagged) + // -- esp[0] : return address + // ----------------------------------- + __ SmiTag(edx); + __ PopReturnAddressTo(ecx); + __ Push(edx); + __ PushReturnAddressFrom(ecx); + __ Move(esi, Smi::kZero); + __ TailCallRuntime(Runtime::kAllocateInNewSpace); +} + +// static +void Builtins::Generate_AllocateInOldSpace(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- edx : requested object size (untagged) + // -- esp[0] : return address + // ----------------------------------- + __ SmiTag(edx); + __ PopReturnAddressTo(ecx); + __ Push(edx); + __ Push(Smi::FromInt(AllocateTargetSpace::encode(OLD_SPACE))); + __ PushReturnAddressFrom(ecx); + __ Move(esi, Smi::kZero); + __ TailCallRuntime(Runtime::kAllocateInTargetSpace); +} + +// static +void Builtins::Generate_Abort(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- edx : message_id as Smi + // -- esp[0] : return address + // ----------------------------------- + __ PopReturnAddressTo(ecx); + __ Push(edx); + __ PushReturnAddressFrom(ecx); + __ Move(esi, Smi::kZero); + __ TailCallRuntime(Runtime::kAbort); +} + +void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- eax : actual number of arguments + // -- ebx : expected number of arguments + // -- edx : new target (passed through to callee) + // -- edi : function (passed through to callee) + // ----------------------------------- + + Label invoke, dont_adapt_arguments, stack_overflow; + __ IncrementCounter(masm->isolate()->counters()->arguments_adaptors(), 1); + + Label enough, too_few; + __ cmp(eax, ebx); + __ j(less, &too_few); + __ cmp(ebx, SharedFunctionInfo::kDontAdaptArgumentsSentinel); + __ j(equal, &dont_adapt_arguments); + + { // Enough parameters: Actual >= expected. + __ bind(&enough); + EnterArgumentsAdaptorFrame(masm); + // edi is used as a scratch register. It should be restored from the frame + // when needed. + Generate_StackOverflowCheck(masm, ebx, ecx, edi, &stack_overflow); + + // Copy receiver and all expected arguments. + const int offset = StandardFrameConstants::kCallerSPOffset; + __ lea(edi, Operand(ebp, eax, times_4, offset)); + __ mov(eax, -1); // account for receiver + + Label copy; + __ bind(©); + __ inc(eax); + __ push(Operand(edi, 0)); + __ sub(edi, Immediate(kPointerSize)); + __ cmp(eax, ebx); + __ j(less, ©); + // eax now contains the expected number of arguments. + __ jmp(&invoke); + } + + { // Too few parameters: Actual < expected. + __ bind(&too_few); + EnterArgumentsAdaptorFrame(masm); + // edi is used as a scratch register. It should be restored from the frame + // when needed. + Generate_StackOverflowCheck(masm, ebx, ecx, edi, &stack_overflow); + + // Remember expected arguments in ecx. + __ mov(ecx, ebx); + + // Copy receiver and all actual arguments. + const int offset = StandardFrameConstants::kCallerSPOffset; + __ lea(edi, Operand(ebp, eax, times_4, offset)); + // ebx = expected - actual. + __ sub(ebx, eax); + // eax = -actual - 1 + __ neg(eax); + __ sub(eax, Immediate(1)); + + Label copy; + __ bind(©); + __ inc(eax); + __ push(Operand(edi, 0)); + __ sub(edi, Immediate(kPointerSize)); + __ test(eax, eax); + __ j(not_zero, ©); + + // Fill remaining expected arguments with undefined values. + Label fill; + __ bind(&fill); + __ inc(eax); + __ push(Immediate(masm->isolate()->factory()->undefined_value())); + __ cmp(eax, ebx); + __ j(less, &fill); + + // Restore expected arguments. + __ mov(eax, ecx); + } + + // Call the entry point. + __ bind(&invoke); + // Restore function pointer. + __ mov(edi, Operand(ebp, ArgumentsAdaptorFrameConstants::kFunctionOffset)); + // eax : expected number of arguments + // edx : new target (passed through to callee) + // edi : function (passed through to callee) + __ mov(ecx, FieldOperand(edi, JSFunction::kCodeEntryOffset)); + __ call(ecx); + + // Store offset of return address for deoptimizer. + masm->isolate()->heap()->SetArgumentsAdaptorDeoptPCOffset(masm->pc_offset()); + + // Leave frame and return. + LeaveArgumentsAdaptorFrame(masm); + __ ret(0); + + // ------------------------------------------- + // Dont adapt arguments. + // ------------------------------------------- + __ bind(&dont_adapt_arguments); + __ mov(ecx, FieldOperand(edi, JSFunction::kCodeEntryOffset)); + __ jmp(ecx); + + __ bind(&stack_overflow); + { + FrameScope frame(masm, StackFrame::MANUAL); + __ CallRuntime(Runtime::kThrowStackOverflow); + __ int3(); + } +} + +static void Generate_OnStackReplacementHelper(MacroAssembler* masm, + bool has_handler_frame) { + // Lookup the function in the JavaScript frame. + if (has_handler_frame) { + __ mov(eax, Operand(ebp, StandardFrameConstants::kCallerFPOffset)); + __ mov(eax, Operand(eax, JavaScriptFrameConstants::kFunctionOffset)); + } else { + __ mov(eax, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); + } + + { + FrameScope scope(masm, StackFrame::INTERNAL); + // Pass function as argument. + __ push(eax); + __ CallRuntime(Runtime::kCompileForOnStackReplacement); + } + + Label skip; + // If the code object is null, just return to the caller. + __ cmp(eax, Immediate(0)); + __ j(not_equal, &skip, Label::kNear); + __ ret(0); + + __ bind(&skip); + + // Drop any potential handler frame that is be sitting on top of the actual + // JavaScript frame. This is the case then OSR is triggered from bytecode. + if (has_handler_frame) { + __ leave(); + } + + // Load deoptimization data from the code object. + __ mov(ebx, Operand(eax, Code::kDeoptimizationDataOffset - kHeapObjectTag)); + + // Load the OSR entrypoint offset from the deoptimization data. + __ mov(ebx, Operand(ebx, FixedArray::OffsetOfElementAt( + DeoptimizationInputData::kOsrPcOffsetIndex) - + kHeapObjectTag)); + __ SmiUntag(ebx); + + // Compute the target address = code_obj + header_size + osr_offset + __ lea(eax, Operand(eax, ebx, times_1, Code::kHeaderSize - kHeapObjectTag)); + + // Overwrite the return address on the stack. + __ mov(Operand(esp, 0), eax); + + // And "return" to the OSR entry point of the function. + __ ret(0); +} + +void Builtins::Generate_OnStackReplacement(MacroAssembler* masm) { + Generate_OnStackReplacementHelper(masm, false); +} + +void Builtins::Generate_InterpreterOnStackReplacement(MacroAssembler* masm) { + Generate_OnStackReplacementHelper(masm, true); +} + +void Builtins::Generate_WasmCompileLazy(MacroAssembler* masm) { + { + FrameScope scope(masm, StackFrame::INTERNAL); + + // Save all parameter registers (see wasm-linkage.cc). They might be + // overwritten in the runtime call below. We don't have any callee-saved + // registers in wasm, so no need to store anything else. + constexpr Register gp_regs[]{eax, ebx, ecx, edx, esi}; + + for (auto reg : gp_regs) { + __ Push(reg); + } + + // Initialize esi register with kZero, CEntryStub will use it to set the + // current context on the isolate. + __ Move(esi, Smi::kZero); + __ CallRuntime(Runtime::kWasmCompileLazy); + // Store returned instruction start in edi. + __ lea(edi, FieldOperand(eax, Code::kHeaderSize)); + + // Restore registers. + for (int i = arraysize(gp_regs) - 1; i >= 0; --i) { + __ Pop(gp_regs[i]); + } + } + // Now jump to the instructions of the returned code object. + __ jmp(edi); +} + +#undef __ +} // namespace internal +} // namespace v8 + +#endif // V8_TARGET_ARCH_X87 diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/builtins/x87/OWNERS qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/builtins/x87/OWNERS --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/builtins/x87/OWNERS 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/builtins/x87/OWNERS 2017-12-25 17:42:57.201465852 +0100 @@ -0,0 +1,2 @@ +weiliang.lin@intel.com +chunyang.dai@intel.com diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/codegen.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/codegen.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/codegen.h 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/codegen.h 2017-12-25 17:42:57.205465793 +0100 @@ -59,6 +59,8 @@ #include "src/mips64/codegen-mips64.h" // NOLINT #elif V8_TARGET_ARCH_S390 #include "src/s390/codegen-s390.h" // NOLINT +#elif V8_TARGET_ARCH_X87 +#include "src/x87/codegen-x87.h" // NOLINT #else #error Unsupported target architecture. #endif diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/code-stubs.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/code-stubs.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/code-stubs.h 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/code-stubs.h 2017-12-25 17:42:57.205465793 +0100 @@ -514,6 +514,8 @@ #include "src/mips64/code-stubs-mips64.h" #elif V8_TARGET_ARCH_S390 #include "src/s390/code-stubs-s390.h" +#elif V8_TARGET_ARCH_X87 +#include "src/x87/code-stubs-x87.h" #else #error Unsupported target architecture. #endif diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/compiler/c-linkage.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/compiler/c-linkage.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/compiler/c-linkage.cc 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/compiler/c-linkage.cc 2017-12-25 17:42:57.205465793 +0100 @@ -50,6 +50,12 @@ rbx.bit() | r12.bit() | r13.bit() | r14.bit() | r15.bit() #endif +#elif V8_TARGET_ARCH_X87 +// =========================================================================== +// == x87 ==================================================================== +// =========================================================================== +#define CALLEE_SAVE_REGISTERS esi.bit() | edi.bit() | ebx.bit() + #elif V8_TARGET_ARCH_ARM // =========================================================================== // == arm ==================================================================== @@ -155,7 +161,7 @@ msig->parameter_count()); // Check the types of the signature. // Currently no floating point parameters or returns are allowed because - // on ia32, the FP top of stack is involved. + // on x87 and ia32, the FP top of stack is involved. for (size_t i = 0; i < msig->return_count(); i++) { MachineRepresentation rep = msig->GetReturn(i).representation(); CHECK_NE(MachineRepresentation::kFloat32, rep); diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/compiler/instruction-codes.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/compiler/instruction-codes.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/compiler/instruction-codes.h 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/compiler/instruction-codes.h 2017-12-25 17:42:57.205465793 +0100 @@ -23,6 +23,8 @@ #include "src/compiler/ppc/instruction-codes-ppc.h" #elif V8_TARGET_ARCH_S390 #include "src/compiler/s390/instruction-codes-s390.h" +#elif V8_TARGET_ARCH_X87 +#include "src/compiler/x87/instruction-codes-x87.h" #else #define TARGET_ARCH_OPCODE_LIST(V) #define TARGET_ADDRESSING_MODE_LIST(V) diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/compiler/wasm-linkage.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/compiler/wasm-linkage.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/compiler/wasm-linkage.cc 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/compiler/wasm-linkage.cc 2017-12-25 17:42:57.205465793 +0100 @@ -69,6 +69,14 @@ #define FP_PARAM_REGISTERS xmm1, xmm2, xmm3, xmm4, xmm5, xmm6 #define FP_RETURN_REGISTERS xmm1, xmm2 +#elif V8_TARGET_ARCH_X87 +// =========================================================================== +// == x87 ==================================================================== +// =========================================================================== +#define GP_PARAM_REGISTERS eax, edx, ecx, ebx, esi +#define GP_RETURN_REGISTERS eax, edx +#define FP_RETURN_REGISTERS stX_0 + #elif V8_TARGET_ARCH_ARM // =========================================================================== // == arm ==================================================================== diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/compiler/x87/code-generator-x87.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/compiler/x87/code-generator-x87.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/compiler/x87/code-generator-x87.cc 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/compiler/x87/code-generator-x87.cc 2017-12-28 03:58:53.829141647 +0100 @@ -0,0 +1,2878 @@ +// Copyright 2013 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/compiler/code-generator.h" + +#include "src/compilation-info.h" +#include "src/compiler/code-generator-impl.h" +#include "src/compiler/gap-resolver.h" +#include "src/compiler/node-matchers.h" +#include "src/compiler/osr.h" +#include "src/frames.h" +#include "src/x87/assembler-x87.h" +#include "src/x87/frames-x87.h" +#include "src/x87/macro-assembler-x87.h" + +namespace v8 { +namespace internal { +namespace compiler { + +#define __ tasm()-> + + +// Adds X87 specific methods for decoding operands. +class X87OperandConverter : public InstructionOperandConverter { + public: + X87OperandConverter(CodeGenerator* gen, Instruction* instr) + : InstructionOperandConverter(gen, instr) {} + + Operand InputOperand(size_t index, int extra = 0) { + return ToOperand(instr_->InputAt(index), extra); + } + + Immediate InputImmediate(size_t index) { + return ToImmediate(instr_->InputAt(index)); + } + + Operand OutputOperand() { return ToOperand(instr_->Output()); } + + Operand ToOperand(InstructionOperand* op, int extra = 0) { + if (op->IsRegister()) { + DCHECK(extra == 0); + return Operand(ToRegister(op)); + } + DCHECK(op->IsStackSlot() || op->IsFPStackSlot()); + return SlotToOperand(AllocatedOperand::cast(op)->index(), extra); + } + + Operand SlotToOperand(int slot, int extra = 0) { + FrameOffset offset = frame_access_state()->GetFrameOffset(slot); + return Operand(offset.from_stack_pointer() ? esp : ebp, + offset.offset() + extra); + } + + Operand HighOperand(InstructionOperand* op) { + DCHECK(op->IsFPStackSlot()); + return ToOperand(op, kPointerSize); + } + + Immediate ToImmediate(InstructionOperand* operand) { + Constant constant = ToConstant(operand); + if (constant.type() == Constant::kInt32 && + RelocInfo::IsWasmReference(constant.rmode())) { + return Immediate(reinterpret_cast
(constant.ToInt32()), + constant.rmode()); + } + switch (constant.type()) { + case Constant::kInt32: + return Immediate(constant.ToInt32()); + case Constant::kFloat32: + return Immediate::EmbeddedNumber(constant.ToFloat32()); + case Constant::kFloat64: + return Immediate::EmbeddedNumber(constant.ToFloat64().value()); + case Constant::kExternalReference: + return Immediate(constant.ToExternalReference()); + case Constant::kHeapObject: + return Immediate(constant.ToHeapObject()); + case Constant::kInt64: + break; + case Constant::kRpoNumber: + return Immediate::CodeRelativeOffset(ToLabel(operand)); + } + UNREACHABLE(); + } + + static size_t NextOffset(size_t* offset) { + size_t i = *offset; + (*offset)++; + return i; + } + + static ScaleFactor ScaleFor(AddressingMode one, AddressingMode mode) { + STATIC_ASSERT(0 == static_cast(times_1)); + STATIC_ASSERT(1 == static_cast(times_2)); + STATIC_ASSERT(2 == static_cast(times_4)); + STATIC_ASSERT(3 == static_cast(times_8)); + int scale = static_cast(mode - one); + DCHECK(scale >= 0 && scale < 4); + return static_cast(scale); + } + + Operand MemoryOperand(size_t* offset) { + AddressingMode mode = AddressingModeField::decode(instr_->opcode()); + switch (mode) { + case kMode_MR: { + Register base = InputRegister(NextOffset(offset)); + int32_t disp = 0; + return Operand(base, disp); + } + case kMode_MRI: { + Register base = InputRegister(NextOffset(offset)); + Constant ctant = ToConstant(instr_->InputAt(NextOffset(offset))); + return Operand(base, ctant.ToInt32(), ctant.rmode()); + } + case kMode_MR1: + case kMode_MR2: + case kMode_MR4: + case kMode_MR8: { + Register base = InputRegister(NextOffset(offset)); + Register index = InputRegister(NextOffset(offset)); + ScaleFactor scale = ScaleFor(kMode_MR1, mode); + int32_t disp = 0; + return Operand(base, index, scale, disp); + } + case kMode_MR1I: + case kMode_MR2I: + case kMode_MR4I: + case kMode_MR8I: { + Register base = InputRegister(NextOffset(offset)); + Register index = InputRegister(NextOffset(offset)); + ScaleFactor scale = ScaleFor(kMode_MR1I, mode); + Constant ctant = ToConstant(instr_->InputAt(NextOffset(offset))); + return Operand(base, index, scale, ctant.ToInt32(), ctant.rmode()); + } + case kMode_M1: + case kMode_M2: + case kMode_M4: + case kMode_M8: { + Register index = InputRegister(NextOffset(offset)); + ScaleFactor scale = ScaleFor(kMode_M1, mode); + int32_t disp = 0; + return Operand(index, scale, disp); + } + case kMode_M1I: + case kMode_M2I: + case kMode_M4I: + case kMode_M8I: { + Register index = InputRegister(NextOffset(offset)); + ScaleFactor scale = ScaleFor(kMode_M1I, mode); + Constant ctant = ToConstant(instr_->InputAt(NextOffset(offset))); + return Operand(index, scale, ctant.ToInt32(), ctant.rmode()); + } + case kMode_MI: { + Constant ctant = ToConstant(instr_->InputAt(NextOffset(offset))); + return Operand(ctant.ToInt32(), ctant.rmode()); + } + case kMode_None: + UNREACHABLE(); + } + UNREACHABLE(); + } + + Operand MemoryOperand(size_t first_input = 0) { + return MemoryOperand(&first_input); + } +}; + + +namespace { + +bool HasImmediateInput(Instruction* instr, size_t index) { + return instr->InputAt(index)->IsImmediate(); +} + + +class OutOfLineLoadInteger final : public OutOfLineCode { + public: + OutOfLineLoadInteger(CodeGenerator* gen, Register result) + : OutOfLineCode(gen), result_(result) {} + + void Generate() final { __ xor_(result_, result_); } + + private: + Register const result_; +}; + +class OutOfLineLoadFloat32NaN final : public OutOfLineCode { + public: + OutOfLineLoadFloat32NaN(CodeGenerator* gen, X87Register result) + : OutOfLineCode(gen), result_(result) {} + + void Generate() final { + DCHECK(result_.code() == 0); + USE(result_); + __ fstp(0); + __ push(Immediate(0xffc00000)); + __ fld_s(MemOperand(esp, 0)); + __ lea(esp, Operand(esp, kFloatSize)); + } + + private: + X87Register const result_; +}; + +class OutOfLineLoadFloat64NaN final : public OutOfLineCode { + public: + OutOfLineLoadFloat64NaN(CodeGenerator* gen, X87Register result) + : OutOfLineCode(gen), result_(result) {} + + void Generate() final { + DCHECK(result_.code() == 0); + USE(result_); + __ fstp(0); + __ push(Immediate(0xfff80000)); + __ push(Immediate(0x00000000)); + __ fld_d(MemOperand(esp, 0)); + __ lea(esp, Operand(esp, kDoubleSize)); + } + + private: + X87Register const result_; +}; + +class OutOfLineTruncateDoubleToI final : public OutOfLineCode { + public: + OutOfLineTruncateDoubleToI(CodeGenerator* gen, Register result, + X87Register input) + : OutOfLineCode(gen), + result_(result), + input_(input), + zone_(gen->zone()) {} + + void Generate() final { + UNIMPLEMENTED(); + USE(result_); + USE(input_); + } + + private: + Register const result_; + X87Register const input_; + Zone* zone_; +}; + + +class OutOfLineRecordWrite final : public OutOfLineCode { + public: + OutOfLineRecordWrite(CodeGenerator* gen, Register object, Operand operand, + Register value, Register scratch0, Register scratch1, + RecordWriteMode mode) + : OutOfLineCode(gen), + object_(object), + operand_(operand), + value_(value), + scratch0_(scratch0), + scratch1_(scratch1), + mode_(mode), + zone_(gen->zone()) {} + + void Generate() final { + if (mode_ > RecordWriteMode::kValueIsPointer) { + __ JumpIfSmi(value_, exit()); + } + __ CheckPageFlag(value_, scratch0_, + MemoryChunk::kPointersToHereAreInterestingMask, zero, + exit()); + RememberedSetAction const remembered_set_action = + mode_ > RecordWriteMode::kValueIsMap ? EMIT_REMEMBERED_SET + : OMIT_REMEMBERED_SET; + SaveFPRegsMode const save_fp_mode = + frame()->DidAllocateDoubleRegisters() ? kSaveFPRegs : kDontSaveFPRegs; + __ lea(scratch1_, operand_); + __ CallStubDelayed( + new (zone_) RecordWriteStub(nullptr, object_, scratch0_, scratch1_, + remembered_set_action, save_fp_mode)); + } + + private: + Register const object_; + Operand const operand_; + Register const value_; + Register const scratch0_; + Register const scratch1_; + RecordWriteMode const mode_; + Zone* zone_; +}; + +} // namespace + +#define ASSEMBLE_CHECKED_LOAD_FLOAT(asm_instr, OutOfLineLoadNaN) \ + do { \ + auto result = i.OutputDoubleRegister(); \ + auto offset = i.InputRegister(0); \ + DCHECK(result.code() == 0); \ + if (instr->InputAt(1)->IsRegister()) { \ + __ cmp(offset, i.InputRegister(1)); \ + } else { \ + __ cmp(offset, i.InputImmediate(1)); \ + } \ + OutOfLineCode* ool = new (zone()) OutOfLineLoadNaN(this, result); \ + __ j(above_equal, ool->entry()); \ + __ fstp(0); \ + __ asm_instr(i.MemoryOperand(2)); \ + __ bind(ool->exit()); \ + } while (false) + +#define ASSEMBLE_CHECKED_LOAD_INTEGER(asm_instr) \ + do { \ + auto result = i.OutputRegister(); \ + auto offset = i.InputRegister(0); \ + if (instr->InputAt(1)->IsRegister()) { \ + __ cmp(offset, i.InputRegister(1)); \ + } else { \ + __ cmp(offset, i.InputImmediate(1)); \ + } \ + OutOfLineCode* ool = new (zone()) OutOfLineLoadInteger(this, result); \ + __ j(above_equal, ool->entry()); \ + __ asm_instr(result, i.MemoryOperand(2)); \ + __ bind(ool->exit()); \ + } while (false) + + +#define ASSEMBLE_CHECKED_STORE_FLOAT(asm_instr) \ + do { \ + auto offset = i.InputRegister(0); \ + if (instr->InputAt(1)->IsRegister()) { \ + __ cmp(offset, i.InputRegister(1)); \ + } else { \ + __ cmp(offset, i.InputImmediate(1)); \ + } \ + Label done; \ + DCHECK(i.InputDoubleRegister(2).code() == 0); \ + __ j(above_equal, &done, Label::kNear); \ + __ asm_instr(i.MemoryOperand(3)); \ + __ bind(&done); \ + } while (false) + + +#define ASSEMBLE_CHECKED_STORE_INTEGER(asm_instr) \ + do { \ + auto offset = i.InputRegister(0); \ + if (instr->InputAt(1)->IsRegister()) { \ + __ cmp(offset, i.InputRegister(1)); \ + } else { \ + __ cmp(offset, i.InputImmediate(1)); \ + } \ + Label done; \ + __ j(above_equal, &done, Label::kNear); \ + if (instr->InputAt(2)->IsRegister()) { \ + __ asm_instr(i.MemoryOperand(3), i.InputRegister(2)); \ + } else { \ + __ asm_instr(i.MemoryOperand(3), i.InputImmediate(2)); \ + } \ + __ bind(&done); \ + } while (false) + +#define ASSEMBLE_COMPARE(asm_instr) \ + do { \ + if (AddressingModeField::decode(instr->opcode()) != kMode_None) { \ + size_t index = 0; \ + Operand left = i.MemoryOperand(&index); \ + if (HasImmediateInput(instr, index)) { \ + __ asm_instr(left, i.InputImmediate(index)); \ + } else { \ + __ asm_instr(left, i.InputRegister(index)); \ + } \ + } else { \ + if (HasImmediateInput(instr, 1)) { \ + if (instr->InputAt(0)->IsRegister()) { \ + __ asm_instr(i.InputRegister(0), i.InputImmediate(1)); \ + } else { \ + __ asm_instr(i.InputOperand(0), i.InputImmediate(1)); \ + } \ + } else { \ + if (instr->InputAt(1)->IsRegister()) { \ + __ asm_instr(i.InputRegister(0), i.InputRegister(1)); \ + } else { \ + __ asm_instr(i.InputRegister(0), i.InputOperand(1)); \ + } \ + } \ + } \ + } while (0) + +#define ASSEMBLE_IEEE754_BINOP(name) \ + do { \ + /* Saves the esp into ebx */ \ + __ push(ebx); \ + __ mov(ebx, esp); \ + /* Pass one double as argument on the stack. */ \ + __ PrepareCallCFunction(4, eax); \ + __ fstp(0); \ + /* Load first operand from original stack */ \ + __ fld_d(MemOperand(ebx, 4 + kDoubleSize)); \ + /* Put first operand into stack for function call */ \ + __ fstp_d(Operand(esp, 0 * kDoubleSize)); \ + /* Load second operand from original stack */ \ + __ fld_d(MemOperand(ebx, 4)); \ + /* Put second operand into stack for function call */ \ + __ fstp_d(Operand(esp, 1 * kDoubleSize)); \ + __ CallCFunction( \ + ExternalReference::ieee754_##name##_function(__ isolate()), 4); \ + /* Restore the ebx */ \ + __ pop(ebx); \ + /* Return value is in st(0) on x87. */ \ + __ lea(esp, Operand(esp, 2 * kDoubleSize)); \ + } while (false) + +#define ASSEMBLE_IEEE754_UNOP(name) \ + do { \ + /* Saves the esp into ebx */ \ + __ push(ebx); \ + __ mov(ebx, esp); \ + /* Pass one double as argument on the stack. */ \ + __ PrepareCallCFunction(2, eax); \ + __ fstp(0); \ + /* Load operand from original stack */ \ + __ fld_d(MemOperand(ebx, 4)); \ + /* Put operand into stack for function call */ \ + __ fstp_d(Operand(esp, 0)); \ + __ CallCFunction( \ + ExternalReference::ieee754_##name##_function(__ isolate()), 2); \ + /* Restore the ebx */ \ + __ pop(ebx); \ + /* Return value is in st(0) on x87. */ \ + __ lea(esp, Operand(esp, kDoubleSize)); \ + } while (false) + +#define ASSEMBLE_ATOMIC_BINOP(bin_inst, mov_inst, cmpxchg_inst) \ + do { \ + Label binop; \ + __ bind(&binop); \ + __ mov_inst(eax, i.MemoryOperand(1)); \ + __ Move(i.TempRegister(0), eax); \ + __ bin_inst(i.TempRegister(0), i.InputRegister(0)); \ + __ lock(); \ + __ cmpxchg_inst(i.MemoryOperand(1), i.TempRegister(0)); \ + __ j(not_equal, &binop); \ + } while (false) + +void CodeGenerator::AssembleDeconstructFrame() { + __ mov(esp, ebp); + __ pop(ebp); +} + +void CodeGenerator::AssemblePrepareTailCall() { + if (frame_access_state()->has_frame()) { + __ mov(ebp, MemOperand(ebp, 0)); + } + frame_access_state()->SetFrameAccessToSP(); +} + +void CodeGenerator::AssemblePopArgumentsAdaptorFrame(Register args_reg, + Register, Register, + Register) { + // There are not enough temp registers left on ia32 for a call instruction + // so we pick some scratch registers and save/restore them manually here. + int scratch_count = 3; + Register scratch1 = ebx; + Register scratch2 = ecx; + Register scratch3 = edx; + DCHECK(!AreAliased(args_reg, scratch1, scratch2, scratch3)); + Label done; + + // Check if current frame is an arguments adaptor frame. + __ cmp(Operand(ebp, StandardFrameConstants::kContextOffset), + Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); + __ j(not_equal, &done, Label::kNear); + + __ push(scratch1); + __ push(scratch2); + __ push(scratch3); + + // Load arguments count from current arguments adaptor frame (note, it + // does not include receiver). + Register caller_args_count_reg = scratch1; + __ mov(caller_args_count_reg, + Operand(ebp, ArgumentsAdaptorFrameConstants::kLengthOffset)); + __ SmiUntag(caller_args_count_reg); + + ParameterCount callee_args_count(args_reg); + __ PrepareForTailCall(callee_args_count, caller_args_count_reg, scratch2, + scratch3, ReturnAddressState::kOnStack, scratch_count); + __ pop(scratch3); + __ pop(scratch2); + __ pop(scratch1); + + __ bind(&done); +} + +namespace { + +void AdjustStackPointerForTailCall(TurboAssembler* tasm, + FrameAccessState* state, + int new_slot_above_sp, + bool allow_shrinkage = true) { + int current_sp_offset = state->GetSPToFPSlotCount() + + StandardFrameConstants::kFixedSlotCountAboveFp; + int stack_slot_delta = new_slot_above_sp - current_sp_offset; + if (stack_slot_delta > 0) { + tasm->sub(esp, Immediate(stack_slot_delta * kPointerSize)); + state->IncreaseSPDelta(stack_slot_delta); + } else if (allow_shrinkage && stack_slot_delta < 0) { + tasm->add(esp, Immediate(-stack_slot_delta * kPointerSize)); + state->IncreaseSPDelta(stack_slot_delta); + } +} + +} // namespace + +void CodeGenerator::AssembleTailCallBeforeGap(Instruction* instr, + int first_unused_stack_slot) { + CodeGenerator::PushTypeFlags flags(kImmediatePush | kScalarPush); + ZoneVector pushes(zone()); + GetPushCompatibleMoves(instr, flags, &pushes); + + if (!pushes.empty() && + (LocationOperand::cast(pushes.back()->destination()).index() + 1 == + first_unused_stack_slot)) { + X87OperandConverter g(this, instr); + for (auto move : pushes) { + LocationOperand destination_location( + LocationOperand::cast(move->destination())); + InstructionOperand source(move->source()); + AdjustStackPointerForTailCall(tasm(), frame_access_state(), + destination_location.index()); + if (source.IsStackSlot()) { + LocationOperand source_location(LocationOperand::cast(source)); + __ push(g.SlotToOperand(source_location.index())); + } else if (source.IsRegister()) { + LocationOperand source_location(LocationOperand::cast(source)); + __ push(source_location.GetRegister()); + } else if (source.IsImmediate()) { + __ push(Immediate(ImmediateOperand::cast(source).inline_value())); + } else { + // Pushes of non-scalar data types is not supported. + UNIMPLEMENTED(); + } + frame_access_state()->IncreaseSPDelta(1); + move->Eliminate(); + } + } + AdjustStackPointerForTailCall(tasm(), frame_access_state(), + first_unused_stack_slot, false); +} + +void CodeGenerator::AssembleTailCallAfterGap(Instruction* instr, + int first_unused_stack_slot) { + AdjustStackPointerForTailCall(tasm(), frame_access_state(), + first_unused_stack_slot); +} + +// Assembles an instruction after register allocation, producing machine code. +CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction( + Instruction* instr) { + X87OperandConverter i(this, instr); + InstructionCode opcode = instr->opcode(); + ArchOpcode arch_opcode = ArchOpcodeField::decode(opcode); + + switch (arch_opcode) { + case kArchCallCodeObject: { + if (FLAG_debug_code && FLAG_enable_slow_asserts) { + __ VerifyX87StackDepth(1); + } + __ fstp(0); + EnsureSpaceForLazyDeopt(); + if (HasImmediateInput(instr, 0)) { + Handle code = i.InputCode(0); + __ call(code, RelocInfo::CODE_TARGET); + } else { + Register reg = i.InputRegister(0); + __ add(reg, Immediate(Code::kHeaderSize - kHeapObjectTag)); + __ call(reg); + } + RecordCallPosition(instr); + bool double_result = + instr->HasOutput() && instr->Output()->IsFPRegister(); + if (double_result) { + __ lea(esp, Operand(esp, -kDoubleSize)); + __ fstp_d(Operand(esp, 0)); + } + __ fninit(); + if (double_result) { + __ fld_d(Operand(esp, 0)); + __ lea(esp, Operand(esp, kDoubleSize)); + } else { + __ fld1(); + } + frame_access_state()->ClearSPDelta(); + break; + } + case kArchTailCallCodeObjectFromJSFunction: + case kArchTailCallCodeObject: { + if (FLAG_debug_code && FLAG_enable_slow_asserts) { + __ VerifyX87StackDepth(1); + } + __ fstp(0); + if (arch_opcode == kArchTailCallCodeObjectFromJSFunction) { + AssemblePopArgumentsAdaptorFrame(kJavaScriptCallArgCountRegister, + no_reg, no_reg, no_reg); + } + if (HasImmediateInput(instr, 0)) { + Handle code = i.InputCode(0); + __ jmp(code, RelocInfo::CODE_TARGET); + } else { + Register reg = i.InputRegister(0); + __ add(reg, Immediate(Code::kHeaderSize - kHeapObjectTag)); + __ jmp(reg); + } + frame_access_state()->ClearSPDelta(); + frame_access_state()->SetFrameAccessToDefault(); + break; + } + case kArchTailCallAddress: { + CHECK(!HasImmediateInput(instr, 0)); + Register reg = i.InputRegister(0); + __ jmp(reg); + frame_access_state()->ClearSPDelta(); + frame_access_state()->SetFrameAccessToDefault(); + break; + } + case kArchCallJSFunction: { + EnsureSpaceForLazyDeopt(); + Register func = i.InputRegister(0); + if (FLAG_debug_code) { + // Check the function's context matches the context argument. + __ cmp(esi, FieldOperand(func, JSFunction::kContextOffset)); + __ Assert(equal, kWrongFunctionContext); + } + if (FLAG_debug_code && FLAG_enable_slow_asserts) { + __ VerifyX87StackDepth(1); + } + __ fstp(0); + __ call(FieldOperand(func, JSFunction::kCodeEntryOffset)); + RecordCallPosition(instr); + bool double_result = + instr->HasOutput() && instr->Output()->IsFPRegister(); + if (double_result) { + __ lea(esp, Operand(esp, -kDoubleSize)); + __ fstp_d(Operand(esp, 0)); + } + __ fninit(); + if (double_result) { + __ fld_d(Operand(esp, 0)); + __ lea(esp, Operand(esp, kDoubleSize)); + } else { + __ fld1(); + } + frame_access_state()->ClearSPDelta(); + break; + } + case kArchTailCallJSFunctionFromJSFunction: { + Register func = i.InputRegister(0); + if (FLAG_debug_code) { + // Check the function's context matches the context argument. + __ cmp(esi, FieldOperand(func, JSFunction::kContextOffset)); + __ Assert(equal, kWrongFunctionContext); + } + if (FLAG_debug_code && FLAG_enable_slow_asserts) { + __ VerifyX87StackDepth(1); + } + __ fstp(0); + AssemblePopArgumentsAdaptorFrame(kJavaScriptCallArgCountRegister, no_reg, + no_reg, no_reg); + __ jmp(FieldOperand(func, JSFunction::kCodeEntryOffset)); + frame_access_state()->ClearSPDelta(); + frame_access_state()->SetFrameAccessToDefault(); + break; + } + case kArchPrepareCallCFunction: { + // Frame alignment requires using FP-relative frame addressing. + frame_access_state()->SetFrameAccessToFP(); + int const num_parameters = MiscField::decode(instr->opcode()); + __ PrepareCallCFunction(num_parameters, i.TempRegister(0)); + break; + } + case kArchPrepareTailCall: + AssemblePrepareTailCall(); + break; + case kArchCallCFunction: { + if (FLAG_debug_code && FLAG_enable_slow_asserts) { + __ VerifyX87StackDepth(1); + } + __ fstp(0); + int const num_parameters = MiscField::decode(instr->opcode()); + if (HasImmediateInput(instr, 0)) { + ExternalReference ref = i.InputExternalReference(0); + __ CallCFunction(ref, num_parameters); + } else { + Register func = i.InputRegister(0); + __ CallCFunction(func, num_parameters); + } + bool double_result = + instr->HasOutput() && instr->Output()->IsFPRegister(); + if (double_result) { + __ lea(esp, Operand(esp, -kDoubleSize)); + __ fstp_d(Operand(esp, 0)); + } + __ fninit(); + if (double_result) { + __ fld_d(Operand(esp, 0)); + __ lea(esp, Operand(esp, kDoubleSize)); + } else { + __ fld1(); + } + frame_access_state()->SetFrameAccessToDefault(); + frame_access_state()->ClearSPDelta(); + break; + } + case kArchJmp: + AssembleArchJump(i.InputRpo(0)); + break; + case kArchLookupSwitch: + AssembleArchLookupSwitch(instr); + break; + case kArchTableSwitch: + AssembleArchTableSwitch(instr); + break; + case kArchComment: { + Address comment_string = i.InputExternalReference(0).address(); + __ RecordComment(reinterpret_cast(comment_string)); + break; + } + case kArchDebugBreak: + __ int3(); + break; + case kArchNop: + case kArchThrowTerminator: + // don't emit code for nops. + break; + case kArchDeoptimize: { + int deopt_state_id = + BuildTranslation(instr, -1, 0, OutputFrameStateCombine::Ignore()); + int double_register_param_count = 0; + int x87_layout = 0; + for (size_t i = 0; i < instr->InputCount(); i++) { + if (instr->InputAt(i)->IsFPRegister()) { + double_register_param_count++; + } + } + // Currently we use only one X87 register. If double_register_param_count + // is bigger than 1, it means duplicated double register is added to input + // of this instruction. + if (double_register_param_count > 0) { + x87_layout = (0 << 3) | 1; + } + // The layout of x87 register stack is loaded on the top of FPU register + // stack for deoptimization. + __ push(Immediate(x87_layout)); + __ fild_s(MemOperand(esp, 0)); + __ lea(esp, Operand(esp, kPointerSize)); + + CodeGenResult result = + AssembleDeoptimizerCall(deopt_state_id, current_source_position_); + if (result != kSuccess) return result; + break; + } + case kArchRet: + AssembleReturn(instr->InputAt(0)); + break; + case kArchFramePointer: + __ mov(i.OutputRegister(), ebp); + break; + case kArchStackPointer: + __ mov(i.OutputRegister(), esp); + break; + case kArchParentFramePointer: + if (frame_access_state()->has_frame()) { + __ mov(i.OutputRegister(), Operand(ebp, 0)); + } else { + __ mov(i.OutputRegister(), ebp); + } + break; + case kArchTruncateDoubleToI: { + if (!instr->InputAt(0)->IsFPRegister()) { + __ fld_d(i.InputOperand(0)); + } + __ TruncateX87TOSToI(zone(), i.OutputRegister()); + if (!instr->InputAt(0)->IsFPRegister()) { + __ fstp(0); + } + break; + } + case kArchStoreWithWriteBarrier: { + RecordWriteMode mode = + static_cast(MiscField::decode(instr->opcode())); + Register object = i.InputRegister(0); + size_t index = 0; + Operand operand = i.MemoryOperand(&index); + Register value = i.InputRegister(index); + Register scratch0 = i.TempRegister(0); + Register scratch1 = i.TempRegister(1); + auto ool = new (zone()) OutOfLineRecordWrite(this, object, operand, value, + scratch0, scratch1, mode); + __ mov(operand, value); + __ CheckPageFlag(object, scratch0, + MemoryChunk::kPointersFromHereAreInterestingMask, + not_zero, ool->entry()); + __ bind(ool->exit()); + break; + } + case kArchStackSlot: { + FrameOffset offset = + frame_access_state()->GetFrameOffset(i.InputInt32(0)); + Register base; + if (offset.from_stack_pointer()) { + base = esp; + } else { + base = ebp; + } + __ lea(i.OutputRegister(), Operand(base, offset.offset())); + break; + } + case kIeee754Float64Acos: + ASSEMBLE_IEEE754_UNOP(acos); + break; + case kIeee754Float64Acosh: + ASSEMBLE_IEEE754_UNOP(acosh); + break; + case kIeee754Float64Asin: + ASSEMBLE_IEEE754_UNOP(asin); + break; + case kIeee754Float64Asinh: + ASSEMBLE_IEEE754_UNOP(asinh); + break; + case kIeee754Float64Atan: + ASSEMBLE_IEEE754_UNOP(atan); + break; + case kIeee754Float64Atanh: + ASSEMBLE_IEEE754_UNOP(atanh); + break; + case kIeee754Float64Atan2: + ASSEMBLE_IEEE754_BINOP(atan2); + break; + case kIeee754Float64Cbrt: + ASSEMBLE_IEEE754_UNOP(cbrt); + break; + case kIeee754Float64Cos: + __ X87SetFPUCW(0x027F); + ASSEMBLE_IEEE754_UNOP(cos); + __ X87SetFPUCW(0x037F); + break; + case kIeee754Float64Cosh: + ASSEMBLE_IEEE754_UNOP(cosh); + break; + case kIeee754Float64Expm1: + __ X87SetFPUCW(0x027F); + ASSEMBLE_IEEE754_UNOP(expm1); + __ X87SetFPUCW(0x037F); + break; + case kIeee754Float64Exp: + ASSEMBLE_IEEE754_UNOP(exp); + break; + case kIeee754Float64Log: + ASSEMBLE_IEEE754_UNOP(log); + break; + case kIeee754Float64Log1p: + ASSEMBLE_IEEE754_UNOP(log1p); + break; + case kIeee754Float64Log2: + ASSEMBLE_IEEE754_UNOP(log2); + break; + case kIeee754Float64Log10: + ASSEMBLE_IEEE754_UNOP(log10); + break; + case kIeee754Float64Pow: { + // Keep the x87 FPU stack empty before calling stub code + __ fstp(0); + // Call the MathStub and put return value in stX_0 + __ CallStubDelayed(new (zone()) + MathPowStub(nullptr, MathPowStub::DOUBLE)); + /* Return value is in st(0) on x87. */ + __ lea(esp, Operand(esp, 2 * kDoubleSize)); + break; + } + case kIeee754Float64Sin: + __ X87SetFPUCW(0x027F); + ASSEMBLE_IEEE754_UNOP(sin); + __ X87SetFPUCW(0x037F); + break; + case kIeee754Float64Sinh: + ASSEMBLE_IEEE754_UNOP(sinh); + break; + case kIeee754Float64Tan: + __ X87SetFPUCW(0x027F); + ASSEMBLE_IEEE754_UNOP(tan); + __ X87SetFPUCW(0x037F); + break; + case kIeee754Float64Tanh: + ASSEMBLE_IEEE754_UNOP(tanh); + break; + case kX87Add: + if (HasImmediateInput(instr, 1)) { + __ add(i.InputOperand(0), i.InputImmediate(1)); + } else { + __ add(i.InputRegister(0), i.InputOperand(1)); + } + break; + case kX87And: + if (HasImmediateInput(instr, 1)) { + __ and_(i.InputOperand(0), i.InputImmediate(1)); + } else { + __ and_(i.InputRegister(0), i.InputOperand(1)); + } + break; + case kX87Cmp: + ASSEMBLE_COMPARE(cmp); + break; + case kX87Cmp16: + ASSEMBLE_COMPARE(cmpw); + break; + case kX87Cmp8: + ASSEMBLE_COMPARE(cmpb); + break; + case kX87Test: + ASSEMBLE_COMPARE(test); + break; + case kX87Test16: + ASSEMBLE_COMPARE(test_w); + break; + case kX87Test8: + ASSEMBLE_COMPARE(test_b); + break; + case kX87Imul: + if (HasImmediateInput(instr, 1)) { + __ imul(i.OutputRegister(), i.InputOperand(0), i.InputInt32(1)); + } else { + __ imul(i.OutputRegister(), i.InputOperand(1)); + } + break; + case kX87ImulHigh: + __ imul(i.InputRegister(1)); + break; + case kX87UmulHigh: + __ mul(i.InputRegister(1)); + break; + case kX87Idiv: + __ cdq(); + __ idiv(i.InputOperand(1)); + break; + case kX87Udiv: + __ Move(edx, Immediate(0)); + __ div(i.InputOperand(1)); + break; + case kX87Not: + __ not_(i.OutputOperand()); + break; + case kX87Neg: + __ neg(i.OutputOperand()); + break; + case kX87Or: + if (HasImmediateInput(instr, 1)) { + __ or_(i.InputOperand(0), i.InputImmediate(1)); + } else { + __ or_(i.InputRegister(0), i.InputOperand(1)); + } + break; + case kX87Xor: + if (HasImmediateInput(instr, 1)) { + __ xor_(i.InputOperand(0), i.InputImmediate(1)); + } else { + __ xor_(i.InputRegister(0), i.InputOperand(1)); + } + break; + case kX87Sub: + if (HasImmediateInput(instr, 1)) { + __ sub(i.InputOperand(0), i.InputImmediate(1)); + } else { + __ sub(i.InputRegister(0), i.InputOperand(1)); + } + break; + case kX87Shl: + if (HasImmediateInput(instr, 1)) { + __ shl(i.OutputOperand(), i.InputInt5(1)); + } else { + __ shl_cl(i.OutputOperand()); + } + break; + case kX87Shr: + if (HasImmediateInput(instr, 1)) { + __ shr(i.OutputOperand(), i.InputInt5(1)); + } else { + __ shr_cl(i.OutputOperand()); + } + break; + case kX87Sar: + if (HasImmediateInput(instr, 1)) { + __ sar(i.OutputOperand(), i.InputInt5(1)); + } else { + __ sar_cl(i.OutputOperand()); + } + break; + case kX87AddPair: { + // i.OutputRegister(0) == i.InputRegister(0) ... left low word. + // i.InputRegister(1) ... left high word. + // i.InputRegister(2) ... right low word. + // i.InputRegister(3) ... right high word. + bool use_temp = false; + if (i.OutputRegister(0).code() == i.InputRegister(1).code() || + i.OutputRegister(0).code() == i.InputRegister(3).code()) { + // We cannot write to the output register directly, because it would + // overwrite an input for adc. We have to use the temp register. + use_temp = true; + __ Move(i.TempRegister(0), i.InputRegister(0)); + __ add(i.TempRegister(0), i.InputRegister(2)); + } else { + __ add(i.OutputRegister(0), i.InputRegister(2)); + } + if (i.OutputRegister(1).code() != i.InputRegister(1).code()) { + __ Move(i.OutputRegister(1), i.InputRegister(1)); + } + __ adc(i.OutputRegister(1), Operand(i.InputRegister(3))); + if (use_temp) { + __ Move(i.OutputRegister(0), i.TempRegister(0)); + } + break; + } + case kX87SubPair: { + // i.OutputRegister(0) == i.InputRegister(0) ... left low word. + // i.InputRegister(1) ... left high word. + // i.InputRegister(2) ... right low word. + // i.InputRegister(3) ... right high word. + bool use_temp = false; + if (i.OutputRegister(0).code() == i.InputRegister(1).code() || + i.OutputRegister(0).code() == i.InputRegister(3).code()) { + // We cannot write to the output register directly, because it would + // overwrite an input for adc. We have to use the temp register. + use_temp = true; + __ Move(i.TempRegister(0), i.InputRegister(0)); + __ sub(i.TempRegister(0), i.InputRegister(2)); + } else { + __ sub(i.OutputRegister(0), i.InputRegister(2)); + } + if (i.OutputRegister(1).code() != i.InputRegister(1).code()) { + __ Move(i.OutputRegister(1), i.InputRegister(1)); + } + __ sbb(i.OutputRegister(1), Operand(i.InputRegister(3))); + if (use_temp) { + __ Move(i.OutputRegister(0), i.TempRegister(0)); + } + break; + } + case kX87MulPair: { + __ imul(i.OutputRegister(1), i.InputOperand(0)); + __ mov(i.TempRegister(0), i.InputOperand(1)); + __ imul(i.TempRegister(0), i.InputOperand(2)); + __ add(i.OutputRegister(1), i.TempRegister(0)); + __ mov(i.OutputRegister(0), i.InputOperand(0)); + // Multiplies the low words and stores them in eax and edx. + __ mul(i.InputRegister(2)); + __ add(i.OutputRegister(1), i.TempRegister(0)); + + break; + } + case kX87ShlPair: + if (HasImmediateInput(instr, 2)) { + __ ShlPair(i.InputRegister(1), i.InputRegister(0), i.InputInt6(2)); + } else { + // Shift has been loaded into CL by the register allocator. + __ ShlPair_cl(i.InputRegister(1), i.InputRegister(0)); + } + break; + case kX87ShrPair: + if (HasImmediateInput(instr, 2)) { + __ ShrPair(i.InputRegister(1), i.InputRegister(0), i.InputInt6(2)); + } else { + // Shift has been loaded into CL by the register allocator. + __ ShrPair_cl(i.InputRegister(1), i.InputRegister(0)); + } + break; + case kX87SarPair: + if (HasImmediateInput(instr, 2)) { + __ SarPair(i.InputRegister(1), i.InputRegister(0), i.InputInt6(2)); + } else { + // Shift has been loaded into CL by the register allocator. + __ SarPair_cl(i.InputRegister(1), i.InputRegister(0)); + } + break; + case kX87Ror: + if (HasImmediateInput(instr, 1)) { + __ ror(i.OutputOperand(), i.InputInt5(1)); + } else { + __ ror_cl(i.OutputOperand()); + } + break; + case kX87Lzcnt: + __ Lzcnt(i.OutputRegister(), i.InputOperand(0)); + break; + case kX87Popcnt: + __ Popcnt(i.OutputRegister(), i.InputOperand(0)); + break; + case kX87LoadFloat64Constant: { + InstructionOperand* source = instr->InputAt(0); + InstructionOperand* destination = instr->Output(); + DCHECK(source->IsConstant()); + X87OperandConverter g(this, nullptr); + Constant src_constant = g.ToConstant(source); + + DCHECK_EQ(Constant::kFloat64, src_constant.type()); + uint64_t src = src_constant.ToFloat64().AsUint64(); + uint32_t lower = static_cast(src); + uint32_t upper = static_cast(src >> 32); + if (destination->IsFPRegister()) { + __ sub(esp, Immediate(kDoubleSize)); + __ mov(MemOperand(esp, 0), Immediate(lower)); + __ mov(MemOperand(esp, kInt32Size), Immediate(upper)); + __ fstp(0); + __ fld_d(MemOperand(esp, 0)); + __ add(esp, Immediate(kDoubleSize)); + } else { + UNREACHABLE(); + } + break; + } + case kX87Float32Cmp: { + __ fld_s(MemOperand(esp, kFloatSize)); + __ fld_s(MemOperand(esp, 0)); + __ FCmp(); + __ lea(esp, Operand(esp, 2 * kFloatSize)); + break; + } + case kX87Float32Add: { + if (FLAG_debug_code && FLAG_enable_slow_asserts) { + __ VerifyX87StackDepth(1); + } + __ X87SetFPUCW(0x027F); + __ fstp(0); + __ fld_s(MemOperand(esp, 0)); + __ fld_s(MemOperand(esp, kFloatSize)); + __ faddp(); + // Clear stack. + __ lea(esp, Operand(esp, 2 * kFloatSize)); + // Restore the default value of control word. + __ X87SetFPUCW(0x037F); + break; + } + case kX87Float32Sub: { + if (FLAG_debug_code && FLAG_enable_slow_asserts) { + __ VerifyX87StackDepth(1); + } + __ X87SetFPUCW(0x027F); + __ fstp(0); + __ fld_s(MemOperand(esp, kFloatSize)); + __ fld_s(MemOperand(esp, 0)); + __ fsubp(); + // Clear stack. + __ lea(esp, Operand(esp, 2 * kFloatSize)); + // Restore the default value of control word. + __ X87SetFPUCW(0x037F); + break; + } + case kX87Float32Mul: { + if (FLAG_debug_code && FLAG_enable_slow_asserts) { + __ VerifyX87StackDepth(1); + } + __ X87SetFPUCW(0x027F); + __ fstp(0); + __ fld_s(MemOperand(esp, kFloatSize)); + __ fld_s(MemOperand(esp, 0)); + __ fmulp(); + // Clear stack. + __ lea(esp, Operand(esp, 2 * kFloatSize)); + // Restore the default value of control word. + __ X87SetFPUCW(0x037F); + break; + } + case kX87Float32Div: { + if (FLAG_debug_code && FLAG_enable_slow_asserts) { + __ VerifyX87StackDepth(1); + } + __ X87SetFPUCW(0x027F); + __ fstp(0); + __ fld_s(MemOperand(esp, kFloatSize)); + __ fld_s(MemOperand(esp, 0)); + __ fdivp(); + // Clear stack. + __ lea(esp, Operand(esp, 2 * kFloatSize)); + // Restore the default value of control word. + __ X87SetFPUCW(0x037F); + break; + } + + case kX87Float32Sqrt: { + if (FLAG_debug_code && FLAG_enable_slow_asserts) { + __ VerifyX87StackDepth(1); + } + __ fstp(0); + __ fld_s(MemOperand(esp, 0)); + __ fsqrt(); + __ lea(esp, Operand(esp, kFloatSize)); + break; + } + case kX87Float32Abs: { + if (FLAG_debug_code && FLAG_enable_slow_asserts) { + __ VerifyX87StackDepth(1); + } + __ fstp(0); + __ fld_s(MemOperand(esp, 0)); + __ fabs(); + __ lea(esp, Operand(esp, kFloatSize)); + break; + } + case kX87Float32Neg: { + if (FLAG_debug_code && FLAG_enable_slow_asserts) { + __ VerifyX87StackDepth(1); + } + __ fstp(0); + __ fld_s(MemOperand(esp, 0)); + __ fchs(); + __ lea(esp, Operand(esp, kFloatSize)); + break; + } + case kX87Float32Round: { + RoundingMode mode = + static_cast(MiscField::decode(instr->opcode())); + // Set the correct round mode in x87 control register + __ X87SetRC((mode << 10)); + + if (!instr->InputAt(0)->IsFPRegister()) { + InstructionOperand* input = instr->InputAt(0); + USE(input); + DCHECK(input->IsFPStackSlot()); + if (FLAG_debug_code && FLAG_enable_slow_asserts) { + __ VerifyX87StackDepth(1); + } + __ fstp(0); + __ fld_s(i.InputOperand(0)); + } + __ frndint(); + __ X87SetRC(0x0000); + break; + } + case kX87Float64Add: { + if (FLAG_debug_code && FLAG_enable_slow_asserts) { + __ VerifyX87StackDepth(1); + } + __ X87SetFPUCW(0x027F); + __ fstp(0); + __ fld_d(MemOperand(esp, 0)); + __ fld_d(MemOperand(esp, kDoubleSize)); + __ faddp(); + // Clear stack. + __ lea(esp, Operand(esp, 2 * kDoubleSize)); + // Restore the default value of control word. + __ X87SetFPUCW(0x037F); + break; + } + case kX87Float64Sub: { + if (FLAG_debug_code && FLAG_enable_slow_asserts) { + __ VerifyX87StackDepth(1); + } + __ X87SetFPUCW(0x027F); + __ fstp(0); + __ fld_d(MemOperand(esp, kDoubleSize)); + __ fsub_d(MemOperand(esp, 0)); + // Clear stack. + __ lea(esp, Operand(esp, 2 * kDoubleSize)); + // Restore the default value of control word. + __ X87SetFPUCW(0x037F); + break; + } + case kX87Float64Mul: { + if (FLAG_debug_code && FLAG_enable_slow_asserts) { + __ VerifyX87StackDepth(1); + } + __ X87SetFPUCW(0x027F); + __ fstp(0); + __ fld_d(MemOperand(esp, kDoubleSize)); + __ fmul_d(MemOperand(esp, 0)); + // Clear stack. + __ lea(esp, Operand(esp, 2 * kDoubleSize)); + // Restore the default value of control word. + __ X87SetFPUCW(0x037F); + break; + } + case kX87Float64Div: { + if (FLAG_debug_code && FLAG_enable_slow_asserts) { + __ VerifyX87StackDepth(1); + } + __ X87SetFPUCW(0x027F); + __ fstp(0); + __ fld_d(MemOperand(esp, kDoubleSize)); + __ fdiv_d(MemOperand(esp, 0)); + // Clear stack. + __ lea(esp, Operand(esp, 2 * kDoubleSize)); + // Restore the default value of control word. + __ X87SetFPUCW(0x037F); + break; + } + case kX87Float64Mod: { + FrameScope frame_scope(tasm(), StackFrame::MANUAL); + if (FLAG_debug_code && FLAG_enable_slow_asserts) { + __ VerifyX87StackDepth(1); + } + __ mov(eax, esp); + __ PrepareCallCFunction(4, eax); + __ fstp(0); + __ fld_d(MemOperand(eax, 0)); + __ fstp_d(Operand(esp, 1 * kDoubleSize)); + __ fld_d(MemOperand(eax, kDoubleSize)); + __ fstp_d(Operand(esp, 0)); + __ CallCFunction(ExternalReference::mod_two_doubles_operation(isolate()), + 4); + __ lea(esp, Operand(esp, 2 * kDoubleSize)); + break; + } + case kX87Float32Max: { + Label compare_swap, done_compare; + if (FLAG_debug_code && FLAG_enable_slow_asserts) { + __ VerifyX87StackDepth(1); + } + __ fstp(0); + __ fld_s(MemOperand(esp, kFloatSize)); + __ fld_s(MemOperand(esp, 0)); + __ fld(1); + __ fld(1); + __ FCmp(); + + auto ool = + new (zone()) OutOfLineLoadFloat32NaN(this, i.OutputDoubleRegister()); + __ j(parity_even, ool->entry()); + __ j(below, &done_compare, Label::kNear); + __ j(above, &compare_swap, Label::kNear); + __ push(eax); + __ lea(esp, Operand(esp, -kFloatSize)); + __ fld(1); + __ fstp_s(Operand(esp, 0)); + __ mov(eax, MemOperand(esp, 0)); + __ and_(eax, Immediate(0x80000000)); + __ lea(esp, Operand(esp, kFloatSize)); + __ pop(eax); + __ j(zero, &done_compare, Label::kNear); + + __ bind(&compare_swap); + __ bind(ool->exit()); + __ fxch(1); + + __ bind(&done_compare); + __ fstp(0); + __ lea(esp, Operand(esp, 2 * kFloatSize)); + break; + } + case kX87Float64Max: { + Label compare_swap, done_compare; + if (FLAG_debug_code && FLAG_enable_slow_asserts) { + __ VerifyX87StackDepth(1); + } + __ fstp(0); + __ fld_d(MemOperand(esp, kDoubleSize)); + __ fld_d(MemOperand(esp, 0)); + __ fld(1); + __ fld(1); + __ FCmp(); + + auto ool = + new (zone()) OutOfLineLoadFloat64NaN(this, i.OutputDoubleRegister()); + __ j(parity_even, ool->entry()); + __ j(below, &done_compare, Label::kNear); + __ j(above, &compare_swap, Label::kNear); + __ push(eax); + __ lea(esp, Operand(esp, -kDoubleSize)); + __ fld(1); + __ fstp_d(Operand(esp, 0)); + __ mov(eax, MemOperand(esp, 4)); + __ and_(eax, Immediate(0x80000000)); + __ lea(esp, Operand(esp, kDoubleSize)); + __ pop(eax); + __ j(zero, &done_compare, Label::kNear); + + __ bind(&compare_swap); + __ bind(ool->exit()); + __ fxch(1); + + __ bind(&done_compare); + __ fstp(0); + __ lea(esp, Operand(esp, 2 * kDoubleSize)); + break; + } + case kX87Float32Min: { + Label compare_swap, done_compare; + if (FLAG_debug_code && FLAG_enable_slow_asserts) { + __ VerifyX87StackDepth(1); + } + __ fstp(0); + __ fld_s(MemOperand(esp, kFloatSize)); + __ fld_s(MemOperand(esp, 0)); + __ fld(1); + __ fld(1); + __ FCmp(); + + auto ool = + new (zone()) OutOfLineLoadFloat32NaN(this, i.OutputDoubleRegister()); + __ j(parity_even, ool->entry()); + __ j(above, &done_compare, Label::kNear); + __ j(below, &compare_swap, Label::kNear); + __ push(eax); + __ lea(esp, Operand(esp, -kFloatSize)); + __ fld(0); + __ fstp_s(Operand(esp, 0)); + __ mov(eax, MemOperand(esp, 0)); + __ and_(eax, Immediate(0x80000000)); + __ lea(esp, Operand(esp, kFloatSize)); + __ pop(eax); + __ j(zero, &done_compare, Label::kNear); + + __ bind(&compare_swap); + __ bind(ool->exit()); + __ fxch(1); + + __ bind(&done_compare); + __ fstp(0); + __ lea(esp, Operand(esp, 2 * kFloatSize)); + break; + } + case kX87Float64Min: { + Label compare_swap, done_compare; + if (FLAG_debug_code && FLAG_enable_slow_asserts) { + __ VerifyX87StackDepth(1); + } + __ fstp(0); + __ fld_d(MemOperand(esp, kDoubleSize)); + __ fld_d(MemOperand(esp, 0)); + __ fld(1); + __ fld(1); + __ FCmp(); + + auto ool = + new (zone()) OutOfLineLoadFloat64NaN(this, i.OutputDoubleRegister()); + __ j(parity_even, ool->entry()); + __ j(above, &done_compare, Label::kNear); + __ j(below, &compare_swap, Label::kNear); + __ push(eax); + __ lea(esp, Operand(esp, -kDoubleSize)); + __ fld(0); + __ fstp_d(Operand(esp, 0)); + __ mov(eax, MemOperand(esp, 4)); + __ and_(eax, Immediate(0x80000000)); + __ lea(esp, Operand(esp, kDoubleSize)); + __ pop(eax); + __ j(zero, &done_compare, Label::kNear); + + __ bind(&compare_swap); + __ bind(ool->exit()); + __ fxch(1); + + __ bind(&done_compare); + __ fstp(0); + __ lea(esp, Operand(esp, 2 * kDoubleSize)); + break; + } + case kX87Float64Abs: { + if (FLAG_debug_code && FLAG_enable_slow_asserts) { + __ VerifyX87StackDepth(1); + } + __ fstp(0); + __ fld_d(MemOperand(esp, 0)); + __ fabs(); + __ lea(esp, Operand(esp, kDoubleSize)); + break; + } + case kX87Float64Neg: { + if (FLAG_debug_code && FLAG_enable_slow_asserts) { + __ VerifyX87StackDepth(1); + } + __ fstp(0); + __ fld_d(MemOperand(esp, 0)); + __ fchs(); + __ lea(esp, Operand(esp, kDoubleSize)); + break; + } + case kX87Int32ToFloat32: { + InstructionOperand* input = instr->InputAt(0); + DCHECK(input->IsRegister() || input->IsStackSlot()); + if (FLAG_debug_code && FLAG_enable_slow_asserts) { + __ VerifyX87StackDepth(1); + } + __ fstp(0); + if (input->IsRegister()) { + Register input_reg = i.InputRegister(0); + __ push(input_reg); + __ fild_s(Operand(esp, 0)); + __ pop(input_reg); + } else { + __ fild_s(i.InputOperand(0)); + } + break; + } + case kX87Uint32ToFloat32: { + InstructionOperand* input = instr->InputAt(0); + DCHECK(input->IsRegister() || input->IsStackSlot()); + if (FLAG_debug_code && FLAG_enable_slow_asserts) { + __ VerifyX87StackDepth(1); + } + __ fstp(0); + Label msb_set_src; + Label jmp_return; + // Put input integer into eax(tmporarilly) + __ push(eax); + if (input->IsRegister()) + __ mov(eax, i.InputRegister(0)); + else + __ mov(eax, i.InputOperand(0)); + + __ test(eax, eax); + __ j(sign, &msb_set_src, Label::kNear); + __ push(eax); + __ fild_s(Operand(esp, 0)); + __ pop(eax); + + __ jmp(&jmp_return, Label::kNear); + __ bind(&msb_set_src); + // Need another temp reg + __ push(ebx); + __ mov(ebx, eax); + __ shr(eax, 1); + // Recover the least significant bit to avoid rounding errors. + __ and_(ebx, Immediate(1)); + __ or_(eax, ebx); + __ push(eax); + __ fild_s(Operand(esp, 0)); + __ pop(eax); + __ fld(0); + __ faddp(); + // Restore the ebx + __ pop(ebx); + __ bind(&jmp_return); + // Restore the eax + __ pop(eax); + break; + } + case kX87Int32ToFloat64: { + InstructionOperand* input = instr->InputAt(0); + DCHECK(input->IsRegister() || input->IsStackSlot()); + if (FLAG_debug_code && FLAG_enable_slow_asserts) { + __ VerifyX87StackDepth(1); + } + __ fstp(0); + if (input->IsRegister()) { + Register input_reg = i.InputRegister(0); + __ push(input_reg); + __ fild_s(Operand(esp, 0)); + __ pop(input_reg); + } else { + __ fild_s(i.InputOperand(0)); + } + break; + } + case kX87Float32ToFloat64: { + InstructionOperand* input = instr->InputAt(0); + if (input->IsFPRegister()) { + __ sub(esp, Immediate(kDoubleSize)); + __ fstp_s(MemOperand(esp, 0)); + __ fld_s(MemOperand(esp, 0)); + __ add(esp, Immediate(kDoubleSize)); + } else { + DCHECK(input->IsFPStackSlot()); + if (FLAG_debug_code && FLAG_enable_slow_asserts) { + __ VerifyX87StackDepth(1); + } + __ fstp(0); + __ fld_s(i.InputOperand(0)); + } + break; + } + case kX87Uint32ToFloat64: { + if (FLAG_debug_code && FLAG_enable_slow_asserts) { + __ VerifyX87StackDepth(1); + } + __ fstp(0); + __ LoadUint32NoSSE2(i.InputRegister(0)); + break; + } + case kX87Float32ToInt32: { + if (!instr->InputAt(0)->IsFPRegister()) { + __ fld_s(i.InputOperand(0)); + } + __ TruncateX87TOSToI(zone(), i.OutputRegister(0)); + if (!instr->InputAt(0)->IsFPRegister()) { + __ fstp(0); + } + break; + } + case kX87Float32ToUint32: { + if (!instr->InputAt(0)->IsFPRegister()) { + __ fld_s(i.InputOperand(0)); + } + Label success; + __ TruncateX87TOSToI(zone(), i.OutputRegister(0)); + __ test(i.OutputRegister(0), i.OutputRegister(0)); + __ j(positive, &success); + // Need to reserve the input float32 data. + __ fld(0); + __ push(Immediate(INT32_MIN)); + __ fild_s(Operand(esp, 0)); + __ lea(esp, Operand(esp, kPointerSize)); + __ faddp(); + __ TruncateX87TOSToI(zone(), i.OutputRegister(0)); + __ or_(i.OutputRegister(0), Immediate(0x80000000)); + // Only keep input float32 data in x87 stack when return. + __ fstp(0); + __ bind(&success); + if (!instr->InputAt(0)->IsFPRegister()) { + __ fstp(0); + } + break; + } + case kX87Float64ToInt32: { + if (!instr->InputAt(0)->IsFPRegister()) { + __ fld_d(i.InputOperand(0)); + } + __ TruncateX87TOSToI(zone(), i.OutputRegister(0)); + if (!instr->InputAt(0)->IsFPRegister()) { + __ fstp(0); + } + break; + } + case kX87Float64ToFloat32: { + InstructionOperand* input = instr->InputAt(0); + if (input->IsFPRegister()) { + __ sub(esp, Immediate(kDoubleSize)); + __ fstp_s(MemOperand(esp, 0)); + __ fld_s(MemOperand(esp, 0)); + __ add(esp, Immediate(kDoubleSize)); + } else { + DCHECK(input->IsFPStackSlot()); + if (FLAG_debug_code && FLAG_enable_slow_asserts) { + __ VerifyX87StackDepth(1); + } + __ fstp(0); + __ fld_d(i.InputOperand(0)); + __ sub(esp, Immediate(kDoubleSize)); + __ fstp_s(MemOperand(esp, 0)); + __ fld_s(MemOperand(esp, 0)); + __ add(esp, Immediate(kDoubleSize)); + } + break; + } + case kX87Float64ToUint32: { + __ push_imm32(-2147483648); + if (!instr->InputAt(0)->IsFPRegister()) { + __ fld_d(i.InputOperand(0)); + } + __ fild_s(Operand(esp, 0)); + __ fld(1); + __ faddp(); + __ TruncateX87TOSToI(zone(), i.OutputRegister(0)); + __ add(esp, Immediate(kInt32Size)); + __ add(i.OutputRegister(), Immediate(0x80000000)); + __ fstp(0); + if (!instr->InputAt(0)->IsFPRegister()) { + __ fstp(0); + } + break; + } + case kX87Float64ExtractHighWord32: { + if (instr->InputAt(0)->IsFPRegister()) { + __ sub(esp, Immediate(kDoubleSize)); + __ fst_d(MemOperand(esp, 0)); + __ mov(i.OutputRegister(), MemOperand(esp, kDoubleSize / 2)); + __ add(esp, Immediate(kDoubleSize)); + } else { + InstructionOperand* input = instr->InputAt(0); + USE(input); + DCHECK(input->IsFPStackSlot()); + __ mov(i.OutputRegister(), i.InputOperand(0, kDoubleSize / 2)); + } + break; + } + case kX87Float64ExtractLowWord32: { + if (instr->InputAt(0)->IsFPRegister()) { + __ sub(esp, Immediate(kDoubleSize)); + __ fst_d(MemOperand(esp, 0)); + __ mov(i.OutputRegister(), MemOperand(esp, 0)); + __ add(esp, Immediate(kDoubleSize)); + } else { + InstructionOperand* input = instr->InputAt(0); + USE(input); + DCHECK(input->IsFPStackSlot()); + __ mov(i.OutputRegister(), i.InputOperand(0)); + } + break; + } + case kX87Float64InsertHighWord32: { + __ sub(esp, Immediate(kDoubleSize)); + __ fstp_d(MemOperand(esp, 0)); + __ mov(MemOperand(esp, kDoubleSize / 2), i.InputRegister(1)); + __ fld_d(MemOperand(esp, 0)); + __ add(esp, Immediate(kDoubleSize)); + break; + } + case kX87Float64InsertLowWord32: { + __ sub(esp, Immediate(kDoubleSize)); + __ fstp_d(MemOperand(esp, 0)); + __ mov(MemOperand(esp, 0), i.InputRegister(1)); + __ fld_d(MemOperand(esp, 0)); + __ add(esp, Immediate(kDoubleSize)); + break; + } + case kX87Float64Sqrt: { + if (FLAG_debug_code && FLAG_enable_slow_asserts) { + __ VerifyX87StackDepth(1); + } + __ X87SetFPUCW(0x027F); + __ fstp(0); + __ fld_d(MemOperand(esp, 0)); + __ fsqrt(); + __ lea(esp, Operand(esp, kDoubleSize)); + __ X87SetFPUCW(0x037F); + break; + } + case kX87Float64Round: { + RoundingMode mode = + static_cast(MiscField::decode(instr->opcode())); + // Set the correct round mode in x87 control register + __ X87SetRC((mode << 10)); + + if (!instr->InputAt(0)->IsFPRegister()) { + InstructionOperand* input = instr->InputAt(0); + USE(input); + DCHECK(input->IsFPStackSlot()); + if (FLAG_debug_code && FLAG_enable_slow_asserts) { + __ VerifyX87StackDepth(1); + } + __ fstp(0); + __ fld_d(i.InputOperand(0)); + } + __ frndint(); + __ X87SetRC(0x0000); + break; + } + case kX87Float64Cmp: { + __ fld_d(MemOperand(esp, kDoubleSize)); + __ fld_d(MemOperand(esp, 0)); + __ FCmp(); + __ lea(esp, Operand(esp, 2 * kDoubleSize)); + break; + } + case kX87Float64SilenceNaN: { + Label end, return_qnan; + __ fstp(0); + __ push(ebx); + // Load Half word of HoleNan(SNaN) into ebx + __ mov(ebx, MemOperand(esp, 2 * kInt32Size)); + __ cmp(ebx, Immediate(kHoleNanUpper32)); + // Check input is HoleNaN(SNaN)? + __ j(equal, &return_qnan, Label::kNear); + // If input isn't HoleNaN(SNaN), just load it and return + __ fld_d(MemOperand(esp, 1 * kInt32Size)); + __ jmp(&end); + __ bind(&return_qnan); + // If input is HoleNaN(SNaN), Return QNaN + __ push(Immediate(0xffffffff)); + __ push(Immediate(0xfff7ffff)); + __ fld_d(MemOperand(esp, 0)); + __ lea(esp, Operand(esp, kDoubleSize)); + __ bind(&end); + __ pop(ebx); + // Clear stack. + __ lea(esp, Operand(esp, 1 * kDoubleSize)); + break; + } + case kX87Movsxbl: + __ movsx_b(i.OutputRegister(), i.MemoryOperand()); + break; + case kX87Movzxbl: + __ movzx_b(i.OutputRegister(), i.MemoryOperand()); + break; + case kX87Movb: { + size_t index = 0; + Operand operand = i.MemoryOperand(&index); + if (HasImmediateInput(instr, index)) { + __ mov_b(operand, i.InputInt8(index)); + } else { + __ mov_b(operand, i.InputRegister(index)); + } + break; + } + case kX87Movsxwl: + __ movsx_w(i.OutputRegister(), i.MemoryOperand()); + break; + case kX87Movzxwl: + __ movzx_w(i.OutputRegister(), i.MemoryOperand()); + break; + case kX87Movw: { + size_t index = 0; + Operand operand = i.MemoryOperand(&index); + if (HasImmediateInput(instr, index)) { + __ mov_w(operand, i.InputInt16(index)); + } else { + __ mov_w(operand, i.InputRegister(index)); + } + break; + } + case kX87Movl: + if (instr->HasOutput()) { + __ mov(i.OutputRegister(), i.MemoryOperand()); + } else { + size_t index = 0; + Operand operand = i.MemoryOperand(&index); + if (HasImmediateInput(instr, index)) { + __ mov(operand, i.InputImmediate(index)); + } else { + __ mov(operand, i.InputRegister(index)); + } + } + break; + case kX87Movsd: { + if (instr->HasOutput()) { + X87Register output = i.OutputDoubleRegister(); + USE(output); + DCHECK(output.code() == 0); + if (FLAG_debug_code && FLAG_enable_slow_asserts) { + __ VerifyX87StackDepth(1); + } + __ fstp(0); + __ fld_d(i.MemoryOperand()); + } else { + size_t index = 0; + Operand operand = i.MemoryOperand(&index); + __ fst_d(operand); + } + break; + } + case kX87Movss: { + if (instr->HasOutput()) { + X87Register output = i.OutputDoubleRegister(); + USE(output); + DCHECK(output.code() == 0); + if (FLAG_debug_code && FLAG_enable_slow_asserts) { + __ VerifyX87StackDepth(1); + } + __ fstp(0); + __ fld_s(i.MemoryOperand()); + } else { + size_t index = 0; + Operand operand = i.MemoryOperand(&index); + __ fst_s(operand); + } + break; + } + case kX87BitcastFI: { + __ mov(i.OutputRegister(), MemOperand(esp, 0)); + __ lea(esp, Operand(esp, kFloatSize)); + break; + } + case kX87BitcastIF: { + if (FLAG_debug_code && FLAG_enable_slow_asserts) { + __ VerifyX87StackDepth(1); + } + __ fstp(0); + if (instr->InputAt(0)->IsRegister()) { + __ lea(esp, Operand(esp, -kFloatSize)); + __ mov(MemOperand(esp, 0), i.InputRegister(0)); + __ fld_s(MemOperand(esp, 0)); + __ lea(esp, Operand(esp, kFloatSize)); + } else { + __ fld_s(i.InputOperand(0)); + } + break; + } + case kX87Lea: { + AddressingMode mode = AddressingModeField::decode(instr->opcode()); + // Shorten "leal" to "addl", "subl" or "shll" if the register allocation + // and addressing mode just happens to work out. The "addl"/"subl" forms + // in these cases are faster based on measurements. + if (mode == kMode_MI) { + __ Move(i.OutputRegister(), Immediate(i.InputInt32(0))); + } else if (i.InputRegister(0).is(i.OutputRegister())) { + if (mode == kMode_MRI) { + int32_t constant_summand = i.InputInt32(1); + if (constant_summand > 0) { + __ add(i.OutputRegister(), Immediate(constant_summand)); + } else if (constant_summand < 0) { + __ sub(i.OutputRegister(), Immediate(-constant_summand)); + } + } else if (mode == kMode_MR1) { + if (i.InputRegister(1).is(i.OutputRegister())) { + __ shl(i.OutputRegister(), 1); + } else { + __ add(i.OutputRegister(), i.InputRegister(1)); + } + } else if (mode == kMode_M2) { + __ shl(i.OutputRegister(), 1); + } else if (mode == kMode_M4) { + __ shl(i.OutputRegister(), 2); + } else if (mode == kMode_M8) { + __ shl(i.OutputRegister(), 3); + } else { + __ lea(i.OutputRegister(), i.MemoryOperand()); + } + } else if (mode == kMode_MR1 && + i.InputRegister(1).is(i.OutputRegister())) { + __ add(i.OutputRegister(), i.InputRegister(0)); + } else { + __ lea(i.OutputRegister(), i.MemoryOperand()); + } + break; + } + case kX87Push: + if (instr->InputAt(0)->IsFPRegister()) { + auto allocated = AllocatedOperand::cast(*instr->InputAt(0)); + if (allocated.representation() == MachineRepresentation::kFloat32) { + __ sub(esp, Immediate(kFloatSize)); + __ fst_s(Operand(esp, 0)); + frame_access_state()->IncreaseSPDelta(kFloatSize / kPointerSize); + } else { + DCHECK(allocated.representation() == MachineRepresentation::kFloat64); + __ sub(esp, Immediate(kDoubleSize)); + __ fst_d(Operand(esp, 0)); + frame_access_state()->IncreaseSPDelta(kDoubleSize / kPointerSize); + } + } else if (instr->InputAt(0)->IsFPStackSlot()) { + auto allocated = AllocatedOperand::cast(*instr->InputAt(0)); + if (allocated.representation() == MachineRepresentation::kFloat32) { + __ sub(esp, Immediate(kFloatSize)); + __ fld_s(i.InputOperand(0)); + __ fstp_s(MemOperand(esp, 0)); + frame_access_state()->IncreaseSPDelta(kFloatSize / kPointerSize); + } else { + DCHECK(allocated.representation() == MachineRepresentation::kFloat64); + __ sub(esp, Immediate(kDoubleSize)); + __ fld_d(i.InputOperand(0)); + __ fstp_d(MemOperand(esp, 0)); + frame_access_state()->IncreaseSPDelta(kDoubleSize / kPointerSize); + } + } else if (HasImmediateInput(instr, 0)) { + __ push(i.InputImmediate(0)); + frame_access_state()->IncreaseSPDelta(1); + } else { + __ push(i.InputOperand(0)); + frame_access_state()->IncreaseSPDelta(1); + } + break; + case kX87Poke: { + int const slot = MiscField::decode(instr->opcode()); + if (HasImmediateInput(instr, 0)) { + __ mov(Operand(esp, slot * kPointerSize), i.InputImmediate(0)); + } else { + __ mov(Operand(esp, slot * kPointerSize), i.InputRegister(0)); + } + break; + } + case kX87PushFloat32: + __ lea(esp, Operand(esp, -kFloatSize)); + if (instr->InputAt(0)->IsFPStackSlot()) { + __ fld_s(i.InputOperand(0)); + __ fstp_s(MemOperand(esp, 0)); + } else if (instr->InputAt(0)->IsFPRegister()) { + __ fst_s(MemOperand(esp, 0)); + } else { + UNREACHABLE(); + } + break; + case kX87PushFloat64: + __ lea(esp, Operand(esp, -kDoubleSize)); + if (instr->InputAt(0)->IsFPStackSlot()) { + __ fld_d(i.InputOperand(0)); + __ fstp_d(MemOperand(esp, 0)); + } else if (instr->InputAt(0)->IsFPRegister()) { + __ fst_d(MemOperand(esp, 0)); + } else { + UNREACHABLE(); + } + break; + case kCheckedLoadInt8: + ASSEMBLE_CHECKED_LOAD_INTEGER(movsx_b); + break; + case kCheckedLoadUint8: + ASSEMBLE_CHECKED_LOAD_INTEGER(movzx_b); + break; + case kCheckedLoadInt16: + ASSEMBLE_CHECKED_LOAD_INTEGER(movsx_w); + break; + case kCheckedLoadUint16: + ASSEMBLE_CHECKED_LOAD_INTEGER(movzx_w); + break; + case kCheckedLoadWord32: + ASSEMBLE_CHECKED_LOAD_INTEGER(mov); + break; + case kCheckedLoadFloat32: + ASSEMBLE_CHECKED_LOAD_FLOAT(fld_s, OutOfLineLoadFloat32NaN); + break; + case kCheckedLoadFloat64: + ASSEMBLE_CHECKED_LOAD_FLOAT(fld_d, OutOfLineLoadFloat64NaN); + break; + case kCheckedStoreWord8: + ASSEMBLE_CHECKED_STORE_INTEGER(mov_b); + break; + case kCheckedStoreWord16: + ASSEMBLE_CHECKED_STORE_INTEGER(mov_w); + break; + case kCheckedStoreWord32: + ASSEMBLE_CHECKED_STORE_INTEGER(mov); + break; + case kCheckedStoreFloat32: + ASSEMBLE_CHECKED_STORE_FLOAT(fst_s); + break; + case kCheckedStoreFloat64: + ASSEMBLE_CHECKED_STORE_FLOAT(fst_d); + break; + case kX87StackCheck: { + ExternalReference const stack_limit = + ExternalReference::address_of_stack_limit(isolate()); + __ cmp(esp, Operand::StaticVariable(stack_limit)); + break; + } + case kCheckedLoadWord64: + case kCheckedStoreWord64: + UNREACHABLE(); // currently unsupported checked int64 load/store. + break; + case kAtomicExchangeInt8: { + __ xchg_b(i.InputRegister(0), i.MemoryOperand(1)); + __ movsx_b(i.InputRegister(0), i.InputRegister(0)); + break; + } + case kAtomicExchangeUint8: { + __ xchg_b(i.InputRegister(0), i.MemoryOperand(1)); + __ movzx_b(i.InputRegister(0), i.InputRegister(0)); + break; + } + case kAtomicExchangeInt16: { + __ xchg_w(i.InputRegister(0), i.MemoryOperand(1)); + __ movsx_w(i.InputRegister(0), i.InputRegister(0)); + break; + } + case kAtomicExchangeUint16: { + __ xchg_w(i.InputRegister(0), i.MemoryOperand(1)); + __ movzx_w(i.InputRegister(0), i.InputRegister(0)); + break; + } + case kAtomicExchangeWord32: { + __ xchg(i.InputRegister(0), i.MemoryOperand(1)); + break; + } + case kAtomicCompareExchangeInt8: { + __ lock(); + __ cmpxchg_b(i.MemoryOperand(2), i.InputRegister(1)); + __ movsx_b(eax, eax); + break; + } + case kAtomicCompareExchangeUint8: { + __ lock(); + __ cmpxchg_b(i.MemoryOperand(2), i.InputRegister(1)); + __ movzx_b(eax, eax); + break; + } + case kAtomicCompareExchangeInt16: { + __ lock(); + __ cmpxchg_w(i.MemoryOperand(2), i.InputRegister(1)); + __ movsx_w(eax, eax); + break; + } + case kAtomicCompareExchangeUint16: { + __ lock(); + __ cmpxchg_w(i.MemoryOperand(2), i.InputRegister(1)); + __ movzx_w(eax, eax); + break; + } + case kAtomicCompareExchangeWord32: { + __ lock(); + __ cmpxchg(i.MemoryOperand(2), i.InputRegister(1)); + break; + } +#define ATOMIC_BINOP_CASE(op, inst) \ + case kAtomic##op##Int8: { \ + ASSEMBLE_ATOMIC_BINOP(inst, mov_b, cmpxchg_b); \ + __ movsx_b(eax, eax); \ + break; \ + } \ + case kAtomic##op##Uint8: { \ + ASSEMBLE_ATOMIC_BINOP(inst, mov_b, cmpxchg_b); \ + __ movzx_b(eax, eax); \ + break; \ + } \ + case kAtomic##op##Int16: { \ + ASSEMBLE_ATOMIC_BINOP(inst, mov_w, cmpxchg_w); \ + __ movsx_w(eax, eax); \ + break; \ + } \ + case kAtomic##op##Uint16: { \ + ASSEMBLE_ATOMIC_BINOP(inst, mov_w, cmpxchg_w); \ + __ movzx_w(eax, eax); \ + break; \ + } \ + case kAtomic##op##Word32: { \ + ASSEMBLE_ATOMIC_BINOP(inst, mov, cmpxchg); \ + break; \ + } + ATOMIC_BINOP_CASE(Add, add) + ATOMIC_BINOP_CASE(Sub, sub) + ATOMIC_BINOP_CASE(And, and_) + ATOMIC_BINOP_CASE(Or, or_) + ATOMIC_BINOP_CASE(Xor, xor_) +#undef ATOMIC_BINOP_CASE + case kAtomicLoadInt8: + case kAtomicLoadUint8: + case kAtomicLoadInt16: + case kAtomicLoadUint16: + case kAtomicLoadWord32: + case kAtomicStoreWord8: + case kAtomicStoreWord16: + case kAtomicStoreWord32: + UNREACHABLE(); // Won't be generated by instruction selector. + break; + } + return kSuccess; +} // NOLINT(readability/fn_size) + +static Condition FlagsConditionToCondition(FlagsCondition condition) { + switch (condition) { + case kUnorderedEqual: + case kEqual: + return equal; + break; + case kUnorderedNotEqual: + case kNotEqual: + return not_equal; + break; + case kSignedLessThan: + return less; + break; + case kSignedGreaterThanOrEqual: + return greater_equal; + break; + case kSignedLessThanOrEqual: + return less_equal; + break; + case kSignedGreaterThan: + return greater; + break; + case kUnsignedLessThan: + return below; + break; + case kUnsignedGreaterThanOrEqual: + return above_equal; + break; + case kUnsignedLessThanOrEqual: + return below_equal; + break; + case kUnsignedGreaterThan: + return above; + break; + case kOverflow: + return overflow; + break; + case kNotOverflow: + return no_overflow; + break; + default: + UNREACHABLE(); + break; + } +} + +// Assembles a branch after an instruction. +void CodeGenerator::AssembleArchBranch(Instruction* instr, BranchInfo* branch) { + Label::Distance flabel_distance = + branch->fallthru ? Label::kNear : Label::kFar; + + Label done; + Label tlabel_tmp; + Label flabel_tmp; + Label* tlabel = &tlabel_tmp; + Label* flabel = &flabel_tmp; + + Label* tlabel_dst = branch->true_label; + Label* flabel_dst = branch->false_label; + + if (branch->condition == kUnorderedEqual) { + __ j(parity_even, flabel, flabel_distance); + } else if (branch->condition == kUnorderedNotEqual) { + __ j(parity_even, tlabel); + } + __ j(FlagsConditionToCondition(branch->condition), tlabel); + + // Add a jump if not falling through to the next block. + if (!branch->fallthru) __ jmp(flabel); + + __ jmp(&done); + __ bind(&tlabel_tmp); + FlagsMode mode = FlagsModeField::decode(instr->opcode()); + if (mode == kFlags_deoptimize) { + int double_register_param_count = 0; + int x87_layout = 0; + for (size_t i = 0; i < instr->InputCount(); i++) { + if (instr->InputAt(i)->IsFPRegister()) { + double_register_param_count++; + } + } + // Currently we use only one X87 register. If double_register_param_count + // is bigger than 1, it means duplicated double register is added to input + // of this instruction. + if (double_register_param_count > 0) { + x87_layout = (0 << 3) | 1; + } + // The layout of x87 register stack is loaded on the top of FPU register + // stack for deoptimization. + __ push(Immediate(x87_layout)); + __ fild_s(MemOperand(esp, 0)); + __ lea(esp, Operand(esp, kPointerSize)); + } + __ jmp(tlabel_dst); + __ bind(&flabel_tmp); + __ jmp(flabel_dst); + __ bind(&done); +} + + +void CodeGenerator::AssembleArchJump(RpoNumber target) { + if (!IsNextInAssemblyOrder(target)) __ jmp(GetLabel(target)); +} + +void CodeGenerator::AssembleArchTrap(Instruction* instr, + FlagsCondition condition) { + class OutOfLineTrap final : public OutOfLineCode { + public: + OutOfLineTrap(CodeGenerator* gen, bool frame_elided, Instruction* instr) + : OutOfLineCode(gen), + frame_elided_(frame_elided), + instr_(instr), + gen_(gen) {} + + void Generate() final { + X87OperandConverter i(gen_, instr_); + + Builtins::Name trap_id = + static_cast(i.InputInt32(instr_->InputCount() - 1)); + bool old_has_frame = __ has_frame(); + if (frame_elided_) { + __ set_has_frame(true); + __ EnterFrame(StackFrame::WASM_COMPILED); + } + GenerateCallToTrap(trap_id); + if (frame_elided_) { + __ set_has_frame(old_has_frame); + } + } + + private: + void GenerateCallToTrap(Builtins::Name trap_id) { + if (trap_id == Builtins::builtin_count) { + // We cannot test calls to the runtime in cctest/test-run-wasm. + // Therefore we emit a call to C here instead of a call to the runtime. + __ PrepareCallCFunction(0, esi); + __ CallCFunction(ExternalReference::wasm_call_trap_callback_for_testing( + __ isolate()), + 0); + __ LeaveFrame(StackFrame::WASM_COMPILED); + __ Ret(); + } else { + gen_->AssembleSourcePosition(instr_); + __ Call(__ isolate()->builtins()->builtin_handle(trap_id), + RelocInfo::CODE_TARGET); + ReferenceMap* reference_map = + new (gen_->zone()) ReferenceMap(gen_->zone()); + gen_->RecordSafepoint(reference_map, Safepoint::kSimple, 0, + Safepoint::kNoLazyDeopt); + __ AssertUnreachable(kUnexpectedReturnFromWasmTrap); + } + } + + bool frame_elided_; + Instruction* instr_; + CodeGenerator* gen_; + }; + bool frame_elided = !frame_access_state()->has_frame(); + auto ool = new (zone()) OutOfLineTrap(this, frame_elided, instr); + Label* tlabel = ool->entry(); + Label end; + if (condition == kUnorderedEqual) { + __ j(parity_even, &end); + } else if (condition == kUnorderedNotEqual) { + __ j(parity_even, tlabel); + } + __ j(FlagsConditionToCondition(condition), tlabel); + __ bind(&end); +} + +// Assembles boolean materializations after an instruction. +void CodeGenerator::AssembleArchBoolean(Instruction* instr, + FlagsCondition condition) { + X87OperandConverter i(this, instr); + Label done; + + // Materialize a full 32-bit 1 or 0 value. The result register is always the + // last output of the instruction. + Label check; + DCHECK_NE(0u, instr->OutputCount()); + Register reg = i.OutputRegister(instr->OutputCount() - 1); + if (condition == kUnorderedEqual) { + __ j(parity_odd, &check, Label::kNear); + __ Move(reg, Immediate(0)); + __ jmp(&done, Label::kNear); + } else if (condition == kUnorderedNotEqual) { + __ j(parity_odd, &check, Label::kNear); + __ mov(reg, Immediate(1)); + __ jmp(&done, Label::kNear); + } + Condition cc = FlagsConditionToCondition(condition); + + __ bind(&check); + if (reg.is_byte_register()) { + // setcc for byte registers (al, bl, cl, dl). + __ setcc(cc, reg); + __ movzx_b(reg, reg); + } else { + // Emit a branch to set a register to either 1 or 0. + Label set; + __ j(cc, &set, Label::kNear); + __ Move(reg, Immediate(0)); + __ jmp(&done, Label::kNear); + __ bind(&set); + __ mov(reg, Immediate(1)); + } + __ bind(&done); +} + + +void CodeGenerator::AssembleArchLookupSwitch(Instruction* instr) { + X87OperandConverter i(this, instr); + Register input = i.InputRegister(0); + for (size_t index = 2; index < instr->InputCount(); index += 2) { + __ cmp(input, Immediate(i.InputInt32(index + 0))); + __ j(equal, GetLabel(i.InputRpo(index + 1))); + } + AssembleArchJump(i.InputRpo(1)); +} + + +void CodeGenerator::AssembleArchTableSwitch(Instruction* instr) { + X87OperandConverter i(this, instr); + Register input = i.InputRegister(0); + size_t const case_count = instr->InputCount() - 2; + Label** cases = zone()->NewArray(case_count); + for (size_t index = 0; index < case_count; ++index) { + cases[index] = GetLabel(i.InputRpo(index + 2)); + } + Label* const table = AddJumpTable(cases, case_count); + __ cmp(input, Immediate(case_count)); + __ j(above_equal, GetLabel(i.InputRpo(1))); + __ jmp(Operand::JumpTable(input, times_4, table)); +} + +CodeGenerator::CodeGenResult CodeGenerator::AssembleDeoptimizerCall( + int deoptimization_id, SourcePosition pos) { + DeoptimizeKind deoptimization_kind = GetDeoptimizationKind(deoptimization_id); + DeoptimizeReason deoptimization_reason = + GetDeoptimizationReason(deoptimization_id); + Deoptimizer::BailoutType bailout_type = + deoptimization_kind == DeoptimizeKind::kSoft ? Deoptimizer::SOFT + : Deoptimizer::EAGER; + Address deopt_entry = Deoptimizer::GetDeoptimizationEntry( + isolate(), deoptimization_id, bailout_type); + if (deopt_entry == nullptr) return kTooManyDeoptimizationBailouts; + __ RecordDeoptReason(deoptimization_reason, pos, deoptimization_id); + __ call(deopt_entry, RelocInfo::RUNTIME_ENTRY); + return kSuccess; +} + + +// The calling convention for JSFunctions on X87 passes arguments on the +// stack and the JSFunction and context in EDI and ESI, respectively, thus +// the steps of the call look as follows: + +// --{ before the call instruction }-------------------------------------------- +// | caller frame | +// ^ esp ^ ebp + +// --{ push arguments and setup ESI, EDI }-------------------------------------- +// | args + receiver | caller frame | +// ^ esp ^ ebp +// [edi = JSFunction, esi = context] + +// --{ call [edi + kCodeEntryOffset] }------------------------------------------ +// | RET | args + receiver | caller frame | +// ^ esp ^ ebp + +// =={ prologue of called function }============================================ +// --{ push ebp }--------------------------------------------------------------- +// | FP | RET | args + receiver | caller frame | +// ^ esp ^ ebp + +// --{ mov ebp, esp }----------------------------------------------------------- +// | FP | RET | args + receiver | caller frame | +// ^ ebp,esp + +// --{ push esi }--------------------------------------------------------------- +// | CTX | FP | RET | args + receiver | caller frame | +// ^esp ^ ebp + +// --{ push edi }--------------------------------------------------------------- +// | FNC | CTX | FP | RET | args + receiver | caller frame | +// ^esp ^ ebp + +// --{ subi esp, #N }----------------------------------------------------------- +// | callee frame | FNC | CTX | FP | RET | args + receiver | caller frame | +// ^esp ^ ebp + +// =={ body of called function }================================================ + +// =={ epilogue of called function }============================================ +// --{ mov esp, ebp }----------------------------------------------------------- +// | FP | RET | args + receiver | caller frame | +// ^ esp,ebp + +// --{ pop ebp }----------------------------------------------------------- +// | | RET | args + receiver | caller frame | +// ^ esp ^ ebp + +// --{ ret #A+1 }----------------------------------------------------------- +// | | caller frame | +// ^ esp ^ ebp + + +// Runtime function calls are accomplished by doing a stub call to the +// CEntryStub (a real code object). On X87 passes arguments on the +// stack, the number of arguments in EAX, the address of the runtime function +// in EBX, and the context in ESI. + +// --{ before the call instruction }-------------------------------------------- +// | caller frame | +// ^ esp ^ ebp + +// --{ push arguments and setup EAX, EBX, and ESI }----------------------------- +// | args + receiver | caller frame | +// ^ esp ^ ebp +// [eax = #args, ebx = runtime function, esi = context] + +// --{ call #CEntryStub }------------------------------------------------------- +// | RET | args + receiver | caller frame | +// ^ esp ^ ebp + +// =={ body of runtime function }=============================================== + +// --{ runtime returns }-------------------------------------------------------- +// | caller frame | +// ^ esp ^ ebp + +// Other custom linkages (e.g. for calling directly into and out of C++) may +// need to save callee-saved registers on the stack, which is done in the +// function prologue of generated code. + +// --{ before the call instruction }-------------------------------------------- +// | caller frame | +// ^ esp ^ ebp + +// --{ set up arguments in registers on stack }--------------------------------- +// | args | caller frame | +// ^ esp ^ ebp +// [r0 = arg0, r1 = arg1, ...] + +// --{ call code }-------------------------------------------------------------- +// | RET | args | caller frame | +// ^ esp ^ ebp + +// =={ prologue of called function }============================================ +// --{ push ebp }--------------------------------------------------------------- +// | FP | RET | args | caller frame | +// ^ esp ^ ebp + +// --{ mov ebp, esp }----------------------------------------------------------- +// | FP | RET | args | caller frame | +// ^ ebp,esp + +// --{ save registers }--------------------------------------------------------- +// | regs | FP | RET | args | caller frame | +// ^ esp ^ ebp + +// --{ subi esp, #N }----------------------------------------------------------- +// | callee frame | regs | FP | RET | args | caller frame | +// ^esp ^ ebp + +// =={ body of called function }================================================ + +// =={ epilogue of called function }============================================ +// --{ restore registers }------------------------------------------------------ +// | regs | FP | RET | args | caller frame | +// ^ esp ^ ebp + +// --{ mov esp, ebp }----------------------------------------------------------- +// | FP | RET | args | caller frame | +// ^ esp,ebp + +// --{ pop ebp }---------------------------------------------------------------- +// | RET | args | caller frame | +// ^ esp ^ ebp + +void CodeGenerator::FinishFrame(Frame* frame) { + CallDescriptor* descriptor = linkage()->GetIncomingDescriptor(); + const RegList saves = descriptor->CalleeSavedRegisters(); + if (saves != 0) { // Save callee-saved registers. + DCHECK(!info()->is_osr()); + int pushed = 0; + for (int i = Register::kNumRegisters - 1; i >= 0; i--) { + if (!((1 << i) & saves)) continue; + ++pushed; + } + frame->AllocateSavedCalleeRegisterSlots(pushed); + } + + // Initailize FPU state. + __ fninit(); + __ fld1(); +} + +void CodeGenerator::AssembleConstructFrame() { + CallDescriptor* descriptor = linkage()->GetIncomingDescriptor(); + if (frame_access_state()->has_frame()) { + if (descriptor->IsCFunctionCall()) { + __ push(ebp); + __ mov(ebp, esp); + } else if (descriptor->IsJSFunctionCall()) { + __ Prologue(this->info()->GeneratePreagedPrologue()); + if (descriptor->PushArgumentCount()) { + __ push(kJavaScriptCallArgCountRegister); + } + } else { + __ StubPrologue(info()->GetOutputStackFrameType()); + } + } + + int shrink_slots = + frame()->GetTotalFrameSlotCount() - descriptor->CalculateFixedFrameSize(); + + if (info()->is_osr()) { + // TurboFan OSR-compiled functions cannot be entered directly. + __ Abort(kShouldNotDirectlyEnterOsrFunction); + + // Unoptimized code jumps directly to this entrypoint while the unoptimized + // frame is still on the stack. Optimized code uses OSR values directly from + // the unoptimized frame. Thus, all that needs to be done is to allocate the + // remaining stack slots. + if (FLAG_code_comments) __ RecordComment("-- OSR entrypoint --"); + osr_pc_offset_ = __ pc_offset(); + shrink_slots -= osr_helper()->UnoptimizedFrameSlots(); + + // Initailize FPU state. + __ fninit(); + __ fld1(); + } + + const RegList saves = descriptor->CalleeSavedRegisters(); + if (shrink_slots > 0) { + if (info()->IsWasm() && shrink_slots > 128) { + // For WebAssembly functions with big frames we have to do the stack + // overflow check before we construct the frame. Otherwise we may not + // have enough space on the stack to call the runtime for the stack + // overflow. + Label done; + + // If the frame is bigger than the stack, we throw the stack overflow + // exception unconditionally. Thereby we can avoid the integer overflow + // check in the condition code. + if (shrink_slots * kPointerSize < FLAG_stack_size * 1024) { + Register scratch = esi; + __ push(scratch); + __ mov(scratch, + Immediate(ExternalReference::address_of_real_stack_limit( + __ isolate()))); + __ mov(scratch, Operand(scratch, 0)); + __ add(scratch, Immediate(shrink_slots * kPointerSize)); + __ cmp(esp, scratch); + __ pop(scratch); + __ j(above_equal, &done); + } + if (!frame_access_state()->has_frame()) { + __ set_has_frame(true); + __ EnterFrame(StackFrame::WASM_COMPILED); + } + __ Move(esi, Smi::kZero); + __ CallRuntimeDelayed(zone(), Runtime::kThrowWasmStackOverflow); + ReferenceMap* reference_map = new (zone()) ReferenceMap(zone()); + RecordSafepoint(reference_map, Safepoint::kSimple, 0, + Safepoint::kNoLazyDeopt); + __ AssertUnreachable(kUnexpectedReturnFromWasmTrap); + __ bind(&done); + } + __ sub(esp, Immediate(shrink_slots * kPointerSize)); + } + + if (saves != 0) { // Save callee-saved registers. + DCHECK(!info()->is_osr()); + int pushed = 0; + for (int i = Register::kNumRegisters - 1; i >= 0; i--) { + if (!((1 << i) & saves)) continue; + __ push(Register::from_code(i)); + ++pushed; + } + } +} + +void CodeGenerator::AssembleReturn(InstructionOperand* pop) { + CallDescriptor* descriptor = linkage()->GetIncomingDescriptor(); + + // Clear the FPU stack only if there is no return value in the stack. + if (FLAG_debug_code && FLAG_enable_slow_asserts) { + __ VerifyX87StackDepth(1); + } + bool clear_stack = true; + for (size_t i = 0; i < descriptor->ReturnCount(); i++) { + MachineRepresentation rep = descriptor->GetReturnType(i).representation(); + LinkageLocation loc = descriptor->GetReturnLocation(i); + if (IsFloatingPoint(rep) && loc == LinkageLocation::ForRegister(0)) { + clear_stack = false; + break; + } + } + if (clear_stack) __ fstp(0); + + const RegList saves = descriptor->CalleeSavedRegisters(); + // Restore registers. + if (saves != 0) { + for (int i = 0; i < Register::kNumRegisters; i++) { + if (!((1 << i) & saves)) continue; + __ pop(Register::from_code(i)); + } + } + + // Might need ecx for scratch if pop_size is too big or if there is a variable + // pop count. + DCHECK_EQ(0u, descriptor->CalleeSavedRegisters() & ecx.bit()); + size_t pop_size = descriptor->StackParameterCount() * kPointerSize; + X87OperandConverter g(this, nullptr); + if (descriptor->IsCFunctionCall()) { + AssembleDeconstructFrame(); + } else if (frame_access_state()->has_frame()) { + // Canonicalize JSFunction return sites for now if they always have the same + // number of return args. + if (pop->IsImmediate() && g.ToConstant(pop).ToInt32() == 0) { + if (return_label_.is_bound()) { + __ jmp(&return_label_); + return; + } else { + __ bind(&return_label_); + AssembleDeconstructFrame(); + } + } else { + AssembleDeconstructFrame(); + } + } + DCHECK_EQ(0u, descriptor->CalleeSavedRegisters() & edx.bit()); + DCHECK_EQ(0u, descriptor->CalleeSavedRegisters() & ecx.bit()); + if (pop->IsImmediate()) { + DCHECK_EQ(Constant::kInt32, g.ToConstant(pop).type()); + pop_size += g.ToConstant(pop).ToInt32() * kPointerSize; + __ Ret(static_cast(pop_size), ecx); + } else { + Register pop_reg = g.ToRegister(pop); + Register scratch_reg = pop_reg.is(ecx) ? edx : ecx; + __ pop(scratch_reg); + __ lea(esp, Operand(esp, pop_reg, times_4, static_cast(pop_size))); + __ jmp(scratch_reg); + } +} + +void CodeGenerator::FinishCode() {} + +void CodeGenerator::AssembleMove(InstructionOperand* source, + InstructionOperand* destination) { + X87OperandConverter g(this, nullptr); + // Dispatch on the source and destination operand kinds. Not all + // combinations are possible. + if (source->IsRegister()) { + DCHECK(destination->IsRegister() || destination->IsStackSlot()); + Register src = g.ToRegister(source); + Operand dst = g.ToOperand(destination); + __ mov(dst, src); + } else if (source->IsStackSlot()) { + DCHECK(destination->IsRegister() || destination->IsStackSlot()); + Operand src = g.ToOperand(source); + if (destination->IsRegister()) { + Register dst = g.ToRegister(destination); + __ mov(dst, src); + } else { + Operand dst = g.ToOperand(destination); + __ push(src); + __ pop(dst); + } + } else if (source->IsConstant()) { + Constant src_constant = g.ToConstant(source); + if (src_constant.type() == Constant::kHeapObject) { + Handle src = src_constant.ToHeapObject(); + if (destination->IsRegister()) { + Register dst = g.ToRegister(destination); + __ Move(dst, src); + } else { + DCHECK(destination->IsStackSlot()); + Operand dst = g.ToOperand(destination); + __ mov(dst, src); + } + } else if (destination->IsRegister()) { + Register dst = g.ToRegister(destination); + __ Move(dst, g.ToImmediate(source)); + } else if (destination->IsStackSlot()) { + Operand dst = g.ToOperand(destination); + __ Move(dst, g.ToImmediate(source)); + } else if (src_constant.type() == Constant::kFloat32) { + // TODO(turbofan): Can we do better here? + uint32_t src = src_constant.ToFloat32AsInt(); + if (destination->IsFPRegister()) { + __ sub(esp, Immediate(kInt32Size)); + __ mov(MemOperand(esp, 0), Immediate(src)); + // always only push one value into the x87 stack. + __ fstp(0); + __ fld_s(MemOperand(esp, 0)); + __ add(esp, Immediate(kInt32Size)); + } else { + DCHECK(destination->IsFPStackSlot()); + Operand dst = g.ToOperand(destination); + __ Move(dst, Immediate(src)); + } + } else { + DCHECK_EQ(Constant::kFloat64, src_constant.type()); + uint64_t src = src_constant.ToFloat64().AsUint64(); + uint32_t lower = static_cast(src); + uint32_t upper = static_cast(src >> 32); + if (destination->IsFPRegister()) { + __ sub(esp, Immediate(kDoubleSize)); + __ mov(MemOperand(esp, 0), Immediate(lower)); + __ mov(MemOperand(esp, kInt32Size), Immediate(upper)); + // always only push one value into the x87 stack. + __ fstp(0); + __ fld_d(MemOperand(esp, 0)); + __ add(esp, Immediate(kDoubleSize)); + } else { + DCHECK(destination->IsFPStackSlot()); + Operand dst0 = g.ToOperand(destination); + Operand dst1 = g.HighOperand(destination); + __ Move(dst0, Immediate(lower)); + __ Move(dst1, Immediate(upper)); + } + } + } else if (source->IsFPRegister()) { + DCHECK(destination->IsFPStackSlot()); + Operand dst = g.ToOperand(destination); + auto allocated = AllocatedOperand::cast(*source); + switch (allocated.representation()) { + case MachineRepresentation::kFloat32: + __ fst_s(dst); + break; + case MachineRepresentation::kFloat64: + __ fst_d(dst); + break; + default: + UNREACHABLE(); + } + } else if (source->IsFPStackSlot()) { + DCHECK(destination->IsFPRegister() || destination->IsFPStackSlot()); + Operand src = g.ToOperand(source); + auto allocated = AllocatedOperand::cast(*source); + if (destination->IsFPRegister()) { + // always only push one value into the x87 stack. + __ fstp(0); + switch (allocated.representation()) { + case MachineRepresentation::kFloat32: + __ fld_s(src); + break; + case MachineRepresentation::kFloat64: + __ fld_d(src); + break; + default: + UNREACHABLE(); + } + } else { + Operand dst = g.ToOperand(destination); + switch (allocated.representation()) { + case MachineRepresentation::kFloat32: + __ fld_s(src); + __ fstp_s(dst); + break; + case MachineRepresentation::kFloat64: + __ fld_d(src); + __ fstp_d(dst); + break; + default: + UNREACHABLE(); + } + } + } else { + UNREACHABLE(); + } +} + + +void CodeGenerator::AssembleSwap(InstructionOperand* source, + InstructionOperand* destination) { + X87OperandConverter g(this, nullptr); + // Dispatch on the source and destination operand kinds. Not all + // combinations are possible. + if (source->IsRegister() && destination->IsRegister()) { + // Register-register. + Register src = g.ToRegister(source); + Register dst = g.ToRegister(destination); + __ xchg(dst, src); + } else if (source->IsRegister() && destination->IsStackSlot()) { + // Register-memory. + __ xchg(g.ToRegister(source), g.ToOperand(destination)); + } else if (source->IsStackSlot() && destination->IsStackSlot()) { + // Memory-memory. + Operand dst1 = g.ToOperand(destination); + __ push(dst1); + frame_access_state()->IncreaseSPDelta(1); + Operand src1 = g.ToOperand(source); + __ push(src1); + Operand dst2 = g.ToOperand(destination); + __ pop(dst2); + frame_access_state()->IncreaseSPDelta(-1); + Operand src2 = g.ToOperand(source); + __ pop(src2); + } else if (source->IsFPRegister() && destination->IsFPRegister()) { + UNREACHABLE(); + } else if (source->IsFPRegister() && destination->IsFPStackSlot()) { + auto allocated = AllocatedOperand::cast(*source); + switch (allocated.representation()) { + case MachineRepresentation::kFloat32: + __ fld_s(g.ToOperand(destination)); + __ fxch(); + __ fstp_s(g.ToOperand(destination)); + break; + case MachineRepresentation::kFloat64: + __ fld_d(g.ToOperand(destination)); + __ fxch(); + __ fstp_d(g.ToOperand(destination)); + break; + default: + UNREACHABLE(); + } + } else if (source->IsFPStackSlot() && destination->IsFPStackSlot()) { + auto allocated = AllocatedOperand::cast(*source); + switch (allocated.representation()) { + case MachineRepresentation::kFloat32: + __ fld_s(g.ToOperand(source)); + __ fld_s(g.ToOperand(destination)); + __ fstp_s(g.ToOperand(source)); + __ fstp_s(g.ToOperand(destination)); + break; + case MachineRepresentation::kFloat64: + __ fld_d(g.ToOperand(source)); + __ fld_d(g.ToOperand(destination)); + __ fstp_d(g.ToOperand(source)); + __ fstp_d(g.ToOperand(destination)); + break; + default: + UNREACHABLE(); + } + } else { + // No other combinations are possible. + UNREACHABLE(); + } +} + + +void CodeGenerator::AssembleJumpTable(Label** targets, size_t target_count) { + for (size_t index = 0; index < target_count; ++index) { + __ dd(targets[index]); + } +} + + +void CodeGenerator::EnsureSpaceForLazyDeopt() { + if (!info()->ShouldEnsureSpaceForLazyDeopt()) { + return; + } + + int space_needed = Deoptimizer::patch_size(); + // Ensure that we have enough space after the previous lazy-bailout + // instruction for patching the code here. + int current_pc = tasm()->pc_offset(); + if (current_pc < last_lazy_deopt_pc_ + space_needed) { + int padding_size = last_lazy_deopt_pc_ + space_needed - current_pc; + __ Nop(padding_size); + } +} + +#undef __ + +} // namespace compiler +} // namespace internal +} // namespace v8 diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/compiler/x87/instruction-codes-x87.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/compiler/x87/instruction-codes-x87.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/compiler/x87/instruction-codes-x87.h 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/compiler/x87/instruction-codes-x87.h 2017-12-28 04:01:21.688880592 +0100 @@ -0,0 +1,141 @@ +// Copyright 2014 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_COMPILER_X87_INSTRUCTION_CODES_X87_H_ +#define V8_COMPILER_X87_INSTRUCTION_CODES_X87_H_ + +#include "src/compiler/instruction.h" +#include "src/compiler/instruction-codes.h" +namespace v8 { +namespace internal { +namespace compiler { + +// X87-specific opcodes that specify which assembly sequence to emit. +// Most opcodes specify a single instruction. +#define TARGET_ARCH_OPCODE_LIST(V) \ + V(X87Add) \ + V(X87And) \ + V(X87Cmp) \ + V(X87Cmp16) \ + V(X87Cmp8) \ + V(X87Test) \ + V(X87Test16) \ + V(X87Test8) \ + V(X87Or) \ + V(X87Xor) \ + V(X87Sub) \ + V(X87Imul) \ + V(X87ImulHigh) \ + V(X87UmulHigh) \ + V(X87Idiv) \ + V(X87Udiv) \ + V(X87Not) \ + V(X87Neg) \ + V(X87Shl) \ + V(X87Shr) \ + V(X87Sar) \ + V(X87AddPair) \ + V(X87SubPair) \ + V(X87MulPair) \ + V(X87ShlPair) \ + V(X87ShrPair) \ + V(X87SarPair) \ + V(X87Ror) \ + V(X87Lzcnt) \ + V(X87Popcnt) \ + V(X87Float32Cmp) \ + V(X87Float32Add) \ + V(X87Float32Sub) \ + V(X87Float32Mul) \ + V(X87Float32Div) \ + V(X87Float32Abs) \ + V(X87Float32Neg) \ + V(X87Float32Sqrt) \ + V(X87Float32Round) \ + V(X87LoadFloat64Constant) \ + V(X87Float64Add) \ + V(X87Float64Sub) \ + V(X87Float64Mul) \ + V(X87Float64Div) \ + V(X87Float64Mod) \ + V(X87Float32Max) \ + V(X87Float64Max) \ + V(X87Float32Min) \ + V(X87Float64Min) \ + V(X87Float64Abs) \ + V(X87Float64Neg) \ + V(X87Int32ToFloat32) \ + V(X87Uint32ToFloat32) \ + V(X87Int32ToFloat64) \ + V(X87Float32ToFloat64) \ + V(X87Uint32ToFloat64) \ + V(X87Float64ToInt32) \ + V(X87Float32ToInt32) \ + V(X87Float32ToUint32) \ + V(X87Float64ToFloat32) \ + V(X87Float64ToUint32) \ + V(X87Float64ExtractHighWord32) \ + V(X87Float64ExtractLowWord32) \ + V(X87Float64InsertHighWord32) \ + V(X87Float64InsertLowWord32) \ + V(X87Float64Sqrt) \ + V(X87Float64Round) \ + V(X87Float64Cmp) \ + V(X87Float64SilenceNaN) \ + V(X87Movsxbl) \ + V(X87Movzxbl) \ + V(X87Movb) \ + V(X87Movsxwl) \ + V(X87Movzxwl) \ + V(X87Movw) \ + V(X87Movl) \ + V(X87Movss) \ + V(X87Movsd) \ + V(X87Lea) \ + V(X87BitcastFI) \ + V(X87BitcastIF) \ + V(X87Push) \ + V(X87PushFloat64) \ + V(X87PushFloat32) \ + V(X87Poke) \ + V(X87StackCheck) + +// Addressing modes represent the "shape" of inputs to an instruction. +// Many instructions support multiple addressing modes. Addressing modes +// are encoded into the InstructionCode of the instruction and tell the +// code generator after register allocation which assembler method to call. +// +// We use the following local notation for addressing modes: +// +// M = memory operand +// R = base register +// N = index register * N for N in {1, 2, 4, 8} +// I = immediate displacement (int32_t) + +#define TARGET_ADDRESSING_MODE_LIST(V) \ + V(MR) /* [%r1 ] */ \ + V(MRI) /* [%r1 + K] */ \ + V(MR1) /* [%r1 + %r2*1 ] */ \ + V(MR2) /* [%r1 + %r2*2 ] */ \ + V(MR4) /* [%r1 + %r2*4 ] */ \ + V(MR8) /* [%r1 + %r2*8 ] */ \ + V(MR1I) /* [%r1 + %r2*1 + K] */ \ + V(MR2I) /* [%r1 + %r2*2 + K] */ \ + V(MR4I) /* [%r1 + %r2*3 + K] */ \ + V(MR8I) /* [%r1 + %r2*4 + K] */ \ + V(M1) /* [ %r2*1 ] */ \ + V(M2) /* [ %r2*2 ] */ \ + V(M4) /* [ %r2*4 ] */ \ + V(M8) /* [ %r2*8 ] */ \ + V(M1I) /* [ %r2*1 + K] */ \ + V(M2I) /* [ %r2*2 + K] */ \ + V(M4I) /* [ %r2*4 + K] */ \ + V(M8I) /* [ %r2*8 + K] */ \ + V(MI) /* [ K] */ + +} // namespace compiler +} // namespace internal +} // namespace v8 + +#endif // V8_COMPILER_X87_INSTRUCTION_CODES_X87_H_ diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/compiler/x87/instruction-scheduler-x87.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/compiler/x87/instruction-scheduler-x87.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/compiler/x87/instruction-scheduler-x87.cc 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/compiler/x87/instruction-scheduler-x87.cc 2017-12-25 17:42:57.208465749 +0100 @@ -0,0 +1,26 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/compiler/instruction-scheduler.h" + +namespace v8 { +namespace internal { +namespace compiler { + +bool InstructionScheduler::SchedulerSupported() { return false; } + + +int InstructionScheduler::GetTargetInstructionFlags( + const Instruction* instr) const { + UNIMPLEMENTED(); +} + + +int InstructionScheduler::GetInstructionLatency(const Instruction* instr) { + UNIMPLEMENTED(); +} + +} // namespace compiler +} // namespace internal +} // namespace v8 diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/compiler/x87/instruction-selector-x87.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/compiler/x87/instruction-selector-x87.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/compiler/x87/instruction-selector-x87.cc 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/compiler/x87/instruction-selector-x87.cc 2017-12-28 04:08:42.202144307 +0100 @@ -0,0 +1,2031 @@ +// Copyright 2014 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/base/adapters.h" +#include "src/compiler/instruction-selector-impl.h" +#include "src/compiler/node-matchers.h" +#include "src/compiler/node-properties.h" + +namespace v8 { +namespace internal { +namespace compiler { + +// Adds X87-specific methods for generating operands. +class X87OperandGenerator final : public OperandGenerator { + public: + explicit X87OperandGenerator(InstructionSelector* selector) + : OperandGenerator(selector) {} + + InstructionOperand UseByteRegister(Node* node) { + // TODO(titzer): encode byte register use constraints. + return UseFixed(node, edx); + } + + InstructionOperand DefineAsByteRegister(Node* node) { + // TODO(titzer): encode byte register def constraints. + return DefineAsRegister(node); + } + + bool CanBeMemoryOperand(InstructionCode opcode, Node* node, Node* input, + int effect_level) { + if (input->opcode() != IrOpcode::kLoad || + !selector()->CanCover(node, input)) { + return false; + } + if (effect_level != selector()->GetEffectLevel(input)) { + return false; + } + MachineRepresentation rep = + LoadRepresentationOf(input->op()).representation(); + switch (opcode) { + case kX87Cmp: + case kX87Test: + return rep == MachineRepresentation::kWord32 || + rep == MachineRepresentation::kTagged; + case kX87Cmp16: + case kX87Test16: + return rep == MachineRepresentation::kWord16; + case kX87Cmp8: + case kX87Test8: + return rep == MachineRepresentation::kWord8; + default: + break; + } + return false; + } + + InstructionOperand CreateImmediate(int imm) { + return sequence()->AddImmediate(Constant(imm)); + } + + bool CanBeImmediate(Node* node) { + switch (node->opcode()) { + case IrOpcode::kInt32Constant: + case IrOpcode::kNumberConstant: + case IrOpcode::kExternalConstant: + case IrOpcode::kRelocatableInt32Constant: + case IrOpcode::kRelocatableInt64Constant: + return true; + case IrOpcode::kHeapConstant: { +// TODO(bmeurer): We must not dereference handles concurrently. If we +// really have to this here, then we need to find a way to put this +// information on the HeapConstant node already. +#if 0 + // Constants in new space cannot be used as immediates in V8 because + // the GC does not scan code objects when collecting the new generation. + Handle value = OpParameter>(node); + Isolate* isolate = value->GetIsolate(); + return !isolate->heap()->InNewSpace(*value); +#endif + } + default: + return false; + } + } + + AddressingMode GenerateMemoryOperandInputs(Node* index, int scale, Node* base, + Node* displacement_node, + DisplacementMode displacement_mode, + InstructionOperand inputs[], + size_t* input_count) { + AddressingMode mode = kMode_MRI; + int32_t displacement = (displacement_node == nullptr) + ? 0 + : OpParameter(displacement_node); + if (displacement_mode == kNegativeDisplacement) { + displacement = -displacement; + } + if (base != nullptr) { + if (base->opcode() == IrOpcode::kInt32Constant) { + displacement += OpParameter(base); + base = nullptr; + } + } + if (base != nullptr) { + inputs[(*input_count)++] = UseRegister(base); + if (index != nullptr) { + DCHECK(scale >= 0 && scale <= 3); + inputs[(*input_count)++] = UseRegister(index); + if (displacement != 0) { + inputs[(*input_count)++] = TempImmediate(displacement); + static const AddressingMode kMRnI_modes[] = {kMode_MR1I, kMode_MR2I, + kMode_MR4I, kMode_MR8I}; + mode = kMRnI_modes[scale]; + } else { + static const AddressingMode kMRn_modes[] = {kMode_MR1, kMode_MR2, + kMode_MR4, kMode_MR8}; + mode = kMRn_modes[scale]; + } + } else { + if (displacement == 0) { + mode = kMode_MR; + } else { + inputs[(*input_count)++] = TempImmediate(displacement); + mode = kMode_MRI; + } + } + } else { + DCHECK(scale >= 0 && scale <= 3); + if (index != nullptr) { + inputs[(*input_count)++] = UseRegister(index); + if (displacement != 0) { + inputs[(*input_count)++] = TempImmediate(displacement); + static const AddressingMode kMnI_modes[] = {kMode_MRI, kMode_M2I, + kMode_M4I, kMode_M8I}; + mode = kMnI_modes[scale]; + } else { + static const AddressingMode kMn_modes[] = {kMode_MR, kMode_M2, + kMode_M4, kMode_M8}; + mode = kMn_modes[scale]; + } + } else { + inputs[(*input_count)++] = TempImmediate(displacement); + return kMode_MI; + } + } + return mode; + } + + AddressingMode GetEffectiveAddressMemoryOperand(Node* node, + InstructionOperand inputs[], + size_t* input_count) { + BaseWithIndexAndDisplacement32Matcher m(node, AddressOption::kAllowAll); + DCHECK(m.matches()); + if ((m.displacement() == nullptr || CanBeImmediate(m.displacement()))) { + return GenerateMemoryOperandInputs( + m.index(), m.scale(), m.base(), m.displacement(), + m.displacement_mode(), inputs, input_count); + } else { + inputs[(*input_count)++] = UseRegister(node->InputAt(0)); + inputs[(*input_count)++] = UseRegister(node->InputAt(1)); + return kMode_MR1; + } + } + + bool CanBeBetterLeftOperand(Node* node) const { + return !selector()->IsLive(node); + } +}; + +void InstructionSelector::VisitStackSlot(Node* node) { + StackSlotRepresentation rep = StackSlotRepresentationOf(node->op()); + int slot = frame_->AllocateSpillSlot(rep.size()); + OperandGenerator g(this); + + Emit(kArchStackSlot, g.DefineAsRegister(node), + sequence()->AddImmediate(Constant(slot)), 0, nullptr); +} + +void InstructionSelector::VisitLoad(Node* node) { + LoadRepresentation load_rep = LoadRepresentationOf(node->op()); + + ArchOpcode opcode = kArchNop; + switch (load_rep.representation()) { + case MachineRepresentation::kFloat32: + opcode = kX87Movss; + break; + case MachineRepresentation::kFloat64: + opcode = kX87Movsd; + break; + case MachineRepresentation::kBit: // Fall through. + case MachineRepresentation::kWord8: + opcode = load_rep.IsSigned() ? kX87Movsxbl : kX87Movzxbl; + break; + case MachineRepresentation::kWord16: + opcode = load_rep.IsSigned() ? kX87Movsxwl : kX87Movzxwl; + break; + case MachineRepresentation::kTaggedSigned: // Fall through. + case MachineRepresentation::kTaggedPointer: // Fall through. + case MachineRepresentation::kTagged: // Fall through. + case MachineRepresentation::kWord32: + opcode = kX87Movl; + break; + case MachineRepresentation::kWord64: // Fall through. + case MachineRepresentation::kSimd128: // Fall through. + case MachineRepresentation::kNone: + UNREACHABLE(); + return; + } + + X87OperandGenerator g(this); + InstructionOperand outputs[1]; + outputs[0] = g.DefineAsRegister(node); + InstructionOperand inputs[3]; + size_t input_count = 0; + AddressingMode mode = + g.GetEffectiveAddressMemoryOperand(node, inputs, &input_count); + InstructionCode code = opcode | AddressingModeField::encode(mode); + Emit(code, 1, outputs, input_count, inputs); +} + +void InstructionSelector::VisitProtectedLoad(Node* node) { + // TODO(eholk) + UNIMPLEMENTED(); +} + +void InstructionSelector::VisitStore(Node* node) { + X87OperandGenerator g(this); + Node* base = node->InputAt(0); + Node* index = node->InputAt(1); + Node* value = node->InputAt(2); + + StoreRepresentation store_rep = StoreRepresentationOf(node->op()); + WriteBarrierKind write_barrier_kind = store_rep.write_barrier_kind(); + MachineRepresentation rep = store_rep.representation(); + + if (write_barrier_kind != kNoWriteBarrier) { + DCHECK(CanBeTaggedPointer(rep)); + AddressingMode addressing_mode; + InstructionOperand inputs[3]; + size_t input_count = 0; + inputs[input_count++] = g.UseUniqueRegister(base); + if (g.CanBeImmediate(index)) { + inputs[input_count++] = g.UseImmediate(index); + addressing_mode = kMode_MRI; + } else { + inputs[input_count++] = g.UseUniqueRegister(index); + addressing_mode = kMode_MR1; + } + inputs[input_count++] = g.UseUniqueRegister(value); + RecordWriteMode record_write_mode = RecordWriteMode::kValueIsAny; + switch (write_barrier_kind) { + case kNoWriteBarrier: + UNREACHABLE(); + break; + case kMapWriteBarrier: + record_write_mode = RecordWriteMode::kValueIsMap; + break; + case kPointerWriteBarrier: + record_write_mode = RecordWriteMode::kValueIsPointer; + break; + case kFullWriteBarrier: + record_write_mode = RecordWriteMode::kValueIsAny; + break; + } + InstructionOperand temps[] = {g.TempRegister(), g.TempRegister()}; + size_t const temp_count = arraysize(temps); + InstructionCode code = kArchStoreWithWriteBarrier; + code |= AddressingModeField::encode(addressing_mode); + code |= MiscField::encode(static_cast(record_write_mode)); + Emit(code, 0, nullptr, input_count, inputs, temp_count, temps); + } else { + ArchOpcode opcode = kArchNop; + switch (rep) { + case MachineRepresentation::kFloat32: + opcode = kX87Movss; + break; + case MachineRepresentation::kFloat64: + opcode = kX87Movsd; + break; + case MachineRepresentation::kBit: // Fall through. + case MachineRepresentation::kWord8: + opcode = kX87Movb; + break; + case MachineRepresentation::kWord16: + opcode = kX87Movw; + break; + case MachineRepresentation::kTaggedSigned: // Fall through. + case MachineRepresentation::kTaggedPointer: // Fall through. + case MachineRepresentation::kTagged: // Fall through. + case MachineRepresentation::kWord32: + opcode = kX87Movl; + break; + case MachineRepresentation::kWord64: // Fall through. + case MachineRepresentation::kSimd128: // Fall through. + case MachineRepresentation::kNone: + UNREACHABLE(); + return; + } + + InstructionOperand val; + if (g.CanBeImmediate(value)) { + val = g.UseImmediate(value); + } else if (rep == MachineRepresentation::kWord8 || + rep == MachineRepresentation::kBit) { + val = g.UseByteRegister(value); + } else { + val = g.UseRegister(value); + } + + InstructionOperand inputs[4]; + size_t input_count = 0; + AddressingMode addressing_mode = + g.GetEffectiveAddressMemoryOperand(node, inputs, &input_count); + InstructionCode code = + opcode | AddressingModeField::encode(addressing_mode); + inputs[input_count++] = val; + Emit(code, 0, static_cast(nullptr), input_count, + inputs); + } +} + +void InstructionSelector::VisitProtectedStore(Node* node) { + // TODO(eholk) + UNIMPLEMENTED(); +} + +// Architecture supports unaligned access, therefore VisitLoad is used instead +void InstructionSelector::VisitUnalignedLoad(Node* node) { UNREACHABLE(); } + +// Architecture supports unaligned access, therefore VisitStore is used instead +void InstructionSelector::VisitUnalignedStore(Node* node) { UNREACHABLE(); } + +void InstructionSelector::VisitCheckedLoad(Node* node) { + CheckedLoadRepresentation load_rep = CheckedLoadRepresentationOf(node->op()); + X87OperandGenerator g(this); + Node* const buffer = node->InputAt(0); + Node* const offset = node->InputAt(1); + Node* const length = node->InputAt(2); + ArchOpcode opcode = kArchNop; + switch (load_rep.representation()) { + case MachineRepresentation::kWord8: + opcode = load_rep.IsSigned() ? kCheckedLoadInt8 : kCheckedLoadUint8; + break; + case MachineRepresentation::kWord16: + opcode = load_rep.IsSigned() ? kCheckedLoadInt16 : kCheckedLoadUint16; + break; + case MachineRepresentation::kWord32: + opcode = kCheckedLoadWord32; + break; + case MachineRepresentation::kFloat32: + opcode = kCheckedLoadFloat32; + break; + case MachineRepresentation::kFloat64: + opcode = kCheckedLoadFloat64; + break; + case MachineRepresentation::kBit: // Fall through. + case MachineRepresentation::kTaggedSigned: // Fall through. + case MachineRepresentation::kTaggedPointer: // Fall through. + case MachineRepresentation::kTagged: // Fall through. + case MachineRepresentation::kWord64: // Fall through. + case MachineRepresentation::kSimd128: // Fall through. + case MachineRepresentation::kNone: + UNREACHABLE(); + return; + } + InstructionOperand offset_operand = g.UseRegister(offset); + InstructionOperand length_operand = + g.CanBeImmediate(length) ? g.UseImmediate(length) : g.UseRegister(length); + if (g.CanBeImmediate(buffer)) { + Emit(opcode | AddressingModeField::encode(kMode_MRI), + g.DefineAsRegister(node), offset_operand, length_operand, + offset_operand, g.UseImmediate(buffer)); + } else { + Emit(opcode | AddressingModeField::encode(kMode_MR1), + g.DefineAsRegister(node), offset_operand, length_operand, + g.UseRegister(buffer), offset_operand); + } +} + + +void InstructionSelector::VisitCheckedStore(Node* node) { + MachineRepresentation rep = CheckedStoreRepresentationOf(node->op()); + X87OperandGenerator g(this); + Node* const buffer = node->InputAt(0); + Node* const offset = node->InputAt(1); + Node* const length = node->InputAt(2); + Node* const value = node->InputAt(3); + ArchOpcode opcode = kArchNop; + switch (rep) { + case MachineRepresentation::kWord8: + opcode = kCheckedStoreWord8; + break; + case MachineRepresentation::kWord16: + opcode = kCheckedStoreWord16; + break; + case MachineRepresentation::kWord32: + opcode = kCheckedStoreWord32; + break; + case MachineRepresentation::kFloat32: + opcode = kCheckedStoreFloat32; + break; + case MachineRepresentation::kFloat64: + opcode = kCheckedStoreFloat64; + break; + case MachineRepresentation::kBit: // Fall through. + case MachineRepresentation::kTaggedSigned: // Fall through. + case MachineRepresentation::kTaggedPointer: // Fall through. + case MachineRepresentation::kTagged: // Fall through. + case MachineRepresentation::kWord64: // Fall through. + case MachineRepresentation::kSimd128: // Fall through. + case MachineRepresentation::kNone: + UNREACHABLE(); + return; + } + InstructionOperand value_operand = + g.CanBeImmediate(value) ? g.UseImmediate(value) + : ((rep == MachineRepresentation::kWord8 || + rep == MachineRepresentation::kBit) + ? g.UseByteRegister(value) + : g.UseRegister(value)); + InstructionOperand offset_operand = g.UseRegister(offset); + InstructionOperand length_operand = + g.CanBeImmediate(length) ? g.UseImmediate(length) : g.UseRegister(length); + if (g.CanBeImmediate(buffer)) { + Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(), + offset_operand, length_operand, value_operand, offset_operand, + g.UseImmediate(buffer)); + } else { + Emit(opcode | AddressingModeField::encode(kMode_MR1), g.NoOutput(), + offset_operand, length_operand, value_operand, g.UseRegister(buffer), + offset_operand); + } +} + +namespace { + +// Shared routine for multiple binary operations. +void VisitBinop(InstructionSelector* selector, Node* node, + InstructionCode opcode, FlagsContinuation* cont) { + X87OperandGenerator g(selector); + Int32BinopMatcher m(node); + Node* left = m.left().node(); + Node* right = m.right().node(); + InstructionOperand inputs[4]; + size_t input_count = 0; + InstructionOperand outputs[2]; + size_t output_count = 0; + + // TODO(turbofan): match complex addressing modes. + if (left == right) { + // If both inputs refer to the same operand, enforce allocating a register + // for both of them to ensure that we don't end up generating code like + // this: + // + // mov eax, [ebp-0x10] + // add eax, [ebp-0x10] + // jo label + InstructionOperand const input = g.UseRegister(left); + inputs[input_count++] = input; + inputs[input_count++] = input; + } else if (g.CanBeImmediate(right)) { + inputs[input_count++] = g.UseRegister(left); + inputs[input_count++] = g.UseImmediate(right); + } else { + if (node->op()->HasProperty(Operator::kCommutative) && + g.CanBeBetterLeftOperand(right)) { + std::swap(left, right); + } + inputs[input_count++] = g.UseRegister(left); + inputs[input_count++] = g.Use(right); + } + + if (cont->IsBranch()) { + inputs[input_count++] = g.Label(cont->true_block()); + inputs[input_count++] = g.Label(cont->false_block()); + } + + outputs[output_count++] = g.DefineSameAsFirst(node); + if (cont->IsSet()) { + outputs[output_count++] = g.DefineAsRegister(cont->result()); + } + + DCHECK_NE(0u, input_count); + DCHECK_NE(0u, output_count); + DCHECK_GE(arraysize(inputs), input_count); + DCHECK_GE(arraysize(outputs), output_count); + + opcode = cont->Encode(opcode); + if (cont->IsDeoptimize()) { + selector->EmitDeoptimize(opcode, output_count, outputs, input_count, inputs, + cont->kind(), cont->reason(), cont->frame_state()); + } else { + selector->Emit(opcode, output_count, outputs, input_count, inputs); + } +} + + +// Shared routine for multiple binary operations. +void VisitBinop(InstructionSelector* selector, Node* node, + InstructionCode opcode) { + FlagsContinuation cont; + VisitBinop(selector, node, opcode, &cont); +} + +} // namespace + +void InstructionSelector::VisitWord32And(Node* node) { + VisitBinop(this, node, kX87And); +} + + +void InstructionSelector::VisitWord32Or(Node* node) { + VisitBinop(this, node, kX87Or); +} + + +void InstructionSelector::VisitWord32Xor(Node* node) { + X87OperandGenerator g(this); + Int32BinopMatcher m(node); + if (m.right().Is(-1)) { + Emit(kX87Not, g.DefineSameAsFirst(node), g.UseRegister(m.left().node())); + } else { + VisitBinop(this, node, kX87Xor); + } +} + + +// Shared routine for multiple shift operations. +static inline void VisitShift(InstructionSelector* selector, Node* node, + ArchOpcode opcode) { + X87OperandGenerator g(selector); + Node* left = node->InputAt(0); + Node* right = node->InputAt(1); + + if (g.CanBeImmediate(right)) { + selector->Emit(opcode, g.DefineSameAsFirst(node), g.UseRegister(left), + g.UseImmediate(right)); + } else { + selector->Emit(opcode, g.DefineSameAsFirst(node), g.UseRegister(left), + g.UseFixed(right, ecx)); + } +} + + +namespace { + +void VisitMulHigh(InstructionSelector* selector, Node* node, + ArchOpcode opcode) { + X87OperandGenerator g(selector); + InstructionOperand temps[] = {g.TempRegister(eax)}; + selector->Emit( + opcode, g.DefineAsFixed(node, edx), g.UseFixed(node->InputAt(0), eax), + g.UseUniqueRegister(node->InputAt(1)), arraysize(temps), temps); +} + + +void VisitDiv(InstructionSelector* selector, Node* node, ArchOpcode opcode) { + X87OperandGenerator g(selector); + InstructionOperand temps[] = {g.TempRegister(edx)}; + selector->Emit(opcode, g.DefineAsFixed(node, eax), + g.UseFixed(node->InputAt(0), eax), + g.UseUnique(node->InputAt(1)), arraysize(temps), temps); +} + + +void VisitMod(InstructionSelector* selector, Node* node, ArchOpcode opcode) { + X87OperandGenerator g(selector); + InstructionOperand temps[] = {g.TempRegister(eax)}; + selector->Emit(opcode, g.DefineAsFixed(node, edx), + g.UseFixed(node->InputAt(0), eax), + g.UseUnique(node->InputAt(1)), arraysize(temps), temps); +} + +void EmitLea(InstructionSelector* selector, Node* result, Node* index, + int scale, Node* base, Node* displacement, + DisplacementMode displacement_mode) { + X87OperandGenerator g(selector); + InstructionOperand inputs[4]; + size_t input_count = 0; + AddressingMode mode = + g.GenerateMemoryOperandInputs(index, scale, base, displacement, + displacement_mode, inputs, &input_count); + + DCHECK_NE(0u, input_count); + DCHECK_GE(arraysize(inputs), input_count); + + InstructionOperand outputs[1]; + outputs[0] = g.DefineAsRegister(result); + + InstructionCode opcode = AddressingModeField::encode(mode) | kX87Lea; + + selector->Emit(opcode, 1, outputs, input_count, inputs); +} + +} // namespace + + +void InstructionSelector::VisitWord32Shl(Node* node) { + Int32ScaleMatcher m(node, true); + if (m.matches()) { + Node* index = node->InputAt(0); + Node* base = m.power_of_two_plus_one() ? index : nullptr; + EmitLea(this, node, index, m.scale(), base, nullptr, kPositiveDisplacement); + return; + } + VisitShift(this, node, kX87Shl); +} + + +void InstructionSelector::VisitWord32Shr(Node* node) { + VisitShift(this, node, kX87Shr); +} + + +void InstructionSelector::VisitWord32Sar(Node* node) { + VisitShift(this, node, kX87Sar); +} + +void InstructionSelector::VisitInt32PairAdd(Node* node) { + X87OperandGenerator g(this); + + Node* projection1 = NodeProperties::FindProjection(node, 1); + if (projection1) { + // We use UseUniqueRegister here to avoid register sharing with the temp + // register. + InstructionOperand inputs[] = { + g.UseRegister(node->InputAt(0)), g.UseUniqueRegister(node->InputAt(1)), + g.UseRegister(node->InputAt(2)), g.UseUniqueRegister(node->InputAt(3))}; + + InstructionOperand outputs[] = {g.DefineSameAsFirst(node), + g.DefineAsRegister(projection1)}; + + InstructionOperand temps[] = {g.TempRegister()}; + + Emit(kX87AddPair, 2, outputs, 4, inputs, 1, temps); + } else { + // The high word of the result is not used, so we emit the standard 32 bit + // instruction. + Emit(kX87Add, g.DefineSameAsFirst(node), g.UseRegister(node->InputAt(0)), + g.Use(node->InputAt(2))); + } +} + +void InstructionSelector::VisitInt32PairSub(Node* node) { + X87OperandGenerator g(this); + + Node* projection1 = NodeProperties::FindProjection(node, 1); + if (projection1) { + // We use UseUniqueRegister here to avoid register sharing with the temp + // register. + InstructionOperand inputs[] = { + g.UseRegister(node->InputAt(0)), g.UseUniqueRegister(node->InputAt(1)), + g.UseRegister(node->InputAt(2)), g.UseUniqueRegister(node->InputAt(3))}; + + InstructionOperand outputs[] = {g.DefineSameAsFirst(node), + g.DefineAsRegister(projection1)}; + + InstructionOperand temps[] = {g.TempRegister()}; + + Emit(kX87SubPair, 2, outputs, 4, inputs, 1, temps); + } else { + // The high word of the result is not used, so we emit the standard 32 bit + // instruction. + Emit(kX87Sub, g.DefineSameAsFirst(node), g.UseRegister(node->InputAt(0)), + g.Use(node->InputAt(2))); + } +} + +void InstructionSelector::VisitInt32PairMul(Node* node) { + X87OperandGenerator g(this); + + Node* projection1 = NodeProperties::FindProjection(node, 1); + if (projection1) { + // InputAt(3) explicitly shares ecx with OutputRegister(1) to save one + // register and one mov instruction. + InstructionOperand inputs[] = {g.UseUnique(node->InputAt(0)), + g.UseUnique(node->InputAt(1)), + g.UseUniqueRegister(node->InputAt(2)), + g.UseFixed(node->InputAt(3), ecx)}; + + InstructionOperand outputs[] = { + g.DefineAsFixed(node, eax), + g.DefineAsFixed(NodeProperties::FindProjection(node, 1), ecx)}; + + InstructionOperand temps[] = {g.TempRegister(edx)}; + + Emit(kX87MulPair, 2, outputs, 4, inputs, 1, temps); + } else { + // The high word of the result is not used, so we emit the standard 32 bit + // instruction. + Emit(kX87Imul, g.DefineSameAsFirst(node), g.UseRegister(node->InputAt(0)), + g.Use(node->InputAt(2))); + } +} + +void VisitWord32PairShift(InstructionSelector* selector, InstructionCode opcode, + Node* node) { + X87OperandGenerator g(selector); + + Node* shift = node->InputAt(2); + InstructionOperand shift_operand; + if (g.CanBeImmediate(shift)) { + shift_operand = g.UseImmediate(shift); + } else { + shift_operand = g.UseFixed(shift, ecx); + } + InstructionOperand inputs[] = {g.UseFixed(node->InputAt(0), eax), + g.UseFixed(node->InputAt(1), edx), + shift_operand}; + + InstructionOperand outputs[2]; + InstructionOperand temps[1]; + int32_t output_count = 0; + int32_t temp_count = 0; + outputs[output_count++] = g.DefineAsFixed(node, eax); + Node* projection1 = NodeProperties::FindProjection(node, 1); + if (projection1) { + outputs[output_count++] = g.DefineAsFixed(projection1, edx); + } else { + temps[temp_count++] = g.TempRegister(edx); + } + + selector->Emit(opcode, output_count, outputs, 3, inputs, temp_count, temps); +} + +void InstructionSelector::VisitWord32PairShl(Node* node) { + VisitWord32PairShift(this, kX87ShlPair, node); +} + +void InstructionSelector::VisitWord32PairShr(Node* node) { + VisitWord32PairShift(this, kX87ShrPair, node); +} + +void InstructionSelector::VisitWord32PairSar(Node* node) { + VisitWord32PairShift(this, kX87SarPair, node); +} + +void InstructionSelector::VisitWord32Ror(Node* node) { + VisitShift(this, node, kX87Ror); +} + + +void InstructionSelector::VisitWord32Clz(Node* node) { + X87OperandGenerator g(this); + Emit(kX87Lzcnt, g.DefineAsRegister(node), g.Use(node->InputAt(0))); +} + + +void InstructionSelector::VisitWord32Ctz(Node* node) { UNREACHABLE(); } + + +void InstructionSelector::VisitWord32ReverseBits(Node* node) { UNREACHABLE(); } + +void InstructionSelector::VisitWord64ReverseBytes(Node* node) { UNREACHABLE(); } + +void InstructionSelector::VisitWord32ReverseBytes(Node* node) { UNREACHABLE(); } + +void InstructionSelector::VisitWord32Popcnt(Node* node) { + X87OperandGenerator g(this); + Emit(kX87Popcnt, g.DefineAsRegister(node), g.Use(node->InputAt(0))); +} + + +void InstructionSelector::VisitInt32Add(Node* node) { + X87OperandGenerator g(this); + + // Try to match the Add to a lea pattern + BaseWithIndexAndDisplacement32Matcher m(node); + if (m.matches() && + (m.displacement() == nullptr || g.CanBeImmediate(m.displacement()))) { + InstructionOperand inputs[4]; + size_t input_count = 0; + AddressingMode mode = g.GenerateMemoryOperandInputs( + m.index(), m.scale(), m.base(), m.displacement(), m.displacement_mode(), + inputs, &input_count); + + DCHECK_NE(0u, input_count); + DCHECK_GE(arraysize(inputs), input_count); + + InstructionOperand outputs[1]; + outputs[0] = g.DefineAsRegister(node); + + InstructionCode opcode = AddressingModeField::encode(mode) | kX87Lea; + Emit(opcode, 1, outputs, input_count, inputs); + return; + } + + // No lea pattern match, use add + VisitBinop(this, node, kX87Add); +} + + +void InstructionSelector::VisitInt32Sub(Node* node) { + X87OperandGenerator g(this); + Int32BinopMatcher m(node); + if (m.left().Is(0)) { + Emit(kX87Neg, g.DefineSameAsFirst(node), g.Use(m.right().node())); + } else { + VisitBinop(this, node, kX87Sub); + } +} + + +void InstructionSelector::VisitInt32Mul(Node* node) { + Int32ScaleMatcher m(node, true); + if (m.matches()) { + Node* index = node->InputAt(0); + Node* base = m.power_of_two_plus_one() ? index : nullptr; + EmitLea(this, node, index, m.scale(), base, nullptr, kPositiveDisplacement); + return; + } + X87OperandGenerator g(this); + Node* left = node->InputAt(0); + Node* right = node->InputAt(1); + if (g.CanBeImmediate(right)) { + Emit(kX87Imul, g.DefineAsRegister(node), g.Use(left), + g.UseImmediate(right)); + } else { + if (g.CanBeBetterLeftOperand(right)) { + std::swap(left, right); + } + Emit(kX87Imul, g.DefineSameAsFirst(node), g.UseRegister(left), + g.Use(right)); + } +} + + +void InstructionSelector::VisitInt32MulHigh(Node* node) { + VisitMulHigh(this, node, kX87ImulHigh); +} + + +void InstructionSelector::VisitUint32MulHigh(Node* node) { + VisitMulHigh(this, node, kX87UmulHigh); +} + + +void InstructionSelector::VisitInt32Div(Node* node) { + VisitDiv(this, node, kX87Idiv); +} + + +void InstructionSelector::VisitUint32Div(Node* node) { + VisitDiv(this, node, kX87Udiv); +} + + +void InstructionSelector::VisitInt32Mod(Node* node) { + VisitMod(this, node, kX87Idiv); +} + + +void InstructionSelector::VisitUint32Mod(Node* node) { + VisitMod(this, node, kX87Udiv); +} + + +void InstructionSelector::VisitChangeFloat32ToFloat64(Node* node) { + X87OperandGenerator g(this); + Emit(kX87Float32ToFloat64, g.DefineAsFixed(node, stX_0), + g.Use(node->InputAt(0))); +} + + +void InstructionSelector::VisitRoundInt32ToFloat32(Node* node) { + X87OperandGenerator g(this); + Emit(kX87Int32ToFloat32, g.DefineAsFixed(node, stX_0), + g.Use(node->InputAt(0))); +} + + +void InstructionSelector::VisitRoundUint32ToFloat32(Node* node) { + X87OperandGenerator g(this); + Emit(kX87Uint32ToFloat32, g.DefineAsFixed(node, stX_0), + g.Use(node->InputAt(0))); +} + + +void InstructionSelector::VisitChangeInt32ToFloat64(Node* node) { + X87OperandGenerator g(this); + Emit(kX87Int32ToFloat64, g.DefineAsFixed(node, stX_0), + g.Use(node->InputAt(0))); +} + + +void InstructionSelector::VisitChangeUint32ToFloat64(Node* node) { + X87OperandGenerator g(this); + Emit(kX87Uint32ToFloat64, g.DefineAsFixed(node, stX_0), + g.UseRegister(node->InputAt(0))); +} + + +void InstructionSelector::VisitTruncateFloat32ToInt32(Node* node) { + X87OperandGenerator g(this); + Emit(kX87Float32ToInt32, g.DefineAsRegister(node), g.Use(node->InputAt(0))); +} + + +void InstructionSelector::VisitTruncateFloat32ToUint32(Node* node) { + X87OperandGenerator g(this); + Emit(kX87Float32ToUint32, g.DefineAsRegister(node), g.Use(node->InputAt(0))); +} + + +void InstructionSelector::VisitChangeFloat64ToInt32(Node* node) { + X87OperandGenerator g(this); + Emit(kX87Float64ToInt32, g.DefineAsRegister(node), g.Use(node->InputAt(0))); +} + + +void InstructionSelector::VisitChangeFloat64ToUint32(Node* node) { + X87OperandGenerator g(this); + Emit(kX87Float64ToUint32, g.DefineAsRegister(node), g.Use(node->InputAt(0))); +} + +void InstructionSelector::VisitTruncateFloat64ToUint32(Node* node) { + X87OperandGenerator g(this); + Emit(kX87Float64ToUint32, g.DefineAsRegister(node), g.Use(node->InputAt(0))); +} + +void InstructionSelector::VisitTruncateFloat64ToFloat32(Node* node) { + X87OperandGenerator g(this); + Emit(kX87Float64ToFloat32, g.DefineAsFixed(node, stX_0), + g.Use(node->InputAt(0))); +} + +void InstructionSelector::VisitTruncateFloat64ToWord32(Node* node) { + X87OperandGenerator g(this); + Emit(kArchTruncateDoubleToI, g.DefineAsRegister(node), + g.Use(node->InputAt(0))); +} + +void InstructionSelector::VisitRoundFloat64ToInt32(Node* node) { + X87OperandGenerator g(this); + Emit(kX87Float64ToInt32, g.DefineAsRegister(node), g.Use(node->InputAt(0))); +} + + +void InstructionSelector::VisitBitcastFloat32ToInt32(Node* node) { + X87OperandGenerator g(this); + Emit(kX87PushFloat32, g.NoOutput(), g.Use(node->InputAt(0))); + Emit(kX87BitcastFI, g.DefineAsRegister(node), 0, nullptr); +} + + +void InstructionSelector::VisitBitcastInt32ToFloat32(Node* node) { + X87OperandGenerator g(this); + Emit(kX87BitcastIF, g.DefineAsFixed(node, stX_0), g.Use(node->InputAt(0))); +} + + +void InstructionSelector::VisitFloat32Add(Node* node) { + X87OperandGenerator g(this); + Emit(kX87PushFloat32, g.NoOutput(), g.Use(node->InputAt(0))); + Emit(kX87PushFloat32, g.NoOutput(), g.Use(node->InputAt(1))); + Emit(kX87Float32Add, g.DefineAsFixed(node, stX_0), 0, nullptr); +} + + +void InstructionSelector::VisitFloat64Add(Node* node) { + X87OperandGenerator g(this); + Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(0))); + Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(1))); + Emit(kX87Float64Add, g.DefineAsFixed(node, stX_0), 0, nullptr); +} + + +void InstructionSelector::VisitFloat32Sub(Node* node) { + X87OperandGenerator g(this); + Emit(kX87PushFloat32, g.NoOutput(), g.Use(node->InputAt(0))); + Emit(kX87PushFloat32, g.NoOutput(), g.Use(node->InputAt(1))); + Emit(kX87Float32Sub, g.DefineAsFixed(node, stX_0), 0, nullptr); +} + +void InstructionSelector::VisitFloat64Sub(Node* node) { + X87OperandGenerator g(this); + Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(0))); + Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(1))); + Emit(kX87Float64Sub, g.DefineAsFixed(node, stX_0), 0, nullptr); +} + +void InstructionSelector::VisitFloat32Mul(Node* node) { + X87OperandGenerator g(this); + Emit(kX87PushFloat32, g.NoOutput(), g.Use(node->InputAt(0))); + Emit(kX87PushFloat32, g.NoOutput(), g.Use(node->InputAt(1))); + Emit(kX87Float32Mul, g.DefineAsFixed(node, stX_0), 0, nullptr); +} + + +void InstructionSelector::VisitFloat64Mul(Node* node) { + X87OperandGenerator g(this); + Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(0))); + Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(1))); + Emit(kX87Float64Mul, g.DefineAsFixed(node, stX_0), 0, nullptr); +} + + +void InstructionSelector::VisitFloat32Div(Node* node) { + X87OperandGenerator g(this); + Emit(kX87PushFloat32, g.NoOutput(), g.Use(node->InputAt(0))); + Emit(kX87PushFloat32, g.NoOutput(), g.Use(node->InputAt(1))); + Emit(kX87Float32Div, g.DefineAsFixed(node, stX_0), 0, nullptr); +} + + +void InstructionSelector::VisitFloat64Div(Node* node) { + X87OperandGenerator g(this); + Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(0))); + Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(1))); + Emit(kX87Float64Div, g.DefineAsFixed(node, stX_0), 0, nullptr); +} + + +void InstructionSelector::VisitFloat64Mod(Node* node) { + X87OperandGenerator g(this); + InstructionOperand temps[] = {g.TempRegister(eax)}; + Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(0))); + Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(1))); + Emit(kX87Float64Mod, g.DefineAsFixed(node, stX_0), 1, temps)->MarkAsCall(); +} + +void InstructionSelector::VisitFloat32Max(Node* node) { + X87OperandGenerator g(this); + Emit(kX87PushFloat32, g.NoOutput(), g.Use(node->InputAt(0))); + Emit(kX87PushFloat32, g.NoOutput(), g.Use(node->InputAt(1))); + Emit(kX87Float32Max, g.DefineAsFixed(node, stX_0), 0, nullptr); +} + +void InstructionSelector::VisitFloat64Max(Node* node) { + X87OperandGenerator g(this); + Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(0))); + Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(1))); + Emit(kX87Float64Max, g.DefineAsFixed(node, stX_0), 0, nullptr); +} + +void InstructionSelector::VisitFloat32Min(Node* node) { + X87OperandGenerator g(this); + Emit(kX87PushFloat32, g.NoOutput(), g.Use(node->InputAt(0))); + Emit(kX87PushFloat32, g.NoOutput(), g.Use(node->InputAt(1))); + Emit(kX87Float32Min, g.DefineAsFixed(node, stX_0), 0, nullptr); +} + +void InstructionSelector::VisitFloat64Min(Node* node) { + X87OperandGenerator g(this); + Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(0))); + Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(1))); + Emit(kX87Float64Min, g.DefineAsFixed(node, stX_0), 0, nullptr); +} + + +void InstructionSelector::VisitFloat32Abs(Node* node) { + X87OperandGenerator g(this); + Emit(kX87PushFloat32, g.NoOutput(), g.Use(node->InputAt(0))); + Emit(kX87Float32Abs, g.DefineAsFixed(node, stX_0), 0, nullptr); +} + + +void InstructionSelector::VisitFloat64Abs(Node* node) { + X87OperandGenerator g(this); + Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(0))); + Emit(kX87Float64Abs, g.DefineAsFixed(node, stX_0), 0, nullptr); +} + +void InstructionSelector::VisitFloat32Sqrt(Node* node) { + X87OperandGenerator g(this); + Emit(kX87PushFloat32, g.NoOutput(), g.Use(node->InputAt(0))); + Emit(kX87Float32Sqrt, g.DefineAsFixed(node, stX_0), 0, nullptr); +} + + +void InstructionSelector::VisitFloat64Sqrt(Node* node) { + X87OperandGenerator g(this); + Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(0))); + Emit(kX87Float64Sqrt, g.DefineAsFixed(node, stX_0), 0, nullptr); +} + + +void InstructionSelector::VisitFloat32RoundDown(Node* node) { + X87OperandGenerator g(this); + Emit(kX87Float32Round | MiscField::encode(kRoundDown), + g.UseFixed(node, stX_0), g.Use(node->InputAt(0))); +} + + +void InstructionSelector::VisitFloat64RoundDown(Node* node) { + X87OperandGenerator g(this); + Emit(kX87Float64Round | MiscField::encode(kRoundDown), + g.UseFixed(node, stX_0), g.Use(node->InputAt(0))); +} + + +void InstructionSelector::VisitFloat32RoundUp(Node* node) { + X87OperandGenerator g(this); + Emit(kX87Float32Round | MiscField::encode(kRoundUp), g.UseFixed(node, stX_0), + g.Use(node->InputAt(0))); +} + + +void InstructionSelector::VisitFloat64RoundUp(Node* node) { + X87OperandGenerator g(this); + Emit(kX87Float64Round | MiscField::encode(kRoundUp), g.UseFixed(node, stX_0), + g.Use(node->InputAt(0))); +} + + +void InstructionSelector::VisitFloat32RoundTruncate(Node* node) { + X87OperandGenerator g(this); + Emit(kX87Float32Round | MiscField::encode(kRoundToZero), + g.UseFixed(node, stX_0), g.Use(node->InputAt(0))); +} + + +void InstructionSelector::VisitFloat64RoundTruncate(Node* node) { + X87OperandGenerator g(this); + Emit(kX87Float64Round | MiscField::encode(kRoundToZero), + g.UseFixed(node, stX_0), g.Use(node->InputAt(0))); +} + + +void InstructionSelector::VisitFloat64RoundTiesAway(Node* node) { + UNREACHABLE(); +} + + +void InstructionSelector::VisitFloat32RoundTiesEven(Node* node) { + X87OperandGenerator g(this); + Emit(kX87Float32Round | MiscField::encode(kRoundToNearest), + g.UseFixed(node, stX_0), g.Use(node->InputAt(0))); +} + + +void InstructionSelector::VisitFloat64RoundTiesEven(Node* node) { + X87OperandGenerator g(this); + Emit(kX87Float64Round | MiscField::encode(kRoundToNearest), + g.UseFixed(node, stX_0), g.Use(node->InputAt(0))); +} + +void InstructionSelector::VisitFloat32Neg(Node* node) { + X87OperandGenerator g(this); + Emit(kX87PushFloat32, g.NoOutput(), g.Use(node->InputAt(0))); + Emit(kX87Float32Neg, g.DefineAsFixed(node, stX_0), 0, nullptr); +} + +void InstructionSelector::VisitFloat64Neg(Node* node) { + X87OperandGenerator g(this); + Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(0))); + Emit(kX87Float64Neg, g.DefineAsFixed(node, stX_0), 0, nullptr); +} + +void InstructionSelector::VisitFloat64Ieee754Binop(Node* node, + InstructionCode opcode) { + X87OperandGenerator g(this); + Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(0))); + Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(1))); + Emit(opcode, g.DefineAsFixed(node, stX_0), 0, nullptr)->MarkAsCall(); +} + +void InstructionSelector::VisitFloat64Ieee754Unop(Node* node, + InstructionCode opcode) { + X87OperandGenerator g(this); + Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(0))); + Emit(opcode, g.DefineAsFixed(node, stX_0), 0, nullptr)->MarkAsCall(); +} + +void InstructionSelector::EmitPrepareArguments( + ZoneVector* arguments, const CallDescriptor* descriptor, + Node* node) { + X87OperandGenerator g(this); + + // Prepare for C function call. + if (descriptor->IsCFunctionCall()) { + InstructionOperand temps[] = {g.TempRegister()}; + size_t const temp_count = arraysize(temps); + Emit(kArchPrepareCallCFunction | + MiscField::encode(static_cast(descriptor->ParameterCount())), + 0, nullptr, 0, nullptr, temp_count, temps); + + // Poke any stack arguments. + for (size_t n = 0; n < arguments->size(); ++n) { + PushParameter input = (*arguments)[n]; + if (input.node()) { + int const slot = static_cast(n); + InstructionOperand value = g.CanBeImmediate(input.node()) + ? g.UseImmediate(input.node()) + : g.UseRegister(input.node()); + Emit(kX87Poke | MiscField::encode(slot), g.NoOutput(), value); + } + } + } else { + // Push any stack arguments. + for (PushParameter input : base::Reversed(*arguments)) { + // TODO(titzer): handle pushing double parameters. + if (input.node() == nullptr) continue; + InstructionOperand value = + g.CanBeImmediate(input.node()) + ? g.UseImmediate(input.node()) + : IsSupported(ATOM) || + sequence()->IsFP(GetVirtualRegister(input.node())) + ? g.UseRegister(input.node()) + : g.Use(input.node()); + Emit(kX87Push, g.NoOutput(), value); + } + } +} + + +bool InstructionSelector::IsTailCallAddressImmediate() { return true; } + +int InstructionSelector::GetTempsCountForTailCallFromJSFunction() { return 0; } + +namespace { + +void VisitCompareWithMemoryOperand(InstructionSelector* selector, + InstructionCode opcode, Node* left, + InstructionOperand right, + FlagsContinuation* cont) { + DCHECK(left->opcode() == IrOpcode::kLoad); + X87OperandGenerator g(selector); + size_t input_count = 0; + InstructionOperand inputs[6]; + AddressingMode addressing_mode = + g.GetEffectiveAddressMemoryOperand(left, inputs, &input_count); + opcode |= AddressingModeField::encode(addressing_mode); + opcode = cont->Encode(opcode); + inputs[input_count++] = right; + + if (cont->IsBranch()) { + inputs[input_count++] = g.Label(cont->true_block()); + inputs[input_count++] = g.Label(cont->false_block()); + selector->Emit(opcode, 0, nullptr, input_count, inputs); + } else if (cont->IsDeoptimize()) { + selector->EmitDeoptimize(opcode, 0, nullptr, input_count, inputs, + cont->kind(), cont->reason(), cont->frame_state()); + } else if (cont->IsSet()) { + InstructionOperand output = g.DefineAsRegister(cont->result()); + selector->Emit(opcode, 1, &output, input_count, inputs); + } else { + DCHECK(cont->IsTrap()); + inputs[input_count++] = g.UseImmediate(cont->trap_id()); + selector->Emit(opcode, 0, nullptr, input_count, inputs); + } +} + +// Shared routine for multiple compare operations. +void VisitCompare(InstructionSelector* selector, InstructionCode opcode, + InstructionOperand left, InstructionOperand right, + FlagsContinuation* cont) { + X87OperandGenerator g(selector); + opcode = cont->Encode(opcode); + if (cont->IsBranch()) { + selector->Emit(opcode, g.NoOutput(), left, right, + g.Label(cont->true_block()), g.Label(cont->false_block())); + } else if (cont->IsDeoptimize()) { + selector->EmitDeoptimize(opcode, g.NoOutput(), left, right, cont->kind(), + cont->reason(), cont->frame_state()); + } else if (cont->IsSet()) { + selector->Emit(opcode, g.DefineAsByteRegister(cont->result()), left, right); + } else { + DCHECK(cont->IsTrap()); + selector->Emit(opcode, g.NoOutput(), left, right, + g.UseImmediate(cont->trap_id())); + } +} + + +// Shared routine for multiple compare operations. +void VisitCompare(InstructionSelector* selector, InstructionCode opcode, + Node* left, Node* right, FlagsContinuation* cont, + bool commutative) { + X87OperandGenerator g(selector); + if (commutative && g.CanBeBetterLeftOperand(right)) { + std::swap(left, right); + } + VisitCompare(selector, opcode, g.UseRegister(left), g.Use(right), cont); +} + +MachineType MachineTypeForNarrow(Node* node, Node* hint_node) { + if (hint_node->opcode() == IrOpcode::kLoad) { + MachineType hint = LoadRepresentationOf(hint_node->op()); + if (node->opcode() == IrOpcode::kInt32Constant || + node->opcode() == IrOpcode::kInt64Constant) { + int64_t constant = node->opcode() == IrOpcode::kInt32Constant + ? OpParameter(node) + : OpParameter(node); + if (hint == MachineType::Int8()) { + if (constant >= std::numeric_limits::min() && + constant <= std::numeric_limits::max()) { + return hint; + } + } else if (hint == MachineType::Uint8()) { + if (constant >= std::numeric_limits::min() && + constant <= std::numeric_limits::max()) { + return hint; + } + } else if (hint == MachineType::Int16()) { + if (constant >= std::numeric_limits::min() && + constant <= std::numeric_limits::max()) { + return hint; + } + } else if (hint == MachineType::Uint16()) { + if (constant >= std::numeric_limits::min() && + constant <= std::numeric_limits::max()) { + return hint; + } + } else if (hint == MachineType::Int32()) { + return hint; + } else if (hint == MachineType::Uint32()) { + if (constant >= 0) return hint; + } + } + } + return node->opcode() == IrOpcode::kLoad ? LoadRepresentationOf(node->op()) + : MachineType::None(); +} + +// Tries to match the size of the given opcode to that of the operands, if +// possible. +InstructionCode TryNarrowOpcodeSize(InstructionCode opcode, Node* left, + Node* right, FlagsContinuation* cont) { + // TODO(epertoso): we can probably get some size information out of phi nodes. + // If the load representations don't match, both operands will be + // zero/sign-extended to 32bit. + MachineType left_type = MachineTypeForNarrow(left, right); + MachineType right_type = MachineTypeForNarrow(right, left); + if (left_type == right_type) { + switch (left_type.representation()) { + case MachineRepresentation::kBit: + case MachineRepresentation::kWord8: { + if (opcode == kX87Test) return kX87Test8; + if (opcode == kX87Cmp) { + if (left_type.semantic() == MachineSemantic::kUint32) { + cont->OverwriteUnsignedIfSigned(); + } else { + CHECK_EQ(MachineSemantic::kInt32, left_type.semantic()); + } + return kX87Cmp8; + } + break; + } + case MachineRepresentation::kWord16: + if (opcode == kX87Test) return kX87Test16; + if (opcode == kX87Cmp) { + if (left_type.semantic() == MachineSemantic::kUint32) { + cont->OverwriteUnsignedIfSigned(); + } else { + CHECK_EQ(MachineSemantic::kInt32, left_type.semantic()); + } + return kX87Cmp16; + } + break; + default: + break; + } + } + return opcode; +} + +// Shared routine for multiple float32 compare operations (inputs commuted). +void VisitFloat32Compare(InstructionSelector* selector, Node* node, + FlagsContinuation* cont) { + X87OperandGenerator g(selector); + selector->Emit(kX87PushFloat32, g.NoOutput(), g.Use(node->InputAt(0))); + selector->Emit(kX87PushFloat32, g.NoOutput(), g.Use(node->InputAt(1))); + if (cont->IsBranch()) { + selector->Emit(cont->Encode(kX87Float32Cmp), g.NoOutput(), + g.Label(cont->true_block()), g.Label(cont->false_block())); + } else if (cont->IsDeoptimize()) { + selector->EmitDeoptimize(cont->Encode(kX87Float32Cmp), g.NoOutput(), + g.Use(node->InputAt(0)), g.Use(node->InputAt(1)), + cont->kind(), cont->reason(), cont->frame_state()); + } else if (cont->IsSet()) { + selector->Emit(cont->Encode(kX87Float32Cmp), + g.DefineAsByteRegister(cont->result())); + } else { + DCHECK(cont->IsTrap()); + selector->Emit(cont->Encode(kX87Float32Cmp), g.NoOutput(), + g.UseImmediate(cont->trap_id())); + } +} + + +// Shared routine for multiple float64 compare operations (inputs commuted). +void VisitFloat64Compare(InstructionSelector* selector, Node* node, + FlagsContinuation* cont) { + X87OperandGenerator g(selector); + selector->Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(0))); + selector->Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(1))); + if (cont->IsBranch()) { + selector->Emit(cont->Encode(kX87Float64Cmp), g.NoOutput(), + g.Label(cont->true_block()), g.Label(cont->false_block())); + } else if (cont->IsDeoptimize()) { + selector->EmitDeoptimize(cont->Encode(kX87Float64Cmp), g.NoOutput(), + g.Use(node->InputAt(0)), g.Use(node->InputAt(1)), + cont->kind(), cont->reason(), cont->frame_state()); + } else if (cont->IsSet()) { + selector->Emit(cont->Encode(kX87Float64Cmp), + g.DefineAsByteRegister(cont->result())); + } else { + DCHECK(cont->IsTrap()); + selector->Emit(cont->Encode(kX87Float64Cmp), g.NoOutput(), + g.UseImmediate(cont->trap_id())); + } +} + +// Shared routine for multiple word compare operations. +void VisitWordCompare(InstructionSelector* selector, Node* node, + InstructionCode opcode, FlagsContinuation* cont) { + X87OperandGenerator g(selector); + Node* left = node->InputAt(0); + Node* right = node->InputAt(1); + + InstructionCode narrowed_opcode = + TryNarrowOpcodeSize(opcode, left, right, cont); + + int effect_level = selector->GetEffectLevel(node); + if (cont->IsBranch()) { + effect_level = selector->GetEffectLevel( + cont->true_block()->PredecessorAt(0)->control_input()); + } + + // If one of the two inputs is an immediate, make sure it's on the right, or + // if one of the two inputs is a memory operand, make sure it's on the left. + if ((!g.CanBeImmediate(right) && g.CanBeImmediate(left)) || + (g.CanBeMemoryOperand(narrowed_opcode, node, right, effect_level) && + !g.CanBeMemoryOperand(narrowed_opcode, node, left, effect_level))) { + if (!node->op()->HasProperty(Operator::kCommutative)) cont->Commute(); + std::swap(left, right); + } + + // Match immediates on right side of comparison. + if (g.CanBeImmediate(right)) { + if (g.CanBeMemoryOperand(narrowed_opcode, node, left, effect_level)) { + return VisitCompareWithMemoryOperand(selector, narrowed_opcode, left, + g.UseImmediate(right), cont); + } + return VisitCompare(selector, opcode, g.Use(left), g.UseImmediate(right), + cont); + } + + // Match memory operands on left side of comparison. + if (g.CanBeMemoryOperand(narrowed_opcode, node, left, effect_level)) { + bool needs_byte_register = + narrowed_opcode == kX87Test8 || narrowed_opcode == kX87Cmp8; + return VisitCompareWithMemoryOperand( + selector, narrowed_opcode, left, + needs_byte_register ? g.UseByteRegister(right) : g.UseRegister(right), + cont); + } + + if (g.CanBeBetterLeftOperand(right)) { + if (!node->op()->HasProperty(Operator::kCommutative)) cont->Commute(); + std::swap(left, right); + } + + return VisitCompare(selector, opcode, left, right, cont, + node->op()->HasProperty(Operator::kCommutative)); +} + +void VisitWordCompare(InstructionSelector* selector, Node* node, + FlagsContinuation* cont) { + X87OperandGenerator g(selector); + Int32BinopMatcher m(node); + if (m.left().IsLoad() && m.right().IsLoadStackPointer()) { + LoadMatcher mleft(m.left().node()); + ExternalReference js_stack_limit = + ExternalReference::address_of_stack_limit(selector->isolate()); + if (mleft.object().Is(js_stack_limit) && mleft.index().Is(0)) { + // Compare(Load(js_stack_limit), LoadStackPointer) + if (!node->op()->HasProperty(Operator::kCommutative)) cont->Commute(); + InstructionCode opcode = cont->Encode(kX87StackCheck); + if (cont->IsBranch()) { + selector->Emit(opcode, g.NoOutput(), g.Label(cont->true_block()), + g.Label(cont->false_block())); + } else if (cont->IsDeoptimize()) { + selector->EmitDeoptimize(opcode, 0, nullptr, 0, nullptr, cont->kind(), + cont->reason(), cont->frame_state()); + } else { + DCHECK(cont->IsSet()); + selector->Emit(opcode, g.DefineAsRegister(cont->result())); + } + return; + } + } + VisitWordCompare(selector, node, kX87Cmp, cont); +} + + +// Shared routine for word comparison with zero. +void VisitWordCompareZero(InstructionSelector* selector, Node* user, + Node* value, FlagsContinuation* cont) { + // Try to combine with comparisons against 0 by simply inverting the branch. + while (value->opcode() == IrOpcode::kWord32Equal && + selector->CanCover(user, value)) { + Int32BinopMatcher m(value); + if (!m.right().Is(0)) break; + + user = value; + value = m.left().node(); + cont->Negate(); + } + + if (selector->CanCover(user, value)) { + switch (value->opcode()) { + case IrOpcode::kWord32Equal: + cont->OverwriteAndNegateIfEqual(kEqual); + return VisitWordCompare(selector, value, cont); + case IrOpcode::kInt32LessThan: + cont->OverwriteAndNegateIfEqual(kSignedLessThan); + return VisitWordCompare(selector, value, cont); + case IrOpcode::kInt32LessThanOrEqual: + cont->OverwriteAndNegateIfEqual(kSignedLessThanOrEqual); + return VisitWordCompare(selector, value, cont); + case IrOpcode::kUint32LessThan: + cont->OverwriteAndNegateIfEqual(kUnsignedLessThan); + return VisitWordCompare(selector, value, cont); + case IrOpcode::kUint32LessThanOrEqual: + cont->OverwriteAndNegateIfEqual(kUnsignedLessThanOrEqual); + return VisitWordCompare(selector, value, cont); + case IrOpcode::kFloat32Equal: + cont->OverwriteAndNegateIfEqual(kUnorderedEqual); + return VisitFloat32Compare(selector, value, cont); + case IrOpcode::kFloat32LessThan: + cont->OverwriteAndNegateIfEqual(kUnsignedGreaterThan); + return VisitFloat32Compare(selector, value, cont); + case IrOpcode::kFloat32LessThanOrEqual: + cont->OverwriteAndNegateIfEqual(kUnsignedGreaterThanOrEqual); + return VisitFloat32Compare(selector, value, cont); + case IrOpcode::kFloat64Equal: + cont->OverwriteAndNegateIfEqual(kUnorderedEqual); + return VisitFloat64Compare(selector, value, cont); + case IrOpcode::kFloat64LessThan: + cont->OverwriteAndNegateIfEqual(kUnsignedGreaterThan); + return VisitFloat64Compare(selector, value, cont); + case IrOpcode::kFloat64LessThanOrEqual: + cont->OverwriteAndNegateIfEqual(kUnsignedGreaterThanOrEqual); + return VisitFloat64Compare(selector, value, cont); + case IrOpcode::kProjection: + // Check if this is the overflow output projection of an + // WithOverflow node. + if (ProjectionIndexOf(value->op()) == 1u) { + // We cannot combine the WithOverflow with this branch + // unless the 0th projection (the use of the actual value of the + // is either nullptr, which means there's no use of the + // actual value, or was already defined, which means it is scheduled + // *AFTER* this branch). + Node* const node = value->InputAt(0); + Node* const result = NodeProperties::FindProjection(node, 0); + if (result == nullptr || selector->IsDefined(result)) { + switch (node->opcode()) { + case IrOpcode::kInt32AddWithOverflow: + cont->OverwriteAndNegateIfEqual(kOverflow); + return VisitBinop(selector, node, kX87Add, cont); + case IrOpcode::kInt32SubWithOverflow: + cont->OverwriteAndNegateIfEqual(kOverflow); + return VisitBinop(selector, node, kX87Sub, cont); + case IrOpcode::kInt32MulWithOverflow: + cont->OverwriteAndNegateIfEqual(kOverflow); + return VisitBinop(selector, node, kX87Imul, cont); + default: + break; + } + } + } + break; + case IrOpcode::kInt32Sub: + return VisitWordCompare(selector, value, cont); + case IrOpcode::kWord32And: + return VisitWordCompare(selector, value, kX87Test, cont); + default: + break; + } + } + + // Continuation could not be combined with a compare, emit compare against 0. + X87OperandGenerator g(selector); + VisitCompare(selector, kX87Cmp, g.Use(value), g.TempImmediate(0), cont); +} + +} // namespace + + +void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch, + BasicBlock* fbranch) { + FlagsContinuation cont(kNotEqual, tbranch, fbranch); + VisitWordCompareZero(this, branch, branch->InputAt(0), &cont); +} + +void InstructionSelector::VisitDeoptimizeIf(Node* node) { + DeoptimizeParameters p = DeoptimizeParametersOf(node->op()); + FlagsContinuation cont = FlagsContinuation::ForDeoptimize( + kNotEqual, p.kind(), p.reason(), node->InputAt(1)); + VisitWordCompareZero(this, node, node->InputAt(0), &cont); +} + +void InstructionSelector::VisitDeoptimizeUnless(Node* node) { + DeoptimizeParameters p = DeoptimizeParametersOf(node->op()); + FlagsContinuation cont = FlagsContinuation::ForDeoptimize( + kEqual, p.kind(), p.reason(), node->InputAt(1)); + VisitWordCompareZero(this, node, node->InputAt(0), &cont); +} + +void InstructionSelector::VisitTrapIf(Node* node, Runtime::FunctionId func_id) { + FlagsContinuation cont = + FlagsContinuation::ForTrap(kNotEqual, func_id, node->InputAt(1)); + VisitWordCompareZero(this, node, node->InputAt(0), &cont); +} + +void InstructionSelector::VisitTrapUnless(Node* node, + Runtime::FunctionId func_id) { + FlagsContinuation cont = + FlagsContinuation::ForTrap(kEqual, func_id, node->InputAt(1)); + VisitWordCompareZero(this, node, node->InputAt(0), &cont); +} + +void InstructionSelector::VisitSwitch(Node* node, const SwitchInfo& sw) { + X87OperandGenerator g(this); + InstructionOperand value_operand = g.UseRegister(node->InputAt(0)); + + // Emit either ArchTableSwitch or ArchLookupSwitch. + static const size_t kMaxTableSwitchValueRange = 2 << 16; + size_t table_space_cost = 4 + sw.value_range; + size_t table_time_cost = 3; + size_t lookup_space_cost = 3 + 2 * sw.case_count; + size_t lookup_time_cost = sw.case_count; + if (sw.case_count > 4 && + table_space_cost + 3 * table_time_cost <= + lookup_space_cost + 3 * lookup_time_cost && + sw.min_value > std::numeric_limits::min() && + sw.value_range <= kMaxTableSwitchValueRange) { + InstructionOperand index_operand = value_operand; + if (sw.min_value) { + index_operand = g.TempRegister(); + Emit(kX87Lea | AddressingModeField::encode(kMode_MRI), index_operand, + value_operand, g.TempImmediate(-sw.min_value)); + } + // Generate a table lookup. + return EmitTableSwitch(sw, index_operand); + } + + // Generate a sequence of conditional jumps. + return EmitLookupSwitch(sw, value_operand); +} + + +void InstructionSelector::VisitWord32Equal(Node* const node) { + FlagsContinuation cont = FlagsContinuation::ForSet(kEqual, node); + Int32BinopMatcher m(node); + if (m.right().Is(0)) { + return VisitWordCompareZero(this, m.node(), m.left().node(), &cont); + } + VisitWordCompare(this, node, &cont); +} + + +void InstructionSelector::VisitInt32LessThan(Node* node) { + FlagsContinuation cont = FlagsContinuation::ForSet(kSignedLessThan, node); + VisitWordCompare(this, node, &cont); +} + + +void InstructionSelector::VisitInt32LessThanOrEqual(Node* node) { + FlagsContinuation cont = + FlagsContinuation::ForSet(kSignedLessThanOrEqual, node); + VisitWordCompare(this, node, &cont); +} + + +void InstructionSelector::VisitUint32LessThan(Node* node) { + FlagsContinuation cont = FlagsContinuation::ForSet(kUnsignedLessThan, node); + VisitWordCompare(this, node, &cont); +} + + +void InstructionSelector::VisitUint32LessThanOrEqual(Node* node) { + FlagsContinuation cont = + FlagsContinuation::ForSet(kUnsignedLessThanOrEqual, node); + VisitWordCompare(this, node, &cont); +} + + +void InstructionSelector::VisitInt32AddWithOverflow(Node* node) { + if (Node* ovf = NodeProperties::FindProjection(node, 1)) { + FlagsContinuation cont = FlagsContinuation::ForSet(kOverflow, ovf); + return VisitBinop(this, node, kX87Add, &cont); + } + FlagsContinuation cont; + VisitBinop(this, node, kX87Add, &cont); +} + + +void InstructionSelector::VisitInt32SubWithOverflow(Node* node) { + if (Node* ovf = NodeProperties::FindProjection(node, 1)) { + FlagsContinuation cont = FlagsContinuation::ForSet(kOverflow, ovf); + return VisitBinop(this, node, kX87Sub, &cont); + } + FlagsContinuation cont; + VisitBinop(this, node, kX87Sub, &cont); +} + +void InstructionSelector::VisitInt32MulWithOverflow(Node* node) { + if (Node* ovf = NodeProperties::FindProjection(node, 1)) { + FlagsContinuation cont = FlagsContinuation::ForSet(kOverflow, ovf); + return VisitBinop(this, node, kX87Imul, &cont); + } + FlagsContinuation cont; + VisitBinop(this, node, kX87Imul, &cont); +} + +void InstructionSelector::VisitFloat32Equal(Node* node) { + FlagsContinuation cont = FlagsContinuation::ForSet(kUnorderedEqual, node); + VisitFloat32Compare(this, node, &cont); +} + + +void InstructionSelector::VisitFloat32LessThan(Node* node) { + FlagsContinuation cont = + FlagsContinuation::ForSet(kUnsignedGreaterThan, node); + VisitFloat32Compare(this, node, &cont); +} + + +void InstructionSelector::VisitFloat32LessThanOrEqual(Node* node) { + FlagsContinuation cont = + FlagsContinuation::ForSet(kUnsignedGreaterThanOrEqual, node); + VisitFloat32Compare(this, node, &cont); +} + + +void InstructionSelector::VisitFloat64Equal(Node* node) { + FlagsContinuation cont = FlagsContinuation::ForSet(kUnorderedEqual, node); + VisitFloat64Compare(this, node, &cont); +} + + +void InstructionSelector::VisitFloat64LessThan(Node* node) { + FlagsContinuation cont = + FlagsContinuation::ForSet(kUnsignedGreaterThan, node); + VisitFloat64Compare(this, node, &cont); +} + + +void InstructionSelector::VisitFloat64LessThanOrEqual(Node* node) { + FlagsContinuation cont = + FlagsContinuation::ForSet(kUnsignedGreaterThanOrEqual, node); + VisitFloat64Compare(this, node, &cont); +} + + +void InstructionSelector::VisitFloat64ExtractLowWord32(Node* node) { + X87OperandGenerator g(this); + Emit(kX87Float64ExtractLowWord32, g.DefineAsRegister(node), + g.Use(node->InputAt(0))); +} + + +void InstructionSelector::VisitFloat64ExtractHighWord32(Node* node) { + X87OperandGenerator g(this); + Emit(kX87Float64ExtractHighWord32, g.DefineAsRegister(node), + g.Use(node->InputAt(0))); +} + + +void InstructionSelector::VisitFloat64InsertLowWord32(Node* node) { + X87OperandGenerator g(this); + Node* left = node->InputAt(0); + Node* right = node->InputAt(1); + Emit(kX87Float64InsertLowWord32, g.UseFixed(node, stX_0), g.UseRegister(left), + g.UseRegister(right)); +} + + +void InstructionSelector::VisitFloat64InsertHighWord32(Node* node) { + X87OperandGenerator g(this); + Node* left = node->InputAt(0); + Node* right = node->InputAt(1); + Emit(kX87Float64InsertHighWord32, g.UseFixed(node, stX_0), + g.UseRegister(left), g.UseRegister(right)); +} + +void InstructionSelector::VisitFloat64SilenceNaN(Node* node) { + X87OperandGenerator g(this); + Emit(kX87PushFloat64, g.NoOutput(), g.Use(node->InputAt(0))); + Emit(kX87Float64SilenceNaN, g.DefineAsFixed(node, stX_0), 0, nullptr); +} + +void InstructionSelector::VisitAtomicLoad(Node* node) { + LoadRepresentation load_rep = LoadRepresentationOf(node->op()); + DCHECK(load_rep.representation() == MachineRepresentation::kWord8 || + load_rep.representation() == MachineRepresentation::kWord16 || + load_rep.representation() == MachineRepresentation::kWord32); + USE(load_rep); + VisitLoad(node); +} + +void InstructionSelector::VisitAtomicStore(Node* node) { + X87OperandGenerator g(this); + Node* base = node->InputAt(0); + Node* index = node->InputAt(1); + Node* value = node->InputAt(2); + + MachineRepresentation rep = AtomicStoreRepresentationOf(node->op()); + ArchOpcode opcode = kArchNop; + switch (rep) { + case MachineRepresentation::kWord8: + opcode = kAtomicExchangeInt8; + break; + case MachineRepresentation::kWord16: + opcode = kAtomicExchangeInt16; + break; + case MachineRepresentation::kWord32: + opcode = kAtomicExchangeWord32; + break; + default: + UNREACHABLE(); + break; + } + AddressingMode addressing_mode; + InstructionOperand inputs[4]; + size_t input_count = 0; + if (rep == MachineRepresentation::kWord8) { + inputs[input_count++] = g.UseByteRegister(value); + } else { + inputs[input_count++] = g.UseUniqueRegister(value); + } + inputs[input_count++] = g.UseUniqueRegister(base); + if (g.CanBeImmediate(index)) { + inputs[input_count++] = g.UseImmediate(index); + addressing_mode = kMode_MRI; + } else { + inputs[input_count++] = g.UseUniqueRegister(index); + addressing_mode = kMode_MR1; + } + InstructionCode code = opcode | AddressingModeField::encode(addressing_mode); + Emit(code, 0, nullptr, input_count, inputs); +} + +void InstructionSelector::VisitAtomicExchange(Node* node) { + X87OperandGenerator g(this); + Node* base = node->InputAt(0); + Node* index = node->InputAt(1); + Node* value = node->InputAt(2); + + MachineType type = AtomicOpRepresentationOf(node->op()); + ArchOpcode opcode = kArchNop; + if (type == MachineType::Int8()) { + opcode = kAtomicExchangeInt8; + } else if (type == MachineType::Uint8()) { + opcode = kAtomicExchangeUint8; + } else if (type == MachineType::Int16()) { + opcode = kAtomicExchangeInt16; + } else if (type == MachineType::Uint16()) { + opcode = kAtomicExchangeUint16; + } else if (type == MachineType::Int32() || type == MachineType::Uint32()) { + opcode = kAtomicExchangeWord32; + } else { + UNREACHABLE(); + return; + } + InstructionOperand outputs[1]; + AddressingMode addressing_mode; + InstructionOperand inputs[3]; + size_t input_count = 0; + if (type == MachineType::Int8() || type == MachineType::Uint8()) { + inputs[input_count++] = g.UseFixed(value, edx); + } else { + inputs[input_count++] = g.UseUniqueRegister(value); + } + inputs[input_count++] = g.UseUniqueRegister(base); + if (g.CanBeImmediate(index)) { + inputs[input_count++] = g.UseImmediate(index); + addressing_mode = kMode_MRI; + } else { + inputs[input_count++] = g.UseUniqueRegister(index); + addressing_mode = kMode_MR1; + } + if (type == MachineType::Int8() || type == MachineType::Uint8()) { + // Using DefineSameAsFirst requires the register to be unallocated. + outputs[0] = g.DefineAsFixed(node, edx); + } else { + outputs[0] = g.DefineSameAsFirst(node); + } + InstructionCode code = opcode | AddressingModeField::encode(addressing_mode); + Emit(code, 1, outputs, input_count, inputs); +} + +void InstructionSelector::VisitAtomicCompareExchange(Node* node) { + X87OperandGenerator g(this); + Node* base = node->InputAt(0); + Node* index = node->InputAt(1); + Node* old_value = node->InputAt(2); + Node* new_value = node->InputAt(3); + + MachineType type = AtomicOpRepresentationOf(node->op()); + ArchOpcode opcode = kArchNop; + if (type == MachineType::Int8()) { + opcode = kAtomicCompareExchangeInt8; + } else if (type == MachineType::Uint8()) { + opcode = kAtomicCompareExchangeUint8; + } else if (type == MachineType::Int16()) { + opcode = kAtomicCompareExchangeInt16; + } else if (type == MachineType::Uint16()) { + opcode = kAtomicCompareExchangeUint16; + } else if (type == MachineType::Int32() || type == MachineType::Uint32()) { + opcode = kAtomicCompareExchangeWord32; + } else { + UNREACHABLE(); + return; + } + InstructionOperand outputs[1]; + AddressingMode addressing_mode; + InstructionOperand inputs[4]; + size_t input_count = 0; + inputs[input_count++] = g.UseFixed(old_value, eax); + if (type == MachineType::Int8() || type == MachineType::Uint8()) { + inputs[input_count++] = g.UseByteRegister(new_value); + } else { + inputs[input_count++] = g.UseUniqueRegister(new_value); + } + inputs[input_count++] = g.UseUniqueRegister(base); + if (g.CanBeImmediate(index)) { + inputs[input_count++] = g.UseImmediate(index); + addressing_mode = kMode_MRI; + } else { + inputs[input_count++] = g.UseUniqueRegister(index); + addressing_mode = kMode_MR1; + } + outputs[0] = g.DefineAsFixed(node, eax); + InstructionCode code = opcode | AddressingModeField::encode(addressing_mode); + Emit(code, 1, outputs, input_count, inputs); +} + +void InstructionSelector::VisitAtomicBinaryOperation( + Node* node, ArchOpcode int8_op, ArchOpcode uint8_op, ArchOpcode int16_op, + ArchOpcode uint16_op, ArchOpcode word32_op) { + X87OperandGenerator g(this); + Node* base = node->InputAt(0); + Node* index = node->InputAt(1); + Node* value = node->InputAt(2); + + MachineType type = AtomicOpRepresentationOf(node->op()); + ArchOpcode opcode = kArchNop; + if (type == MachineType::Int8()) { + opcode = int8_op; + } else if (type == MachineType::Uint8()) { + opcode = uint8_op; + } else if (type == MachineType::Int16()) { + opcode = int16_op; + } else if (type == MachineType::Uint16()) { + opcode = uint16_op; + } else if (type == MachineType::Int32() || type == MachineType::Uint32()) { + opcode = word32_op; + } else { + UNREACHABLE(); + return; + } + InstructionOperand outputs[1]; + AddressingMode addressing_mode; + InstructionOperand inputs[3]; + size_t input_count = 0; + inputs[input_count++] = g.UseUniqueRegister(value); + inputs[input_count++] = g.UseUniqueRegister(base); + if (g.CanBeImmediate(index)) { + inputs[input_count++] = g.UseImmediate(index); + addressing_mode = kMode_MRI; + } else { + inputs[input_count++] = g.UseUniqueRegister(index); + addressing_mode = kMode_MR1; + } + outputs[0] = g.DefineAsFixed(node, eax); + InstructionOperand temp[1]; + if (type == MachineType::Int8() || type == MachineType::Uint8()) { + temp[0] = g.UseByteRegister(node); + } else { + temp[0] = g.TempRegister(); + } + InstructionCode code = opcode | AddressingModeField::encode(addressing_mode); + Emit(code, 1, outputs, input_count, inputs, 1, temp); +} + +#define VISIT_ATOMIC_BINOP(op) \ + void InstructionSelector::VisitAtomic##op(Node* node) { \ + VisitAtomicBinaryOperation(node, kAtomic##op##Int8, kAtomic##op##Uint8, \ + kAtomic##op##Int16, kAtomic##op##Uint16, \ + kAtomic##op##Word32); \ + } +VISIT_ATOMIC_BINOP(Add) +VISIT_ATOMIC_BINOP(Sub) +VISIT_ATOMIC_BINOP(And) +VISIT_ATOMIC_BINOP(Or) +VISIT_ATOMIC_BINOP(Xor) +#undef VISIT_ATOMIC_BINOP + +void InstructionSelector::VisitInt32AbsWithOverflow(Node* node) { + UNREACHABLE(); +} + +void InstructionSelector::VisitInt64AbsWithOverflow(Node* node) { + UNREACHABLE(); +} + +// static +MachineOperatorBuilder::Flags +InstructionSelector::SupportedMachineOperatorFlags() { + MachineOperatorBuilder::Flags flags = + MachineOperatorBuilder::kWord32ShiftIsSafe; + if (CpuFeatures::IsSupported(POPCNT)) { + flags |= MachineOperatorBuilder::kWord32Popcnt; + } + + flags |= MachineOperatorBuilder::kFloat32RoundDown | + MachineOperatorBuilder::kFloat64RoundDown | + MachineOperatorBuilder::kFloat32RoundUp | + MachineOperatorBuilder::kFloat64RoundUp | + MachineOperatorBuilder::kFloat32RoundTruncate | + MachineOperatorBuilder::kFloat64RoundTruncate | + MachineOperatorBuilder::kFloat32RoundTiesEven | + MachineOperatorBuilder::kFloat64RoundTiesEven; + return flags; +} + +// static +MachineOperatorBuilder::AlignmentRequirements +InstructionSelector::AlignmentRequirements() { + return MachineOperatorBuilder::AlignmentRequirements:: + FullUnalignedAccessSupport(); +} + +} // namespace compiler +} // namespace internal +} // namespace v8 diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/compiler/x87/OWNERS qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/compiler/x87/OWNERS --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/compiler/x87/OWNERS 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/compiler/x87/OWNERS 2017-12-25 17:42:57.205465793 +0100 @@ -0,0 +1,2 @@ +weiliang.lin@intel.com +chunyang.dai@intel.com diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/debug/x87/debug-x87.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/debug/x87/debug-x87.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/debug/x87/debug-x87.cc 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/debug/x87/debug-x87.cc 2017-12-28 03:21:22.149118881 +0100 @@ -0,0 +1,141 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#if V8_TARGET_ARCH_X87 + +#include "src/debug/debug.h" + +#include "src/codegen.h" +#include "src/debug/liveedit.h" +#include "src/x87/frames-x87.h" + +namespace v8 { +namespace internal { + +#define __ ACCESS_MASM(masm) + + +void EmitDebugBreakSlot(MacroAssembler* masm) { + Label check_codesize; + __ bind(&check_codesize); + __ Nop(Assembler::kDebugBreakSlotLength); + DCHECK_EQ(Assembler::kDebugBreakSlotLength, + masm->SizeOfCodeGeneratedSince(&check_codesize)); +} + + +void DebugCodegen::GenerateSlot(MacroAssembler* masm, RelocInfo::Mode mode) { + // Generate enough nop's to make space for a call instruction. + masm->RecordDebugBreakSlot(mode); + EmitDebugBreakSlot(masm); +} + + +void DebugCodegen::ClearDebugBreakSlot(Isolate* isolate, Address pc) { + CodePatcher patcher(isolate, pc, Assembler::kDebugBreakSlotLength); + EmitDebugBreakSlot(patcher.masm()); +} + + +void DebugCodegen::PatchDebugBreakSlot(Isolate* isolate, Address pc, + Handle code) { + DCHECK(code->is_debug_stub()); + static const int kSize = Assembler::kDebugBreakSlotLength; + CodePatcher patcher(isolate, pc, kSize); + + // Add a label for checking the size of the code used for returning. + Label check_codesize; + patcher.masm()->bind(&check_codesize); + patcher.masm()->call(code->entry(), RelocInfo::NONE32); + // Check that the size of the code generated is as expected. + DCHECK_EQ(kSize, patcher.masm()->SizeOfCodeGeneratedSince(&check_codesize)); +} + +bool DebugCodegen::DebugBreakSlotIsPatched(Address pc) { + return !Assembler::IsNop(pc); +} + +void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm, + DebugBreakCallHelperMode mode) { + __ RecordComment("Debug break"); + + // Enter an internal frame. + { + FrameScope scope(masm, StackFrame::INTERNAL); + + // Push arguments for DebugBreak call. + if (mode == SAVE_RESULT_REGISTER) { + // Break on return. + __ push(eax); + } else { + // Non-return breaks. + __ Push(masm->isolate()->factory()->the_hole_value()); + } + __ Move(eax, Immediate(1)); + __ mov(ebx, + Immediate(ExternalReference( + Runtime::FunctionForId(Runtime::kDebugBreak), masm->isolate()))); + + CEntryStub ceb(masm->isolate(), 1); + __ CallStub(&ceb); + + if (FLAG_debug_code) { + for (int i = 0; i < kNumJSCallerSaved; ++i) { + Register reg = {JSCallerSavedCode(i)}; + // Do not clobber eax if mode is SAVE_RESULT_REGISTER. It will + // contain return value of the function. + if (!(reg.is(eax) && (mode == SAVE_RESULT_REGISTER))) { + __ Move(reg, Immediate(kDebugZapValue)); + } + } + } + + // Get rid of the internal frame. + } + + __ MaybeDropFrames(); + + // Return to caller. + __ ret(0); +} + +void DebugCodegen::GenerateHandleDebuggerStatement(MacroAssembler* masm) { + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ CallRuntime(Runtime::kHandleDebuggerStatement, 0); + } + __ MaybeDropFrames(); + + // Return to caller. + __ ret(0); +} + +void DebugCodegen::GenerateFrameDropperTrampoline(MacroAssembler* masm) { + // Frame is being dropped: + // - Drop to the target frame specified by ebx. + // - Look up current function on the frame. + // - Leave the frame. + // - Restart the frame by calling the function. + __ mov(ebp, ebx); + __ mov(edi, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); + __ leave(); + + __ mov(ebx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); + __ mov(ebx, + FieldOperand(ebx, SharedFunctionInfo::kFormalParameterCountOffset)); + + ParameterCount dummy(ebx); + __ InvokeFunction(edi, dummy, dummy, JUMP_FUNCTION, + CheckDebugStepCallWrapper()); +} + + +const bool LiveEdit::kFrameDropperSupported = true; + +#undef __ + +} // namespace internal +} // namespace v8 + +#endif // V8_TARGET_ARCH_X87 diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/debug/x87/OWNERS qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/debug/x87/OWNERS --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/debug/x87/OWNERS 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/debug/x87/OWNERS 2017-12-25 17:42:57.210465720 +0100 @@ -0,0 +1,2 @@ +weiliang.lin@intel.com +chunyang.dai@intel.com diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/frames-inl.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/frames-inl.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/frames-inl.h 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/frames-inl.h 2017-12-25 17:42:57.210465720 +0100 @@ -26,6 +26,8 @@ #include "src/mips64/frames-mips64.h" // NOLINT #elif V8_TARGET_ARCH_S390 #include "src/s390/frames-s390.h" // NOLINT +#elif V8_TARGET_ARCH_X87 +#include "src/x87/frames-x87.h" // NOLINT #else #error Unsupported target architecture. #endif diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/full-codegen/full-codegen.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/full-codegen/full-codegen.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/full-codegen/full-codegen.h 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/full-codegen/full-codegen.h 2017-12-25 17:42:57.211465705 +0100 @@ -45,7 +45,7 @@ static const int kMaxBackEdgeWeight = 127; // Platform-specific code size multiplier. -#if V8_TARGET_ARCH_IA32 +#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X87 static const int kCodeSizeMultiplier = 105; #elif V8_TARGET_ARCH_X64 static const int kCodeSizeMultiplier = 165; diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/full-codegen/x87/full-codegen-x87.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/full-codegen/x87/full-codegen-x87.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/full-codegen/x87/full-codegen-x87.cc 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/full-codegen/x87/full-codegen-x87.cc 2017-12-28 03:52:59.410562490 +0100 @@ -0,0 +1,2410 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#if V8_TARGET_ARCH_X87 + +#include "src/ast/compile-time-value.h" +#include "src/ast/scopes.h" +#include "src/builtins/builtins-constructor.h" +#include "src/code-factory.h" +#include "src/code-stubs.h" +#include "src/codegen.h" +#include "src/compilation-info.h" +#include "src/compiler.h" +#include "src/debug/debug.h" +#include "src/full-codegen/full-codegen.h" +#include "src/ic/ic.h" +#include "src/x87/frames-x87.h" + +namespace v8 { +namespace internal { + +#define __ ACCESS_MASM(masm()) + +class JumpPatchSite BASE_EMBEDDED { + public: + explicit JumpPatchSite(MacroAssembler* masm) : masm_(masm) { +#ifdef DEBUG + info_emitted_ = false; +#endif + } + + ~JumpPatchSite() { + DCHECK(patch_site_.is_bound() == info_emitted_); + } + + void EmitJumpIfNotSmi(Register reg, + Label* target, + Label::Distance distance = Label::kFar) { + __ test(reg, Immediate(kSmiTagMask)); + EmitJump(not_carry, target, distance); // Always taken before patched. + } + + void EmitJumpIfSmi(Register reg, + Label* target, + Label::Distance distance = Label::kFar) { + __ test(reg, Immediate(kSmiTagMask)); + EmitJump(carry, target, distance); // Never taken before patched. + } + + void EmitPatchInfo() { + if (patch_site_.is_bound()) { + int delta_to_patch_site = masm_->SizeOfCodeGeneratedSince(&patch_site_); + DCHECK(is_uint8(delta_to_patch_site)); + __ test(eax, Immediate(delta_to_patch_site)); +#ifdef DEBUG + info_emitted_ = true; +#endif + } else { + __ nop(); // Signals no inlined code. + } + } + + private: + // jc will be patched with jz, jnc will become jnz. + void EmitJump(Condition cc, Label* target, Label::Distance distance) { + DCHECK(!patch_site_.is_bound() && !info_emitted_); + DCHECK(cc == carry || cc == not_carry); + __ bind(&patch_site_); + __ j(cc, target, distance); + } + + MacroAssembler* masm() { return masm_; } + MacroAssembler* masm_; + Label patch_site_; +#ifdef DEBUG + bool info_emitted_; +#endif +}; + + +// Generate code for a JS function. On entry to the function the receiver +// and arguments have been pushed on the stack left to right, with the +// return address on top of them. The actual argument count matches the +// formal parameter count expected by the function. +// +// The live registers are: +// o edi: the JS function object being called (i.e. ourselves) +// o edx: the new target value +// o esi: our context +// o ebp: our caller's frame pointer +// o esp: stack pointer (pointing to return address) +// +// The function builds a JS frame. Please see JavaScriptFrameConstants in +// frames-x87.h for its layout. +void FullCodeGenerator::Generate() { + CompilationInfo* info = info_; + profiling_counter_ = isolate()->factory()->NewCell( + Handle(Smi::FromInt(FLAG_interrupt_budget), isolate())); + SetFunctionPosition(literal()); + Comment cmnt(masm_, "[ function compiled by full code generator"); + + ProfileEntryHookStub::MaybeCallEntryHook(masm_); + + if (FLAG_debug_code && info->ExpectsJSReceiverAsReceiver()) { + int receiver_offset = (info->scope()->num_parameters() + 1) * kPointerSize; + __ mov(ecx, Operand(esp, receiver_offset)); + __ AssertNotSmi(ecx); + __ CmpObjectType(ecx, FIRST_JS_RECEIVER_TYPE, ecx); + __ Assert(above_equal, kSloppyFunctionExpectsJSReceiverReceiver); + } + + // Open a frame scope to indicate that there is a frame on the stack. The + // MANUAL indicates that the scope shouldn't actually generate code to set up + // the frame (that is done below). + FrameScope frame_scope(masm_, StackFrame::MANUAL); + + info->set_prologue_offset(masm_->pc_offset()); + __ Prologue(info->GeneratePreagedPrologue()); + + // Increment invocation count for the function. + { + Comment cmnt(masm_, "[ Increment invocation count"); + __ mov(ecx, FieldOperand(edi, JSFunction::kFeedbackVectorOffset)); + __ mov(ecx, FieldOperand(ecx, Cell::kValueOffset)); + __ add( + FieldOperand(ecx, FeedbackVector::kInvocationCountIndex * kPointerSize + + FeedbackVector::kHeaderSize), + Immediate(Smi::FromInt(1))); + } + + { Comment cmnt(masm_, "[ Allocate locals"); + int locals_count = info->scope()->num_stack_slots(); + OperandStackDepthIncrement(locals_count); + if (locals_count == 1) { + __ push(Immediate(isolate()->factory()->undefined_value())); + } else if (locals_count > 1) { + if (locals_count >= 128) { + Label ok; + __ mov(ecx, esp); + __ sub(ecx, Immediate(locals_count * kPointerSize)); + ExternalReference stack_limit = + ExternalReference::address_of_real_stack_limit(isolate()); + __ cmp(ecx, Operand::StaticVariable(stack_limit)); + __ j(above_equal, &ok, Label::kNear); + __ CallRuntime(Runtime::kThrowStackOverflow); + __ bind(&ok); + } + __ mov(eax, Immediate(isolate()->factory()->undefined_value())); + const int kMaxPushes = 32; + if (locals_count >= kMaxPushes) { + int loop_iterations = locals_count / kMaxPushes; + __ mov(ecx, loop_iterations); + Label loop_header; + __ bind(&loop_header); + // Do pushes. + for (int i = 0; i < kMaxPushes; i++) { + __ push(eax); + } + __ dec(ecx); + __ j(not_zero, &loop_header, Label::kNear); + } + int remaining = locals_count % kMaxPushes; + // Emit the remaining pushes. + for (int i = 0; i < remaining; i++) { + __ push(eax); + } + } + } + + bool function_in_register = true; + + // Possibly allocate a local context. + if (info->scope()->NeedsContext()) { + Comment cmnt(masm_, "[ Allocate context"); + bool need_write_barrier = true; + int slots = info->scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS; + // Argument to NewContext is the function, which is still in edi. + if (info->scope()->is_script_scope()) { + __ push(edi); + __ Push(info->scope()->scope_info()); + __ CallRuntime(Runtime::kNewScriptContext); + // The new target value is not used, clobbering is safe. + DCHECK_NULL(info->scope()->new_target_var()); + } else { + if (info->scope()->new_target_var() != nullptr) { + __ push(edx); // Preserve new target. + } + if (slots <= ConstructorBuiltins::MaximumFunctionContextSlots()) { + Callable callable = CodeFactory::FastNewFunctionContext( + isolate(), info->scope()->scope_type()); + __ mov(FastNewFunctionContextDescriptor::SlotsRegister(), + Immediate(slots)); + __ Call(callable.code(), RelocInfo::CODE_TARGET); + // Result of the FastNewFunctionContext builtin is always in new space. + need_write_barrier = false; + } else { + __ push(edi); + __ Push(Smi::FromInt(info->scope()->scope_type())); + __ CallRuntime(Runtime::kNewFunctionContext); + } + if (info->scope()->new_target_var() != nullptr) { + __ pop(edx); // Restore new target. + } + } + function_in_register = false; + // Context is returned in eax. It replaces the context passed to us. + // It's saved in the stack and kept live in esi. + __ mov(esi, eax); + __ mov(Operand(ebp, StandardFrameConstants::kContextOffset), eax); + + // Copy parameters into context if necessary. + int num_parameters = info->scope()->num_parameters(); + int first_parameter = info->scope()->has_this_declaration() ? -1 : 0; + for (int i = first_parameter; i < num_parameters; i++) { + Variable* var = + (i == -1) ? info->scope()->receiver() : info->scope()->parameter(i); + if (var->IsContextSlot()) { + int parameter_offset = StandardFrameConstants::kCallerSPOffset + + (num_parameters - 1 - i) * kPointerSize; + // Load parameter from stack. + __ mov(eax, Operand(ebp, parameter_offset)); + // Store it in the context. + int context_offset = Context::SlotOffset(var->index()); + __ mov(Operand(esi, context_offset), eax); + // Update the write barrier. This clobbers eax and ebx. + if (need_write_barrier) { + __ RecordWriteContextSlot(esi, context_offset, eax, ebx, + kDontSaveFPRegs); + } else if (FLAG_debug_code) { + Label done; + __ JumpIfInNewSpace(esi, eax, &done, Label::kNear); + __ Abort(kExpectedNewSpaceObject); + __ bind(&done); + } + } + } + } + + // We don't support new.target and rest parameters here. + DCHECK_NULL(info->scope()->new_target_var()); + DCHECK_NULL(info->scope()->rest_parameter()); + DCHECK_NULL(info->scope()->this_function_var()); + + Variable* arguments = info->scope()->arguments(); + if (arguments != NULL) { + // Arguments object must be allocated after the context object, in + // case the "arguments" or ".arguments" variables are in the context. + Comment cmnt(masm_, "[ Allocate arguments object"); + if (!function_in_register) { + __ mov(edi, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); + } + if (is_strict(language_mode()) || !has_simple_parameters()) { + __ call(isolate()->builtins()->FastNewStrictArguments(), + RelocInfo::CODE_TARGET); + RestoreContext(); + } else if (literal()->has_duplicate_parameters()) { + __ Push(edi); + __ CallRuntime(Runtime::kNewSloppyArguments_Generic); + } else { + __ call(isolate()->builtins()->FastNewSloppyArguments(), + RelocInfo::CODE_TARGET); + RestoreContext(); + } + + SetVar(arguments, eax, ebx, edx); + } + + if (FLAG_trace) { + __ CallRuntime(Runtime::kTraceEnter); + } + + // Visit the declarations and body. + { + Comment cmnt(masm_, "[ Declarations"); + VisitDeclarations(scope()->declarations()); + } + + // Assert that the declarations do not use ICs. Otherwise the debugger + // won't be able to redirect a PC at an IC to the correct IC in newly + // recompiled code. + DCHECK_EQ(0, ic_total_count_); + + { + Comment cmnt(masm_, "[ Stack check"); + Label ok; + ExternalReference stack_limit = + ExternalReference::address_of_stack_limit(isolate()); + __ cmp(esp, Operand::StaticVariable(stack_limit)); + __ j(above_equal, &ok, Label::kNear); + __ call(isolate()->builtins()->StackCheck(), RelocInfo::CODE_TARGET); + __ bind(&ok); + } + + { + Comment cmnt(masm_, "[ Body"); + DCHECK(loop_depth() == 0); + VisitStatements(literal()->body()); + DCHECK(loop_depth() == 0); + } + + // Always emit a 'return undefined' in case control fell off the end of + // the body. + { Comment cmnt(masm_, "[ return ;"); + __ mov(eax, isolate()->factory()->undefined_value()); + EmitReturnSequence(); + } +} + + +void FullCodeGenerator::ClearAccumulator() { + __ Move(eax, Immediate(Smi::kZero)); +} + + +void FullCodeGenerator::EmitProfilingCounterDecrement(int delta) { + __ mov(ebx, Immediate(profiling_counter_)); + __ sub(FieldOperand(ebx, Cell::kValueOffset), + Immediate(Smi::FromInt(delta))); +} + + +void FullCodeGenerator::EmitProfilingCounterReset() { + int reset_value = FLAG_interrupt_budget; + __ mov(ebx, Immediate(profiling_counter_)); + __ mov(FieldOperand(ebx, Cell::kValueOffset), + Immediate(Smi::FromInt(reset_value))); +} + + +void FullCodeGenerator::EmitBackEdgeBookkeeping(IterationStatement* stmt, + Label* back_edge_target) { + Comment cmnt(masm_, "[ Back edge bookkeeping"); + Label ok; + + DCHECK(back_edge_target->is_bound()); + int distance = masm_->SizeOfCodeGeneratedSince(back_edge_target); + int weight = Min(kMaxBackEdgeWeight, + Max(1, distance / kCodeSizeMultiplier)); + EmitProfilingCounterDecrement(weight); + __ j(positive, &ok, Label::kNear); + __ call(isolate()->builtins()->InterruptCheck(), RelocInfo::CODE_TARGET); + + // Record a mapping of this PC offset to the OSR id. This is used to find + // the AST id from the unoptimized code in order to use it as a key into + // the deoptimization input data found in the optimized code. + RecordBackEdge(stmt->OsrEntryId()); + + EmitProfilingCounterReset(); + + __ bind(&ok); +} + +void FullCodeGenerator::EmitProfilingCounterHandlingForReturnSequence( + bool is_tail_call) { + // Pretend that the exit is a backwards jump to the entry. + int weight = 1; + if (info_->ShouldSelfOptimize()) { + weight = FLAG_interrupt_budget / FLAG_self_opt_count; + } else { + int distance = masm_->pc_offset(); + weight = Min(kMaxBackEdgeWeight, Max(1, distance / kCodeSizeMultiplier)); + } + EmitProfilingCounterDecrement(weight); + Label ok; + __ j(positive, &ok, Label::kNear); + // Don't need to save result register if we are going to do a tail call. + if (!is_tail_call) { + __ push(eax); + } + __ call(isolate()->builtins()->InterruptCheck(), RelocInfo::CODE_TARGET); + if (!is_tail_call) { + __ pop(eax); + } + EmitProfilingCounterReset(); + __ bind(&ok); +} + +void FullCodeGenerator::EmitReturnSequence() { + Comment cmnt(masm_, "[ Return sequence"); + if (return_label_.is_bound()) { + __ jmp(&return_label_); + } else { + // Common return label + __ bind(&return_label_); + if (FLAG_trace) { + __ push(eax); + __ CallRuntime(Runtime::kTraceExit); + } + EmitProfilingCounterHandlingForReturnSequence(false); + + SetReturnPosition(literal()); + __ leave(); + + int arg_count = info_->scope()->num_parameters() + 1; + int arguments_bytes = arg_count * kPointerSize; + __ Ret(arguments_bytes, ecx); + } +} + +void FullCodeGenerator::RestoreContext() { + __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); +} + +void FullCodeGenerator::StackValueContext::Plug(Variable* var) const { + DCHECK(var->IsStackAllocated() || var->IsContextSlot()); + MemOperand operand = codegen()->VarOperand(var, result_register()); + // Memory operands can be pushed directly. + codegen()->PushOperand(operand); +} + + +void FullCodeGenerator::EffectContext::Plug(Heap::RootListIndex index) const { + UNREACHABLE(); // Not used on X87. +} + + +void FullCodeGenerator::AccumulatorValueContext::Plug( + Heap::RootListIndex index) const { + UNREACHABLE(); // Not used on X87. +} + + +void FullCodeGenerator::StackValueContext::Plug( + Heap::RootListIndex index) const { + UNREACHABLE(); // Not used on X87. +} + + +void FullCodeGenerator::TestContext::Plug(Heap::RootListIndex index) const { + UNREACHABLE(); // Not used on X87. +} + + +void FullCodeGenerator::EffectContext::Plug(Handle lit) const { +} + + +void FullCodeGenerator::AccumulatorValueContext::Plug( + Handle lit) const { + if (lit->IsSmi()) { + __ SafeMove(result_register(), Immediate(Smi::cast(*lit))); + } else { + __ Move(result_register(), Immediate(Handle::cast(lit))); + } +} + + +void FullCodeGenerator::StackValueContext::Plug(Handle lit) const { + codegen()->OperandStackDepthIncrement(1); + if (lit->IsSmi()) { + __ SafePush(Immediate(Smi::cast(*lit))); + } else { + __ push(Immediate(Handle::cast(lit))); + } +} + + +void FullCodeGenerator::TestContext::Plug(Handle lit) const { + DCHECK(lit->IsNullOrUndefined(isolate()) || !lit->IsUndetectable()); + if (lit->IsNullOrUndefined(isolate()) || lit->IsFalse(isolate())) { + if (false_label_ != fall_through_) __ jmp(false_label_); + } else if (lit->IsTrue(isolate()) || lit->IsJSObject()) { + if (true_label_ != fall_through_) __ jmp(true_label_); + } else if (lit->IsString()) { + if (String::cast(*lit)->length() == 0) { + if (false_label_ != fall_through_) __ jmp(false_label_); + } else { + if (true_label_ != fall_through_) __ jmp(true_label_); + } + } else if (lit->IsSmi()) { + if (Smi::ToInt(*lit) == 0) { + if (false_label_ != fall_through_) __ jmp(false_label_); + } else { + if (true_label_ != fall_through_) __ jmp(true_label_); + } + } else { + // For simplicity we always test the accumulator register. + __ mov(result_register(), Handle::cast(lit)); + codegen()->DoTest(this); + } +} + + +void FullCodeGenerator::StackValueContext::DropAndPlug(int count, + Register reg) const { + DCHECK(count > 0); + if (count > 1) codegen()->DropOperands(count - 1); + __ mov(Operand(esp, 0), reg); +} + + +void FullCodeGenerator::EffectContext::Plug(Label* materialize_true, + Label* materialize_false) const { + DCHECK(materialize_true == materialize_false); + __ bind(materialize_true); +} + + +void FullCodeGenerator::AccumulatorValueContext::Plug( + Label* materialize_true, + Label* materialize_false) const { + Label done; + __ bind(materialize_true); + __ mov(result_register(), isolate()->factory()->true_value()); + __ jmp(&done, Label::kNear); + __ bind(materialize_false); + __ mov(result_register(), isolate()->factory()->false_value()); + __ bind(&done); +} + + +void FullCodeGenerator::StackValueContext::Plug( + Label* materialize_true, + Label* materialize_false) const { + codegen()->OperandStackDepthIncrement(1); + Label done; + __ bind(materialize_true); + __ push(Immediate(isolate()->factory()->true_value())); + __ jmp(&done, Label::kNear); + __ bind(materialize_false); + __ push(Immediate(isolate()->factory()->false_value())); + __ bind(&done); +} + + +void FullCodeGenerator::TestContext::Plug(Label* materialize_true, + Label* materialize_false) const { + DCHECK(materialize_true == true_label_); + DCHECK(materialize_false == false_label_); +} + + +void FullCodeGenerator::AccumulatorValueContext::Plug(bool flag) const { + Handle value = flag ? isolate()->factory()->true_value() + : isolate()->factory()->false_value(); + __ mov(result_register(), value); +} + + +void FullCodeGenerator::StackValueContext::Plug(bool flag) const { + codegen()->OperandStackDepthIncrement(1); + Handle value = flag ? isolate()->factory()->true_value() + : isolate()->factory()->false_value(); + __ push(Immediate(value)); +} + + +void FullCodeGenerator::TestContext::Plug(bool flag) const { + if (flag) { + if (true_label_ != fall_through_) __ jmp(true_label_); + } else { + if (false_label_ != fall_through_) __ jmp(false_label_); + } +} + + +void FullCodeGenerator::DoTest(Expression* condition, + Label* if_true, + Label* if_false, + Label* fall_through) { + Callable callable = Builtins::CallableFor(isolate(), Builtins::kToBoolean); + __ Call(callable.code(), RelocInfo::CODE_TARGET); + RestoreContext(); + __ CompareRoot(result_register(), Heap::kTrueValueRootIndex); + Split(equal, if_true, if_false, fall_through); +} + + +void FullCodeGenerator::Split(Condition cc, + Label* if_true, + Label* if_false, + Label* fall_through) { + if (if_false == fall_through) { + __ j(cc, if_true); + } else if (if_true == fall_through) { + __ j(NegateCondition(cc), if_false); + } else { + __ j(cc, if_true); + __ jmp(if_false); + } +} + + +MemOperand FullCodeGenerator::StackOperand(Variable* var) { + DCHECK(var->IsStackAllocated()); + // Offset is negative because higher indexes are at lower addresses. + int offset = -var->index() * kPointerSize; + // Adjust by a (parameter or local) base offset. + if (var->IsParameter()) { + offset += (info_->scope()->num_parameters() + 1) * kPointerSize; + } else { + offset += JavaScriptFrameConstants::kLocal0Offset; + } + return Operand(ebp, offset); +} + + +MemOperand FullCodeGenerator::VarOperand(Variable* var, Register scratch) { + DCHECK(var->IsContextSlot() || var->IsStackAllocated()); + if (var->IsContextSlot()) { + int context_chain_length = scope()->ContextChainLength(var->scope()); + __ LoadContext(scratch, context_chain_length); + return ContextOperand(scratch, var->index()); + } else { + return StackOperand(var); + } +} + + +void FullCodeGenerator::GetVar(Register dest, Variable* var) { + DCHECK(var->IsContextSlot() || var->IsStackAllocated()); + MemOperand location = VarOperand(var, dest); + __ mov(dest, location); +} + + +void FullCodeGenerator::SetVar(Variable* var, + Register src, + Register scratch0, + Register scratch1) { + DCHECK(var->IsContextSlot() || var->IsStackAllocated()); + DCHECK(!scratch0.is(src)); + DCHECK(!scratch0.is(scratch1)); + DCHECK(!scratch1.is(src)); + MemOperand location = VarOperand(var, scratch0); + __ mov(location, src); + + // Emit the write barrier code if the location is in the heap. + if (var->IsContextSlot()) { + int offset = Context::SlotOffset(var->index()); + DCHECK(!scratch0.is(esi) && !src.is(esi) && !scratch1.is(esi)); + __ RecordWriteContextSlot(scratch0, offset, src, scratch1, kDontSaveFPRegs); + } +} + + +void FullCodeGenerator::EmitDebugCheckDeclarationContext(Variable* variable) { + // The variable in the declaration always resides in the current context. + DCHECK_EQ(0, scope()->ContextChainLength(variable->scope())); + if (FLAG_debug_code) { + // Check that we're not inside a with or catch context. + __ mov(ebx, FieldOperand(esi, HeapObject::kMapOffset)); + __ cmp(ebx, isolate()->factory()->with_context_map()); + __ Check(not_equal, kDeclarationInWithContext); + __ cmp(ebx, isolate()->factory()->catch_context_map()); + __ Check(not_equal, kDeclarationInCatchContext); + } +} + + +void FullCodeGenerator::VisitVariableDeclaration( + VariableDeclaration* declaration) { + VariableProxy* proxy = declaration->proxy(); + Variable* variable = proxy->var(); + switch (variable->location()) { + case VariableLocation::UNALLOCATED: { + DCHECK(!variable->binding_needs_init()); + globals_->Add(variable->name(), zone()); + FeedbackSlot slot = proxy->VariableFeedbackSlot(); + DCHECK(!slot.IsInvalid()); + globals_->Add(handle(Smi::FromInt(slot.ToInt()), isolate()), zone()); + globals_->Add(isolate()->factory()->undefined_value(), zone()); + globals_->Add(isolate()->factory()->undefined_value(), zone()); + break; + } + case VariableLocation::PARAMETER: + case VariableLocation::LOCAL: + if (variable->binding_needs_init()) { + Comment cmnt(masm_, "[ VariableDeclaration"); + __ mov(StackOperand(variable), + Immediate(isolate()->factory()->the_hole_value())); + } + break; + + case VariableLocation::CONTEXT: + if (variable->binding_needs_init()) { + Comment cmnt(masm_, "[ VariableDeclaration"); + EmitDebugCheckDeclarationContext(variable); + __ mov(ContextOperand(esi, variable->index()), + Immediate(isolate()->factory()->the_hole_value())); + // No write barrier since the hole value is in old space. + } + break; + + case VariableLocation::LOOKUP: + case VariableLocation::MODULE: + UNREACHABLE(); + } +} + +void FullCodeGenerator::VisitFunctionDeclaration( + FunctionDeclaration* declaration) { + VariableProxy* proxy = declaration->proxy(); + Variable* variable = proxy->var(); + switch (variable->location()) { + case VariableLocation::UNALLOCATED: { + globals_->Add(variable->name(), zone()); + FeedbackSlot slot = proxy->VariableFeedbackSlot(); + DCHECK(!slot.IsInvalid()); + globals_->Add(handle(Smi::FromInt(slot.ToInt()), isolate()), zone()); + + // We need the slot where the literals array lives, too. + slot = declaration->fun()->LiteralFeedbackSlot(); + DCHECK(!slot.IsInvalid()); + globals_->Add(handle(Smi::FromInt(slot.ToInt()), isolate()), zone()); + + Handle function = + Compiler::GetSharedFunctionInfo(declaration->fun(), script(), info_); + // Check for stack-overflow exception. + if (function.is_null()) return SetStackOverflow(); + globals_->Add(function, zone()); + break; + } + + case VariableLocation::PARAMETER: + case VariableLocation::LOCAL: { + Comment cmnt(masm_, "[ FunctionDeclaration"); + VisitForAccumulatorValue(declaration->fun()); + __ mov(StackOperand(variable), result_register()); + break; + } + + case VariableLocation::CONTEXT: { + Comment cmnt(masm_, "[ FunctionDeclaration"); + EmitDebugCheckDeclarationContext(variable); + VisitForAccumulatorValue(declaration->fun()); + __ mov(ContextOperand(esi, variable->index()), result_register()); + // We know that we have written a function, which is not a smi. + __ RecordWriteContextSlot(esi, Context::SlotOffset(variable->index()), + result_register(), ecx, kDontSaveFPRegs, + EMIT_REMEMBERED_SET, OMIT_SMI_CHECK); + break; + } + + case VariableLocation::LOOKUP: + case VariableLocation::MODULE: + UNREACHABLE(); + } +} + + +void FullCodeGenerator::DeclareGlobals(Handle pairs) { + // Call the runtime to declare the globals. + __ Push(pairs); + __ Push(Smi::FromInt(DeclareGlobalsFlags())); + __ EmitLoadFeedbackVector(eax); + __ Push(eax); + __ CallRuntime(Runtime::kDeclareGlobals); + // Return value is ignored. +} + + +void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { + Comment cmnt(masm_, "[ SwitchStatement"); + Breakable nested_statement(this, stmt); + SetStatementPosition(stmt); + + // Keep the switch value on the stack until a case matches. + VisitForStackValue(stmt->tag()); + + ZoneList* clauses = stmt->cases(); + CaseClause* default_clause = NULL; // Can occur anywhere in the list. + + Label next_test; // Recycled for each test. + // Compile all the tests with branches to their bodies. + for (int i = 0; i < clauses->length(); i++) { + CaseClause* clause = clauses->at(i); + clause->body_target()->Unuse(); + + // The default is not a test, but remember it as final fall through. + if (clause->is_default()) { + default_clause = clause; + continue; + } + + Comment cmnt(masm_, "[ Case comparison"); + __ bind(&next_test); + next_test.Unuse(); + + // Compile the label expression. + VisitForAccumulatorValue(clause->label()); + + // Perform the comparison as if via '==='. + __ mov(edx, Operand(esp, 0)); // Switch value. + bool inline_smi_code = ShouldInlineSmiCase(Token::EQ_STRICT); + JumpPatchSite patch_site(masm_); + if (inline_smi_code) { + Label slow_case; + __ mov(ecx, edx); + __ or_(ecx, eax); + patch_site.EmitJumpIfNotSmi(ecx, &slow_case, Label::kNear); + + __ cmp(edx, eax); + __ j(not_equal, &next_test); + __ Drop(1); // Switch value is no longer needed. + __ jmp(clause->body_target()); + __ bind(&slow_case); + } + + SetExpressionPosition(clause); + Handle ic = + CodeFactory::CompareIC(isolate(), Token::EQ_STRICT).code(); + CallIC(ic); + patch_site.EmitPatchInfo(); + + Label skip; + __ jmp(&skip, Label::kNear); + __ cmp(eax, isolate()->factory()->true_value()); + __ j(not_equal, &next_test); + __ Drop(1); + __ jmp(clause->body_target()); + __ bind(&skip); + + __ test(eax, eax); + __ j(not_equal, &next_test); + __ Drop(1); // Switch value is no longer needed. + __ jmp(clause->body_target()); + } + + // Discard the test value and jump to the default if present, otherwise to + // the end of the statement. + __ bind(&next_test); + DropOperands(1); // Switch value is no longer needed. + if (default_clause == NULL) { + __ jmp(nested_statement.break_label()); + } else { + __ jmp(default_clause->body_target()); + } + + // Compile all the case bodies. + for (int i = 0; i < clauses->length(); i++) { + Comment cmnt(masm_, "[ Case body"); + CaseClause* clause = clauses->at(i); + __ bind(clause->body_target()); + VisitStatements(clause->statements()); + } + + __ bind(nested_statement.break_label()); +} + + +void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { + Comment cmnt(masm_, "[ ForInStatement"); + SetStatementPosition(stmt, SKIP_BREAK); + + FeedbackSlot slot = stmt->ForInFeedbackSlot(); + + // Get the object to enumerate over. + SetExpressionAsStatementPosition(stmt->enumerable()); + VisitForAccumulatorValue(stmt->enumerable()); + OperandStackDepthIncrement(5); + + Label loop, exit; + Iteration loop_statement(this, stmt); + increment_loop_depth(); + + // If the object is null or undefined, skip over the loop, otherwise convert + // it to a JS receiver. See ECMA-262 version 5, section 12.6.4. + Label convert, done_convert; + __ JumpIfSmi(eax, &convert, Label::kNear); + __ CmpObjectType(eax, FIRST_JS_RECEIVER_TYPE, ecx); + __ j(above_equal, &done_convert, Label::kNear); + __ cmp(eax, isolate()->factory()->undefined_value()); + __ j(equal, &exit); + __ cmp(eax, isolate()->factory()->null_value()); + __ j(equal, &exit); + __ bind(&convert); + __ Call(isolate()->builtins()->ToObject(), RelocInfo::CODE_TARGET); + RestoreContext(); + __ bind(&done_convert); + __ push(eax); + + // Check cache validity in generated code. If we cannot guarantee cache + // validity, call the runtime system to check cache validity or get the + // property names in a fixed array. Note: Proxies never have an enum cache, + // so will always take the slow path. + Label call_runtime, use_cache, fixed_array; + __ CheckEnumCache(&call_runtime); + + __ mov(eax, FieldOperand(eax, HeapObject::kMapOffset)); + __ jmp(&use_cache, Label::kNear); + + // Get the set of properties to enumerate. + __ bind(&call_runtime); + __ push(eax); + __ CallRuntime(Runtime::kForInEnumerate); + __ cmp(FieldOperand(eax, HeapObject::kMapOffset), + isolate()->factory()->meta_map()); + __ j(not_equal, &fixed_array); + + + // We got a map in register eax. Get the enumeration cache from it. + Label no_descriptors; + __ bind(&use_cache); + + __ EnumLength(edx, eax); + __ cmp(edx, Immediate(Smi::kZero)); + __ j(equal, &no_descriptors); + + __ LoadInstanceDescriptors(eax, ecx); + __ mov(ecx, FieldOperand(ecx, DescriptorArray::kEnumCacheBridgeOffset)); + __ mov(ecx, FieldOperand(ecx, DescriptorArray::kEnumCacheBridgeCacheOffset)); + + // Set up the four remaining stack slots. + __ push(eax); // Map. + __ push(ecx); // Enumeration cache. + __ push(edx); // Number of valid entries for the map in the enum cache. + __ push(Immediate(Smi::kZero)); // Initial index. + __ jmp(&loop); + + __ bind(&no_descriptors); + __ add(esp, Immediate(kPointerSize)); + __ jmp(&exit); + + // We got a fixed array in register eax. Iterate through that. + __ bind(&fixed_array); + + __ push(Immediate(Smi::FromInt(1))); // Smi(1) indicates slow check + __ push(eax); // Array + __ mov(eax, FieldOperand(eax, FixedArray::kLengthOffset)); + __ push(eax); // Fixed array length (as smi). + __ push(Immediate(Smi::kZero)); // Initial index. + + // Generate code for doing the condition check. + __ bind(&loop); + SetExpressionAsStatementPosition(stmt->each()); + + __ mov(eax, Operand(esp, 0 * kPointerSize)); // Get the current index. + __ cmp(eax, Operand(esp, 1 * kPointerSize)); // Compare to the array length. + __ j(above_equal, loop_statement.break_label()); + + // Get the current entry of the array into register eax. + __ mov(ebx, Operand(esp, 2 * kPointerSize)); + __ mov(eax, FieldOperand(ebx, eax, times_2, FixedArray::kHeaderSize)); + + // Get the expected map from the stack or a smi in the + // permanent slow case into register edx. + __ mov(edx, Operand(esp, 3 * kPointerSize)); + + // Check if the expected map still matches that of the enumerable. + // If not, we may have to filter the key. + Label update_each; + __ mov(ebx, Operand(esp, 4 * kPointerSize)); + __ cmp(edx, FieldOperand(ebx, HeapObject::kMapOffset)); + __ j(equal, &update_each, Label::kNear); + + // We need to filter the key, record slow-path here. + int const vector_index = SmiFromSlot(slot)->value(); + __ EmitLoadFeedbackVector(edx); + __ mov(FieldOperand(edx, FixedArray::OffsetOfElementAt(vector_index)), + Immediate(FeedbackVector::MegamorphicSentinel(isolate()))); + + // eax contains the key. The receiver in ebx is the second argument to the + // ForInFilter. ForInFilter returns undefined if the receiver doesn't + // have the key or returns the name-converted key. + __ Call(isolate()->builtins()->ForInFilter(), RelocInfo::CODE_TARGET); + RestoreContext(); + __ JumpIfRoot(result_register(), Heap::kUndefinedValueRootIndex, + loop_statement.continue_label()); + + // Update the 'each' property or variable from the possibly filtered + // entry in register eax. + __ bind(&update_each); + // Perform the assignment as if via '='. + { EffectContext context(this); + EmitAssignment(stmt->each(), stmt->EachFeedbackSlot()); + } + + // Generate code for the body of the loop. + Visit(stmt->body()); + + // Generate code for going to the next element by incrementing the + // index (smi) stored on top of the stack. + __ bind(loop_statement.continue_label()); + __ add(Operand(esp, 0 * kPointerSize), Immediate(Smi::FromInt(1))); + + EmitBackEdgeBookkeeping(stmt, &loop); + __ jmp(&loop); + + // Remove the pointers stored on the stack. + __ bind(loop_statement.break_label()); + DropOperands(5); + + // Exit and decrement the loop depth. + __ bind(&exit); + decrement_loop_depth(); +} + +void FullCodeGenerator::EmitSetHomeObject(Expression* initializer, int offset, + FeedbackSlot slot) { + DCHECK(NeedsHomeObject(initializer)); + __ mov(StoreDescriptor::ReceiverRegister(), Operand(esp, 0)); + __ mov(StoreDescriptor::ValueRegister(), Operand(esp, offset * kPointerSize)); + CallStoreIC(slot, isolate()->factory()->home_object_symbol()); +} + +void FullCodeGenerator::EmitSetHomeObjectAccumulator(Expression* initializer, + int offset, + FeedbackSlot slot) { + DCHECK(NeedsHomeObject(initializer)); + __ mov(StoreDescriptor::ReceiverRegister(), eax); + __ mov(StoreDescriptor::ValueRegister(), Operand(esp, offset * kPointerSize)); + CallStoreIC(slot, isolate()->factory()->home_object_symbol()); +} + +void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy, + TypeofMode typeof_mode) { + SetExpressionPosition(proxy); + Variable* var = proxy->var(); + + // Two cases: global variables and all other types of variables. + switch (var->location()) { + case VariableLocation::UNALLOCATED: { + Comment cmnt(masm_, "[ Global variable"); + EmitGlobalVariableLoad(proxy, typeof_mode); + context()->Plug(eax); + break; + } + + case VariableLocation::PARAMETER: + case VariableLocation::LOCAL: + case VariableLocation::CONTEXT: { + DCHECK_EQ(NOT_INSIDE_TYPEOF, typeof_mode); + Comment cmnt(masm_, var->IsContextSlot() ? "[ Context variable" + : "[ Stack variable"); + + if (proxy->hole_check_mode() == HoleCheckMode::kRequired) { + // Throw a reference error when using an uninitialized let/const + // binding in harmony mode. + Label done; + GetVar(eax, var); + __ cmp(eax, isolate()->factory()->the_hole_value()); + __ j(not_equal, &done, Label::kNear); + __ push(Immediate(var->name())); + __ CallRuntime(Runtime::kThrowReferenceError); + __ bind(&done); + context()->Plug(eax); + break; + } + context()->Plug(var); + break; + } + + case VariableLocation::LOOKUP: + case VariableLocation::MODULE: + UNREACHABLE(); + } +} + + +void FullCodeGenerator::EmitAccessor(ObjectLiteralProperty* property) { + Expression* expression = (property == NULL) ? NULL : property->value(); + if (expression == NULL) { + PushOperand(isolate()->factory()->null_value()); + } else { + VisitForStackValue(expression); + if (NeedsHomeObject(expression)) { + DCHECK(property->kind() == ObjectLiteral::Property::GETTER || + property->kind() == ObjectLiteral::Property::SETTER); + int offset = property->kind() == ObjectLiteral::Property::GETTER ? 2 : 3; + EmitSetHomeObject(expression, offset, property->GetSlot()); + } + } +} + + +void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { + Comment cmnt(masm_, "[ ObjectLiteral"); + + Handle constant_properties = + expr->GetOrBuildConstantProperties(isolate()); + int flags = expr->ComputeFlags(); + // If any of the keys would store to the elements array, then we shouldn't + // allow it. + if (MustCreateObjectLiteralWithRuntime(expr)) { + __ push(Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); + __ push(Immediate(SmiFromSlot(expr->literal_slot()))); + __ push(Immediate(constant_properties)); + __ push(Immediate(Smi::FromInt(flags))); + __ CallRuntime(Runtime::kCreateObjectLiteral); + } else { + __ mov(eax, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); + __ mov(ebx, Immediate(SmiFromSlot(expr->literal_slot()))); + __ mov(ecx, Immediate(constant_properties)); + __ mov(edx, Immediate(Smi::FromInt(flags))); + Callable callable = + Builtins::CallableFor(isolate(), Builtins::kFastCloneShallowObject); + __ Call(callable.code(), RelocInfo::CODE_TARGET); + RestoreContext(); + } + + // If result_saved is true the result is on top of the stack. If + // result_saved is false the result is in eax. + bool result_saved = false; + + AccessorTable accessor_table(zone()); + for (int i = 0; i < expr->properties()->length(); i++) { + ObjectLiteral::Property* property = expr->properties()->at(i); + DCHECK(!property->is_computed_name()); + if (property->IsCompileTimeValue()) continue; + + Literal* key = property->key()->AsLiteral(); + Expression* value = property->value(); + if (!result_saved) { + PushOperand(eax); // Save result on the stack + result_saved = true; + } + switch (property->kind()) { + case ObjectLiteral::Property::SPREAD: + case ObjectLiteral::Property::CONSTANT: + UNREACHABLE(); + case ObjectLiteral::Property::MATERIALIZED_LITERAL: + DCHECK(!CompileTimeValue::IsCompileTimeValue(value)); + // Fall through. + case ObjectLiteral::Property::COMPUTED: + // It is safe to use [[Put]] here because the boilerplate already + // contains computed properties with an uninitialized value. + if (key->IsStringLiteral()) { + DCHECK(key->IsPropertyName()); + if (property->emit_store()) { + VisitForAccumulatorValue(value); + DCHECK(StoreDescriptor::ValueRegister().is(eax)); + __ mov(StoreDescriptor::ReceiverRegister(), Operand(esp, 0)); + CallStoreIC(property->GetSlot(0), key->value(), kStoreOwn); + if (NeedsHomeObject(value)) { + EmitSetHomeObjectAccumulator(value, 0, property->GetSlot(1)); + } + } else { + VisitForEffect(value); + } + break; + } + PushOperand(Operand(esp, 0)); // Duplicate receiver. + VisitForStackValue(key); + VisitForStackValue(value); + if (property->emit_store()) { + if (NeedsHomeObject(value)) { + EmitSetHomeObject(value, 2, property->GetSlot()); + } + PushOperand(Smi::FromInt(SLOPPY)); // Language mode + CallRuntimeWithOperands(Runtime::kSetProperty); + } else { + DropOperands(3); + } + break; + case ObjectLiteral::Property::PROTOTYPE: + PushOperand(Operand(esp, 0)); // Duplicate receiver. + VisitForStackValue(value); + DCHECK(property->emit_store()); + CallRuntimeWithOperands(Runtime::kInternalSetPrototype); + break; + case ObjectLiteral::Property::GETTER: + if (property->emit_store()) { + AccessorTable::Iterator it = accessor_table.lookup(key); + it->second->getter = property; + } + break; + case ObjectLiteral::Property::SETTER: + if (property->emit_store()) { + AccessorTable::Iterator it = accessor_table.lookup(key); + it->second->setter = property; + } + break; + } + } + + // Emit code to define accessors, using only a single call to the runtime for + // each pair of corresponding getters and setters. + for (AccessorTable::Iterator it = accessor_table.begin(); + it != accessor_table.end(); + ++it) { + PushOperand(Operand(esp, 0)); // Duplicate receiver. + VisitForStackValue(it->first); + + EmitAccessor(it->second->getter); + EmitAccessor(it->second->setter); + + PushOperand(Smi::FromInt(NONE)); + CallRuntimeWithOperands(Runtime::kDefineAccessorPropertyUnchecked); + } + + if (result_saved) { + context()->PlugTOS(); + } else { + context()->Plug(eax); + } +} + + +void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { + Comment cmnt(masm_, "[ ArrayLiteral"); + + Handle constant_elements = + expr->GetOrBuildConstantElements(isolate()); + + if (MustCreateArrayLiteralWithRuntime(expr)) { + __ push(Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); + __ push(Immediate(SmiFromSlot(expr->literal_slot()))); + __ push(Immediate(constant_elements)); + __ push(Immediate(Smi::FromInt(expr->ComputeFlags()))); + __ CallRuntime(Runtime::kCreateArrayLiteral); + } else { + __ mov(eax, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); + __ mov(ebx, Immediate(SmiFromSlot(expr->literal_slot()))); + __ mov(ecx, Immediate(constant_elements)); + Callable callable = + CodeFactory::FastCloneShallowArray(isolate(), TRACK_ALLOCATION_SITE); + __ Call(callable.code(), RelocInfo::CODE_TARGET); + RestoreContext(); + } + + bool result_saved = false; // Is the result saved to the stack? + ZoneList* subexprs = expr->values(); + int length = subexprs->length(); + + // Emit code to evaluate all the non-constant subexpressions and to store + // them into the newly cloned array. + for (int array_index = 0; array_index < length; array_index++) { + Expression* subexpr = subexprs->at(array_index); + DCHECK(!subexpr->IsSpread()); + + // If the subexpression is a literal or a simple materialized literal it + // is already set in the cloned array. + if (CompileTimeValue::IsCompileTimeValue(subexpr)) continue; + + if (!result_saved) { + PushOperand(eax); // array literal. + result_saved = true; + } + VisitForAccumulatorValue(subexpr); + + __ mov(StoreDescriptor::NameRegister(), + Immediate(Smi::FromInt(array_index))); + __ mov(StoreDescriptor::ReceiverRegister(), Operand(esp, 0)); + CallKeyedStoreIC(expr->LiteralFeedbackSlot()); + } + + if (result_saved) { + context()->PlugTOS(); + } else { + context()->Plug(eax); + } +} + + +void FullCodeGenerator::VisitAssignment(Assignment* expr) { + DCHECK(expr->target()->IsValidReferenceExpressionOrThis()); + + Comment cmnt(masm_, "[ Assignment"); + + Property* property = expr->target()->AsProperty(); + LhsKind assign_type = Property::GetAssignType(property); + + // Evaluate LHS expression. + switch (assign_type) { + case VARIABLE: + // Nothing to do here. + break; + case NAMED_PROPERTY: + if (expr->is_compound()) { + // We need the receiver both on the stack and in the register. + VisitForStackValue(property->obj()); + __ mov(LoadDescriptor::ReceiverRegister(), Operand(esp, 0)); + } else { + VisitForStackValue(property->obj()); + } + break; + case KEYED_PROPERTY: { + if (expr->is_compound()) { + VisitForStackValue(property->obj()); + VisitForStackValue(property->key()); + __ mov(LoadDescriptor::ReceiverRegister(), Operand(esp, kPointerSize)); + __ mov(LoadDescriptor::NameRegister(), Operand(esp, 0)); + } else { + VisitForStackValue(property->obj()); + VisitForStackValue(property->key()); + } + break; + } + case NAMED_SUPER_PROPERTY: + case KEYED_SUPER_PROPERTY: + UNREACHABLE(); + break; + } + + // For compound assignments we need another deoptimization point after the + // variable/property load. + if (expr->is_compound()) { + AccumulatorValueContext result_context(this); + { AccumulatorValueContext left_operand_context(this); + switch (assign_type) { + case VARIABLE: + EmitVariableLoad(expr->target()->AsVariableProxy()); + break; + case NAMED_PROPERTY: + EmitNamedPropertyLoad(property); + break; + case KEYED_PROPERTY: + EmitKeyedPropertyLoad(property); + break; + case NAMED_SUPER_PROPERTY: + case KEYED_SUPER_PROPERTY: + UNREACHABLE(); + break; + } + } + + Token::Value op = expr->binary_op(); + PushOperand(eax); // Left operand goes on the stack. + VisitForAccumulatorValue(expr->value()); + + EmitBinaryOp(expr->binary_operation(), op); + } else { + VisitForAccumulatorValue(expr->value()); + } + + SetExpressionPosition(expr); + + // Store the value. + switch (assign_type) { + case VARIABLE: { + VariableProxy* proxy = expr->target()->AsVariableProxy(); + EmitVariableAssignment(proxy->var(), expr->op(), expr->AssignmentSlot(), + proxy->hole_check_mode()); + context()->Plug(eax); + break; + } + case NAMED_PROPERTY: + EmitNamedPropertyAssignment(expr); + break; + case KEYED_PROPERTY: + EmitKeyedPropertyAssignment(expr); + break; + case NAMED_SUPER_PROPERTY: + case KEYED_SUPER_PROPERTY: + UNREACHABLE(); + break; + } +} + +void FullCodeGenerator::PushOperand(MemOperand operand) { + OperandStackDepthIncrement(1); + __ Push(operand); +} + +void FullCodeGenerator::EmitOperandStackDepthCheck() { + if (FLAG_debug_code) { + int expected_diff = StandardFrameConstants::kFixedFrameSizeFromFp + + operand_stack_depth_ * kPointerSize; + __ mov(eax, ebp); + __ sub(eax, esp); + __ cmp(eax, Immediate(expected_diff)); + __ Assert(equal, kUnexpectedStackDepth); + } +} + + +void FullCodeGenerator::EmitBinaryOp(BinaryOperation* expr, Token::Value op) { + PopOperand(edx); + Handle code = CodeFactory::BinaryOperation(isolate(), op).code(); + __ Call(code, RelocInfo::CODE_TARGET); + RestoreContext(); + context()->Plug(eax); +} + +void FullCodeGenerator::EmitAssignment(Expression* expr, FeedbackSlot slot) { + DCHECK(expr->IsValidReferenceExpressionOrThis()); + + Property* prop = expr->AsProperty(); + LhsKind assign_type = Property::GetAssignType(prop); + + switch (assign_type) { + case VARIABLE: { + VariableProxy* proxy = expr->AsVariableProxy(); + EffectContext context(this); + EmitVariableAssignment(proxy->var(), Token::ASSIGN, slot, + proxy->hole_check_mode()); + break; + } + case NAMED_PROPERTY: { + PushOperand(eax); // Preserve value. + VisitForAccumulatorValue(prop->obj()); + __ Move(StoreDescriptor::ReceiverRegister(), eax); + PopOperand(StoreDescriptor::ValueRegister()); // Restore value. + CallStoreIC(slot, prop->key()->AsLiteral()->value()); + break; + } + case KEYED_PROPERTY: { + PushOperand(eax); // Preserve value. + VisitForStackValue(prop->obj()); + VisitForAccumulatorValue(prop->key()); + __ Move(StoreDescriptor::NameRegister(), eax); + PopOperand(StoreDescriptor::ReceiverRegister()); // Receiver. + PopOperand(StoreDescriptor::ValueRegister()); // Restore value. + CallKeyedStoreIC(slot); + break; + } + case NAMED_SUPER_PROPERTY: + case KEYED_SUPER_PROPERTY: + UNREACHABLE(); + break; + } + context()->Plug(eax); +} + + +void FullCodeGenerator::EmitStoreToStackLocalOrContextSlot( + Variable* var, MemOperand location) { + __ mov(location, eax); + if (var->IsContextSlot()) { + __ mov(edx, eax); + int offset = Context::SlotOffset(var->index()); + __ RecordWriteContextSlot(ecx, offset, edx, ebx, kDontSaveFPRegs); + } +} + +void FullCodeGenerator::EmitVariableAssignment(Variable* var, Token::Value op, + FeedbackSlot slot, + HoleCheckMode hole_check_mode) { + if (var->IsUnallocated()) { + // Global var, const, or let. + __ mov(StoreDescriptor::ReceiverRegister(), NativeContextOperand()); + __ mov(StoreDescriptor::ReceiverRegister(), + ContextOperand(StoreDescriptor::ReceiverRegister(), + Context::EXTENSION_INDEX)); + CallStoreIC(slot, var->name(), kStoreGlobal); + + } else if (IsLexicalVariableMode(var->mode()) && op != Token::INIT) { + DCHECK(!var->IsLookupSlot()); + DCHECK(var->IsStackAllocated() || var->IsContextSlot()); + MemOperand location = VarOperand(var, ecx); + // Perform an initialization check for lexically declared variables. + if (hole_check_mode == HoleCheckMode::kRequired) { + Label assign; + __ mov(edx, location); + __ cmp(edx, isolate()->factory()->the_hole_value()); + __ j(not_equal, &assign, Label::kNear); + __ push(Immediate(var->name())); + __ CallRuntime(Runtime::kThrowReferenceError); + __ bind(&assign); + } + if (var->mode() != CONST) { + EmitStoreToStackLocalOrContextSlot(var, location); + } else if (var->throw_on_const_assignment(language_mode())) { + __ CallRuntime(Runtime::kThrowConstAssignError); + } + } else if (var->is_this() && var->mode() == CONST && op == Token::INIT) { + // Initializing assignment to const {this} needs a write barrier. + DCHECK(var->IsStackAllocated() || var->IsContextSlot()); + Label uninitialized_this; + MemOperand location = VarOperand(var, ecx); + __ mov(edx, location); + __ cmp(edx, isolate()->factory()->the_hole_value()); + __ j(equal, &uninitialized_this); + __ push(Immediate(var->name())); + __ CallRuntime(Runtime::kThrowReferenceError); + __ bind(&uninitialized_this); + EmitStoreToStackLocalOrContextSlot(var, location); + + } else { + DCHECK(var->mode() != CONST || op == Token::INIT); + DCHECK(var->IsStackAllocated() || var->IsContextSlot()); + DCHECK(!var->IsLookupSlot()); + // Assignment to var or initializing assignment to let/const in harmony + // mode. + MemOperand location = VarOperand(var, ecx); + EmitStoreToStackLocalOrContextSlot(var, location); + } +} + + +void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { + // Assignment to a property, using a named store IC. + // eax : value + // esp[0] : receiver + Property* prop = expr->target()->AsProperty(); + DCHECK(prop != NULL); + DCHECK(prop->key()->IsLiteral()); + + PopOperand(StoreDescriptor::ReceiverRegister()); + CallStoreIC(expr->AssignmentSlot(), prop->key()->AsLiteral()->value()); + context()->Plug(eax); +} + + +void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) { + // Assignment to a property, using a keyed store IC. + // eax : value + // esp[0] : key + // esp[kPointerSize] : receiver + + PopOperand(StoreDescriptor::NameRegister()); // Key. + PopOperand(StoreDescriptor::ReceiverRegister()); + DCHECK(StoreDescriptor::ValueRegister().is(eax)); + CallKeyedStoreIC(expr->AssignmentSlot()); + context()->Plug(eax); +} + +// Code common for calls using the IC. +void FullCodeGenerator::EmitCallWithLoadIC(Call* expr) { + Expression* callee = expr->expression(); + + // Get the target function. + ConvertReceiverMode convert_mode; + if (callee->IsVariableProxy()) { + { StackValueContext context(this); + EmitVariableLoad(callee->AsVariableProxy()); + } + // Push undefined as receiver. This is patched in the method prologue if it + // is a sloppy mode method. + PushOperand(isolate()->factory()->undefined_value()); + convert_mode = ConvertReceiverMode::kNullOrUndefined; + } else { + // Load the function from the receiver. + DCHECK(callee->IsProperty()); + DCHECK(!callee->AsProperty()->IsSuperAccess()); + __ mov(LoadDescriptor::ReceiverRegister(), Operand(esp, 0)); + EmitNamedPropertyLoad(callee->AsProperty()); + // Push the target function under the receiver. + PushOperand(Operand(esp, 0)); + __ mov(Operand(esp, kPointerSize), eax); + convert_mode = ConvertReceiverMode::kNotNullOrUndefined; + } + + EmitCall(expr, convert_mode); +} + + +// Code common for calls using the IC. +void FullCodeGenerator::EmitKeyedCallWithLoadIC(Call* expr, + Expression* key) { + // Load the key. + VisitForAccumulatorValue(key); + + Expression* callee = expr->expression(); + + // Load the function from the receiver. + DCHECK(callee->IsProperty()); + __ mov(LoadDescriptor::ReceiverRegister(), Operand(esp, 0)); + __ mov(LoadDescriptor::NameRegister(), eax); + EmitKeyedPropertyLoad(callee->AsProperty()); + + // Push the target function under the receiver. + PushOperand(Operand(esp, 0)); + __ mov(Operand(esp, kPointerSize), eax); + + EmitCall(expr, ConvertReceiverMode::kNotNullOrUndefined); +} + + +void FullCodeGenerator::EmitCall(Call* expr, ConvertReceiverMode mode) { + // Load the arguments. + ZoneList* args = expr->arguments(); + int arg_count = args->length(); + for (int i = 0; i < arg_count; i++) { + VisitForStackValue(args->at(i)); + } + + SetCallPosition(expr); + Handle code = CodeFactory::CallICTrampoline(isolate(), mode).code(); + __ Move(edx, Immediate(SmiFromSlot(expr->CallFeedbackICSlot()))); + __ mov(edi, Operand(esp, (arg_count + 1) * kPointerSize)); + __ Move(eax, Immediate(arg_count)); + CallIC(code); + OperandStackDepthDecrement(arg_count + 1); + + RestoreContext(); + context()->DropAndPlug(1, eax); +} + +void FullCodeGenerator::VisitCallNew(CallNew* expr) { + Comment cmnt(masm_, "[ CallNew"); + // According to ECMA-262, section 11.2.2, page 44, the function + // expression in new calls must be evaluated before the + // arguments. + + // Push constructor on the stack. If it's not a function it's used as + // receiver for CALL_NON_FUNCTION, otherwise the value on the stack is + // ignored. + DCHECK(!expr->expression()->IsSuperPropertyReference()); + VisitForStackValue(expr->expression()); + + // Push the arguments ("left-to-right") on the stack. + ZoneList* args = expr->arguments(); + int arg_count = args->length(); + for (int i = 0; i < arg_count; i++) { + VisitForStackValue(args->at(i)); + } + + // Call the construct call builtin that handles allocation and + // constructor invocation. + SetConstructCallPosition(expr); + + // Load function and argument count into edi and eax. + __ Move(eax, Immediate(arg_count)); + __ mov(edi, Operand(esp, arg_count * kPointerSize)); + + // Record call targets in unoptimized code. + __ EmitLoadFeedbackVector(ebx); + __ mov(edx, Immediate(SmiFromSlot(expr->CallNewFeedbackSlot()))); + + CallConstructStub stub(isolate()); + CallIC(stub.GetCode()); + OperandStackDepthDecrement(arg_count + 1); + RestoreContext(); + context()->Plug(eax); +} + + +void FullCodeGenerator::EmitIsSmi(CallRuntime* expr) { + ZoneList* args = expr->arguments(); + DCHECK(args->length() == 1); + + VisitForAccumulatorValue(args->at(0)); + + Label materialize_true, materialize_false; + Label* if_true = NULL; + Label* if_false = NULL; + Label* fall_through = NULL; + context()->PrepareTest(&materialize_true, &materialize_false, + &if_true, &if_false, &fall_through); + + __ test(eax, Immediate(kSmiTagMask)); + Split(zero, if_true, if_false, fall_through); + + context()->Plug(if_true, if_false); +} + + +void FullCodeGenerator::EmitIsJSReceiver(CallRuntime* expr) { + ZoneList* args = expr->arguments(); + DCHECK(args->length() == 1); + + VisitForAccumulatorValue(args->at(0)); + + Label materialize_true, materialize_false; + Label* if_true = NULL; + Label* if_false = NULL; + Label* fall_through = NULL; + context()->PrepareTest(&materialize_true, &materialize_false, + &if_true, &if_false, &fall_through); + + __ JumpIfSmi(eax, if_false); + __ CmpObjectType(eax, FIRST_JS_RECEIVER_TYPE, ebx); + Split(above_equal, if_true, if_false, fall_through); + + context()->Plug(if_true, if_false); +} + + +void FullCodeGenerator::EmitIsArray(CallRuntime* expr) { + ZoneList* args = expr->arguments(); + DCHECK(args->length() == 1); + + VisitForAccumulatorValue(args->at(0)); + + Label materialize_true, materialize_false; + Label* if_true = NULL; + Label* if_false = NULL; + Label* fall_through = NULL; + context()->PrepareTest(&materialize_true, &materialize_false, + &if_true, &if_false, &fall_through); + + __ JumpIfSmi(eax, if_false); + __ CmpObjectType(eax, JS_ARRAY_TYPE, ebx); + Split(equal, if_true, if_false, fall_through); + + context()->Plug(if_true, if_false); +} + + +void FullCodeGenerator::EmitIsTypedArray(CallRuntime* expr) { + ZoneList* args = expr->arguments(); + DCHECK(args->length() == 1); + + VisitForAccumulatorValue(args->at(0)); + + Label materialize_true, materialize_false; + Label* if_true = NULL; + Label* if_false = NULL; + Label* fall_through = NULL; + context()->PrepareTest(&materialize_true, &materialize_false, &if_true, + &if_false, &fall_through); + + __ JumpIfSmi(eax, if_false); + __ CmpObjectType(eax, JS_TYPED_ARRAY_TYPE, ebx); + Split(equal, if_true, if_false, fall_through); + + context()->Plug(if_true, if_false); +} + + +void FullCodeGenerator::EmitIsJSProxy(CallRuntime* expr) { + ZoneList* args = expr->arguments(); + DCHECK(args->length() == 1); + + VisitForAccumulatorValue(args->at(0)); + + Label materialize_true, materialize_false; + Label* if_true = NULL; + Label* if_false = NULL; + Label* fall_through = NULL; + context()->PrepareTest(&materialize_true, &materialize_false, &if_true, + &if_false, &fall_through); + + __ JumpIfSmi(eax, if_false); + __ CmpObjectType(eax, JS_PROXY_TYPE, ebx); + Split(equal, if_true, if_false, fall_through); + + context()->Plug(if_true, if_false); +} + +void FullCodeGenerator::EmitClassOf(CallRuntime* expr) { + ZoneList* args = expr->arguments(); + DCHECK(args->length() == 1); + Label done, null, function, non_function_constructor; + + VisitForAccumulatorValue(args->at(0)); + + // If the object is not a JSReceiver, we return null. + __ JumpIfSmi(eax, &null, Label::kNear); + STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE); + __ CmpObjectType(eax, FIRST_JS_RECEIVER_TYPE, eax); + __ j(below, &null, Label::kNear); + + // Return 'Function' for JSFunction and JSBoundFunction objects. + __ CmpInstanceType(eax, FIRST_FUNCTION_TYPE); + STATIC_ASSERT(LAST_FUNCTION_TYPE == LAST_TYPE); + __ j(above_equal, &function, Label::kNear); + + // Check if the constructor in the map is a JS function. + __ GetMapConstructor(eax, eax, ebx); + __ CmpInstanceType(ebx, JS_FUNCTION_TYPE); + __ j(not_equal, &non_function_constructor, Label::kNear); + + // eax now contains the constructor function. Grab the + // instance class name from there. + __ mov(eax, FieldOperand(eax, JSFunction::kSharedFunctionInfoOffset)); + __ mov(eax, FieldOperand(eax, SharedFunctionInfo::kInstanceClassNameOffset)); + __ jmp(&done, Label::kNear); + + // Non-JS objects have class null. + __ bind(&null); + __ mov(eax, isolate()->factory()->null_value()); + __ jmp(&done, Label::kNear); + + // Functions have class 'Function'. + __ bind(&function); + __ mov(eax, isolate()->factory()->Function_string()); + __ jmp(&done, Label::kNear); + + // Objects with a non-function constructor have class 'Object'. + __ bind(&non_function_constructor); + __ mov(eax, isolate()->factory()->Object_string()); + + // All done. + __ bind(&done); + + context()->Plug(eax); +} + +void FullCodeGenerator::EmitStringCharCodeAt(CallRuntime* expr) { + ZoneList* args = expr->arguments(); + DCHECK(args->length() == 2); + + VisitForStackValue(args->at(0)); + VisitForAccumulatorValue(args->at(1)); + + Register object = ebx; + Register index = eax; + Register result = edx; + + PopOperand(object); + + Label need_conversion; + Label index_out_of_range; + Label done; + StringCharCodeAtGenerator generator(object, index, result, &need_conversion, + &need_conversion, &index_out_of_range); + generator.GenerateFast(masm_); + __ jmp(&done); + + __ bind(&index_out_of_range); + // When the index is out of range, the spec requires us to return + // NaN. + __ Move(result, Immediate(isolate()->factory()->nan_value())); + __ jmp(&done); + + __ bind(&need_conversion); + // Move the undefined value into the result register, which will + // trigger conversion. + __ Move(result, Immediate(isolate()->factory()->undefined_value())); + __ jmp(&done); + + NopRuntimeCallHelper call_helper; + generator.GenerateSlow(masm_, NOT_PART_OF_IC_HANDLER, call_helper); + + __ bind(&done); + context()->Plug(result); +} + + +void FullCodeGenerator::EmitCall(CallRuntime* expr) { + ZoneList* args = expr->arguments(); + DCHECK_LE(2, args->length()); + // Push target, receiver and arguments onto the stack. + for (Expression* const arg : *args) { + VisitForStackValue(arg); + } + // Move target to edi. + int const argc = args->length() - 2; + __ mov(edi, Operand(esp, (argc + 1) * kPointerSize)); + // Call the target. + __ mov(eax, Immediate(argc)); + __ Call(isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); + OperandStackDepthDecrement(argc + 1); + RestoreContext(); + // Discard the function left on TOS. + context()->DropAndPlug(1, eax); +} + +void FullCodeGenerator::EmitGetSuperConstructor(CallRuntime* expr) { + ZoneList* args = expr->arguments(); + DCHECK_EQ(1, args->length()); + VisitForAccumulatorValue(args->at(0)); + __ AssertFunction(eax); + __ mov(eax, FieldOperand(eax, HeapObject::kMapOffset)); + __ mov(eax, FieldOperand(eax, Map::kPrototypeOffset)); + context()->Plug(eax); +} + +void FullCodeGenerator::EmitDebugIsActive(CallRuntime* expr) { + DCHECK(expr->arguments()->length() == 0); + ExternalReference debug_is_active = + ExternalReference::debug_is_active_address(isolate()); + __ movzx_b(eax, Operand::StaticVariable(debug_is_active)); + __ SmiTag(eax); + context()->Plug(eax); +} + + +void FullCodeGenerator::EmitLoadJSRuntimeFunction(CallRuntime* expr) { + // Push function. + __ LoadGlobalFunction(expr->context_index(), eax); + PushOperand(eax); + + // Push undefined as receiver. + PushOperand(isolate()->factory()->undefined_value()); +} + + +void FullCodeGenerator::EmitCallJSRuntimeFunction(CallRuntime* expr) { + ZoneList* args = expr->arguments(); + int arg_count = args->length(); + + SetCallPosition(expr); + __ mov(edi, Operand(esp, (arg_count + 1) * kPointerSize)); + __ Set(eax, arg_count); + __ Call(isolate()->builtins()->Call(ConvertReceiverMode::kNullOrUndefined), + RelocInfo::CODE_TARGET); + OperandStackDepthDecrement(arg_count + 1); + RestoreContext(); +} + + +void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { + switch (expr->op()) { + case Token::DELETE: { + Comment cmnt(masm_, "[ UnaryOperation (DELETE)"); + Property* property = expr->expression()->AsProperty(); + VariableProxy* proxy = expr->expression()->AsVariableProxy(); + + if (property != NULL) { + VisitForStackValue(property->obj()); + VisitForStackValue(property->key()); + PushOperand(Smi::FromInt(language_mode())); + CallRuntimeWithOperands(Runtime::kDeleteProperty); + context()->Plug(eax); + } else if (proxy != NULL) { + Variable* var = proxy->var(); + // Delete of an unqualified identifier is disallowed in strict mode but + // "delete this" is allowed. + bool is_this = var->is_this(); + DCHECK(is_sloppy(language_mode()) || is_this); + if (var->IsUnallocated()) { + __ mov(eax, NativeContextOperand()); + __ push(ContextOperand(eax, Context::EXTENSION_INDEX)); + __ push(Immediate(var->name())); + __ Push(Smi::FromInt(SLOPPY)); + __ CallRuntime(Runtime::kDeleteProperty); + context()->Plug(eax); + } else { + DCHECK(!var->IsLookupSlot()); + DCHECK(var->IsStackAllocated() || var->IsContextSlot()); + // Result of deleting non-global variables is false. 'this' is + // not really a variable, though we implement it as one. The + // subexpression does not have side effects. + context()->Plug(is_this); + } + } else { + // Result of deleting non-property, non-variable reference is true. + // The subexpression may have side effects. + VisitForEffect(expr->expression()); + context()->Plug(true); + } + break; + } + + case Token::VOID: { + Comment cmnt(masm_, "[ UnaryOperation (VOID)"); + VisitForEffect(expr->expression()); + context()->Plug(isolate()->factory()->undefined_value()); + break; + } + + case Token::NOT: { + Comment cmnt(masm_, "[ UnaryOperation (NOT)"); + if (context()->IsEffect()) { + // Unary NOT has no side effects so it's only necessary to visit the + // subexpression. Match the optimizing compiler by not branching. + VisitForEffect(expr->expression()); + } else if (context()->IsTest()) { + const TestContext* test = TestContext::cast(context()); + // The labels are swapped for the recursive call. + VisitForControl(expr->expression(), + test->false_label(), + test->true_label(), + test->fall_through()); + context()->Plug(test->true_label(), test->false_label()); + } else { + // We handle value contexts explicitly rather than simply visiting + // for control and plugging the control flow into the context, + // because we need to prepare a pair of extra administrative AST ids + // for the optimizing compiler. + DCHECK(context()->IsAccumulatorValue() || context()->IsStackValue()); + Label materialize_true, materialize_false, done; + VisitForControl(expr->expression(), + &materialize_false, + &materialize_true, + &materialize_true); + if (!context()->IsAccumulatorValue()) OperandStackDepthIncrement(1); + __ bind(&materialize_true); + if (context()->IsAccumulatorValue()) { + __ mov(eax, isolate()->factory()->true_value()); + } else { + __ Push(isolate()->factory()->true_value()); + } + __ jmp(&done, Label::kNear); + __ bind(&materialize_false); + if (context()->IsAccumulatorValue()) { + __ mov(eax, isolate()->factory()->false_value()); + } else { + __ Push(isolate()->factory()->false_value()); + } + __ bind(&done); + } + break; + } + + case Token::TYPEOF: { + Comment cmnt(masm_, "[ UnaryOperation (TYPEOF)"); + { + AccumulatorValueContext context(this); + VisitForTypeofValue(expr->expression()); + } + __ mov(ebx, eax); + __ Call(isolate()->builtins()->Typeof(), RelocInfo::CODE_TARGET); + context()->Plug(eax); + break; + } + + default: + UNREACHABLE(); + } +} + + +void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { + DCHECK(expr->expression()->IsValidReferenceExpressionOrThis()); + + Comment cmnt(masm_, "[ CountOperation"); + + Property* prop = expr->expression()->AsProperty(); + LhsKind assign_type = Property::GetAssignType(prop); + + // Evaluate expression and get value. + if (assign_type == VARIABLE) { + DCHECK(expr->expression()->AsVariableProxy()->var() != NULL); + AccumulatorValueContext context(this); + EmitVariableLoad(expr->expression()->AsVariableProxy()); + } else { + // Reserve space for result of postfix operation. + if (expr->is_postfix() && !context()->IsEffect()) { + PushOperand(Smi::kZero); + } + switch (assign_type) { + case NAMED_PROPERTY: { + // Put the object both on the stack and in the register. + VisitForStackValue(prop->obj()); + __ mov(LoadDescriptor::ReceiverRegister(), Operand(esp, 0)); + EmitNamedPropertyLoad(prop); + break; + } + + case KEYED_PROPERTY: { + VisitForStackValue(prop->obj()); + VisitForStackValue(prop->key()); + __ mov(LoadDescriptor::ReceiverRegister(), + Operand(esp, kPointerSize)); // Object. + __ mov(LoadDescriptor::NameRegister(), Operand(esp, 0)); // Key. + EmitKeyedPropertyLoad(prop); + break; + } + + case NAMED_SUPER_PROPERTY: + case KEYED_SUPER_PROPERTY: + case VARIABLE: + UNREACHABLE(); + } + } + + // Convert old value into a number. + __ Call(isolate()->builtins()->ToNumber(), RelocInfo::CODE_TARGET); + RestoreContext(); + + // Save result for postfix expressions. + if (expr->is_postfix()) { + if (!context()->IsEffect()) { + // Save the result on the stack. If we have a named or keyed property + // we store the result under the receiver that is currently on top + // of the stack. + switch (assign_type) { + case VARIABLE: + PushOperand(eax); + break; + case NAMED_PROPERTY: + __ mov(Operand(esp, kPointerSize), eax); + break; + case KEYED_PROPERTY: + __ mov(Operand(esp, 2 * kPointerSize), eax); + break; + case NAMED_SUPER_PROPERTY: + case KEYED_SUPER_PROPERTY: + UNREACHABLE(); + break; + } + } + } + + SetExpressionPosition(expr); + + // Call stub for +1/-1. + __ mov(edx, eax); + __ mov(eax, Immediate(Smi::FromInt(1))); + Handle code = + CodeFactory::BinaryOperation(isolate(), expr->binary_op()).code(); + __ Call(code, RelocInfo::CODE_TARGET); + RestoreContext(); + + // Store the value returned in eax. + switch (assign_type) { + case VARIABLE: { + VariableProxy* proxy = expr->expression()->AsVariableProxy(); + if (expr->is_postfix()) { + // Perform the assignment as if via '='. + { EffectContext context(this); + EmitVariableAssignment(proxy->var(), Token::ASSIGN, expr->CountSlot(), + proxy->hole_check_mode()); + context.Plug(eax); + } + // For all contexts except EffectContext We have the result on + // top of the stack. + if (!context()->IsEffect()) { + context()->PlugTOS(); + } + } else { + // Perform the assignment as if via '='. + EmitVariableAssignment(proxy->var(), Token::ASSIGN, expr->CountSlot(), + proxy->hole_check_mode()); + context()->Plug(eax); + } + break; + } + case NAMED_PROPERTY: { + PopOperand(StoreDescriptor::ReceiverRegister()); + CallStoreIC(expr->CountSlot(), prop->key()->AsLiteral()->value()); + if (expr->is_postfix()) { + if (!context()->IsEffect()) { + context()->PlugTOS(); + } + } else { + context()->Plug(eax); + } + break; + } + case KEYED_PROPERTY: { + PopOperand(StoreDescriptor::NameRegister()); + PopOperand(StoreDescriptor::ReceiverRegister()); + CallKeyedStoreIC(expr->CountSlot()); + if (expr->is_postfix()) { + // Result is on the stack + if (!context()->IsEffect()) { + context()->PlugTOS(); + } + } else { + context()->Plug(eax); + } + break; + } + case NAMED_SUPER_PROPERTY: + case KEYED_SUPER_PROPERTY: + UNREACHABLE(); + break; + } +} + + +void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr, + Expression* sub_expr, + Handle check) { + Label materialize_true, materialize_false; + Label* if_true = NULL; + Label* if_false = NULL; + Label* fall_through = NULL; + context()->PrepareTest(&materialize_true, &materialize_false, + &if_true, &if_false, &fall_through); + + { AccumulatorValueContext context(this); + VisitForTypeofValue(sub_expr); + } + + Factory* factory = isolate()->factory(); + if (String::Equals(check, factory->number_string())) { + __ JumpIfSmi(eax, if_true); + __ cmp(FieldOperand(eax, HeapObject::kMapOffset), + isolate()->factory()->heap_number_map()); + Split(equal, if_true, if_false, fall_through); + } else if (String::Equals(check, factory->string_string())) { + __ JumpIfSmi(eax, if_false); + __ CmpObjectType(eax, FIRST_NONSTRING_TYPE, edx); + Split(below, if_true, if_false, fall_through); + } else if (String::Equals(check, factory->symbol_string())) { + __ JumpIfSmi(eax, if_false); + __ CmpObjectType(eax, SYMBOL_TYPE, edx); + Split(equal, if_true, if_false, fall_through); + } else if (String::Equals(check, factory->boolean_string())) { + __ cmp(eax, isolate()->factory()->true_value()); + __ j(equal, if_true); + __ cmp(eax, isolate()->factory()->false_value()); + Split(equal, if_true, if_false, fall_through); + } else if (String::Equals(check, factory->undefined_string())) { + __ cmp(eax, isolate()->factory()->null_value()); + __ j(equal, if_false); + __ JumpIfSmi(eax, if_false); + // Check for undetectable objects => true. + __ mov(edx, FieldOperand(eax, HeapObject::kMapOffset)); + __ test_b(FieldOperand(edx, Map::kBitFieldOffset), + Immediate(1 << Map::kIsUndetectable)); + Split(not_zero, if_true, if_false, fall_through); + } else if (String::Equals(check, factory->function_string())) { + __ JumpIfSmi(eax, if_false); + // Check for callable and not undetectable objects => true. + __ mov(edx, FieldOperand(eax, HeapObject::kMapOffset)); + __ movzx_b(ecx, FieldOperand(edx, Map::kBitFieldOffset)); + __ and_(ecx, (1 << Map::kIsCallable) | (1 << Map::kIsUndetectable)); + __ cmp(ecx, 1 << Map::kIsCallable); + Split(equal, if_true, if_false, fall_through); + } else if (String::Equals(check, factory->object_string())) { + __ JumpIfSmi(eax, if_false); + __ cmp(eax, isolate()->factory()->null_value()); + __ j(equal, if_true); + STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE); + __ CmpObjectType(eax, FIRST_JS_RECEIVER_TYPE, edx); + __ j(below, if_false); + // Check for callable or undetectable objects => false. + __ test_b(FieldOperand(edx, Map::kBitFieldOffset), + Immediate((1 << Map::kIsCallable) | (1 << Map::kIsUndetectable))); + Split(zero, if_true, if_false, fall_through); + } else { + if (if_false != fall_through) __ jmp(if_false); + } + context()->Plug(if_true, if_false); +} + + +void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { + Comment cmnt(masm_, "[ CompareOperation"); + + // First we try a fast inlined version of the compare when one of + // the operands is a literal. + if (TryLiteralCompare(expr)) return; + + // Always perform the comparison for its control flow. Pack the result + // into the expression's context after the comparison is performed. + Label materialize_true, materialize_false; + Label* if_true = NULL; + Label* if_false = NULL; + Label* fall_through = NULL; + context()->PrepareTest(&materialize_true, &materialize_false, + &if_true, &if_false, &fall_through); + + Token::Value op = expr->op(); + VisitForStackValue(expr->left()); + switch (op) { + case Token::IN: + VisitForStackValue(expr->right()); + SetExpressionPosition(expr); + EmitHasProperty(); + __ cmp(eax, isolate()->factory()->true_value()); + Split(equal, if_true, if_false, fall_through); + break; + + case Token::INSTANCEOF: { + VisitForAccumulatorValue(expr->right()); + SetExpressionPosition(expr); + PopOperand(edx); + __ Call(isolate()->builtins()->InstanceOf(), RelocInfo::CODE_TARGET); + RestoreContext(); + __ cmp(eax, isolate()->factory()->true_value()); + Split(equal, if_true, if_false, fall_through); + break; + } + + default: { + VisitForAccumulatorValue(expr->right()); + SetExpressionPosition(expr); + Condition cc = CompareIC::ComputeCondition(op); + PopOperand(edx); + + bool inline_smi_code = ShouldInlineSmiCase(op); + JumpPatchSite patch_site(masm_); + if (inline_smi_code) { + Label slow_case; + __ mov(ecx, edx); + __ or_(ecx, eax); + patch_site.EmitJumpIfNotSmi(ecx, &slow_case, Label::kNear); + __ cmp(edx, eax); + Split(cc, if_true, if_false, NULL); + __ bind(&slow_case); + } + + Handle ic = CodeFactory::CompareIC(isolate(), op).code(); + CallIC(ic); + patch_site.EmitPatchInfo(); + + __ test(eax, eax); + Split(cc, if_true, if_false, fall_through); + } + } + + // Convert the result of the comparison into one expected for this + // expression's context. + context()->Plug(if_true, if_false); +} + + +void FullCodeGenerator::EmitLiteralCompareNil(CompareOperation* expr, + Expression* sub_expr, + NilValue nil) { + Label materialize_true, materialize_false; + Label* if_true = NULL; + Label* if_false = NULL; + Label* fall_through = NULL; + context()->PrepareTest(&materialize_true, &materialize_false, + &if_true, &if_false, &fall_through); + + VisitForAccumulatorValue(sub_expr); + + Handle nil_value = nil == kNullValue + ? isolate()->factory()->null_value() + : isolate()->factory()->undefined_value(); + if (expr->op() == Token::EQ_STRICT) { + __ cmp(eax, nil_value); + Split(equal, if_true, if_false, fall_through); + } else { + __ JumpIfSmi(eax, if_false); + __ mov(eax, FieldOperand(eax, HeapObject::kMapOffset)); + __ test_b(FieldOperand(eax, Map::kBitFieldOffset), + Immediate(1 << Map::kIsUndetectable)); + Split(not_zero, if_true, if_false, fall_through); + } + context()->Plug(if_true, if_false); +} + + +Register FullCodeGenerator::result_register() { + return eax; +} + + +Register FullCodeGenerator::context_register() { + return esi; +} + +void FullCodeGenerator::LoadFromFrameField(int frame_offset, Register value) { + DCHECK_EQ(POINTER_SIZE_ALIGN(frame_offset), frame_offset); + __ mov(value, Operand(ebp, frame_offset)); +} + +void FullCodeGenerator::StoreToFrameField(int frame_offset, Register value) { + DCHECK_EQ(POINTER_SIZE_ALIGN(frame_offset), frame_offset); + __ mov(Operand(ebp, frame_offset), value); +} + + +void FullCodeGenerator::LoadContextField(Register dst, int context_index) { + __ mov(dst, ContextOperand(esi, context_index)); +} + + +void FullCodeGenerator::PushFunctionArgumentForContextAllocation() { + DeclarationScope* closure_scope = scope()->GetClosureScope(); + if (closure_scope->is_script_scope() || + closure_scope->is_module_scope()) { + // Contexts nested in the native context have a canonical empty function + // as their closure, not the anonymous closure containing the global + // code. + __ mov(eax, NativeContextOperand()); + PushOperand(ContextOperand(eax, Context::CLOSURE_INDEX)); + } else if (closure_scope->is_eval_scope()) { + // Contexts nested inside eval code have the same closure as the context + // calling eval, not the anonymous closure containing the eval code. + // Fetch it from the context. + PushOperand(ContextOperand(esi, Context::CLOSURE_INDEX)); + } else { + DCHECK(closure_scope->is_function_scope()); + PushOperand(Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); + } +} + + +#undef __ + + +static const byte kJnsInstruction = 0x79; +static const byte kJnsOffset = 0x11; +static const byte kNopByteOne = 0x66; +static const byte kNopByteTwo = 0x90; +#ifdef DEBUG +static const byte kCallInstruction = 0xe8; +#endif + + +void BackEdgeTable::PatchAt(Code* unoptimized_code, + Address pc, + BackEdgeState target_state, + Code* replacement_code) { + Address call_target_address = pc - kIntSize; + Address jns_instr_address = call_target_address - 3; + Address jns_offset_address = call_target_address - 2; + + switch (target_state) { + case INTERRUPT: + // sub , ;; Not changed + // jns ok + // call + // ok: + *jns_instr_address = kJnsInstruction; + *jns_offset_address = kJnsOffset; + break; + case ON_STACK_REPLACEMENT: + // sub , ;; Not changed + // nop + // nop + // call + // ok: + *jns_instr_address = kNopByteOne; + *jns_offset_address = kNopByteTwo; + break; + } + + Assembler::set_target_address_at(unoptimized_code->GetIsolate(), + call_target_address, unoptimized_code, + replacement_code->entry()); + unoptimized_code->GetHeap()->incremental_marking()->RecordCodeTargetPatch( + unoptimized_code, call_target_address, replacement_code); +} + + +BackEdgeTable::BackEdgeState BackEdgeTable::GetBackEdgeState( + Isolate* isolate, + Code* unoptimized_code, + Address pc) { + Address call_target_address = pc - kIntSize; + Address jns_instr_address = call_target_address - 3; + DCHECK_EQ(kCallInstruction, *(call_target_address - 1)); + + if (*jns_instr_address == kJnsInstruction) { + DCHECK_EQ(kJnsOffset, *(call_target_address - 2)); + DCHECK_EQ(isolate->builtins()->InterruptCheck()->entry(), + Assembler::target_address_at(call_target_address, + unoptimized_code)); + return INTERRUPT; + } + + DCHECK_EQ(kNopByteOne, *jns_instr_address); + DCHECK_EQ(kNopByteTwo, *(call_target_address - 2)); + + DCHECK_EQ( + isolate->builtins()->OnStackReplacement()->entry(), + Assembler::target_address_at(call_target_address, unoptimized_code)); + return ON_STACK_REPLACEMENT; +} + + +} // namespace internal +} // namespace v8 + +#endif // V8_TARGET_ARCH_X87 diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/full-codegen/x87/OWNERS qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/full-codegen/x87/OWNERS --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/full-codegen/x87/OWNERS 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/full-codegen/x87/OWNERS 2017-12-25 17:42:57.211465705 +0100 @@ -0,0 +1,2 @@ +weiliang.lin@intel.com +chunyang.dai@intel.com diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/gdb-jit.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/gdb-jit.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/gdb-jit.cc 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/gdb-jit.cc 2017-12-25 17:42:57.213465676 +0100 @@ -199,7 +199,7 @@ struct MachOSectionHeader { char sectname[16]; char segname[16]; -#if V8_TARGET_ARCH_IA32 +#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X87 uint32_t addr; uint32_t size; #else @@ -507,7 +507,7 @@ uint32_t cmd; uint32_t cmdsize; char segname[16]; -#if V8_TARGET_ARCH_IA32 +#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X87 uint32_t vmaddr; uint32_t vmsize; uint32_t fileoff; @@ -533,7 +533,7 @@ Writer::Slot WriteHeader(Writer* w) { DCHECK(w->position() == 0); Writer::Slot header = w->CreateSlotHere(); -#if V8_TARGET_ARCH_IA32 +#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X87 header->magic = 0xFEEDFACEu; header->cputype = 7; // i386 header->cpusubtype = 3; // CPU_SUBTYPE_I386_ALL @@ -558,7 +558,7 @@ uintptr_t code_size) { Writer::Slot cmd = w->CreateSlotHere(); -#if V8_TARGET_ARCH_IA32 +#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X87 cmd->cmd = LC_SEGMENT_32; #else cmd->cmd = LC_SEGMENT_64; @@ -646,7 +646,7 @@ void WriteHeader(Writer* w) { DCHECK(w->position() == 0); Writer::Slot header = w->CreateSlotHere(); -#if (V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_ARM || \ +#if (V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_X87 || \ (V8_TARGET_ARCH_X64 && V8_TARGET_ARCH_32_BIT)) const uint8_t ident[16] = { 0x7f, 'E', 'L', 'F', 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; @@ -668,7 +668,7 @@ #endif memcpy(header->ident, ident, 16); header->type = 1; -#if V8_TARGET_ARCH_IA32 +#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X87 header->machine = 3; #elif V8_TARGET_ARCH_X64 // Processor identification value for x64 is 62 as defined in @@ -783,8 +783,8 @@ Binding binding() const { return static_cast(info >> 4); } -#if (V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_ARM || \ - (V8_TARGET_ARCH_X64 && V8_TARGET_ARCH_32_BIT) || \ +#if (V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_X87 || \ + (V8_TARGET_ARCH_X64 && V8_TARGET_ARCH_32_BIT) || \ (V8_TARGET_ARCH_S390 && V8_TARGET_ARCH_32_BIT)) struct SerializedLayout { SerializedLayout(uint32_t name, @@ -1146,7 +1146,7 @@ w->Write(desc_->CodeStart() + desc_->CodeSize()); Writer::Slot fb_block_size = w->CreateSlotHere(); uintptr_t fb_block_start = w->position(); -#if V8_TARGET_ARCH_IA32 +#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X87 w->Write(DW_OP_reg5); // The frame pointer's here on ia32 #elif V8_TARGET_ARCH_X64 w->Write(DW_OP_reg6); // and here on x64. diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/globals.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/globals.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/globals.h 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/globals.h 2017-12-25 17:42:57.214465661 +0100 @@ -167,7 +167,7 @@ const int kPCOnStackSize = kRegisterSize; const int kFPOnStackSize = kRegisterSize; -#if V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_IA32 +#if V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X87 const int kElidedFrameSlots = kPCOnStackSize / kPointerSize; #else const int kElidedFrameSlots = 0; @@ -912,10 +912,16 @@ }; // The mips architecture prior to revision 5 has inverted encoding for sNaN. +// The x87 FPU convert the sNaN to qNaN automatically when loading sNaN from +// memmory. +// Use mips sNaN which is a not used qNaN in x87 port as sNaN to workaround this +// issue +// for some test cases. #if (V8_TARGET_ARCH_MIPS && !defined(_MIPS_ARCH_MIPS32R6) && \ (!defined(USE_SIMULATOR) || !defined(_MIPS_TARGET_SIMULATOR))) || \ (V8_TARGET_ARCH_MIPS64 && !defined(_MIPS_ARCH_MIPS64R6) && \ - (!defined(USE_SIMULATOR) || !defined(_MIPS_TARGET_SIMULATOR))) + (!defined(USE_SIMULATOR) || !defined(_MIPS_TARGET_SIMULATOR))) || \ + (V8_TARGET_ARCH_X87) const uint32_t kHoleNanUpper32 = 0xFFFF7FFF; const uint32_t kHoleNanLower32 = 0xFFFF7FFF; #else diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/ic/x87/access-compiler-x87.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/ic/x87/access-compiler-x87.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/ic/x87/access-compiler-x87.cc 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/ic/x87/access-compiler-x87.cc 2017-12-25 17:42:57.214465661 +0100 @@ -0,0 +1,40 @@ +// Copyright 2014 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#if V8_TARGET_ARCH_X87 + +#include "src/ic/access-compiler.h" + +namespace v8 { +namespace internal { + +#define __ ACCESS_MASM(masm) + +void PropertyAccessCompiler::GenerateTailCall(MacroAssembler* masm, + Handle code) { + __ jmp(code, RelocInfo::CODE_TARGET); +} + +void PropertyAccessCompiler::InitializePlatformSpecific( + AccessCompilerData* data) { + Register receiver = LoadDescriptor::ReceiverRegister(); + Register name = LoadDescriptor::NameRegister(); + + // Load calling convention. + // receiver, name, scratch1, scratch2, scratch3. + Register load_registers[] = {receiver, name, ebx, eax, edi}; + + // Store calling convention. + // receiver, name, scratch1, scratch2. + Register store_registers[] = {receiver, name, ebx, edi}; + + data->Initialize(arraysize(load_registers), load_registers, + arraysize(store_registers), store_registers); +} + +#undef __ +} // namespace internal +} // namespace v8 + +#endif // V8_TARGET_ARCH_X87 diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/ic/x87/handler-compiler-x87.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/ic/x87/handler-compiler-x87.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/ic/x87/handler-compiler-x87.cc 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/ic/x87/handler-compiler-x87.cc 2017-12-28 03:55:18.870428775 +0100 @@ -0,0 +1,447 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#if V8_TARGET_ARCH_X87 + +#include "src/ic/handler-compiler.h" + +#include "src/api-arguments.h" +#include "src/field-type.h" +#include "src/ic/call-optimization.h" +#include "src/ic/ic.h" +#include "src/isolate-inl.h" + +namespace v8 { +namespace internal { + +#define __ ACCESS_MASM(masm) + +void NamedLoadHandlerCompiler::GenerateLoadViaGetterForDeopt( + MacroAssembler* masm) { + { + FrameScope scope(masm, StackFrame::INTERNAL); + // If we generate a global code snippet for deoptimization only, remember + // the place to continue after deoptimization. + masm->isolate()->heap()->SetGetterStubDeoptPCOffset(masm->pc_offset()); + // Restore context register. + __ pop(esi); + } + __ ret(0); +} + + +void PropertyHandlerCompiler::PushVectorAndSlot(Register vector, + Register slot) { + MacroAssembler* masm = this->masm(); + STATIC_ASSERT(LoadWithVectorDescriptor::kSlot < + LoadWithVectorDescriptor::kVector); + STATIC_ASSERT(StoreWithVectorDescriptor::kSlot < + StoreWithVectorDescriptor::kVector); + STATIC_ASSERT(StoreTransitionDescriptor::kSlot < + StoreTransitionDescriptor::kVector); + __ push(slot); + __ push(vector); +} + + +void PropertyHandlerCompiler::PopVectorAndSlot(Register vector, Register slot) { + MacroAssembler* masm = this->masm(); + __ pop(vector); + __ pop(slot); +} + + +void PropertyHandlerCompiler::DiscardVectorAndSlot() { + MacroAssembler* masm = this->masm(); + // Remove vector and slot. + __ add(esp, Immediate(2 * kPointerSize)); +} + +void PropertyHandlerCompiler::GenerateDictionaryNegativeLookup( + MacroAssembler* masm, Label* miss_label, Register receiver, + Handle name, Register scratch0, Register scratch1) { + DCHECK(name->IsUniqueName()); + DCHECK(!receiver.is(scratch0)); + Counters* counters = masm->isolate()->counters(); + __ IncrementCounter(counters->negative_lookups(), 1); + __ IncrementCounter(counters->negative_lookups_miss(), 1); + + __ mov(scratch0, FieldOperand(receiver, HeapObject::kMapOffset)); + + const int kInterceptorOrAccessCheckNeededMask = + (1 << Map::kHasNamedInterceptor) | (1 << Map::kIsAccessCheckNeeded); + + // Bail out if the receiver has a named interceptor or requires access checks. + __ test_b(FieldOperand(scratch0, Map::kBitFieldOffset), + Immediate(kInterceptorOrAccessCheckNeededMask)); + __ j(not_zero, miss_label); + + // Check that receiver is a JSObject. + __ CmpInstanceType(scratch0, FIRST_JS_RECEIVER_TYPE); + __ j(below, miss_label); + + // Load properties array. + Register properties = scratch0; + __ mov(properties, FieldOperand(receiver, JSObject::kPropertiesOrHashOffset)); + + // Check that the properties array is a dictionary. + __ cmp(FieldOperand(properties, HeapObject::kMapOffset), + Immediate(masm->isolate()->factory()->hash_table_map())); + __ j(not_equal, miss_label); + + Label done; + NameDictionaryLookupStub::GenerateNegativeLookup(masm, miss_label, &done, + properties, name, scratch1); + __ bind(&done); + __ DecrementCounter(counters->negative_lookups_miss(), 1); +} + +// Generate call to api function. +// This function uses push() to generate smaller, faster code than +// the version above. It is an optimization that should will be removed +// when api call ICs are generated in hydrogen. +void PropertyHandlerCompiler::GenerateApiAccessorCall( + MacroAssembler* masm, const CallOptimization& optimization, + Handle receiver_map, Register receiver, Register scratch, + bool is_store, Register store_parameter, Register accessor_holder, + int accessor_index) { + DCHECK(!accessor_holder.is(scratch)); + // Copy return value. + __ pop(scratch); + + if (is_store) { + // Discard stack arguments. + __ add(esp, Immediate(StoreWithVectorDescriptor::kStackArgumentsCount * + kPointerSize)); + } + // Write the receiver and arguments to stack frame. + __ push(receiver); + if (is_store) { + DCHECK(!AreAliased(receiver, scratch, store_parameter)); + __ push(store_parameter); + } + __ push(scratch); + // Stack now matches JSFunction abi. + DCHECK(optimization.is_simple_api_call()); + + // Abi for CallApiCallbackStub. + Register callee = edi; + Register data = ebx; + Register holder = ecx; + Register api_function_address = edx; + scratch = no_reg; + + // Put callee in place. + __ LoadAccessor(callee, accessor_holder, accessor_index, + is_store ? ACCESSOR_SETTER : ACCESSOR_GETTER); + + // Put holder in place. + CallOptimization::HolderLookup holder_lookup; + optimization.LookupHolderOfExpectedType(receiver_map, &holder_lookup); + switch (holder_lookup) { + case CallOptimization::kHolderIsReceiver: + __ Move(holder, receiver); + break; + case CallOptimization::kHolderFound: + __ mov(holder, FieldOperand(receiver, HeapObject::kMapOffset)); + __ mov(holder, FieldOperand(holder, Map::kPrototypeOffset)); + break; + case CallOptimization::kHolderNotFound: + UNREACHABLE(); + break; + } + + Isolate* isolate = masm->isolate(); + Handle api_call_info = optimization.api_call_info(); + // Put call data in place. + if (api_call_info->data()->IsUndefined(isolate)) { + __ mov(data, Immediate(isolate->factory()->undefined_value())); + } else { + if (optimization.is_constant_call()) { + __ mov(data, FieldOperand(callee, JSFunction::kSharedFunctionInfoOffset)); + __ mov(data, FieldOperand(data, SharedFunctionInfo::kFunctionDataOffset)); + __ mov(data, FieldOperand(data, FunctionTemplateInfo::kCallCodeOffset)); + } else { + __ mov(data, FieldOperand(callee, FunctionTemplateInfo::kCallCodeOffset)); + } + __ mov(data, FieldOperand(data, CallHandlerInfo::kDataOffset)); + } + + // Put api_function_address in place. + Address function_address = v8::ToCData
(api_call_info->callback()); + __ mov(api_function_address, Immediate(function_address)); + + // Jump to stub. + CallApiCallbackStub stub(isolate, is_store, !optimization.is_constant_call()); + __ TailCallStub(&stub); +} + + +// Generate code to check that a global property cell is empty. Create +// the property cell at compilation time if no cell exists for the +// property. +void PropertyHandlerCompiler::GenerateCheckPropertyCell( + MacroAssembler* masm, Handle global, Handle name, + Register scratch, Label* miss) { + Handle cell = JSGlobalObject::EnsureEmptyPropertyCell( + global, name, PropertyCellType::kInvalidated); + Isolate* isolate = masm->isolate(); + DCHECK(cell->value()->IsTheHole(isolate)); + Handle weak_cell = isolate->factory()->NewWeakCell(cell); + __ LoadWeakValue(scratch, weak_cell, miss); + __ cmp(FieldOperand(scratch, PropertyCell::kValueOffset), + Immediate(isolate->factory()->the_hole_value())); + __ j(not_equal, miss); +} + + +void NamedStoreHandlerCompiler::GenerateStoreViaSetter( + MacroAssembler* masm, Handle map, Register receiver, Register holder, + int accessor_index, int expected_arguments, Register scratch) { + // ----------- S t a t e ------------- + // -- esp[12] : value + // -- esp[8] : slot + // -- esp[4] : vector + // -- esp[0] : return address + // ----------------------------------- + __ LoadParameterFromStack(value(), Descriptor::kValue); + + { + FrameScope scope(masm, StackFrame::INTERNAL); + + // Save context register + __ push(esi); + // Save value register, so we can restore it later. + __ push(value()); + + if (accessor_index >= 0) { + DCHECK(!holder.is(scratch)); + DCHECK(!receiver.is(scratch)); + DCHECK(!value().is(scratch)); + // Call the JavaScript setter with receiver and value on the stack. + if (map->IsJSGlobalObjectMap()) { + __ mov(scratch, + FieldOperand(receiver, JSGlobalObject::kGlobalProxyOffset)); + receiver = scratch; + } + __ push(receiver); + __ push(value()); + __ LoadAccessor(edi, holder, accessor_index, ACCESSOR_SETTER); + __ Set(eax, 1); + __ Call(masm->isolate()->builtins()->CallFunction( + ConvertReceiverMode::kNotNullOrUndefined), + RelocInfo::CODE_TARGET); + } else { + // If we generate a global code snippet for deoptimization only, remember + // the place to continue after deoptimization. + masm->isolate()->heap()->SetSetterStubDeoptPCOffset(masm->pc_offset()); + } + + // We have to return the passed value, not the return value of the setter. + __ pop(eax); + // Restore context register. + __ pop(esi); + } + if (accessor_index >= 0) { + __ ret(StoreWithVectorDescriptor::kStackArgumentsCount * kPointerSize); + } else { + // If we generate a global code snippet for deoptimization only, don't try + // to drop stack arguments for the StoreIC because they are not a part of + // expression stack and deoptimizer does not reconstruct them. + __ ret(0); + } +} + +#undef __ +#define __ ACCESS_MASM(masm()) + + +void NamedStoreHandlerCompiler::GenerateRestoreName(Label* label, + Handle name) { + if (!label->is_unused()) { + __ bind(label); + __ mov(this->name(), Immediate(name)); + } +} + +void PropertyHandlerCompiler::GenerateAccessCheck( + Handle native_context_cell, Register scratch1, Register scratch2, + Label* miss, bool compare_native_contexts_only) { + Label done; + // Load current native context. + __ mov(scratch1, NativeContextOperand()); + // Load expected native context. + __ LoadWeakValue(scratch2, native_context_cell, miss); + __ cmp(scratch1, scratch2); + + if (!compare_native_contexts_only) { + __ j(equal, &done); + + // Compare security tokens of current and expected native contexts. + __ mov(scratch1, ContextOperand(scratch1, Context::SECURITY_TOKEN_INDEX)); + __ mov(scratch2, ContextOperand(scratch2, Context::SECURITY_TOKEN_INDEX)); + __ cmp(scratch1, scratch2); + } + __ j(not_equal, miss); + + __ bind(&done); +} + +Register PropertyHandlerCompiler::CheckPrototypes( + Register object_reg, Register holder_reg, Register scratch1, + Register scratch2, Handle name, Label* miss) { + Handle receiver_map = map(); + + // Make sure there's no overlap between holder and object registers. + DCHECK(!scratch1.is(object_reg) && !scratch1.is(holder_reg)); + DCHECK(!scratch2.is(object_reg) && !scratch2.is(holder_reg) && + !scratch2.is(scratch1)); + + Handle validity_cell = + Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate()); + if (!validity_cell.is_null()) { + DCHECK_EQ(Smi::FromInt(Map::kPrototypeChainValid), validity_cell->value()); + // Operand::ForCell(...) points to the cell's payload! + __ cmp(Operand::ForCell(validity_cell), + Immediate(Smi::FromInt(Map::kPrototypeChainValid))); + __ j(not_equal, miss); + } + + // Keep track of the current object in register reg. + Register reg = object_reg; + int depth = 0; + + Handle current = Handle::null(); + if (receiver_map->IsJSGlobalObjectMap()) { + current = isolate()->global_object(); + } + + Handle current_map(receiver_map->GetPrototypeChainRootMap(isolate()), + isolate()); + Handle holder_map(holder()->map()); + // Traverse the prototype chain and check the maps in the prototype chain for + // fast and global objects or do negative lookup for normal objects. + while (!current_map.is_identical_to(holder_map)) { + ++depth; + + if (current_map->IsJSGlobalObjectMap()) { + GenerateCheckPropertyCell(masm(), Handle::cast(current), + name, scratch2, miss); + } else if (current_map->is_dictionary_map()) { + DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast. + DCHECK(name->IsUniqueName()); + DCHECK(current.is_null() || + current->property_dictionary()->FindEntry(name) == + NameDictionary::kNotFound); + + if (depth > 1) { + Handle weak_cell = + Map::GetOrCreatePrototypeWeakCell(current, isolate()); + __ LoadWeakValue(reg, weak_cell, miss); + } + GenerateDictionaryNegativeLookup(masm(), miss, reg, name, scratch1, + scratch2); + } + + reg = holder_reg; // From now on the object will be in holder_reg. + // Go to the next object in the prototype chain. + current = handle(JSObject::cast(current_map->prototype())); + current_map = handle(current->map()); + } + + DCHECK(!current_map->IsJSGlobalProxyMap()); + + // Log the check depth. + LOG(isolate(), IntEvent("check-maps-depth", depth + 1)); + + if (depth != 0) { + Handle weak_cell = + Map::GetOrCreatePrototypeWeakCell(current, isolate()); + __ LoadWeakValue(reg, weak_cell, miss); + } + + // Return the register containing the holder. + return reg; +} + + +void NamedLoadHandlerCompiler::FrontendFooter(Handle name, Label* miss) { + if (!miss->is_unused()) { + Label success; + __ jmp(&success); + __ bind(miss); + if (IC::ShouldPushPopSlotAndVector(kind())) { + DCHECK(kind() == Code::LOAD_IC); + PopVectorAndSlot(); + } + TailCallBuiltin(masm(), MissBuiltin(kind())); + __ bind(&success); + } +} + + +void NamedStoreHandlerCompiler::FrontendFooter(Handle name, Label* miss) { + if (!miss->is_unused()) { + Label success; + __ jmp(&success); + GenerateRestoreName(miss, name); + DCHECK(!IC::ShouldPushPopSlotAndVector(kind())); + TailCallBuiltin(masm(), MissBuiltin(kind())); + __ bind(&success); + } +} + +void NamedStoreHandlerCompiler::ZapStackArgumentsRegisterAliases() { + // Zap register aliases of the arguments passed on the stack to ensure they + // are properly loaded by the handler (debug-only). + STATIC_ASSERT(Descriptor::kPassLastArgsOnStack); + STATIC_ASSERT(Descriptor::kStackArgumentsCount == 3); + __ mov(Descriptor::ValueRegister(), Immediate(kDebugZapValue)); + __ mov(Descriptor::SlotRegister(), Immediate(kDebugZapValue)); + __ mov(Descriptor::VectorRegister(), Immediate(kDebugZapValue)); +} + +Handle NamedStoreHandlerCompiler::CompileStoreCallback( + Handle object, Handle name, Handle callback, + LanguageMode language_mode) { + Register holder_reg = Frontend(name); + __ LoadParameterFromStack(value(), Descriptor::kValue); + + __ pop(scratch1()); // remove the return address + // Discard stack arguments. + __ add(esp, Immediate(StoreWithVectorDescriptor::kStackArgumentsCount * + kPointerSize)); + __ push(receiver()); + __ push(holder_reg); + // If the callback cannot leak, then push the callback directly, + // otherwise wrap it in a weak cell. + if (callback->data()->IsUndefined(isolate()) || callback->data()->IsSmi()) { + __ Push(callback); + } else { + Handle cell = isolate()->factory()->NewWeakCell(callback); + __ Push(cell); + } + __ Push(name); + __ push(value()); + __ push(Immediate(Smi::FromInt(language_mode))); + __ push(scratch1()); // restore return address + + // Do tail-call to the runtime system. + __ TailCallRuntime(Runtime::kStoreCallbackProperty); + + // Return the generated code. + return GetCode(kind(), name); +} + + +Register NamedStoreHandlerCompiler::value() { + return StoreDescriptor::ValueRegister(); +} + + +#undef __ +} // namespace internal +} // namespace v8 + +#endif // V8_TARGET_ARCH_X87 diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/ic/x87/ic-x87.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/ic/x87/ic-x87.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/ic/x87/ic-x87.cc 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/ic/x87/ic-x87.cc 2017-12-25 17:42:57.215465646 +0100 @@ -0,0 +1,84 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#if V8_TARGET_ARCH_X87 + +#include "src/codegen.h" +#include "src/ic/ic.h" +#include "src/ic/stub-cache.h" + +namespace v8 { +namespace internal { + + +Condition CompareIC::ComputeCondition(Token::Value op) { + switch (op) { + case Token::EQ_STRICT: + case Token::EQ: + return equal; + case Token::LT: + return less; + case Token::GT: + return greater; + case Token::LTE: + return less_equal; + case Token::GTE: + return greater_equal; + default: + UNREACHABLE(); + } +} + + +bool CompareIC::HasInlinedSmiCode(Address address) { + // The address of the instruction following the call. + Address test_instruction_address = + address + Assembler::kCallTargetAddressOffset; + + // If the instruction following the call is not a test al, nothing + // was inlined. + return *test_instruction_address == Assembler::kTestAlByte; +} + + +void PatchInlinedSmiCode(Isolate* isolate, Address address, + InlinedSmiCheck check) { + // The address of the instruction following the call. + Address test_instruction_address = + address + Assembler::kCallTargetAddressOffset; + + // If the instruction following the call is not a test al, nothing + // was inlined. + if (*test_instruction_address != Assembler::kTestAlByte) { + DCHECK(*test_instruction_address == Assembler::kNopByte); + return; + } + + Address delta_address = test_instruction_address + 1; + // The delta to the start of the map check instruction and the + // condition code uses at the patched jump. + uint8_t delta = *reinterpret_cast(delta_address); + if (FLAG_trace_ic) { + LOG(isolate, PatchIC(address, test_instruction_address, delta)); + } + + // Patch with a short conditional jump. Enabling means switching from a short + // jump-if-carry/not-carry to jump-if-zero/not-zero, whereas disabling is the + // reverse operation of that. + Address jmp_address = test_instruction_address - delta; + DCHECK((check == ENABLE_INLINED_SMI_CHECK) + ? (*jmp_address == Assembler::kJncShortOpcode || + *jmp_address == Assembler::kJcShortOpcode) + : (*jmp_address == Assembler::kJnzShortOpcode || + *jmp_address == Assembler::kJzShortOpcode)); + Condition cc = + (check == ENABLE_INLINED_SMI_CHECK) + ? (*jmp_address == Assembler::kJncShortOpcode ? not_zero : zero) + : (*jmp_address == Assembler::kJnzShortOpcode ? not_carry : carry); + *jmp_address = static_cast(Assembler::kJccShortPrefix | cc); +} +} // namespace internal +} // namespace v8 + +#endif // V8_TARGET_ARCH_X87 diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/ic/x87/OWNERS qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/ic/x87/OWNERS --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/ic/x87/OWNERS 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/ic/x87/OWNERS 2017-12-25 17:42:57.214465661 +0100 @@ -0,0 +1,2 @@ +weiliang.lin@intel.com +chunyang.dai@intel.com diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/inspector/BUILD.gn qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/inspector/BUILD.gn --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/inspector/BUILD.gn 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/inspector/BUILD.gn 2017-12-25 13:05:24.029939435 +0100 @@ -106,7 +106,7 @@ "/wd4996", # Deprecated function call. ] } - if (is_component_build) { + if (is_component_build || v8_build_shared) { defines = [ "BUILDING_V8_SHARED" ] } } diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/interface-descriptors.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/interface-descriptors.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/interface-descriptors.h 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/interface-descriptors.h 2017-12-25 17:42:57.215465646 +0100 @@ -392,7 +392,7 @@ static const Register ValueRegister(); static const Register SlotRegister(); -#if V8_TARGET_ARCH_IA32 +#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X87 static const bool kPassLastArgsOnStack = true; #else static const bool kPassLastArgsOnStack = false; diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/interpreter/interpreter-assembler.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/interpreter/interpreter-assembler.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/interpreter/interpreter-assembler.cc 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/interpreter/interpreter-assembler.cc 2017-12-25 17:42:57.215465646 +0100 @@ -1367,8 +1367,9 @@ bool InterpreterAssembler::TargetSupportsUnalignedAccess() { #if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 return false; -#elif V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_S390 || \ - V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_ARM64 || V8_TARGET_ARCH_PPC +#elif V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_X87 || \ + V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_ARM64 || \ + V8_TARGET_ARCH_PPC return true; #else #error "Unknown Architecture" diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/log.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/log.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/log.cc 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/log.cc 2017-12-25 17:42:57.216465632 +0100 @@ -370,6 +370,8 @@ const char arch[] = "ppc"; #elif V8_TARGET_ARCH_MIPS const char arch[] = "mips"; +#elif V8_TARGET_ARCH_X87 + const char arch[] = "x87"; #elif V8_TARGET_ARCH_ARM64 const char arch[] = "arm64"; #elif V8_TARGET_ARCH_S390 diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/macro-assembler.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/macro-assembler.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/macro-assembler.h 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/macro-assembler.h 2017-12-25 17:42:57.216465632 +0100 @@ -52,6 +52,8 @@ #elif V8_TARGET_ARCH_S390 #include "src/s390/constants-s390.h" #include "src/s390/macro-assembler-s390.h" +#elif V8_TARGET_ARCH_X87 +#include "src/x87/macro-assembler-x87.h" #else #error Unsupported target architecture. #endif diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/regexp/jsregexp.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/regexp/jsregexp.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/regexp/jsregexp.cc 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/regexp/jsregexp.cc 2017-12-25 17:42:57.217465617 +0100 @@ -48,6 +48,8 @@ #include "src/regexp/mips/regexp-macro-assembler-mips.h" #elif V8_TARGET_ARCH_MIPS64 #include "src/regexp/mips64/regexp-macro-assembler-mips64.h" +#elif V8_TARGET_ARCH_X87 +#include "src/regexp/x87/regexp-macro-assembler-x87.h" #else #error Unsupported target architecture. #endif @@ -6760,6 +6762,9 @@ #elif V8_TARGET_ARCH_MIPS64 RegExpMacroAssemblerMIPS macro_assembler(isolate, zone, mode, (data->capture_count + 1) * 2); +#elif V8_TARGET_ARCH_X87 + RegExpMacroAssemblerX87 macro_assembler(isolate, zone, mode, + (data->capture_count + 1) * 2); #else #error "Unsupported architecture" #endif diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/regexp/x87/OWNERS qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/regexp/x87/OWNERS --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/regexp/x87/OWNERS 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/regexp/x87/OWNERS 2017-12-25 17:42:57.217465617 +0100 @@ -0,0 +1,2 @@ +weiliang.lin@intel.com +chunyang.dai@intel.com diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/regexp/x87/regexp-macro-assembler-x87.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/regexp/x87/regexp-macro-assembler-x87.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/regexp/x87/regexp-macro-assembler-x87.cc 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/regexp/x87/regexp-macro-assembler-x87.cc 2017-12-28 04:38:12.986938239 +0100 @@ -0,0 +1,1273 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#if V8_TARGET_ARCH_X87 + +#include "src/regexp/x87/regexp-macro-assembler-x87.h" + +#include "src/log.h" +#include "src/macro-assembler.h" +#include "src/regexp/regexp-macro-assembler.h" +#include "src/regexp/regexp-stack.h" +#include "src/unicode.h" + +namespace v8 { +namespace internal { + +#ifndef V8_INTERPRETED_REGEXP +/* + * This assembler uses the following register assignment convention + * - edx : Current character. Must be loaded using LoadCurrentCharacter + * before using any of the dispatch methods. Temporarily stores the + * index of capture start after a matching pass for a global regexp. + * - edi : Current position in input, as negative offset from end of string. + * Please notice that this is the byte offset, not the character offset! + * - esi : end of input (points to byte after last character in input). + * - ebp : Frame pointer. Used to access arguments, local variables and + * RegExp registers. + * - esp : Points to tip of C stack. + * - ecx : Points to tip of backtrack stack + * + * The registers eax and ebx are free to use for computations. + * + * Each call to a public method should retain this convention. + * The stack will have the following structure: + * - Isolate* isolate (address of the current isolate) + * - direct_call (if 1, direct call from JavaScript code, if 0 + * call through the runtime system) + * - stack_area_base (high end of the memory area to use as + * backtracking stack) + * - capture array size (may fit multiple sets of matches) + * - int* capture_array (int[num_saved_registers_], for output). + * - end of input (address of end of string) + * - start of input (address of first character in string) + * - start index (character index of start) + * - String* input_string (location of a handle containing the string) + * --- frame alignment (if applicable) --- + * - return address + * ebp-> - old ebp + * - backup of caller esi + * - backup of caller edi + * - backup of caller ebx + * - success counter (only for global regexps to count matches). + * - Offset of location before start of input (effectively character + * string start - 1). Used to initialize capture registers to a + * non-position. + * - register 0 ebp[-4] (only positions must be stored in the first + * - register 1 ebp[-8] num_saved_registers_ registers) + * - ... + * + * The first num_saved_registers_ registers are initialized to point to + * "character -1" in the string (i.e., char_size() bytes before the first + * character of the string). The remaining registers starts out as garbage. + * + * The data up to the return address must be placed there by the calling + * code, by calling the code entry as cast to a function with the signature: + * int (*match)(String* input_string, + * int start_index, + * Address start, + * Address end, + * int* capture_output_array, + * int num_capture_registers, + * byte* stack_area_base, + * bool direct_call = false, + * Isolate* isolate); + */ + +#define __ ACCESS_MASM(masm_) + +RegExpMacroAssemblerX87::RegExpMacroAssemblerX87(Isolate* isolate, Zone* zone, + Mode mode, + int registers_to_save) + : NativeRegExpMacroAssembler(isolate, zone), + masm_(new MacroAssembler(isolate, NULL, kRegExpCodeSize, + CodeObjectRequired::kYes)), + mode_(mode), + num_registers_(registers_to_save), + num_saved_registers_(registers_to_save), + entry_label_(), + start_label_(), + success_label_(), + backtrack_label_(), + exit_label_() { + DCHECK_EQ(0, registers_to_save % 2); + __ jmp(&entry_label_); // We'll write the entry code later. + __ bind(&start_label_); // And then continue from here. +} + + +RegExpMacroAssemblerX87::~RegExpMacroAssemblerX87() { + delete masm_; + // Unuse labels in case we throw away the assembler without calling GetCode. + entry_label_.Unuse(); + start_label_.Unuse(); + success_label_.Unuse(); + backtrack_label_.Unuse(); + exit_label_.Unuse(); + check_preempt_label_.Unuse(); + stack_overflow_label_.Unuse(); +} + + +int RegExpMacroAssemblerX87::stack_limit_slack() { + return RegExpStack::kStackLimitSlack; +} + + +void RegExpMacroAssemblerX87::AdvanceCurrentPosition(int by) { + if (by != 0) { + __ add(edi, Immediate(by * char_size())); + } +} + + +void RegExpMacroAssemblerX87::AdvanceRegister(int reg, int by) { + DCHECK(reg >= 0); + DCHECK(reg < num_registers_); + if (by != 0) { + __ add(register_location(reg), Immediate(by)); + } +} + + +void RegExpMacroAssemblerX87::Backtrack() { + CheckPreemption(); + // Pop Code* offset from backtrack stack, add Code* and jump to location. + Pop(ebx); + __ add(ebx, Immediate(masm_->CodeObject())); + __ jmp(ebx); +} + + +void RegExpMacroAssemblerX87::Bind(Label* label) { + __ bind(label); +} + + +void RegExpMacroAssemblerX87::CheckCharacter(uint32_t c, Label* on_equal) { + __ cmp(current_character(), c); + BranchOrBacktrack(equal, on_equal); +} + + +void RegExpMacroAssemblerX87::CheckCharacterGT(uc16 limit, Label* on_greater) { + __ cmp(current_character(), limit); + BranchOrBacktrack(greater, on_greater); +} + + +void RegExpMacroAssemblerX87::CheckAtStart(Label* on_at_start) { + __ lea(eax, Operand(edi, -char_size())); + __ cmp(eax, Operand(ebp, kStringStartMinusOne)); + BranchOrBacktrack(equal, on_at_start); +} + + +void RegExpMacroAssemblerX87::CheckNotAtStart(int cp_offset, + Label* on_not_at_start) { + __ lea(eax, Operand(edi, -char_size() + cp_offset * char_size())); + __ cmp(eax, Operand(ebp, kStringStartMinusOne)); + BranchOrBacktrack(not_equal, on_not_at_start); +} + + +void RegExpMacroAssemblerX87::CheckCharacterLT(uc16 limit, Label* on_less) { + __ cmp(current_character(), limit); + BranchOrBacktrack(less, on_less); +} + + +void RegExpMacroAssemblerX87::CheckGreedyLoop(Label* on_equal) { + Label fallthrough; + __ cmp(edi, Operand(backtrack_stackpointer(), 0)); + __ j(not_equal, &fallthrough); + __ add(backtrack_stackpointer(), Immediate(kPointerSize)); // Pop. + BranchOrBacktrack(no_condition, on_equal); + __ bind(&fallthrough); +} + +void RegExpMacroAssemblerX87::CheckNotBackReferenceIgnoreCase( + int start_reg, bool read_backward, bool unicode, Label* on_no_match) { + Label fallthrough; + __ mov(edx, register_location(start_reg)); // Index of start of capture + __ mov(ebx, register_location(start_reg + 1)); // Index of end of capture + __ sub(ebx, edx); // Length of capture. + + // At this point, the capture registers are either both set or both cleared. + // If the capture length is zero, then the capture is either empty or cleared. + // Fall through in both cases. + __ j(equal, &fallthrough); + + // Check that there are sufficient characters left in the input. + if (read_backward) { + __ mov(eax, Operand(ebp, kStringStartMinusOne)); + __ add(eax, ebx); + __ cmp(edi, eax); + BranchOrBacktrack(less_equal, on_no_match); + } else { + __ mov(eax, edi); + __ add(eax, ebx); + BranchOrBacktrack(greater, on_no_match); + } + + if (mode_ == LATIN1) { + Label success; + Label fail; + Label loop_increment; + // Save register contents to make the registers available below. + __ push(edi); + __ push(backtrack_stackpointer()); + // After this, the eax, ecx, and edi registers are available. + + __ add(edx, esi); // Start of capture + __ add(edi, esi); // Start of text to match against capture. + if (read_backward) { + __ sub(edi, ebx); // Offset by length when matching backwards. + } + __ add(ebx, edi); // End of text to match against capture. + + Label loop; + __ bind(&loop); + __ movzx_b(eax, Operand(edi, 0)); + __ cmpb_al(Operand(edx, 0)); + __ j(equal, &loop_increment); + + // Mismatch, try case-insensitive match (converting letters to lower-case). + __ or_(eax, 0x20); // Convert match character to lower-case. + __ lea(ecx, Operand(eax, -'a')); + __ cmp(ecx, static_cast('z' - 'a')); // Is eax a lowercase letter? + Label convert_capture; + __ j(below_equal, &convert_capture); // In range 'a'-'z'. + // Latin-1: Check for values in range [224,254] but not 247. + __ sub(ecx, Immediate(224 - 'a')); + __ cmp(ecx, Immediate(254 - 224)); + __ j(above, &fail); // Weren't Latin-1 letters. + __ cmp(ecx, Immediate(247 - 224)); // Check for 247. + __ j(equal, &fail); + __ bind(&convert_capture); + // Also convert capture character. + __ movzx_b(ecx, Operand(edx, 0)); + __ or_(ecx, 0x20); + + __ cmp(eax, ecx); + __ j(not_equal, &fail); + + __ bind(&loop_increment); + // Increment pointers into match and capture strings. + __ add(edx, Immediate(1)); + __ add(edi, Immediate(1)); + // Compare to end of match, and loop if not done. + __ cmp(edi, ebx); + __ j(below, &loop); + __ jmp(&success); + + __ bind(&fail); + // Restore original values before failing. + __ pop(backtrack_stackpointer()); + __ pop(edi); + BranchOrBacktrack(no_condition, on_no_match); + + __ bind(&success); + // Restore original value before continuing. + __ pop(backtrack_stackpointer()); + // Drop original value of character position. + __ add(esp, Immediate(kPointerSize)); + // Compute new value of character position after the matched part. + __ sub(edi, esi); + if (read_backward) { + // Subtract match length if we matched backward. + __ add(edi, register_location(start_reg)); + __ sub(edi, register_location(start_reg + 1)); + } + } else { + DCHECK(mode_ == UC16); + // Save registers before calling C function. + __ push(esi); + __ push(edi); + __ push(backtrack_stackpointer()); + __ push(ebx); + + static const int argument_count = 4; + __ PrepareCallCFunction(argument_count, ecx); + // Put arguments into allocated stack area, last argument highest on stack. + // Parameters are + // Address byte_offset1 - Address captured substring's start. + // Address byte_offset2 - Address of current character position. + // size_t byte_length - length of capture in bytes(!) +// Isolate* isolate or 0 if unicode flag. + + // Set isolate. +#ifdef V8_INTL_SUPPORT + if (unicode) { + __ mov(Operand(esp, 3 * kPointerSize), Immediate(0)); + } else // NOLINT +#endif // V8_INTL_SUPPORT + { + __ mov(Operand(esp, 3 * kPointerSize), + Immediate(ExternalReference::isolate_address(isolate()))); + } + // Set byte_length. + __ mov(Operand(esp, 2 * kPointerSize), ebx); + // Set byte_offset2. + // Found by adding negative string-end offset of current position (edi) + // to end of string. + __ add(edi, esi); + if (read_backward) { + __ sub(edi, ebx); // Offset by length when matching backwards. + } + __ mov(Operand(esp, 1 * kPointerSize), edi); + // Set byte_offset1. + // Start of capture, where edx already holds string-end negative offset. + __ add(edx, esi); + __ mov(Operand(esp, 0 * kPointerSize), edx); + + { + AllowExternalCallThatCantCauseGC scope(masm_); + ExternalReference compare = + ExternalReference::re_case_insensitive_compare_uc16(isolate()); + __ CallCFunction(compare, argument_count); + } + // Pop original values before reacting on result value. + __ pop(ebx); + __ pop(backtrack_stackpointer()); + __ pop(edi); + __ pop(esi); + + // Check if function returned non-zero for success or zero for failure. + __ or_(eax, eax); + BranchOrBacktrack(zero, on_no_match); + // On success, advance position by length of capture. + if (read_backward) { + __ sub(edi, ebx); + } else { + __ add(edi, ebx); + } + } + __ bind(&fallthrough); +} + + +void RegExpMacroAssemblerX87::CheckNotBackReference(int start_reg, + bool read_backward, + Label* on_no_match) { + Label fallthrough; + Label success; + Label fail; + + // Find length of back-referenced capture. + __ mov(edx, register_location(start_reg)); + __ mov(eax, register_location(start_reg + 1)); + __ sub(eax, edx); // Length to check. + + // At this point, the capture registers are either both set or both cleared. + // If the capture length is zero, then the capture is either empty or cleared. + // Fall through in both cases. + __ j(equal, &fallthrough); + + // Check that there are sufficient characters left in the input. + if (read_backward) { + __ mov(ebx, Operand(ebp, kStringStartMinusOne)); + __ add(ebx, eax); + __ cmp(edi, ebx); + BranchOrBacktrack(less_equal, on_no_match); + } else { + __ mov(ebx, edi); + __ add(ebx, eax); + BranchOrBacktrack(greater, on_no_match); + } + + // Save register to make it available below. + __ push(backtrack_stackpointer()); + + // Compute pointers to match string and capture string + __ add(edx, esi); // Start of capture. + __ lea(ebx, Operand(esi, edi, times_1, 0)); // Start of match. + if (read_backward) { + __ sub(ebx, eax); // Offset by length when matching backwards. + } + __ lea(ecx, Operand(eax, ebx, times_1, 0)); // End of match + + Label loop; + __ bind(&loop); + if (mode_ == LATIN1) { + __ movzx_b(eax, Operand(edx, 0)); + __ cmpb_al(Operand(ebx, 0)); + } else { + DCHECK(mode_ == UC16); + __ movzx_w(eax, Operand(edx, 0)); + __ cmpw_ax(Operand(ebx, 0)); + } + __ j(not_equal, &fail); + // Increment pointers into capture and match string. + __ add(edx, Immediate(char_size())); + __ add(ebx, Immediate(char_size())); + // Check if we have reached end of match area. + __ cmp(ebx, ecx); + __ j(below, &loop); + __ jmp(&success); + + __ bind(&fail); + // Restore backtrack stackpointer. + __ pop(backtrack_stackpointer()); + BranchOrBacktrack(no_condition, on_no_match); + + __ bind(&success); + // Move current character position to position after match. + __ mov(edi, ecx); + __ sub(edi, esi); + if (read_backward) { + // Subtract match length if we matched backward. + __ add(edi, register_location(start_reg)); + __ sub(edi, register_location(start_reg + 1)); + } + // Restore backtrack stackpointer. + __ pop(backtrack_stackpointer()); + + __ bind(&fallthrough); +} + + +void RegExpMacroAssemblerX87::CheckNotCharacter(uint32_t c, + Label* on_not_equal) { + __ cmp(current_character(), c); + BranchOrBacktrack(not_equal, on_not_equal); +} + + +void RegExpMacroAssemblerX87::CheckCharacterAfterAnd(uint32_t c, + uint32_t mask, + Label* on_equal) { + if (c == 0) { + __ test(current_character(), Immediate(mask)); + } else { + __ mov(eax, mask); + __ and_(eax, current_character()); + __ cmp(eax, c); + } + BranchOrBacktrack(equal, on_equal); +} + + +void RegExpMacroAssemblerX87::CheckNotCharacterAfterAnd(uint32_t c, + uint32_t mask, + Label* on_not_equal) { + if (c == 0) { + __ test(current_character(), Immediate(mask)); + } else { + __ mov(eax, mask); + __ and_(eax, current_character()); + __ cmp(eax, c); + } + BranchOrBacktrack(not_equal, on_not_equal); +} + + +void RegExpMacroAssemblerX87::CheckNotCharacterAfterMinusAnd( + uc16 c, + uc16 minus, + uc16 mask, + Label* on_not_equal) { + DCHECK(minus < String::kMaxUtf16CodeUnit); + __ lea(eax, Operand(current_character(), -minus)); + if (c == 0) { + __ test(eax, Immediate(mask)); + } else { + __ and_(eax, mask); + __ cmp(eax, c); + } + BranchOrBacktrack(not_equal, on_not_equal); +} + + +void RegExpMacroAssemblerX87::CheckCharacterInRange( + uc16 from, + uc16 to, + Label* on_in_range) { + __ lea(eax, Operand(current_character(), -from)); + __ cmp(eax, to - from); + BranchOrBacktrack(below_equal, on_in_range); +} + + +void RegExpMacroAssemblerX87::CheckCharacterNotInRange( + uc16 from, + uc16 to, + Label* on_not_in_range) { + __ lea(eax, Operand(current_character(), -from)); + __ cmp(eax, to - from); + BranchOrBacktrack(above, on_not_in_range); +} + + +void RegExpMacroAssemblerX87::CheckBitInTable( + Handle table, + Label* on_bit_set) { + __ mov(eax, Immediate(table)); + Register index = current_character(); + if (mode_ != LATIN1 || kTableMask != String::kMaxOneByteCharCode) { + __ mov(ebx, kTableSize - 1); + __ and_(ebx, current_character()); + index = ebx; + } + __ cmpb(FieldOperand(eax, index, times_1, ByteArray::kHeaderSize), + Immediate(0)); + BranchOrBacktrack(not_equal, on_bit_set); +} + + +bool RegExpMacroAssemblerX87::CheckSpecialCharacterClass(uc16 type, + Label* on_no_match) { + // Range checks (c in min..max) are generally implemented by an unsigned + // (c - min) <= (max - min) check + switch (type) { + case 's': + // Match space-characters + if (mode_ == LATIN1) { + // One byte space characters are '\t'..'\r', ' ' and \u00a0. + Label success; + __ cmp(current_character(), ' '); + __ j(equal, &success, Label::kNear); + // Check range 0x09..0x0d + __ lea(eax, Operand(current_character(), -'\t')); + __ cmp(eax, '\r' - '\t'); + __ j(below_equal, &success, Label::kNear); + // \u00a0 (NBSP). + __ cmp(eax, 0x00a0 - '\t'); + BranchOrBacktrack(not_equal, on_no_match); + __ bind(&success); + return true; + } + return false; + case 'S': + // The emitted code for generic character classes is good enough. + return false; + case 'd': + // Match ASCII digits ('0'..'9') + __ lea(eax, Operand(current_character(), -'0')); + __ cmp(eax, '9' - '0'); + BranchOrBacktrack(above, on_no_match); + return true; + case 'D': + // Match non ASCII-digits + __ lea(eax, Operand(current_character(), -'0')); + __ cmp(eax, '9' - '0'); + BranchOrBacktrack(below_equal, on_no_match); + return true; + case '.': { + // Match non-newlines (not 0x0a('\n'), 0x0d('\r'), 0x2028 and 0x2029) + __ mov(eax, current_character()); + __ xor_(eax, Immediate(0x01)); + // See if current character is '\n'^1 or '\r'^1, i.e., 0x0b or 0x0c + __ sub(eax, Immediate(0x0b)); + __ cmp(eax, 0x0c - 0x0b); + BranchOrBacktrack(below_equal, on_no_match); + if (mode_ == UC16) { + // Compare original value to 0x2028 and 0x2029, using the already + // computed (current_char ^ 0x01 - 0x0b). I.e., check for + // 0x201d (0x2028 - 0x0b) or 0x201e. + __ sub(eax, Immediate(0x2028 - 0x0b)); + __ cmp(eax, 0x2029 - 0x2028); + BranchOrBacktrack(below_equal, on_no_match); + } + return true; + } + case 'w': { + if (mode_ != LATIN1) { + // Table is 256 entries, so all Latin1 characters can be tested. + __ cmp(current_character(), Immediate('z')); + BranchOrBacktrack(above, on_no_match); + } + DCHECK_EQ(0, word_character_map[0]); // Character '\0' is not a word char. + ExternalReference word_map = ExternalReference::re_word_character_map(); + __ test_b(current_character(), + Operand::StaticArray(current_character(), times_1, word_map)); + BranchOrBacktrack(zero, on_no_match); + return true; + } + case 'W': { + Label done; + if (mode_ != LATIN1) { + // Table is 256 entries, so all Latin1 characters can be tested. + __ cmp(current_character(), Immediate('z')); + __ j(above, &done); + } + DCHECK_EQ(0, word_character_map[0]); // Character '\0' is not a word char. + ExternalReference word_map = ExternalReference::re_word_character_map(); + __ test_b(current_character(), + Operand::StaticArray(current_character(), times_1, word_map)); + BranchOrBacktrack(not_zero, on_no_match); + if (mode_ != LATIN1) { + __ bind(&done); + } + return true; + } + // Non-standard classes (with no syntactic shorthand) used internally. + case '*': + // Match any character. + return true; + case 'n': { + // Match newlines (0x0a('\n'), 0x0d('\r'), 0x2028 or 0x2029). + // The opposite of '.'. + __ mov(eax, current_character()); + __ xor_(eax, Immediate(0x01)); + // See if current character is '\n'^1 or '\r'^1, i.e., 0x0b or 0x0c + __ sub(eax, Immediate(0x0b)); + __ cmp(eax, 0x0c - 0x0b); + if (mode_ == LATIN1) { + BranchOrBacktrack(above, on_no_match); + } else { + Label done; + BranchOrBacktrack(below_equal, &done); + DCHECK_EQ(UC16, mode_); + // Compare original value to 0x2028 and 0x2029, using the already + // computed (current_char ^ 0x01 - 0x0b). I.e., check for + // 0x201d (0x2028 - 0x0b) or 0x201e. + __ sub(eax, Immediate(0x2028 - 0x0b)); + __ cmp(eax, 1); + BranchOrBacktrack(above, on_no_match); + __ bind(&done); + } + return true; + } + // No custom implementation (yet): s(UC16), S(UC16). + default: + return false; + } +} + + +void RegExpMacroAssemblerX87::Fail() { + STATIC_ASSERT(FAILURE == 0); // Return value for failure is zero. + if (!global()) { + __ Move(eax, Immediate(FAILURE)); + } + __ jmp(&exit_label_); +} + + +Handle RegExpMacroAssemblerX87::GetCode(Handle source) { + Label return_eax; + // Finalize code - write the entry point code now we know how many + // registers we need. + + // Entry code: + __ bind(&entry_label_); + + // Tell the system that we have a stack frame. Because the type is MANUAL, no + // code is generated. + FrameScope scope(masm_, StackFrame::MANUAL); + + // Actually emit code to start a new stack frame. + __ push(ebp); + __ mov(ebp, esp); + // Save callee-save registers. Order here should correspond to order of + // kBackup_ebx etc. + __ push(esi); + __ push(edi); + __ push(ebx); // Callee-save on MacOS. + __ push(Immediate(0)); // Number of successful matches in a global regexp. + __ push(Immediate(0)); // Make room for "string start - 1" constant. + + // Check if we have space on the stack for registers. + Label stack_limit_hit; + Label stack_ok; + + ExternalReference stack_limit = + ExternalReference::address_of_stack_limit(isolate()); + __ mov(ecx, esp); + __ sub(ecx, Operand::StaticVariable(stack_limit)); + // Handle it if the stack pointer is already below the stack limit. + __ j(below_equal, &stack_limit_hit); + // Check if there is room for the variable number of registers above + // the stack limit. + __ cmp(ecx, num_registers_ * kPointerSize); + __ j(above_equal, &stack_ok); + // Exit with OutOfMemory exception. There is not enough space on the stack + // for our working registers. + __ mov(eax, EXCEPTION); + __ jmp(&return_eax); + + __ bind(&stack_limit_hit); + CallCheckStackGuardState(ebx); + __ or_(eax, eax); + // If returned value is non-zero, we exit with the returned value as result. + __ j(not_zero, &return_eax); + + __ bind(&stack_ok); + // Load start index for later use. + __ mov(ebx, Operand(ebp, kStartIndex)); + + // Allocate space on stack for registers. + __ sub(esp, Immediate(num_registers_ * kPointerSize)); + // Load string length. + __ mov(esi, Operand(ebp, kInputEnd)); + // Load input position. + __ mov(edi, Operand(ebp, kInputStart)); + // Set up edi to be negative offset from string end. + __ sub(edi, esi); + + // Set eax to address of char before start of the string. + // (effectively string position -1). + __ neg(ebx); + if (mode_ == UC16) { + __ lea(eax, Operand(edi, ebx, times_2, -char_size())); + } else { + __ lea(eax, Operand(edi, ebx, times_1, -char_size())); + } + // Store this value in a local variable, for use when clearing + // position registers. + __ mov(Operand(ebp, kStringStartMinusOne), eax); + +#if V8_OS_WIN + // Ensure that we write to each stack page, in order. Skipping a page + // on Windows can cause segmentation faults. Assuming page size is 4k. + const int kPageSize = 4096; + const int kRegistersPerPage = kPageSize / kPointerSize; + for (int i = num_saved_registers_ + kRegistersPerPage - 1; + i < num_registers_; + i += kRegistersPerPage) { + __ mov(register_location(i), eax); // One write every page. + } +#endif // V8_OS_WIN + + Label load_char_start_regexp, start_regexp; + // Load newline if index is at start, previous character otherwise. + __ cmp(Operand(ebp, kStartIndex), Immediate(0)); + __ j(not_equal, &load_char_start_regexp, Label::kNear); + __ mov(current_character(), '\n'); + __ jmp(&start_regexp, Label::kNear); + + // Global regexp restarts matching here. + __ bind(&load_char_start_regexp); + // Load previous char as initial value of current character register. + LoadCurrentCharacterUnchecked(-1, 1); + __ bind(&start_regexp); + + // Initialize on-stack registers. + if (num_saved_registers_ > 0) { // Always is, if generated from a regexp. + // Fill saved registers with initial value = start offset - 1 + // Fill in stack push order, to avoid accessing across an unwritten + // page (a problem on Windows). + if (num_saved_registers_ > 8) { + __ mov(ecx, kRegisterZero); + Label init_loop; + __ bind(&init_loop); + __ mov(Operand(ebp, ecx, times_1, 0), eax); + __ sub(ecx, Immediate(kPointerSize)); + __ cmp(ecx, kRegisterZero - num_saved_registers_ * kPointerSize); + __ j(greater, &init_loop); + } else { // Unroll the loop. + for (int i = 0; i < num_saved_registers_; i++) { + __ mov(register_location(i), eax); + } + } + } + + // Initialize backtrack stack pointer. + __ mov(backtrack_stackpointer(), Operand(ebp, kStackHighEnd)); + + __ jmp(&start_label_); + + // Exit code: + if (success_label_.is_linked()) { + // Save captures when successful. + __ bind(&success_label_); + if (num_saved_registers_ > 0) { + // copy captures to output + __ mov(ebx, Operand(ebp, kRegisterOutput)); + __ mov(ecx, Operand(ebp, kInputEnd)); + __ mov(edx, Operand(ebp, kStartIndex)); + __ sub(ecx, Operand(ebp, kInputStart)); + if (mode_ == UC16) { + __ lea(ecx, Operand(ecx, edx, times_2, 0)); + } else { + __ add(ecx, edx); + } + for (int i = 0; i < num_saved_registers_; i++) { + __ mov(eax, register_location(i)); + if (i == 0 && global_with_zero_length_check()) { + // Keep capture start in edx for the zero-length check later. + __ mov(edx, eax); + } + // Convert to index from start of string, not end. + __ add(eax, ecx); + if (mode_ == UC16) { + __ sar(eax, 1); // Convert byte index to character index. + } + __ mov(Operand(ebx, i * kPointerSize), eax); + } + } + + if (global()) { + // Restart matching if the regular expression is flagged as global. + // Increment success counter. + __ inc(Operand(ebp, kSuccessfulCaptures)); + // Capture results have been stored, so the number of remaining global + // output registers is reduced by the number of stored captures. + __ mov(ecx, Operand(ebp, kNumOutputRegisters)); + __ sub(ecx, Immediate(num_saved_registers_)); + // Check whether we have enough room for another set of capture results. + __ cmp(ecx, Immediate(num_saved_registers_)); + __ j(less, &exit_label_); + + __ mov(Operand(ebp, kNumOutputRegisters), ecx); + // Advance the location for output. + __ add(Operand(ebp, kRegisterOutput), + Immediate(num_saved_registers_ * kPointerSize)); + + // Prepare eax to initialize registers with its value in the next run. + __ mov(eax, Operand(ebp, kStringStartMinusOne)); + + if (global_with_zero_length_check()) { + // Special case for zero-length matches. + // edx: capture start index + __ cmp(edi, edx); + // Not a zero-length match, restart. + __ j(not_equal, &load_char_start_regexp); + // edi (offset from the end) is zero if we already reached the end. + __ test(edi, edi); + __ j(zero, &exit_label_, Label::kNear); + // Advance current position after a zero-length match. + Label advance; + __ bind(&advance); + if (mode_ == UC16) { + __ add(edi, Immediate(2)); + } else { + __ inc(edi); + } + if (global_unicode()) CheckNotInSurrogatePair(0, &advance); + } + __ jmp(&load_char_start_regexp); + } else { + __ mov(eax, Immediate(SUCCESS)); + } + } + + __ bind(&exit_label_); + if (global()) { + // Return the number of successful captures. + __ mov(eax, Operand(ebp, kSuccessfulCaptures)); + } + + __ bind(&return_eax); + // Skip esp past regexp registers. + __ lea(esp, Operand(ebp, kBackup_ebx)); + // Restore callee-save registers. + __ pop(ebx); + __ pop(edi); + __ pop(esi); + // Exit function frame, restore previous one. + __ pop(ebp); + __ ret(0); + + // Backtrack code (branch target for conditional backtracks). + if (backtrack_label_.is_linked()) { + __ bind(&backtrack_label_); + Backtrack(); + } + + Label exit_with_exception; + + // Preempt-code + if (check_preempt_label_.is_linked()) { + SafeCallTarget(&check_preempt_label_); + + __ push(backtrack_stackpointer()); + __ push(edi); + + CallCheckStackGuardState(ebx); + __ or_(eax, eax); + // If returning non-zero, we should end execution with the given + // result as return value. + __ j(not_zero, &return_eax); + + __ pop(edi); + __ pop(backtrack_stackpointer()); + // String might have moved: Reload esi from frame. + __ mov(esi, Operand(ebp, kInputEnd)); + SafeReturn(); + } + + // Backtrack stack overflow code. + if (stack_overflow_label_.is_linked()) { + SafeCallTarget(&stack_overflow_label_); + // Reached if the backtrack-stack limit has been hit. + + Label grow_failed; + // Save registers before calling C function + __ push(esi); + __ push(edi); + + // Call GrowStack(backtrack_stackpointer()) + static const int num_arguments = 3; + __ PrepareCallCFunction(num_arguments, ebx); + __ mov(Operand(esp, 2 * kPointerSize), + Immediate(ExternalReference::isolate_address(isolate()))); + __ lea(eax, Operand(ebp, kStackHighEnd)); + __ mov(Operand(esp, 1 * kPointerSize), eax); + __ mov(Operand(esp, 0 * kPointerSize), backtrack_stackpointer()); + ExternalReference grow_stack = + ExternalReference::re_grow_stack(isolate()); + __ CallCFunction(grow_stack, num_arguments); + // If return NULL, we have failed to grow the stack, and + // must exit with a stack-overflow exception. + __ or_(eax, eax); + __ j(equal, &exit_with_exception); + // Otherwise use return value as new stack pointer. + __ mov(backtrack_stackpointer(), eax); + // Restore saved registers and continue. + __ pop(edi); + __ pop(esi); + SafeReturn(); + } + + if (exit_with_exception.is_linked()) { + // If any of the code above needed to exit with an exception. + __ bind(&exit_with_exception); + // Exit with Result EXCEPTION(-1) to signal thrown exception. + __ mov(eax, EXCEPTION); + __ jmp(&return_eax); + } + + CodeDesc code_desc; + masm_->GetCode(masm_->isolate(), &code_desc); + Handle code = + isolate()->factory()->NewCode(code_desc, + Code::ComputeFlags(Code::REGEXP), + masm_->CodeObject()); + PROFILE(masm_->isolate(), + RegExpCodeCreateEvent(AbstractCode::cast(*code), *source)); + return Handle::cast(code); +} + + +void RegExpMacroAssemblerX87::GoTo(Label* to) { + BranchOrBacktrack(no_condition, to); +} + + +void RegExpMacroAssemblerX87::IfRegisterGE(int reg, + int comparand, + Label* if_ge) { + __ cmp(register_location(reg), Immediate(comparand)); + BranchOrBacktrack(greater_equal, if_ge); +} + + +void RegExpMacroAssemblerX87::IfRegisterLT(int reg, + int comparand, + Label* if_lt) { + __ cmp(register_location(reg), Immediate(comparand)); + BranchOrBacktrack(less, if_lt); +} + + +void RegExpMacroAssemblerX87::IfRegisterEqPos(int reg, + Label* if_eq) { + __ cmp(edi, register_location(reg)); + BranchOrBacktrack(equal, if_eq); +} + + +RegExpMacroAssembler::IrregexpImplementation + RegExpMacroAssemblerX87::Implementation() { + return kX87Implementation; +} + + +void RegExpMacroAssemblerX87::LoadCurrentCharacter(int cp_offset, + Label* on_end_of_input, + bool check_bounds, + int characters) { + DCHECK(cp_offset < (1<<30)); // Be sane! (And ensure negation works) + if (check_bounds) { + if (cp_offset >= 0) { + CheckPosition(cp_offset + characters - 1, on_end_of_input); + } else { + CheckPosition(cp_offset, on_end_of_input); + } + } + LoadCurrentCharacterUnchecked(cp_offset, characters); +} + + +void RegExpMacroAssemblerX87::PopCurrentPosition() { + Pop(edi); +} + + +void RegExpMacroAssemblerX87::PopRegister(int register_index) { + Pop(eax); + __ mov(register_location(register_index), eax); +} + + +void RegExpMacroAssemblerX87::PushBacktrack(Label* label) { + Push(Immediate::CodeRelativeOffset(label)); + CheckStackLimit(); +} + + +void RegExpMacroAssemblerX87::PushCurrentPosition() { + Push(edi); +} + + +void RegExpMacroAssemblerX87::PushRegister(int register_index, + StackCheckFlag check_stack_limit) { + __ mov(eax, register_location(register_index)); + Push(eax); + if (check_stack_limit) CheckStackLimit(); +} + + +void RegExpMacroAssemblerX87::ReadCurrentPositionFromRegister(int reg) { + __ mov(edi, register_location(reg)); +} + + +void RegExpMacroAssemblerX87::ReadStackPointerFromRegister(int reg) { + __ mov(backtrack_stackpointer(), register_location(reg)); + __ add(backtrack_stackpointer(), Operand(ebp, kStackHighEnd)); +} + +void RegExpMacroAssemblerX87::SetCurrentPositionFromEnd(int by) { + Label after_position; + __ cmp(edi, -by * char_size()); + __ j(greater_equal, &after_position, Label::kNear); + __ mov(edi, -by * char_size()); + // On RegExp code entry (where this operation is used), the character before + // the current position is expected to be already loaded. + // We have advanced the position, so it's safe to read backwards. + LoadCurrentCharacterUnchecked(-1, 1); + __ bind(&after_position); +} + + +void RegExpMacroAssemblerX87::SetRegister(int register_index, int to) { + DCHECK(register_index >= num_saved_registers_); // Reserved for positions! + __ mov(register_location(register_index), Immediate(to)); +} + + +bool RegExpMacroAssemblerX87::Succeed() { + __ jmp(&success_label_); + return global(); +} + + +void RegExpMacroAssemblerX87::WriteCurrentPositionToRegister(int reg, + int cp_offset) { + if (cp_offset == 0) { + __ mov(register_location(reg), edi); + } else { + __ lea(eax, Operand(edi, cp_offset * char_size())); + __ mov(register_location(reg), eax); + } +} + + +void RegExpMacroAssemblerX87::ClearRegisters(int reg_from, int reg_to) { + DCHECK(reg_from <= reg_to); + __ mov(eax, Operand(ebp, kStringStartMinusOne)); + for (int reg = reg_from; reg <= reg_to; reg++) { + __ mov(register_location(reg), eax); + } +} + + +void RegExpMacroAssemblerX87::WriteStackPointerToRegister(int reg) { + __ mov(eax, backtrack_stackpointer()); + __ sub(eax, Operand(ebp, kStackHighEnd)); + __ mov(register_location(reg), eax); +} + + +// Private methods: + +void RegExpMacroAssemblerX87::CallCheckStackGuardState(Register scratch) { + static const int num_arguments = 3; + __ PrepareCallCFunction(num_arguments, scratch); + // RegExp code frame pointer. + __ mov(Operand(esp, 2 * kPointerSize), ebp); + // Code* of self. + __ mov(Operand(esp, 1 * kPointerSize), Immediate(masm_->CodeObject())); + // Next address on the stack (will be address of return address). + __ lea(eax, Operand(esp, -kPointerSize)); + __ mov(Operand(esp, 0 * kPointerSize), eax); + ExternalReference check_stack_guard = + ExternalReference::re_check_stack_guard_state(isolate()); + __ CallCFunction(check_stack_guard, num_arguments); +} + + +// Helper function for reading a value out of a stack frame. +template +static T& frame_entry(Address re_frame, int frame_offset) { + return reinterpret_cast(Memory::int32_at(re_frame + frame_offset)); +} + + +template +static T* frame_entry_address(Address re_frame, int frame_offset) { + return reinterpret_cast(re_frame + frame_offset); +} + + +int RegExpMacroAssemblerX87::CheckStackGuardState(Address* return_address, + Code* re_code, + Address re_frame) { + return NativeRegExpMacroAssembler::CheckStackGuardState( + frame_entry(re_frame, kIsolate), + frame_entry(re_frame, kStartIndex), + frame_entry(re_frame, kDirectCall) == 1, return_address, re_code, + frame_entry_address(re_frame, kInputString), + frame_entry_address(re_frame, kInputStart), + frame_entry_address(re_frame, kInputEnd)); +} + + +Operand RegExpMacroAssemblerX87::register_location(int register_index) { + DCHECK(register_index < (1<<30)); + if (num_registers_ <= register_index) { + num_registers_ = register_index + 1; + } + return Operand(ebp, kRegisterZero - register_index * kPointerSize); +} + + +void RegExpMacroAssemblerX87::CheckPosition(int cp_offset, + Label* on_outside_input) { + if (cp_offset >= 0) { + __ cmp(edi, -cp_offset * char_size()); + BranchOrBacktrack(greater_equal, on_outside_input); + } else { + __ lea(eax, Operand(edi, cp_offset * char_size())); + __ cmp(eax, Operand(ebp, kStringStartMinusOne)); + BranchOrBacktrack(less_equal, on_outside_input); + } +} + + +void RegExpMacroAssemblerX87::BranchOrBacktrack(Condition condition, + Label* to) { + if (condition < 0) { // No condition + if (to == NULL) { + Backtrack(); + return; + } + __ jmp(to); + return; + } + if (to == NULL) { + __ j(condition, &backtrack_label_); + return; + } + __ j(condition, to); +} + + +void RegExpMacroAssemblerX87::SafeCall(Label* to) { + Label return_to; + __ push(Immediate::CodeRelativeOffset(&return_to)); + __ jmp(to); + __ bind(&return_to); +} + + +void RegExpMacroAssemblerX87::SafeReturn() { + __ pop(ebx); + __ add(ebx, Immediate(masm_->CodeObject())); + __ jmp(ebx); +} + + +void RegExpMacroAssemblerX87::SafeCallTarget(Label* name) { + __ bind(name); +} + + +void RegExpMacroAssemblerX87::Push(Register source) { + DCHECK(!source.is(backtrack_stackpointer())); + // Notice: This updates flags, unlike normal Push. + __ sub(backtrack_stackpointer(), Immediate(kPointerSize)); + __ mov(Operand(backtrack_stackpointer(), 0), source); +} + + +void RegExpMacroAssemblerX87::Push(Immediate value) { + // Notice: This updates flags, unlike normal Push. + __ sub(backtrack_stackpointer(), Immediate(kPointerSize)); + __ mov(Operand(backtrack_stackpointer(), 0), value); +} + + +void RegExpMacroAssemblerX87::Pop(Register target) { + DCHECK(!target.is(backtrack_stackpointer())); + __ mov(target, Operand(backtrack_stackpointer(), 0)); + // Notice: This updates flags, unlike normal Pop. + __ add(backtrack_stackpointer(), Immediate(kPointerSize)); +} + + +void RegExpMacroAssemblerX87::CheckPreemption() { + // Check for preemption. + Label no_preempt; + ExternalReference stack_limit = + ExternalReference::address_of_stack_limit(isolate()); + __ cmp(esp, Operand::StaticVariable(stack_limit)); + __ j(above, &no_preempt); + + SafeCall(&check_preempt_label_); + + __ bind(&no_preempt); +} + + +void RegExpMacroAssemblerX87::CheckStackLimit() { + Label no_stack_overflow; + ExternalReference stack_limit = + ExternalReference::address_of_regexp_stack_limit(isolate()); + __ cmp(backtrack_stackpointer(), Operand::StaticVariable(stack_limit)); + __ j(above, &no_stack_overflow); + + SafeCall(&stack_overflow_label_); + + __ bind(&no_stack_overflow); +} + + +void RegExpMacroAssemblerX87::LoadCurrentCharacterUnchecked(int cp_offset, + int characters) { + if (mode_ == LATIN1) { + if (characters == 4) { + __ mov(current_character(), Operand(esi, edi, times_1, cp_offset)); + } else if (characters == 2) { + __ movzx_w(current_character(), Operand(esi, edi, times_1, cp_offset)); + } else { + DCHECK(characters == 1); + __ movzx_b(current_character(), Operand(esi, edi, times_1, cp_offset)); + } + } else { + DCHECK(mode_ == UC16); + if (characters == 2) { + __ mov(current_character(), + Operand(esi, edi, times_1, cp_offset * sizeof(uc16))); + } else { + DCHECK(characters == 1); + __ movzx_w(current_character(), + Operand(esi, edi, times_1, cp_offset * sizeof(uc16))); + } + } +} + + +#undef __ + +#endif // V8_INTERPRETED_REGEXP + +} // namespace internal +} // namespace v8 + +#endif // V8_TARGET_ARCH_X87 diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/regexp/x87/regexp-macro-assembler-x87.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/regexp/x87/regexp-macro-assembler-x87.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/regexp/x87/regexp-macro-assembler-x87.h 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/regexp/x87/regexp-macro-assembler-x87.h 2017-12-25 17:42:57.218465603 +0100 @@ -0,0 +1,204 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_REGEXP_X87_REGEXP_MACRO_ASSEMBLER_X87_H_ +#define V8_REGEXP_X87_REGEXP_MACRO_ASSEMBLER_X87_H_ + +#include "src/macro-assembler.h" +#include "src/regexp/regexp-macro-assembler.h" +#include "src/x87/assembler-x87.h" + +namespace v8 { +namespace internal { + +#ifndef V8_INTERPRETED_REGEXP +class RegExpMacroAssemblerX87: public NativeRegExpMacroAssembler { + public: + RegExpMacroAssemblerX87(Isolate* isolate, Zone* zone, Mode mode, + int registers_to_save); + virtual ~RegExpMacroAssemblerX87(); + virtual int stack_limit_slack(); + virtual void AdvanceCurrentPosition(int by); + virtual void AdvanceRegister(int reg, int by); + virtual void Backtrack(); + virtual void Bind(Label* label); + virtual void CheckAtStart(Label* on_at_start); + virtual void CheckCharacter(uint32_t c, Label* on_equal); + virtual void CheckCharacterAfterAnd(uint32_t c, + uint32_t mask, + Label* on_equal); + virtual void CheckCharacterGT(uc16 limit, Label* on_greater); + virtual void CheckCharacterLT(uc16 limit, Label* on_less); + // A "greedy loop" is a loop that is both greedy and with a simple + // body. It has a particularly simple implementation. + virtual void CheckGreedyLoop(Label* on_tos_equals_current_position); + virtual void CheckNotAtStart(int cp_offset, Label* on_not_at_start); + virtual void CheckNotBackReference(int start_reg, bool read_backward, + Label* on_no_match); + virtual void CheckNotBackReferenceIgnoreCase(int start_reg, + bool read_backward, bool unicode, + Label* on_no_match); + virtual void CheckNotCharacter(uint32_t c, Label* on_not_equal); + virtual void CheckNotCharacterAfterAnd(uint32_t c, + uint32_t mask, + Label* on_not_equal); + virtual void CheckNotCharacterAfterMinusAnd(uc16 c, + uc16 minus, + uc16 mask, + Label* on_not_equal); + virtual void CheckCharacterInRange(uc16 from, + uc16 to, + Label* on_in_range); + virtual void CheckCharacterNotInRange(uc16 from, + uc16 to, + Label* on_not_in_range); + virtual void CheckBitInTable(Handle table, Label* on_bit_set); + + // Checks whether the given offset from the current position is before + // the end of the string. + virtual void CheckPosition(int cp_offset, Label* on_outside_input); + virtual bool CheckSpecialCharacterClass(uc16 type, Label* on_no_match); + virtual void Fail(); + virtual Handle GetCode(Handle source); + virtual void GoTo(Label* label); + virtual void IfRegisterGE(int reg, int comparand, Label* if_ge); + virtual void IfRegisterLT(int reg, int comparand, Label* if_lt); + virtual void IfRegisterEqPos(int reg, Label* if_eq); + virtual IrregexpImplementation Implementation(); + virtual void LoadCurrentCharacter(int cp_offset, + Label* on_end_of_input, + bool check_bounds = true, + int characters = 1); + virtual void PopCurrentPosition(); + virtual void PopRegister(int register_index); + virtual void PushBacktrack(Label* label); + virtual void PushCurrentPosition(); + virtual void PushRegister(int register_index, + StackCheckFlag check_stack_limit); + virtual void ReadCurrentPositionFromRegister(int reg); + virtual void ReadStackPointerFromRegister(int reg); + virtual void SetCurrentPositionFromEnd(int by); + virtual void SetRegister(int register_index, int to); + virtual bool Succeed(); + virtual void WriteCurrentPositionToRegister(int reg, int cp_offset); + virtual void ClearRegisters(int reg_from, int reg_to); + virtual void WriteStackPointerToRegister(int reg); + + // Called from RegExp if the stack-guard is triggered. + // If the code object is relocated, the return address is fixed before + // returning. + static int CheckStackGuardState(Address* return_address, + Code* re_code, + Address re_frame); + + private: + // Offsets from ebp of function parameters and stored registers. + static const int kFramePointer = 0; + // Above the frame pointer - function parameters and return address. + static const int kReturn_eip = kFramePointer + kPointerSize; + static const int kFrameAlign = kReturn_eip + kPointerSize; + // Parameters. + static const int kInputString = kFrameAlign; + static const int kStartIndex = kInputString + kPointerSize; + static const int kInputStart = kStartIndex + kPointerSize; + static const int kInputEnd = kInputStart + kPointerSize; + static const int kRegisterOutput = kInputEnd + kPointerSize; + // For the case of global regular expression, we have room to store at least + // one set of capture results. For the case of non-global regexp, we ignore + // this value. + static const int kNumOutputRegisters = kRegisterOutput + kPointerSize; + static const int kStackHighEnd = kNumOutputRegisters + kPointerSize; + static const int kDirectCall = kStackHighEnd + kPointerSize; + static const int kIsolate = kDirectCall + kPointerSize; + // Below the frame pointer - local stack variables. + // When adding local variables remember to push space for them in + // the frame in GetCode. + static const int kBackup_esi = kFramePointer - kPointerSize; + static const int kBackup_edi = kBackup_esi - kPointerSize; + static const int kBackup_ebx = kBackup_edi - kPointerSize; + static const int kSuccessfulCaptures = kBackup_ebx - kPointerSize; + static const int kStringStartMinusOne = kSuccessfulCaptures - kPointerSize; + // First register address. Following registers are below it on the stack. + static const int kRegisterZero = kStringStartMinusOne - kPointerSize; + + // Initial size of code buffer. + static const size_t kRegExpCodeSize = 1024; + + // Load a number of characters at the given offset from the + // current position, into the current-character register. + void LoadCurrentCharacterUnchecked(int cp_offset, int character_count); + + // Check whether preemption has been requested. + void CheckPreemption(); + + // Check whether we are exceeding the stack limit on the backtrack stack. + void CheckStackLimit(); + + // Generate a call to CheckStackGuardState. + void CallCheckStackGuardState(Register scratch); + + // The ebp-relative location of a regexp register. + Operand register_location(int register_index); + + // The register containing the current character after LoadCurrentCharacter. + inline Register current_character() { return edx; } + + // The register containing the backtrack stack top. Provides a meaningful + // name to the register. + inline Register backtrack_stackpointer() { return ecx; } + + // Byte size of chars in the string to match (decided by the Mode argument) + inline int char_size() { return static_cast(mode_); } + + // Equivalent to a conditional branch to the label, unless the label + // is NULL, in which case it is a conditional Backtrack. + void BranchOrBacktrack(Condition condition, Label* to); + + // Call and return internally in the generated code in a way that + // is GC-safe (i.e., doesn't leave absolute code addresses on the stack) + inline void SafeCall(Label* to); + inline void SafeReturn(); + inline void SafeCallTarget(Label* name); + + // Pushes the value of a register on the backtrack stack. Decrements the + // stack pointer (ecx) by a word size and stores the register's value there. + inline void Push(Register source); + + // Pushes a value on the backtrack stack. Decrements the stack pointer (ecx) + // by a word size and stores the value there. + inline void Push(Immediate value); + + // Pops a value from the backtrack stack. Reads the word at the stack pointer + // (ecx) and increments it by a word size. + inline void Pop(Register target); + + Isolate* isolate() const { return masm_->isolate(); } + + MacroAssembler* masm_; + + // Which mode to generate code for (LATIN1 or UC16). + Mode mode_; + + // One greater than maximal register index actually used. + int num_registers_; + + // Number of registers to output at the end (the saved registers + // are always 0..num_saved_registers_-1) + int num_saved_registers_; + + // Labels used internally. + Label entry_label_; + Label start_label_; + Label success_label_; + Label backtrack_label_; + Label exit_label_; + Label check_preempt_label_; + Label stack_overflow_label_; +}; +#endif // V8_INTERPRETED_REGEXP + +} // namespace internal +} // namespace v8 + +#endif // V8_REGEXP_X87_REGEXP_MACRO_ASSEMBLER_X87_H_ diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/register-configuration.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/register-configuration.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/register-configuration.cc 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/register-configuration.cc 2017-12-25 17:42:57.218465603 +0100 @@ -74,6 +74,9 @@ #if V8_TARGET_ARCH_IA32 kMaxAllocatableGeneralRegisterCount, kMaxAllocatableDoubleRegisterCount, +#elif V8_TARGET_ARCH_X87 + kMaxAllocatableGeneralRegisterCount, + compiler == TURBOFAN ? 1 : kMaxAllocatableDoubleRegisterCount, #elif V8_TARGET_ARCH_X64 kMaxAllocatableGeneralRegisterCount, kMaxAllocatableDoubleRegisterCount, diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/register-configuration.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/register-configuration.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/register-configuration.h 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/register-configuration.h 2017-12-25 17:42:57.218465603 +0100 @@ -28,7 +28,8 @@ static const int kMaxFPRegisters = 32; // Default RegisterConfigurations for the target architecture. - // TODO(mstarzinger): Crankshaft is gone. + // TODO(X87): This distinction in RegisterConfigurations is temporary + // until x87 TF supports all of the registers that Crankshaft does. static const RegisterConfiguration* Crankshaft(); static const RegisterConfiguration* Turbofan(); diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/simulator.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/simulator.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/simulator.h 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/simulator.h 2017-12-25 17:42:57.218465603 +0100 @@ -21,6 +21,8 @@ #include "src/mips64/simulator-mips64.h" #elif V8_TARGET_ARCH_S390 #include "src/s390/simulator-s390.h" +#elif V8_TARGET_ARCH_X87 +#include "src/x87/simulator-x87.h" #else #error Unsupported target architecture. #endif diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/strtod.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/strtod.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/strtod.cc 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/strtod.cc 2017-12-25 17:42:57.218465603 +0100 @@ -154,7 +154,8 @@ static bool DoubleStrtod(Vector trimmed, int exponent, double* result) { -#if (V8_TARGET_ARCH_IA32 || defined(USE_SIMULATOR)) && !defined(_MSC_VER) +#if (V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X87 || defined(USE_SIMULATOR)) && \ + !defined(_MSC_VER) // On x86 the floating-point stack can be 64 or 80 bits wide. If it is // 80 bits wide (as is the case on Linux) then double-rounding occurs and the // result is not accurate. diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/utils.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/utils.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/utils.cc 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/utils.cc 2017-12-25 17:42:57.218465603 +0100 @@ -356,7 +356,8 @@ } } -#if V8_TARGET_ARCH_IA32 + +#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X87 static void MemMoveWrapper(void* dest, const void* src, size_t size) { memmove(dest, src, size); } @@ -410,7 +411,7 @@ void init_memcopy_functions(Isolate* isolate) { if (g_memcopy_functions_initialized) return; g_memcopy_functions_initialized = true; -#if V8_TARGET_ARCH_IA32 +#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X87 MemMoveFunction generated_memmove = CreateMemMoveFunction(isolate); if (generated_memmove != NULL) { memmove_function = generated_memmove; diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/utils.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/utils.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/utils.h 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/utils.h 2017-12-25 17:42:57.218465603 +0100 @@ -431,7 +431,7 @@ // Initializes the codegen support that depends on CPU features. void init_memcopy_functions(Isolate* isolate); -#if defined(V8_TARGET_ARCH_IA32) +#if defined(V8_TARGET_ARCH_IA32) || defined(V8_TARGET_ARCH_X87) // Limit below which the extra overhead of the MemCopy function is likely // to outweigh the benefits of faster copying. const int kMinComplexMemCopy = 64; diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/v8.gyp qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/v8.gyp --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/v8.gyp 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/v8.gyp 2017-12-25 17:42:57.218465603 +0100 @@ -279,6 +279,11 @@ 'builtins/s390/builtins-s390.cc', ], }], + ['v8_target_arch=="x87"', { + 'sources': [ ### gcmole(arch:x87) ### + 'builtins/x87/builtins-x87.cc', + ], + }], ['v8_enable_i18n_support==0', { 'sources!': [ 'builtins/builtins-intl-gen.cc', @@ -1587,6 +1592,38 @@ 'regexp/ia32/regexp-macro-assembler-ia32.h', ], }], + ['v8_target_arch=="x87"', { + 'sources': [ ### gcmole(arch:x87) ### + 'x87/assembler-x87-inl.h', + 'x87/assembler-x87.cc', + 'x87/assembler-x87.h', + 'x87/code-stubs-x87.cc', + 'x87/code-stubs-x87.h', + 'x87/codegen-x87.cc', + 'x87/codegen-x87.h', + 'x87/cpu-x87.cc', + 'x87/deoptimizer-x87.cc', + 'x87/disasm-x87.cc', + 'x87/frames-x87.cc', + 'x87/frames-x87.h', + 'x87/interface-descriptors-x87.cc', + 'x87/macro-assembler-x87.cc', + 'x87/macro-assembler-x87.h', + 'x87/simulator-x87.cc', + 'x87/simulator-x87.h', + 'compiler/x87/code-generator-x87.cc', + 'compiler/x87/instruction-codes-x87.h', + 'compiler/x87/instruction-scheduler-x87.cc', + 'compiler/x87/instruction-selector-x87.cc', + 'debug/x87/debug-x87.cc', + 'full-codegen/x87/full-codegen-x87.cc', + 'ic/x87/access-compiler-x87.cc', + 'ic/x87/handler-compiler-x87.cc', + 'ic/x87/ic-x87.cc', + 'regexp/x87/regexp-macro-assembler-x87.cc', + 'regexp/x87/regexp-macro-assembler-x87.h', + ], + }], ['v8_target_arch=="mips" or v8_target_arch=="mipsel"', { 'sources': [ ### gcmole(arch:mipsel) ### 'mips/assembler-mips.cc', diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/x87/assembler-x87.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/x87/assembler-x87.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/x87/assembler-x87.cc 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/x87/assembler-x87.cc 2017-12-28 04:40:51.665451092 +0100 @@ -0,0 +1,2258 @@ +// Copyright (c) 1994-2006 Sun Microsystems Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// - Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// - Redistribution in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the +// distribution. +// +// - Neither the name of Sun Microsystems or the names of contributors may +// be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. + +// The original source code covered by the above license above has been modified +// significantly by Google Inc. +// Copyright 2012 the V8 project authors. All rights reserved. + +#include "src/x87/assembler-x87.h" + +#if V8_TARGET_ARCH_X87 + +#include "src/base/bits.h" +#include "src/base/cpu.h" +#include "src/code-stubs.h" +#include "src/disassembler.h" +#include "src/macro-assembler.h" +#include "src/v8.h" + +namespace v8 { +namespace internal { + +Immediate Immediate::EmbeddedNumber(double value) { + int32_t smi; + if (DoubleToSmiInteger(value, &smi)) return Immediate(Smi::FromInt(smi)); + Immediate result(0, RelocInfo::EMBEDDED_OBJECT); + result.is_heap_object_request_ = true; + result.value_.heap_object_request = HeapObjectRequest(value); + return result; +} + +Immediate Immediate::EmbeddedCode(CodeStub* stub) { + Immediate result(0, RelocInfo::CODE_TARGET); + result.is_heap_object_request_ = true; + result.value_.heap_object_request = HeapObjectRequest(stub); + return result; +} + +// ----------------------------------------------------------------------------- +// Implementation of CpuFeatures + +void CpuFeatures::ProbeImpl(bool cross_compile) { + base::CPU cpu; + + // Only use statically determined features for cross compile (snapshot). + if (cross_compile) return; +} + + +void CpuFeatures::PrintTarget() { } +void CpuFeatures::PrintFeatures() { } + + +// ----------------------------------------------------------------------------- +// Implementation of Displacement + +void Displacement::init(Label* L, Type type) { + DCHECK(!L->is_bound()); + int next = 0; + if (L->is_linked()) { + next = L->pos(); + DCHECK(next > 0); // Displacements must be at positions > 0 + } + // Ensure that we _never_ overflow the next field. + DCHECK(NextField::is_valid(Assembler::kMaximalBufferSize)); + data_ = NextField::encode(next) | TypeField::encode(type); +} + + +// ----------------------------------------------------------------------------- +// Implementation of RelocInfo + + +const int RelocInfo::kApplyMask = + RelocInfo::kCodeTargetMask | 1 << RelocInfo::RUNTIME_ENTRY | + 1 << RelocInfo::INTERNAL_REFERENCE | 1 << RelocInfo::CODE_AGE_SEQUENCE | + RelocInfo::kDebugBreakSlotMask; + + +bool RelocInfo::IsCodedSpecially() { + // The deserializer needs to know whether a pointer is specially coded. Being + // specially coded on IA32 means that it is a relative address, as used by + // branch instructions. These are also the ones that need changing when a + // code object moves. + return (1 << rmode_) & kApplyMask; +} + + +bool RelocInfo::IsInConstantPool() { + return false; +} + +Address RelocInfo::wasm_memory_reference() { + DCHECK(IsWasmMemoryReference(rmode_)); + return Memory::Address_at(pc_); +} + +Address RelocInfo::wasm_global_reference() { + DCHECK(IsWasmGlobalReference(rmode_)); + return Memory::Address_at(pc_); +} + +uint32_t RelocInfo::wasm_memory_size_reference() { + DCHECK(IsWasmMemorySizeReference(rmode_)); + return Memory::uint32_at(pc_); +} + +uint32_t RelocInfo::wasm_function_table_size_reference() { + DCHECK(IsWasmFunctionTableSizeReference(rmode_)); + return Memory::uint32_at(pc_); +} + +void RelocInfo::unchecked_update_wasm_memory_reference( + Isolate* isolate, Address address, ICacheFlushMode icache_flush_mode) { + Memory::Address_at(pc_) = address; + if (icache_flush_mode != SKIP_ICACHE_FLUSH) { + Assembler::FlushICache(isolate, pc_, sizeof(Address)); + } +} + +void RelocInfo::unchecked_update_wasm_size(Isolate* isolate, uint32_t size, + ICacheFlushMode icache_flush_mode) { + Memory::uint32_at(pc_) = size; + if (icache_flush_mode != SKIP_ICACHE_FLUSH) { + Assembler::FlushICache(isolate, pc_, sizeof(uint32_t)); + } +} + +// ----------------------------------------------------------------------------- +// Implementation of Operand + +Operand::Operand(Register base, int32_t disp, RelocInfo::Mode rmode) { + // [base + disp/r] + if (disp == 0 && RelocInfo::IsNone(rmode) && !base.is(ebp)) { + // [base] + set_modrm(0, base); + if (base.is(esp)) set_sib(times_1, esp, base); + } else if (is_int8(disp) && RelocInfo::IsNone(rmode)) { + // [base + disp8] + set_modrm(1, base); + if (base.is(esp)) set_sib(times_1, esp, base); + set_disp8(disp); + } else { + // [base + disp/r] + set_modrm(2, base); + if (base.is(esp)) set_sib(times_1, esp, base); + set_dispr(disp, rmode); + } +} + + +Operand::Operand(Register base, + Register index, + ScaleFactor scale, + int32_t disp, + RelocInfo::Mode rmode) { + DCHECK(!index.is(esp)); // illegal addressing mode + // [base + index*scale + disp/r] + if (disp == 0 && RelocInfo::IsNone(rmode) && !base.is(ebp)) { + // [base + index*scale] + set_modrm(0, esp); + set_sib(scale, index, base); + } else if (is_int8(disp) && RelocInfo::IsNone(rmode)) { + // [base + index*scale + disp8] + set_modrm(1, esp); + set_sib(scale, index, base); + set_disp8(disp); + } else { + // [base + index*scale + disp/r] + set_modrm(2, esp); + set_sib(scale, index, base); + set_dispr(disp, rmode); + } +} + + +Operand::Operand(Register index, + ScaleFactor scale, + int32_t disp, + RelocInfo::Mode rmode) { + DCHECK(!index.is(esp)); // illegal addressing mode + // [index*scale + disp/r] + set_modrm(0, esp); + set_sib(scale, index, ebp); + set_dispr(disp, rmode); +} + + +bool Operand::is_reg(Register reg) const { + return ((buf_[0] & 0xF8) == 0xC0) // addressing mode is register only. + && ((buf_[0] & 0x07) == reg.code()); // register codes match. +} + + +bool Operand::is_reg_only() const { + return (buf_[0] & 0xF8) == 0xC0; // Addressing mode is register only. +} + + +Register Operand::reg() const { + DCHECK(is_reg_only()); + return Register::from_code(buf_[0] & 0x07); +} + +void Assembler::AllocateAndInstallRequestedHeapObjects(Isolate* isolate) { + for (auto& request : heap_object_requests_) { + Handle object; + switch (request.kind()) { + case HeapObjectRequest::kHeapNumber: + object = isolate->factory()->NewHeapNumber(request.heap_number(), + IMMUTABLE, TENURED); + break; + case HeapObjectRequest::kCodeStub: + request.code_stub()->set_isolate(isolate); + object = request.code_stub()->GetCode(); + break; + } + Address pc = buffer_ + request.offset(); + Memory::Object_Handle_at(pc) = object; + } +} + + +// ----------------------------------------------------------------------------- +// Implementation of Assembler. + +// Emit a single byte. Must always be inlined. +#define EMIT(x) \ + *pc_++ = (x) + +Assembler::Assembler(IsolateData isolate_data, void* buffer, int buffer_size) + : AssemblerBase(isolate_data, buffer, buffer_size) { +// Clear the buffer in debug mode unless it was provided by the +// caller in which case we can't be sure it's okay to overwrite +// existing code in it; see CodePatcher::CodePatcher(...). +#ifdef DEBUG + if (own_buffer_) { + memset(buffer_, 0xCC, buffer_size_); // int3 + } +#endif + + reloc_info_writer.Reposition(buffer_ + buffer_size_, pc_); +} + + +void Assembler::GetCode(Isolate* isolate, CodeDesc* desc) { + // Finalize code (at this point overflow() may be true, but the gap ensures + // that we are still not overlapping instructions and relocation info). + DCHECK(pc_ <= reloc_info_writer.pos()); // No overlap. + + AllocateAndInstallRequestedHeapObjects(isolate); + + // Set up code descriptor. + desc->buffer = buffer_; + desc->buffer_size = buffer_size_; + desc->instr_size = pc_offset(); + desc->reloc_size = (buffer_ + buffer_size_) - reloc_info_writer.pos(); + desc->origin = this; + desc->constant_pool_size = 0; + desc->unwinding_info_size = 0; + desc->unwinding_info = nullptr; +} + + +void Assembler::Align(int m) { + DCHECK(base::bits::IsPowerOfTwo(m)); + int mask = m - 1; + int addr = pc_offset(); + Nop((m - (addr & mask)) & mask); +} + + +bool Assembler::IsNop(Address addr) { + Address a = addr; + while (*a == 0x66) a++; + if (*a == 0x90) return true; + if (a[0] == 0xf && a[1] == 0x1f) return true; + return false; +} + + +void Assembler::Nop(int bytes) { + EnsureSpace ensure_space(this); + + // Older CPUs that do not support SSE2 may not support multibyte NOP + // instructions. + for (; bytes > 0; bytes--) { + EMIT(0x90); + } + return; +} + + +void Assembler::CodeTargetAlign() { + Align(16); // Preferred alignment of jump targets on ia32. +} + + +void Assembler::cpuid() { + EnsureSpace ensure_space(this); + EMIT(0x0F); + EMIT(0xA2); +} + + +void Assembler::pushad() { + EnsureSpace ensure_space(this); + EMIT(0x60); +} + + +void Assembler::popad() { + EnsureSpace ensure_space(this); + EMIT(0x61); +} + + +void Assembler::pushfd() { + EnsureSpace ensure_space(this); + EMIT(0x9C); +} + + +void Assembler::popfd() { + EnsureSpace ensure_space(this); + EMIT(0x9D); +} + + +void Assembler::push(const Immediate& x) { + EnsureSpace ensure_space(this); + if (x.is_int8()) { + EMIT(0x6a); + EMIT(x.immediate()); + } else { + EMIT(0x68); + emit(x); + } +} + + +void Assembler::push_imm32(int32_t imm32) { + EnsureSpace ensure_space(this); + EMIT(0x68); + emit(imm32); +} + + +void Assembler::push(Register src) { + EnsureSpace ensure_space(this); + EMIT(0x50 | src.code()); +} + + +void Assembler::push(const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0xFF); + emit_operand(esi, src); +} + + +void Assembler::pop(Register dst) { + DCHECK(reloc_info_writer.last_pc() != NULL); + EnsureSpace ensure_space(this); + EMIT(0x58 | dst.code()); +} + + +void Assembler::pop(const Operand& dst) { + EnsureSpace ensure_space(this); + EMIT(0x8F); + emit_operand(eax, dst); +} + + +void Assembler::enter(const Immediate& size) { + EnsureSpace ensure_space(this); + EMIT(0xC8); + emit_w(size); + EMIT(0); +} + + +void Assembler::leave() { + EnsureSpace ensure_space(this); + EMIT(0xC9); +} + + +void Assembler::mov_b(Register dst, const Operand& src) { + CHECK(dst.is_byte_register()); + EnsureSpace ensure_space(this); + EMIT(0x8A); + emit_operand(dst, src); +} + + +void Assembler::mov_b(const Operand& dst, const Immediate& src) { + EnsureSpace ensure_space(this); + EMIT(0xC6); + emit_operand(eax, dst); + EMIT(static_cast(src.immediate())); +} + + +void Assembler::mov_b(const Operand& dst, int8_t imm8) { + EnsureSpace ensure_space(this); + EMIT(0xC6); + emit_operand(eax, dst); + EMIT(imm8); +} + + +void Assembler::mov_b(const Operand& dst, Register src) { + CHECK(src.is_byte_register()); + EnsureSpace ensure_space(this); + EMIT(0x88); + emit_operand(src, dst); +} + + +void Assembler::mov_w(Register dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0x8B); + emit_operand(dst, src); +} + + +void Assembler::mov_w(const Operand& dst, Register src) { + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0x89); + emit_operand(src, dst); +} + + +void Assembler::mov_w(const Operand& dst, int16_t imm16) { + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0xC7); + emit_operand(eax, dst); + EMIT(static_cast(imm16 & 0xff)); + EMIT(static_cast(imm16 >> 8)); +} + + +void Assembler::mov_w(const Operand& dst, const Immediate& src) { + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0xC7); + emit_operand(eax, dst); + EMIT(static_cast(src.immediate() & 0xff)); + EMIT(static_cast(src.immediate() >> 8)); +} + + +void Assembler::mov(Register dst, int32_t imm32) { + EnsureSpace ensure_space(this); + EMIT(0xB8 | dst.code()); + emit(imm32); +} + + +void Assembler::mov(Register dst, const Immediate& x) { + EnsureSpace ensure_space(this); + EMIT(0xB8 | dst.code()); + emit(x); +} + + +void Assembler::mov(Register dst, Handle handle) { + EnsureSpace ensure_space(this); + EMIT(0xB8 | dst.code()); + emit(handle); +} + + +void Assembler::mov(Register dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x8B); + emit_operand(dst, src); +} + + +void Assembler::mov(Register dst, Register src) { + EnsureSpace ensure_space(this); + EMIT(0x89); + EMIT(0xC0 | src.code() << 3 | dst.code()); +} + + +void Assembler::mov(const Operand& dst, const Immediate& x) { + EnsureSpace ensure_space(this); + EMIT(0xC7); + emit_operand(eax, dst); + emit(x); +} + + +void Assembler::mov(const Operand& dst, Handle handle) { + EnsureSpace ensure_space(this); + EMIT(0xC7); + emit_operand(eax, dst); + emit(handle); +} + + +void Assembler::mov(const Operand& dst, Register src) { + EnsureSpace ensure_space(this); + EMIT(0x89); + emit_operand(src, dst); +} + + +void Assembler::movsx_b(Register dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x0F); + EMIT(0xBE); + emit_operand(dst, src); +} + + +void Assembler::movsx_w(Register dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x0F); + EMIT(0xBF); + emit_operand(dst, src); +} + + +void Assembler::movzx_b(Register dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x0F); + EMIT(0xB6); + emit_operand(dst, src); +} + + +void Assembler::movzx_w(Register dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x0F); + EMIT(0xB7); + emit_operand(dst, src); +} + + +void Assembler::cld() { + EnsureSpace ensure_space(this); + EMIT(0xFC); +} + + +void Assembler::rep_movs() { + EnsureSpace ensure_space(this); + EMIT(0xF3); + EMIT(0xA5); +} + + +void Assembler::rep_stos() { + EnsureSpace ensure_space(this); + EMIT(0xF3); + EMIT(0xAB); +} + + +void Assembler::stos() { + EnsureSpace ensure_space(this); + EMIT(0xAB); +} + + +void Assembler::xchg(Register dst, Register src) { + EnsureSpace ensure_space(this); + if (src.is(eax) || dst.is(eax)) { // Single-byte encoding. + EMIT(0x90 | (src.is(eax) ? dst.code() : src.code())); + } else { + EMIT(0x87); + EMIT(0xC0 | src.code() << 3 | dst.code()); + } +} + + +void Assembler::xchg(Register dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x87); + emit_operand(dst, src); +} + +void Assembler::xchg_b(Register reg, const Operand& op) { + DCHECK(reg.is_byte_register()); + EnsureSpace ensure_space(this); + EMIT(0x86); + emit_operand(reg, op); +} + +void Assembler::xchg_w(Register reg, const Operand& op) { + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0x87); + emit_operand(reg, op); +} + +void Assembler::lock() { + EnsureSpace ensure_space(this); + EMIT(0xF0); +} + +void Assembler::cmpxchg(const Operand& dst, Register src) { + EnsureSpace ensure_space(this); + EMIT(0x0F); + EMIT(0xB1); + emit_operand(src, dst); +} + +void Assembler::cmpxchg_b(const Operand& dst, Register src) { + DCHECK(src.is_byte_register()); + EnsureSpace ensure_space(this); + EMIT(0x0F); + EMIT(0xB0); + emit_operand(src, dst); +} + +void Assembler::cmpxchg_w(const Operand& dst, Register src) { + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0x0F); + EMIT(0xB1); + emit_operand(src, dst); +} + +void Assembler::adc(Register dst, int32_t imm32) { + EnsureSpace ensure_space(this); + emit_arith(2, Operand(dst), Immediate(imm32)); +} + + +void Assembler::adc(Register dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x13); + emit_operand(dst, src); +} + + +void Assembler::add(Register dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x03); + emit_operand(dst, src); +} + + +void Assembler::add(const Operand& dst, Register src) { + EnsureSpace ensure_space(this); + EMIT(0x01); + emit_operand(src, dst); +} + + +void Assembler::add(const Operand& dst, const Immediate& x) { + DCHECK(reloc_info_writer.last_pc() != NULL); + EnsureSpace ensure_space(this); + emit_arith(0, dst, x); +} + + +void Assembler::and_(Register dst, int32_t imm32) { + and_(dst, Immediate(imm32)); +} + + +void Assembler::and_(Register dst, const Immediate& x) { + EnsureSpace ensure_space(this); + emit_arith(4, Operand(dst), x); +} + + +void Assembler::and_(Register dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x23); + emit_operand(dst, src); +} + + +void Assembler::and_(const Operand& dst, const Immediate& x) { + EnsureSpace ensure_space(this); + emit_arith(4, dst, x); +} + + +void Assembler::and_(const Operand& dst, Register src) { + EnsureSpace ensure_space(this); + EMIT(0x21); + emit_operand(src, dst); +} + +void Assembler::cmpb(const Operand& op, Immediate imm8) { + DCHECK(imm8.is_int8() || imm8.is_uint8()); + EnsureSpace ensure_space(this); + if (op.is_reg(eax)) { + EMIT(0x3C); + } else { + EMIT(0x80); + emit_operand(edi, op); // edi == 7 + } + emit_b(imm8); +} + + +void Assembler::cmpb(const Operand& op, Register reg) { + CHECK(reg.is_byte_register()); + EnsureSpace ensure_space(this); + EMIT(0x38); + emit_operand(reg, op); +} + + +void Assembler::cmpb(Register reg, const Operand& op) { + CHECK(reg.is_byte_register()); + EnsureSpace ensure_space(this); + EMIT(0x3A); + emit_operand(reg, op); +} + + +void Assembler::cmpw(const Operand& op, Immediate imm16) { + DCHECK(imm16.is_int16()); + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0x81); + emit_operand(edi, op); + emit_w(imm16); +} + +void Assembler::cmpw(Register reg, const Operand& op) { + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0x3B); + emit_operand(reg, op); +} + +void Assembler::cmpw(const Operand& op, Register reg) { + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0x39); + emit_operand(reg, op); +} + +void Assembler::cmp(Register reg, int32_t imm32) { + EnsureSpace ensure_space(this); + emit_arith(7, Operand(reg), Immediate(imm32)); +} + + +void Assembler::cmp(Register reg, Handle handle) { + EnsureSpace ensure_space(this); + emit_arith(7, Operand(reg), Immediate(handle)); +} + + +void Assembler::cmp(Register reg, const Operand& op) { + EnsureSpace ensure_space(this); + EMIT(0x3B); + emit_operand(reg, op); +} + +void Assembler::cmp(const Operand& op, Register reg) { + EnsureSpace ensure_space(this); + EMIT(0x39); + emit_operand(reg, op); +} + +void Assembler::cmp(const Operand& op, const Immediate& imm) { + EnsureSpace ensure_space(this); + emit_arith(7, op, imm); +} + + +void Assembler::cmp(const Operand& op, Handle handle) { + EnsureSpace ensure_space(this); + emit_arith(7, op, Immediate(handle)); +} + + +void Assembler::cmpb_al(const Operand& op) { + EnsureSpace ensure_space(this); + EMIT(0x38); // CMP r/m8, r8 + emit_operand(eax, op); // eax has same code as register al. +} + + +void Assembler::cmpw_ax(const Operand& op) { + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0x39); // CMP r/m16, r16 + emit_operand(eax, op); // eax has same code as register ax. +} + + +void Assembler::dec_b(Register dst) { + CHECK(dst.is_byte_register()); + EnsureSpace ensure_space(this); + EMIT(0xFE); + EMIT(0xC8 | dst.code()); +} + + +void Assembler::dec_b(const Operand& dst) { + EnsureSpace ensure_space(this); + EMIT(0xFE); + emit_operand(ecx, dst); +} + + +void Assembler::dec(Register dst) { + EnsureSpace ensure_space(this); + EMIT(0x48 | dst.code()); +} + + +void Assembler::dec(const Operand& dst) { + EnsureSpace ensure_space(this); + EMIT(0xFF); + emit_operand(ecx, dst); +} + + +void Assembler::cdq() { + EnsureSpace ensure_space(this); + EMIT(0x99); +} + + +void Assembler::idiv(const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0xF7); + emit_operand(edi, src); +} + + +void Assembler::div(const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0xF7); + emit_operand(esi, src); +} + + +void Assembler::imul(Register reg) { + EnsureSpace ensure_space(this); + EMIT(0xF7); + EMIT(0xE8 | reg.code()); +} + + +void Assembler::imul(Register dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x0F); + EMIT(0xAF); + emit_operand(dst, src); +} + + +void Assembler::imul(Register dst, Register src, int32_t imm32) { + imul(dst, Operand(src), imm32); +} + + +void Assembler::imul(Register dst, const Operand& src, int32_t imm32) { + EnsureSpace ensure_space(this); + if (is_int8(imm32)) { + EMIT(0x6B); + emit_operand(dst, src); + EMIT(imm32); + } else { + EMIT(0x69); + emit_operand(dst, src); + emit(imm32); + } +} + + +void Assembler::inc(Register dst) { + EnsureSpace ensure_space(this); + EMIT(0x40 | dst.code()); +} + + +void Assembler::inc(const Operand& dst) { + EnsureSpace ensure_space(this); + EMIT(0xFF); + emit_operand(eax, dst); +} + + +void Assembler::lea(Register dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x8D); + emit_operand(dst, src); +} + + +void Assembler::mul(Register src) { + EnsureSpace ensure_space(this); + EMIT(0xF7); + EMIT(0xE0 | src.code()); +} + + +void Assembler::neg(Register dst) { + EnsureSpace ensure_space(this); + EMIT(0xF7); + EMIT(0xD8 | dst.code()); +} + + +void Assembler::neg(const Operand& dst) { + EnsureSpace ensure_space(this); + EMIT(0xF7); + emit_operand(ebx, dst); +} + + +void Assembler::not_(Register dst) { + EnsureSpace ensure_space(this); + EMIT(0xF7); + EMIT(0xD0 | dst.code()); +} + + +void Assembler::not_(const Operand& dst) { + EnsureSpace ensure_space(this); + EMIT(0xF7); + emit_operand(edx, dst); +} + + +void Assembler::or_(Register dst, int32_t imm32) { + EnsureSpace ensure_space(this); + emit_arith(1, Operand(dst), Immediate(imm32)); +} + + +void Assembler::or_(Register dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x0B); + emit_operand(dst, src); +} + + +void Assembler::or_(const Operand& dst, const Immediate& x) { + EnsureSpace ensure_space(this); + emit_arith(1, dst, x); +} + + +void Assembler::or_(const Operand& dst, Register src) { + EnsureSpace ensure_space(this); + EMIT(0x09); + emit_operand(src, dst); +} + + +void Assembler::rcl(Register dst, uint8_t imm8) { + EnsureSpace ensure_space(this); + DCHECK(is_uint5(imm8)); // illegal shift count + if (imm8 == 1) { + EMIT(0xD1); + EMIT(0xD0 | dst.code()); + } else { + EMIT(0xC1); + EMIT(0xD0 | dst.code()); + EMIT(imm8); + } +} + + +void Assembler::rcr(Register dst, uint8_t imm8) { + EnsureSpace ensure_space(this); + DCHECK(is_uint5(imm8)); // illegal shift count + if (imm8 == 1) { + EMIT(0xD1); + EMIT(0xD8 | dst.code()); + } else { + EMIT(0xC1); + EMIT(0xD8 | dst.code()); + EMIT(imm8); + } +} + + +void Assembler::ror(const Operand& dst, uint8_t imm8) { + EnsureSpace ensure_space(this); + DCHECK(is_uint5(imm8)); // illegal shift count + if (imm8 == 1) { + EMIT(0xD1); + emit_operand(ecx, dst); + } else { + EMIT(0xC1); + emit_operand(ecx, dst); + EMIT(imm8); + } +} + + +void Assembler::ror_cl(const Operand& dst) { + EnsureSpace ensure_space(this); + EMIT(0xD3); + emit_operand(ecx, dst); +} + + +void Assembler::sar(const Operand& dst, uint8_t imm8) { + EnsureSpace ensure_space(this); + DCHECK(is_uint5(imm8)); // illegal shift count + if (imm8 == 1) { + EMIT(0xD1); + emit_operand(edi, dst); + } else { + EMIT(0xC1); + emit_operand(edi, dst); + EMIT(imm8); + } +} + + +void Assembler::sar_cl(const Operand& dst) { + EnsureSpace ensure_space(this); + EMIT(0xD3); + emit_operand(edi, dst); +} + +void Assembler::sbb(Register dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x1B); + emit_operand(dst, src); +} + +void Assembler::shld(Register dst, Register src, uint8_t shift) { + DCHECK(is_uint5(shift)); + EnsureSpace ensure_space(this); + EMIT(0x0F); + EMIT(0xA4); + emit_operand(src, Operand(dst)); + EMIT(shift); +} + +void Assembler::shld_cl(Register dst, Register src) { + EnsureSpace ensure_space(this); + EMIT(0x0F); + EMIT(0xA5); + emit_operand(src, Operand(dst)); +} + + +void Assembler::shl(const Operand& dst, uint8_t imm8) { + EnsureSpace ensure_space(this); + DCHECK(is_uint5(imm8)); // illegal shift count + if (imm8 == 1) { + EMIT(0xD1); + emit_operand(esp, dst); + } else { + EMIT(0xC1); + emit_operand(esp, dst); + EMIT(imm8); + } +} + + +void Assembler::shl_cl(const Operand& dst) { + EnsureSpace ensure_space(this); + EMIT(0xD3); + emit_operand(esp, dst); +} + +void Assembler::shr(const Operand& dst, uint8_t imm8) { + EnsureSpace ensure_space(this); + DCHECK(is_uint5(imm8)); // illegal shift count + if (imm8 == 1) { + EMIT(0xD1); + emit_operand(ebp, dst); + } else { + EMIT(0xC1); + emit_operand(ebp, dst); + EMIT(imm8); + } +} + + +void Assembler::shr_cl(const Operand& dst) { + EnsureSpace ensure_space(this); + EMIT(0xD3); + emit_operand(ebp, dst); +} + +void Assembler::shrd(Register dst, Register src, uint8_t shift) { + DCHECK(is_uint5(shift)); + EnsureSpace ensure_space(this); + EMIT(0x0F); + EMIT(0xAC); + emit_operand(dst, Operand(src)); + EMIT(shift); +} + +void Assembler::shrd_cl(const Operand& dst, Register src) { + EnsureSpace ensure_space(this); + EMIT(0x0F); + EMIT(0xAD); + emit_operand(src, dst); +} + +void Assembler::sub(const Operand& dst, const Immediate& x) { + EnsureSpace ensure_space(this); + emit_arith(5, dst, x); +} + + +void Assembler::sub(Register dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x2B); + emit_operand(dst, src); +} + + +void Assembler::sub(const Operand& dst, Register src) { + EnsureSpace ensure_space(this); + EMIT(0x29); + emit_operand(src, dst); +} + + +void Assembler::test(Register reg, const Immediate& imm) { + if (imm.is_uint8()) { + test_b(reg, imm); + return; + } + + EnsureSpace ensure_space(this); + // This is not using emit_arith because test doesn't support + // sign-extension of 8-bit operands. + if (reg.is(eax)) { + EMIT(0xA9); + } else { + EMIT(0xF7); + EMIT(0xC0 | reg.code()); + } + emit(imm); +} + + +void Assembler::test(Register reg, const Operand& op) { + EnsureSpace ensure_space(this); + EMIT(0x85); + emit_operand(reg, op); +} + + +void Assembler::test_b(Register reg, const Operand& op) { + CHECK(reg.is_byte_register()); + EnsureSpace ensure_space(this); + EMIT(0x84); + emit_operand(reg, op); +} + + +void Assembler::test(const Operand& op, const Immediate& imm) { + if (op.is_reg_only()) { + test(op.reg(), imm); + return; + } + if (imm.is_uint8()) { + return test_b(op, imm); + } + EnsureSpace ensure_space(this); + EMIT(0xF7); + emit_operand(eax, op); + emit(imm); +} + +void Assembler::test_b(Register reg, Immediate imm8) { + DCHECK(imm8.is_uint8()); + EnsureSpace ensure_space(this); + // Only use test against byte for registers that have a byte + // variant: eax, ebx, ecx, and edx. + if (reg.is(eax)) { + EMIT(0xA8); + emit_b(imm8); + } else if (reg.is_byte_register()) { + emit_arith_b(0xF6, 0xC0, reg, static_cast(imm8.immediate())); + } else { + EMIT(0x66); + EMIT(0xF7); + EMIT(0xC0 | reg.code()); + emit_w(imm8); + } +} + +void Assembler::test_b(const Operand& op, Immediate imm8) { + if (op.is_reg_only()) { + test_b(op.reg(), imm8); + return; + } + EnsureSpace ensure_space(this); + EMIT(0xF6); + emit_operand(eax, op); + emit_b(imm8); +} + +void Assembler::test_w(Register reg, Immediate imm16) { + DCHECK(imm16.is_int16() || imm16.is_uint16()); + EnsureSpace ensure_space(this); + if (reg.is(eax)) { + EMIT(0xA9); + emit_w(imm16); + } else { + EMIT(0x66); + EMIT(0xF7); + EMIT(0xc0 | reg.code()); + emit_w(imm16); + } +} + +void Assembler::test_w(Register reg, const Operand& op) { + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0x85); + emit_operand(reg, op); +} + +void Assembler::test_w(const Operand& op, Immediate imm16) { + DCHECK(imm16.is_int16() || imm16.is_uint16()); + if (op.is_reg_only()) { + test_w(op.reg(), imm16); + return; + } + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0xF7); + emit_operand(eax, op); + emit_w(imm16); +} + +void Assembler::xor_(Register dst, int32_t imm32) { + EnsureSpace ensure_space(this); + emit_arith(6, Operand(dst), Immediate(imm32)); +} + + +void Assembler::xor_(Register dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x33); + emit_operand(dst, src); +} + + +void Assembler::xor_(const Operand& dst, Register src) { + EnsureSpace ensure_space(this); + EMIT(0x31); + emit_operand(src, dst); +} + + +void Assembler::xor_(const Operand& dst, const Immediate& x) { + EnsureSpace ensure_space(this); + emit_arith(6, dst, x); +} + + +void Assembler::bt(const Operand& dst, Register src) { + EnsureSpace ensure_space(this); + EMIT(0x0F); + EMIT(0xA3); + emit_operand(src, dst); +} + + +void Assembler::bts(const Operand& dst, Register src) { + EnsureSpace ensure_space(this); + EMIT(0x0F); + EMIT(0xAB); + emit_operand(src, dst); +} + + +void Assembler::bsr(Register dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x0F); + EMIT(0xBD); + emit_operand(dst, src); +} + + +void Assembler::bsf(Register dst, const Operand& src) { + EnsureSpace ensure_space(this); + EMIT(0x0F); + EMIT(0xBC); + emit_operand(dst, src); +} + + +void Assembler::hlt() { + EnsureSpace ensure_space(this); + EMIT(0xF4); +} + + +void Assembler::int3() { + EnsureSpace ensure_space(this); + EMIT(0xCC); +} + + +void Assembler::nop() { + EnsureSpace ensure_space(this); + EMIT(0x90); +} + + +void Assembler::ret(int imm16) { + EnsureSpace ensure_space(this); + DCHECK(is_uint16(imm16)); + if (imm16 == 0) { + EMIT(0xC3); + } else { + EMIT(0xC2); + EMIT(imm16 & 0xFF); + EMIT((imm16 >> 8) & 0xFF); + } +} + + +void Assembler::ud2() { + EnsureSpace ensure_space(this); + EMIT(0x0F); + EMIT(0x0B); +} + + +// Labels refer to positions in the (to be) generated code. +// There are bound, linked, and unused labels. +// +// Bound labels refer to known positions in the already +// generated code. pos() is the position the label refers to. +// +// Linked labels refer to unknown positions in the code +// to be generated; pos() is the position of the 32bit +// Displacement of the last instruction using the label. + + +void Assembler::print(Label* L) { + if (L->is_unused()) { + PrintF("unused label\n"); + } else if (L->is_bound()) { + PrintF("bound label to %d\n", L->pos()); + } else if (L->is_linked()) { + Label l = *L; + PrintF("unbound label"); + while (l.is_linked()) { + Displacement disp = disp_at(&l); + PrintF("@ %d ", l.pos()); + disp.print(); + PrintF("\n"); + disp.next(&l); + } + } else { + PrintF("label in inconsistent state (pos = %d)\n", L->pos_); + } +} + + +void Assembler::bind_to(Label* L, int pos) { + EnsureSpace ensure_space(this); + DCHECK(0 <= pos && pos <= pc_offset()); // must have a valid binding position + while (L->is_linked()) { + Displacement disp = disp_at(L); + int fixup_pos = L->pos(); + if (disp.type() == Displacement::CODE_ABSOLUTE) { + long_at_put(fixup_pos, reinterpret_cast(buffer_ + pos)); + internal_reference_positions_.push_back(fixup_pos); + } else if (disp.type() == Displacement::CODE_RELATIVE) { + // Relative to Code* heap object pointer. + long_at_put(fixup_pos, pos + Code::kHeaderSize - kHeapObjectTag); + } else { + if (disp.type() == Displacement::UNCONDITIONAL_JUMP) { + DCHECK(byte_at(fixup_pos - 1) == 0xE9); // jmp expected + } + // Relative address, relative to point after address. + int imm32 = pos - (fixup_pos + sizeof(int32_t)); + long_at_put(fixup_pos, imm32); + } + disp.next(L); + } + while (L->is_near_linked()) { + int fixup_pos = L->near_link_pos(); + int offset_to_next = + static_cast(*reinterpret_cast(addr_at(fixup_pos))); + DCHECK(offset_to_next <= 0); + // Relative address, relative to point after address. + int disp = pos - fixup_pos - sizeof(int8_t); + CHECK(0 <= disp && disp <= 127); + set_byte_at(fixup_pos, disp); + if (offset_to_next < 0) { + L->link_to(fixup_pos + offset_to_next, Label::kNear); + } else { + L->UnuseNear(); + } + } + L->bind_to(pos); +} + + +void Assembler::bind(Label* L) { + EnsureSpace ensure_space(this); + DCHECK(!L->is_bound()); // label can only be bound once + bind_to(L, pc_offset()); +} + + +void Assembler::call(Label* L) { + EnsureSpace ensure_space(this); + if (L->is_bound()) { + const int long_size = 5; + int offs = L->pos() - pc_offset(); + DCHECK(offs <= 0); + // 1110 1000 #32-bit disp. + EMIT(0xE8); + emit(offs - long_size); + } else { + // 1110 1000 #32-bit disp. + EMIT(0xE8); + emit_disp(L, Displacement::OTHER); + } +} + + +void Assembler::call(byte* entry, RelocInfo::Mode rmode) { + EnsureSpace ensure_space(this); + DCHECK(!RelocInfo::IsCodeTarget(rmode)); + EMIT(0xE8); + if (RelocInfo::IsRuntimeEntry(rmode)) { + emit(reinterpret_cast(entry), rmode); + } else { + emit(entry - (pc_ + sizeof(int32_t)), rmode); + } +} + + +int Assembler::CallSize(const Operand& adr) { + // Call size is 1 (opcode) + adr.len_ (operand). + return 1 + adr.len_; +} + + +void Assembler::call(const Operand& adr) { + EnsureSpace ensure_space(this); + EMIT(0xFF); + emit_operand(edx, adr); +} + + +int Assembler::CallSize(Handle code, RelocInfo::Mode rmode) { + return 1 /* EMIT */ + sizeof(uint32_t) /* emit */; +} + + +void Assembler::call(Handle code, RelocInfo::Mode rmode) { + EnsureSpace ensure_space(this); + DCHECK(RelocInfo::IsCodeTarget(rmode) + || rmode == RelocInfo::CODE_AGE_SEQUENCE); + EMIT(0xE8); + emit(code, rmode); +} + +void Assembler::call(CodeStub* stub) { + EnsureSpace ensure_space(this); + EMIT(0xE8); + emit(Immediate::EmbeddedCode(stub)); +} + +void Assembler::jmp(Label* L, Label::Distance distance) { + EnsureSpace ensure_space(this); + if (L->is_bound()) { + const int short_size = 2; + const int long_size = 5; + int offs = L->pos() - pc_offset(); + DCHECK(offs <= 0); + if (is_int8(offs - short_size)) { + // 1110 1011 #8-bit disp. + EMIT(0xEB); + EMIT((offs - short_size) & 0xFF); + } else { + // 1110 1001 #32-bit disp. + EMIT(0xE9); + emit(offs - long_size); + } + } else if (distance == Label::kNear) { + EMIT(0xEB); + emit_near_disp(L); + } else { + // 1110 1001 #32-bit disp. + EMIT(0xE9); + emit_disp(L, Displacement::UNCONDITIONAL_JUMP); + } +} + + +void Assembler::jmp(byte* entry, RelocInfo::Mode rmode) { + EnsureSpace ensure_space(this); + DCHECK(!RelocInfo::IsCodeTarget(rmode)); + EMIT(0xE9); + if (RelocInfo::IsRuntimeEntry(rmode)) { + emit(reinterpret_cast(entry), rmode); + } else { + emit(entry - (pc_ + sizeof(int32_t)), rmode); + } +} + + +void Assembler::jmp(const Operand& adr) { + EnsureSpace ensure_space(this); + EMIT(0xFF); + emit_operand(esp, adr); +} + + +void Assembler::jmp(Handle code, RelocInfo::Mode rmode) { + EnsureSpace ensure_space(this); + DCHECK(RelocInfo::IsCodeTarget(rmode)); + EMIT(0xE9); + emit(code, rmode); +} + + +void Assembler::j(Condition cc, Label* L, Label::Distance distance) { + EnsureSpace ensure_space(this); + DCHECK(0 <= cc && static_cast(cc) < 16); + if (L->is_bound()) { + const int short_size = 2; + const int long_size = 6; + int offs = L->pos() - pc_offset(); + DCHECK(offs <= 0); + if (is_int8(offs - short_size)) { + // 0111 tttn #8-bit disp + EMIT(0x70 | cc); + EMIT((offs - short_size) & 0xFF); + } else { + // 0000 1111 1000 tttn #32-bit disp + EMIT(0x0F); + EMIT(0x80 | cc); + emit(offs - long_size); + } + } else if (distance == Label::kNear) { + EMIT(0x70 | cc); + emit_near_disp(L); + } else { + // 0000 1111 1000 tttn #32-bit disp + // Note: could eliminate cond. jumps to this jump if condition + // is the same however, seems to be rather unlikely case. + EMIT(0x0F); + EMIT(0x80 | cc); + emit_disp(L, Displacement::OTHER); + } +} + + +void Assembler::j(Condition cc, byte* entry, RelocInfo::Mode rmode) { + EnsureSpace ensure_space(this); + DCHECK((0 <= cc) && (static_cast(cc) < 16)); + // 0000 1111 1000 tttn #32-bit disp. + EMIT(0x0F); + EMIT(0x80 | cc); + if (RelocInfo::IsRuntimeEntry(rmode)) { + emit(reinterpret_cast(entry), rmode); + } else { + emit(entry - (pc_ + sizeof(int32_t)), rmode); + } +} + + +void Assembler::j(Condition cc, Handle code, RelocInfo::Mode rmode) { + EnsureSpace ensure_space(this); + // 0000 1111 1000 tttn #32-bit disp + EMIT(0x0F); + EMIT(0x80 | cc); + emit(code, rmode); +} + + +// FPU instructions. + +void Assembler::fld(int i) { + EnsureSpace ensure_space(this); + emit_farith(0xD9, 0xC0, i); +} + + +void Assembler::fstp(int i) { + EnsureSpace ensure_space(this); + emit_farith(0xDD, 0xD8, i); +} + + +void Assembler::fld1() { + EnsureSpace ensure_space(this); + EMIT(0xD9); + EMIT(0xE8); +} + + +void Assembler::fldpi() { + EnsureSpace ensure_space(this); + EMIT(0xD9); + EMIT(0xEB); +} + + +void Assembler::fldz() { + EnsureSpace ensure_space(this); + EMIT(0xD9); + EMIT(0xEE); +} + + +void Assembler::fldln2() { + EnsureSpace ensure_space(this); + EMIT(0xD9); + EMIT(0xED); +} + + +void Assembler::fld_s(const Operand& adr) { + EnsureSpace ensure_space(this); + EMIT(0xD9); + emit_operand(eax, adr); +} + + +void Assembler::fld_d(const Operand& adr) { + EnsureSpace ensure_space(this); + EMIT(0xDD); + emit_operand(eax, adr); +} + + +void Assembler::fstp_s(const Operand& adr) { + EnsureSpace ensure_space(this); + EMIT(0xD9); + emit_operand(ebx, adr); +} + + +void Assembler::fst_s(const Operand& adr) { + EnsureSpace ensure_space(this); + EMIT(0xD9); + emit_operand(edx, adr); +} + + +void Assembler::fldcw(const Operand& adr) { + EnsureSpace ensure_space(this); + EMIT(0xD9); + emit_operand(ebp, adr); +} + + +void Assembler::fnstcw(const Operand& adr) { + EnsureSpace ensure_space(this); + EMIT(0xD9); + emit_operand(edi, adr); +} + + +void Assembler::fstp_d(const Operand& adr) { + EnsureSpace ensure_space(this); + EMIT(0xDD); + emit_operand(ebx, adr); +} + + +void Assembler::fst_d(const Operand& adr) { + EnsureSpace ensure_space(this); + EMIT(0xDD); + emit_operand(edx, adr); +} + + +void Assembler::fild_s(const Operand& adr) { + EnsureSpace ensure_space(this); + EMIT(0xDB); + emit_operand(eax, adr); +} + + +void Assembler::fild_d(const Operand& adr) { + EnsureSpace ensure_space(this); + EMIT(0xDF); + emit_operand(ebp, adr); +} + + +void Assembler::fistp_s(const Operand& adr) { + EnsureSpace ensure_space(this); + EMIT(0xDB); + emit_operand(ebx, adr); +} + + +void Assembler::fisttp_s(const Operand& adr) { + DCHECK(IsEnabled(SSE3)); + EnsureSpace ensure_space(this); + EMIT(0xDB); + emit_operand(ecx, adr); +} + + +void Assembler::fisttp_d(const Operand& adr) { + DCHECK(IsEnabled(SSE3)); + EnsureSpace ensure_space(this); + EMIT(0xDD); + emit_operand(ecx, adr); +} + + +void Assembler::fist_s(const Operand& adr) { + EnsureSpace ensure_space(this); + EMIT(0xDB); + emit_operand(edx, adr); +} + + +void Assembler::fistp_d(const Operand& adr) { + EnsureSpace ensure_space(this); + EMIT(0xDF); + emit_operand(edi, adr); +} + + +void Assembler::fabs() { + EnsureSpace ensure_space(this); + EMIT(0xD9); + EMIT(0xE1); +} + + +void Assembler::fchs() { + EnsureSpace ensure_space(this); + EMIT(0xD9); + EMIT(0xE0); +} + + +void Assembler::fsqrt() { + EnsureSpace ensure_space(this); + EMIT(0xD9); + EMIT(0xFA); +} + + +void Assembler::fcos() { + EnsureSpace ensure_space(this); + EMIT(0xD9); + EMIT(0xFF); +} + + +void Assembler::fsin() { + EnsureSpace ensure_space(this); + EMIT(0xD9); + EMIT(0xFE); +} + + +void Assembler::fptan() { + EnsureSpace ensure_space(this); + EMIT(0xD9); + EMIT(0xF2); +} + + +void Assembler::fyl2x() { + EnsureSpace ensure_space(this); + EMIT(0xD9); + EMIT(0xF1); +} + + +void Assembler::f2xm1() { + EnsureSpace ensure_space(this); + EMIT(0xD9); + EMIT(0xF0); +} + + +void Assembler::fscale() { + EnsureSpace ensure_space(this); + EMIT(0xD9); + EMIT(0xFD); +} + + +void Assembler::fninit() { + EnsureSpace ensure_space(this); + EMIT(0xDB); + EMIT(0xE3); +} + + +void Assembler::fadd(int i) { + EnsureSpace ensure_space(this); + emit_farith(0xDC, 0xC0, i); +} + + +void Assembler::fadd_i(int i) { + EnsureSpace ensure_space(this); + emit_farith(0xD8, 0xC0, i); +} + + +void Assembler::fadd_d(const Operand& adr) { + EnsureSpace ensure_space(this); + EMIT(0xDC); + emit_operand(eax, adr); +} + + +void Assembler::fsub(int i) { + EnsureSpace ensure_space(this); + emit_farith(0xDC, 0xE8, i); +} + + +void Assembler::fsub_i(int i) { + EnsureSpace ensure_space(this); + emit_farith(0xD8, 0xE0, i); +} + + +void Assembler::fsubr_d(const Operand& adr) { + EnsureSpace ensure_space(this); + EMIT(0xDC); + emit_operand(ebp, adr); +} + + +void Assembler::fsub_d(const Operand& adr) { + EnsureSpace ensure_space(this); + EMIT(0xDC); + emit_operand(esp, adr); +} + + +void Assembler::fisub_s(const Operand& adr) { + EnsureSpace ensure_space(this); + EMIT(0xDA); + emit_operand(esp, adr); +} + + +void Assembler::fmul_i(int i) { + EnsureSpace ensure_space(this); + emit_farith(0xD8, 0xC8, i); +} + + +void Assembler::fmul(int i) { + EnsureSpace ensure_space(this); + emit_farith(0xDC, 0xC8, i); +} + + +void Assembler::fmul_d(const Operand& adr) { + EnsureSpace ensure_space(this); + EMIT(0xDC); + emit_operand(ecx, adr); +} + + +void Assembler::fdiv(int i) { + EnsureSpace ensure_space(this); + emit_farith(0xDC, 0xF8, i); +} + + +void Assembler::fdiv_d(const Operand& adr) { + EnsureSpace ensure_space(this); + EMIT(0xDC); + emit_operand(esi, adr); +} + + +void Assembler::fdivr_d(const Operand& adr) { + EnsureSpace ensure_space(this); + EMIT(0xDC); + emit_operand(edi, adr); +} + + +void Assembler::fdiv_i(int i) { + EnsureSpace ensure_space(this); + emit_farith(0xD8, 0xF0, i); +} + + +void Assembler::faddp(int i) { + EnsureSpace ensure_space(this); + emit_farith(0xDE, 0xC0, i); +} + + +void Assembler::fsubp(int i) { + EnsureSpace ensure_space(this); + emit_farith(0xDE, 0xE8, i); +} + + +void Assembler::fsubrp(int i) { + EnsureSpace ensure_space(this); + emit_farith(0xDE, 0xE0, i); +} + + +void Assembler::fmulp(int i) { + EnsureSpace ensure_space(this); + emit_farith(0xDE, 0xC8, i); +} + + +void Assembler::fdivp(int i) { + EnsureSpace ensure_space(this); + emit_farith(0xDE, 0xF8, i); +} + + +void Assembler::fprem() { + EnsureSpace ensure_space(this); + EMIT(0xD9); + EMIT(0xF8); +} + + +void Assembler::fprem1() { + EnsureSpace ensure_space(this); + EMIT(0xD9); + EMIT(0xF5); +} + + +void Assembler::fxch(int i) { + EnsureSpace ensure_space(this); + emit_farith(0xD9, 0xC8, i); +} + + +void Assembler::fincstp() { + EnsureSpace ensure_space(this); + EMIT(0xD9); + EMIT(0xF7); +} + + +void Assembler::ffree(int i) { + EnsureSpace ensure_space(this); + emit_farith(0xDD, 0xC0, i); +} + + +void Assembler::ftst() { + EnsureSpace ensure_space(this); + EMIT(0xD9); + EMIT(0xE4); +} + + +void Assembler::fxam() { + EnsureSpace ensure_space(this); + EMIT(0xD9); + EMIT(0xE5); +} + + +void Assembler::fucomp(int i) { + EnsureSpace ensure_space(this); + emit_farith(0xDD, 0xE8, i); +} + + +void Assembler::fucompp() { + EnsureSpace ensure_space(this); + EMIT(0xDA); + EMIT(0xE9); +} + + +void Assembler::fucomi(int i) { + EnsureSpace ensure_space(this); + EMIT(0xDB); + EMIT(0xE8 + i); +} + + +void Assembler::fucomip() { + EnsureSpace ensure_space(this); + EMIT(0xDF); + EMIT(0xE9); +} + + +void Assembler::fcompp() { + EnsureSpace ensure_space(this); + EMIT(0xDE); + EMIT(0xD9); +} + + +void Assembler::fnstsw_ax() { + EnsureSpace ensure_space(this); + EMIT(0xDF); + EMIT(0xE0); +} + + +void Assembler::fwait() { + EnsureSpace ensure_space(this); + EMIT(0x9B); +} + + +void Assembler::frndint() { + EnsureSpace ensure_space(this); + EMIT(0xD9); + EMIT(0xFC); +} + + +void Assembler::fnclex() { + EnsureSpace ensure_space(this); + EMIT(0xDB); + EMIT(0xE2); +} + + +void Assembler::fnsave(const Operand& adr) { + EnsureSpace ensure_space(this); + EMIT(0xDD); + emit_operand(esi, adr); +} + + +void Assembler::frstor(const Operand& adr) { + EnsureSpace ensure_space(this); + EMIT(0xDD); + emit_operand(esp, adr); +} + + +void Assembler::sahf() { + EnsureSpace ensure_space(this); + EMIT(0x9E); +} + + +void Assembler::setcc(Condition cc, Register reg) { + DCHECK(reg.is_byte_register()); + EnsureSpace ensure_space(this); + EMIT(0x0F); + EMIT(0x90 | cc); + EMIT(0xC0 | reg.code()); +} + + +void Assembler::GrowBuffer() { + DCHECK(buffer_overflow()); + if (!own_buffer_) FATAL("external code buffer is too small"); + + // Compute new buffer size. + CodeDesc desc; // the new buffer + desc.buffer_size = 2 * buffer_size_; + + // Some internal data structures overflow for very large buffers, + // they must ensure that kMaximalBufferSize is not too large. + if (desc.buffer_size > kMaximalBufferSize) { + V8::FatalProcessOutOfMemory("Assembler::GrowBuffer"); + } + + // Set up new buffer. + desc.buffer = NewArray(desc.buffer_size); + desc.origin = this; + desc.instr_size = pc_offset(); + desc.reloc_size = (buffer_ + buffer_size_) - (reloc_info_writer.pos()); + + // Clear the buffer in debug mode. Use 'int3' instructions to make + // sure to get into problems if we ever run uninitialized code. +#ifdef DEBUG + memset(desc.buffer, 0xCC, desc.buffer_size); +#endif + + // Copy the data. + int pc_delta = desc.buffer - buffer_; + int rc_delta = (desc.buffer + desc.buffer_size) - (buffer_ + buffer_size_); + MemMove(desc.buffer, buffer_, desc.instr_size); + MemMove(rc_delta + reloc_info_writer.pos(), reloc_info_writer.pos(), + desc.reloc_size); + + DeleteArray(buffer_); + buffer_ = desc.buffer; + buffer_size_ = desc.buffer_size; + pc_ += pc_delta; + reloc_info_writer.Reposition(reloc_info_writer.pos() + rc_delta, + reloc_info_writer.last_pc() + pc_delta); + + // Relocate internal references. + for (auto pos : internal_reference_positions_) { + int32_t* p = reinterpret_cast(buffer_ + pos); + *p += pc_delta; + } + + DCHECK(!buffer_overflow()); +} + + +void Assembler::emit_arith_b(int op1, int op2, Register dst, int imm8) { + DCHECK(is_uint8(op1) && is_uint8(op2)); // wrong opcode + DCHECK(is_uint8(imm8)); + DCHECK((op1 & 0x01) == 0); // should be 8bit operation + EMIT(op1); + EMIT(op2 | dst.code()); + EMIT(imm8); +} + + +void Assembler::emit_arith(int sel, Operand dst, const Immediate& x) { + DCHECK((0 <= sel) && (sel <= 7)); + Register ireg = { sel }; + if (x.is_int8()) { + EMIT(0x83); // using a sign-extended 8-bit immediate. + emit_operand(ireg, dst); + EMIT(x.immediate() & 0xFF); + } else if (dst.is_reg(eax)) { + EMIT((sel << 3) | 0x05); // short form if the destination is eax. + emit(x); + } else { + EMIT(0x81); // using a literal 32-bit immediate. + emit_operand(ireg, dst); + emit(x); + } +} + + +void Assembler::emit_operand(Register reg, const Operand& adr) { + const unsigned length = adr.len_; + DCHECK(length > 0); + + // Emit updated ModRM byte containing the given register. + pc_[0] = (adr.buf_[0] & ~0x38) | (reg.code() << 3); + + // Emit the rest of the encoded operand. + for (unsigned i = 1; i < length; i++) pc_[i] = adr.buf_[i]; + pc_ += length; + + // Emit relocation information if necessary. + if (length >= sizeof(int32_t) && !RelocInfo::IsNone(adr.rmode_)) { + pc_ -= sizeof(int32_t); // pc_ must be *at* disp32 + RecordRelocInfo(adr.rmode_); + if (adr.rmode_ == RelocInfo::INTERNAL_REFERENCE) { // Fixup for labels + emit_label(*reinterpret_cast(pc_)); + } else { + pc_ += sizeof(int32_t); + } + } +} + + +void Assembler::emit_label(Label* label) { + if (label->is_bound()) { + internal_reference_positions_.push_back(pc_offset()); + emit(reinterpret_cast(buffer_ + label->pos())); + } else { + emit_disp(label, Displacement::CODE_ABSOLUTE); + } +} + + +void Assembler::emit_farith(int b1, int b2, int i) { + DCHECK(is_uint8(b1) && is_uint8(b2)); // wrong opcode + DCHECK(0 <= i && i < 8); // illegal stack offset + EMIT(b1); + EMIT(b2 + i); +} + + +void Assembler::db(uint8_t data) { + EnsureSpace ensure_space(this); + EMIT(data); +} + + +void Assembler::dd(uint32_t data) { + EnsureSpace ensure_space(this); + emit(data); +} + + +void Assembler::dq(uint64_t data) { + EnsureSpace ensure_space(this); + emit_q(data); +} + + +void Assembler::dd(Label* label) { + EnsureSpace ensure_space(this); + RecordRelocInfo(RelocInfo::INTERNAL_REFERENCE); + emit_label(label); +} + + +void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) { + DCHECK(!RelocInfo::IsNone(rmode)); + // Don't record external references unless the heap will be serialized. + if (rmode == RelocInfo::EXTERNAL_REFERENCE && + !serializer_enabled() && !emit_debug_code()) { + return; + } + RelocInfo rinfo(pc_, rmode, data, NULL); + reloc_info_writer.Write(&rinfo); +} + +} // namespace internal +} // namespace v8 + +#endif // V8_TARGET_ARCH_X87 diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/x87/assembler-x87.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/x87/assembler-x87.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/x87/assembler-x87.h 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/x87/assembler-x87.h 2017-12-28 05:24:49.899521687 +0100 @@ -0,0 +1,1140 @@ +// Copyright (c) 1994-2006 Sun Microsystems Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// - Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// - Redistribution in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// - Neither the name of Sun Microsystems or the names of contributors may +// be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// The original source code covered by the above license above has been +// modified significantly by Google Inc. +// Copyright 2011 the V8 project authors. All rights reserved. + +// A light-weight IA32 Assembler. + +#ifndef V8_X87_ASSEMBLER_X87_H_ +#define V8_X87_ASSEMBLER_X87_H_ + +#include + +#include "src/assembler.h" +#include "src/isolate.h" +#include "src/utils.h" + +namespace v8 { +namespace internal { + +#define GENERAL_REGISTERS(V) \ + V(eax) \ + V(ecx) \ + V(edx) \ + V(ebx) \ + V(esp) \ + V(ebp) \ + V(esi) \ + V(edi) + +#define ALLOCATABLE_GENERAL_REGISTERS(V) \ + V(eax) \ + V(ecx) \ + V(edx) \ + V(ebx) \ + V(esi) \ + V(edi) + +#define DOUBLE_REGISTERS(V) \ + V(stX_0) \ + V(stX_1) \ + V(stX_2) \ + V(stX_3) \ + V(stX_4) \ + V(stX_5) \ + V(stX_6) \ + V(stX_7) + +#define FLOAT_REGISTERS DOUBLE_REGISTERS +#define SIMD128_REGISTERS DOUBLE_REGISTERS + +#define ALLOCATABLE_DOUBLE_REGISTERS(V) \ + V(stX_0) \ + V(stX_1) \ + V(stX_2) \ + V(stX_3) \ + V(stX_4) \ + V(stX_5) + +// CPU Registers. +// +// 1) We would prefer to use an enum, but enum values are assignment- +// compatible with int, which has caused code-generation bugs. +// +// 2) We would prefer to use a class instead of a struct but we don't like +// the register initialization to depend on the particular initialization +// order (which appears to be different on OS X, Linux, and Windows for the +// installed versions of C++ we tried). Using a struct permits C-style +// "initialization". Also, the Register objects cannot be const as this +// forces initialization stubs in MSVC, making us dependent on initialization +// order. +// +// 3) By not using an enum, we are possibly preventing the compiler from +// doing certain constant folds, which may significantly reduce the +// code generated for some assembly instructions (because they boil down +// to a few constants). If this is a problem, we could change the code +// such that we use an enum in optimized mode, and the struct in debug +// mode. This way we get the compile-time error checking in debug mode +// and best performance in optimized code. +// +struct Register { + enum Code { +#define REGISTER_CODE(R) kCode_##R, + GENERAL_REGISTERS(REGISTER_CODE) +#undef REGISTER_CODE + kAfterLast, + kCode_no_reg = -1 + }; + + static const int kNumRegisters = Code::kAfterLast; + + static Register from_code(int code) { + DCHECK(code >= 0); + DCHECK(code < kNumRegisters); + Register r = {code}; + return r; + } + bool is_valid() const { return 0 <= reg_code && reg_code < kNumRegisters; } + bool is(Register reg) const { return reg_code == reg.reg_code; } + int code() const { + DCHECK(is_valid()); + return reg_code; + } + int bit() const { + DCHECK(is_valid()); + return 1 << reg_code; + } + + bool is_byte_register() const { return reg_code <= 3; } + + // Unfortunately we can't make this private in a struct. + int reg_code; +}; + + +#define DECLARE_REGISTER(R) const Register R = {Register::kCode_##R}; +GENERAL_REGISTERS(DECLARE_REGISTER) +#undef DECLARE_REGISTER +const Register no_reg = {Register::kCode_no_reg}; + +static const bool kSimpleFPAliasing = true; +static const bool kSimdMaskRegisters = false; + +struct X87Register { + enum Code { +#define REGISTER_CODE(R) kCode_##R, + DOUBLE_REGISTERS(REGISTER_CODE) +#undef REGISTER_CODE + kAfterLast, + kCode_no_reg = -1 + }; + + static const int kMaxNumRegisters = Code::kAfterLast; + static const int kMaxNumAllocatableRegisters = 6; + + static X87Register from_code(int code) { + X87Register result = {code}; + return result; + } + + bool is_valid() const { return 0 <= reg_code && reg_code < kMaxNumRegisters; } + + int code() const { + DCHECK(is_valid()); + return reg_code; + } + + bool is(X87Register reg) const { return reg_code == reg.reg_code; } + + int reg_code; +}; + +typedef X87Register FloatRegister; + +typedef X87Register DoubleRegister; + +// TODO(x87) Define SIMD registers. +typedef X87Register Simd128Register; + +#define DECLARE_REGISTER(R) \ + const DoubleRegister R = {DoubleRegister::kCode_##R}; +DOUBLE_REGISTERS(DECLARE_REGISTER) +#undef DECLARE_REGISTER +const DoubleRegister no_double_reg = {DoubleRegister::kCode_no_reg}; + +enum Condition { + // any value < 0 is considered no_condition + no_condition = -1, + + overflow = 0, + no_overflow = 1, + below = 2, + above_equal = 3, + equal = 4, + not_equal = 5, + below_equal = 6, + above = 7, + negative = 8, + positive = 9, + parity_even = 10, + parity_odd = 11, + less = 12, + greater_equal = 13, + less_equal = 14, + greater = 15, + + // aliases + carry = below, + not_carry = above_equal, + zero = equal, + not_zero = not_equal, + sign = negative, + not_sign = positive +}; + + +// Returns the equivalent of !cc. +// Negation of the default no_condition (-1) results in a non-default +// no_condition value (-2). As long as tests for no_condition check +// for condition < 0, this will work as expected. +inline Condition NegateCondition(Condition cc) { + return static_cast(cc ^ 1); +} + + +// Commute a condition such that {a cond b == b cond' a}. +inline Condition CommuteCondition(Condition cc) { + switch (cc) { + case below: + return above; + case above: + return below; + case above_equal: + return below_equal; + case below_equal: + return above_equal; + case less: + return greater; + case greater: + return less; + case greater_equal: + return less_equal; + case less_equal: + return greater_equal; + default: + return cc; + } +} + + +enum RoundingMode { + kRoundToNearest = 0x0, + kRoundDown = 0x1, + kRoundUp = 0x2, + kRoundToZero = 0x3 +}; + + +// ----------------------------------------------------------------------------- +// Machine instruction Immediates + +class Immediate BASE_EMBEDDED { + public: + inline explicit Immediate(int x); + inline explicit Immediate(const ExternalReference& ext); + inline explicit Immediate(Handle handle); + inline explicit Immediate(Smi* value); + inline explicit Immediate(Address addr); + inline explicit Immediate(Address x, RelocInfo::Mode rmode); + + static Immediate EmbeddedNumber(double number); // Smi or HeapNumber. + static Immediate EmbeddedCode(CodeStub* code); + + static Immediate CodeRelativeOffset(Label* label) { + return Immediate(label); + } + + bool is_heap_object_request() const { + DCHECK_IMPLIES(is_heap_object_request_, + rmode_ == RelocInfo::EMBEDDED_OBJECT || + rmode_ == RelocInfo::CODE_TARGET); + return is_heap_object_request_; + } + + HeapObjectRequest heap_object_request() const { + DCHECK(is_heap_object_request()); + return value_.heap_object_request; + } + + int immediate() const { + DCHECK(!is_heap_object_request()); + return value_.immediate; + } + + bool is_zero() const { return RelocInfo::IsNone(rmode_) && immediate() == 0; } + bool is_int8() const { + return RelocInfo::IsNone(rmode_) && i::is_int8(immediate()); + } + bool is_uint8() const { + return RelocInfo::IsNone(rmode_) && i::is_uint8(immediate()); + } + bool is_int16() const { + return RelocInfo::IsNone(rmode_) && i::is_int16(immediate()); + } + + bool is_uint16() const { + return RelocInfo::IsNone(rmode_) && i::is_uint16(immediate()); + } + + RelocInfo::Mode rmode() const { return rmode_; } + + private: + inline explicit Immediate(Label* value); + + union Value { + Value() {} + HeapObjectRequest heap_object_request; + int immediate; + } value_; + bool is_heap_object_request_ = false; + RelocInfo::Mode rmode_; + + friend class Operand; + friend class Assembler; + friend class MacroAssembler; +}; + + +// ----------------------------------------------------------------------------- +// Machine instruction Operands + +enum ScaleFactor { + times_1 = 0, + times_2 = 1, + times_4 = 2, + times_8 = 3, + times_int_size = times_4, + times_half_pointer_size = times_2, + times_pointer_size = times_4, + times_twice_pointer_size = times_8 +}; + + +class Operand BASE_EMBEDDED { + public: + // reg + INLINE(explicit Operand(Register reg)); + + // [disp/r] + INLINE(explicit Operand(int32_t disp, RelocInfo::Mode rmode)); + + // [disp/r] + INLINE(explicit Operand(Immediate imm)); + + // [base + disp/r] + explicit Operand(Register base, int32_t disp, + RelocInfo::Mode rmode = RelocInfo::NONE32); + + // [base + index*scale + disp/r] + explicit Operand(Register base, + Register index, + ScaleFactor scale, + int32_t disp, + RelocInfo::Mode rmode = RelocInfo::NONE32); + + // [index*scale + disp/r] + explicit Operand(Register index, + ScaleFactor scale, + int32_t disp, + RelocInfo::Mode rmode = RelocInfo::NONE32); + + static Operand JumpTable(Register index, ScaleFactor scale, Label* table) { + return Operand(index, scale, reinterpret_cast(table), + RelocInfo::INTERNAL_REFERENCE); + } + + static Operand StaticVariable(const ExternalReference& ext) { + return Operand(reinterpret_cast(ext.address()), + RelocInfo::EXTERNAL_REFERENCE); + } + + static Operand StaticArray(Register index, + ScaleFactor scale, + const ExternalReference& arr) { + return Operand(index, scale, reinterpret_cast(arr.address()), + RelocInfo::EXTERNAL_REFERENCE); + } + + static Operand ForCell(Handle cell) { + return Operand(reinterpret_cast(cell.address()), RelocInfo::CELL); + } + + static Operand ForRegisterPlusImmediate(Register base, Immediate imm) { + return Operand(base, imm.value_.immediate, imm.rmode_); + } + + // Returns true if this Operand is a wrapper for the specified register. + bool is_reg(Register reg) const; + + // Returns true if this Operand is a wrapper for one register. + bool is_reg_only() const; + + // Asserts that this Operand is a wrapper for one register and returns the + // register. + Register reg() const; + + private: + // Set the ModRM byte without an encoded 'reg' register. The + // register is encoded later as part of the emit_operand operation. + inline void set_modrm(int mod, Register rm); + + inline void set_sib(ScaleFactor scale, Register index, Register base); + inline void set_disp8(int8_t disp); + inline void set_dispr(int32_t disp, RelocInfo::Mode rmode); + + byte buf_[6]; + // The number of bytes in buf_. + unsigned int len_; + // Only valid if len_ > 4. + RelocInfo::Mode rmode_; + + friend class Assembler; +}; + + +// ----------------------------------------------------------------------------- +// A Displacement describes the 32bit immediate field of an instruction which +// may be used together with a Label in order to refer to a yet unknown code +// position. Displacements stored in the instruction stream are used to describe +// the instruction and to chain a list of instructions using the same Label. +// A Displacement contains 2 different fields: +// +// next field: position of next displacement in the chain (0 = end of list) +// type field: instruction type +// +// A next value of null (0) indicates the end of a chain (note that there can +// be no displacement at position zero, because there is always at least one +// instruction byte before the displacement). +// +// Displacement _data field layout +// +// |31.....2|1......0| +// [ next | type | + +class Displacement BASE_EMBEDDED { + public: + enum Type { UNCONDITIONAL_JUMP, CODE_RELATIVE, OTHER, CODE_ABSOLUTE }; + + int data() const { return data_; } + Type type() const { return TypeField::decode(data_); } + void next(Label* L) const { + int n = NextField::decode(data_); + n > 0 ? L->link_to(n) : L->Unuse(); + } + void link_to(Label* L) { init(L, type()); } + + explicit Displacement(int data) { data_ = data; } + + Displacement(Label* L, Type type) { init(L, type); } + + void print() { + PrintF("%s (%x) ", (type() == UNCONDITIONAL_JUMP ? "jmp" : "[other]"), + NextField::decode(data_)); + } + + private: + int data_; + + class TypeField: public BitField {}; + class NextField: public BitField {}; + + void init(Label* L, Type type); +}; + + +class Assembler : public AssemblerBase { + private: + // We check before assembling an instruction that there is sufficient + // space to write an instruction and its relocation information. + // The relocation writer's position must be kGap bytes above the end of + // the generated instructions. This leaves enough space for the + // longest possible ia32 instruction, 15 bytes, and the longest possible + // relocation information encoding, RelocInfoWriter::kMaxLength == 16. + // (There is a 15 byte limit on ia32 instruction length that rules out some + // otherwise valid instructions.) + // This allows for a single, fast space check per instruction. + static const int kGap = 32; + + public: + // Create an assembler. Instructions and relocation information are emitted + // into a buffer, with the instructions starting from the beginning and the + // relocation information starting from the end of the buffer. See CodeDesc + // for a detailed comment on the layout (globals.h). + // + // If the provided buffer is NULL, the assembler allocates and grows its own + // buffer, and buffer_size determines the initial buffer size. The buffer is + // owned by the assembler and deallocated upon destruction of the assembler. + // + // If the provided buffer is not NULL, the assembler uses the provided buffer + // for code generation and assumes its size to be buffer_size. If the buffer + // is too small, a fatal error occurs. No deallocation of the buffer is done + // upon destruction of the assembler. + Assembler(Isolate* isolate, void* buffer, int buffer_size) + : Assembler(IsolateData(isolate), buffer, buffer_size) {} + Assembler(IsolateData isolate_data, void* buffer, int buffer_size); + virtual ~Assembler() {} + + // GetCode emits any pending (non-emitted) code and fills the descriptor + // desc. GetCode() is idempotent; it returns the same result if no other + // Assembler functions are invoked in between GetCode() calls. + void GetCode(Isolate* isolate, CodeDesc* desc); + + // Read/Modify the code target in the branch/call instruction at pc. + // The isolate argument is unused (and may be nullptr) when skipping flushing. + inline static Address target_address_at(Address pc, Address constant_pool); + inline static void set_target_address_at( + Isolate* isolate, Address pc, Address constant_pool, Address target, + ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED); + static inline Address target_address_at(Address pc, Code* code); + static inline void set_target_address_at( + Isolate* isolate, Address pc, Code* code, Address target, + ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED); + + // Return the code target address at a call site from the return address + // of that call in the instruction stream. + inline static Address target_address_from_return_address(Address pc); + + // This sets the branch destination (which is in the instruction on x86). + // This is for calls and branches within generated code. + inline static void deserialization_set_special_target_at( + Isolate* isolate, Address instruction_payload, Code* code, + Address target) { + set_target_address_at(isolate, instruction_payload, code, target); + } + + // This sets the internal reference at the pc. + inline static void deserialization_set_target_internal_reference_at( + Isolate* isolate, Address pc, Address target, + RelocInfo::Mode mode = RelocInfo::INTERNAL_REFERENCE); + + static const int kSpecialTargetSize = kPointerSize; + + // Distance between the address of the code target in the call instruction + // and the return address + static const int kCallTargetAddressOffset = kPointerSize; + + static const int kCallInstructionLength = 5; + + // The debug break slot must be able to contain a call instruction. + static const int kDebugBreakSlotLength = kCallInstructionLength; + + // Distance between start of patched debug break slot and the emitted address + // to jump to. + static const int kPatchDebugBreakSlotAddressOffset = 1; // JMP imm32. + + // One byte opcode for test al, 0xXX. + static const byte kTestAlByte = 0xA8; + // One byte opcode for nop. + static const byte kNopByte = 0x90; + + // One byte opcode for a short unconditional jump. + static const byte kJmpShortOpcode = 0xEB; + // One byte prefix for a short conditional jump. + static const byte kJccShortPrefix = 0x70; + static const byte kJncShortOpcode = kJccShortPrefix | not_carry; + static const byte kJcShortOpcode = kJccShortPrefix | carry; + static const byte kJnzShortOpcode = kJccShortPrefix | not_zero; + static const byte kJzShortOpcode = kJccShortPrefix | zero; + + + // --------------------------------------------------------------------------- + // Code generation + // + // - function names correspond one-to-one to ia32 instruction mnemonics + // - unless specified otherwise, instructions operate on 32bit operands + // - instructions on 8bit (byte) operands/registers have a trailing '_b' + // - instructions on 16bit (word) operands/registers have a trailing '_w' + // - naming conflicts with C++ keywords are resolved via a trailing '_' + + // NOTE ON INTERFACE: Currently, the interface is not very consistent + // in the sense that some operations (e.g. mov()) can be called in more + // the one way to generate the same instruction: The Register argument + // can in some cases be replaced with an Operand(Register) argument. + // This should be cleaned up and made more orthogonal. The questions + // is: should we always use Operands instead of Registers where an + // Operand is possible, or should we have a Register (overloaded) form + // instead? We must be careful to make sure that the selected instruction + // is obvious from the parameters to avoid hard-to-find code generation + // bugs. + + // Insert the smallest number of nop instructions + // possible to align the pc offset to a multiple + // of m. m must be a power of 2. + void Align(int m); + // Insert the smallest number of zero bytes possible to align the pc offset + // to a mulitple of m. m must be a power of 2 (>= 2). + void DataAlign(int m); + void Nop(int bytes = 1); + // Aligns code to something that's optimal for a jump target for the platform. + void CodeTargetAlign(); + + // Stack + void pushad(); + void popad(); + + void pushfd(); + void popfd(); + + void push(const Immediate& x); + void push_imm32(int32_t imm32); + void push(Register src); + void push(const Operand& src); + + void pop(Register dst); + void pop(const Operand& dst); + + void enter(const Immediate& size); + void leave(); + + // Moves + void mov_b(Register dst, Register src) { mov_b(dst, Operand(src)); } + void mov_b(Register dst, const Operand& src); + void mov_b(Register dst, int8_t imm8) { mov_b(Operand(dst), imm8); } + void mov_b(const Operand& dst, int8_t imm8); + void mov_b(const Operand& dst, const Immediate& src); + void mov_b(const Operand& dst, Register src); + + void mov_w(Register dst, const Operand& src); + void mov_w(const Operand& dst, Register src); + void mov_w(const Operand& dst, int16_t imm16); + void mov_w(const Operand& dst, const Immediate& src); + + + void mov(Register dst, int32_t imm32); + void mov(Register dst, const Immediate& x); + void mov(Register dst, Handle handle); + void mov(Register dst, const Operand& src); + void mov(Register dst, Register src); + void mov(const Operand& dst, const Immediate& x); + void mov(const Operand& dst, Handle handle); + void mov(const Operand& dst, Register src); + + void movsx_b(Register dst, Register src) { movsx_b(dst, Operand(src)); } + void movsx_b(Register dst, const Operand& src); + + void movsx_w(Register dst, Register src) { movsx_w(dst, Operand(src)); } + void movsx_w(Register dst, const Operand& src); + + void movzx_b(Register dst, Register src) { movzx_b(dst, Operand(src)); } + void movzx_b(Register dst, const Operand& src); + + void movzx_w(Register dst, Register src) { movzx_w(dst, Operand(src)); } + void movzx_w(Register dst, const Operand& src); + + // Flag management. + void cld(); + + // Repetitive string instructions. + void rep_movs(); + void rep_stos(); + void stos(); + + // Exchange + void xchg(Register dst, Register src); + void xchg(Register dst, const Operand& src); + void xchg_b(Register reg, const Operand& op); + void xchg_w(Register reg, const Operand& op); + + // Lock prefix + void lock(); + + // CompareExchange + void cmpxchg(const Operand& dst, Register src); + void cmpxchg_b(const Operand& dst, Register src); + void cmpxchg_w(const Operand& dst, Register src); + + // Arithmetics + void adc(Register dst, int32_t imm32); + void adc(Register dst, const Operand& src); + + void add(Register dst, Register src) { add(dst, Operand(src)); } + void add(Register dst, const Operand& src); + void add(const Operand& dst, Register src); + void add(Register dst, const Immediate& imm) { add(Operand(dst), imm); } + void add(const Operand& dst, const Immediate& x); + + void and_(Register dst, int32_t imm32); + void and_(Register dst, const Immediate& x); + void and_(Register dst, Register src) { and_(dst, Operand(src)); } + void and_(Register dst, const Operand& src); + void and_(const Operand& dst, Register src); + void and_(const Operand& dst, const Immediate& x); + + void cmpb(Register reg, Immediate imm8) { cmpb(Operand(reg), imm8); } + void cmpb(const Operand& op, Immediate imm8); + void cmpb(Register reg, const Operand& op); + void cmpb(const Operand& op, Register reg); + void cmpb(Register dst, Register src) { cmpb(Operand(dst), src); } + void cmpb_al(const Operand& op); + void cmpw_ax(const Operand& op); + void cmpw(const Operand& dst, Immediate src); + void cmpw(Register dst, Immediate src) { cmpw(Operand(dst), src); } + void cmpw(Register dst, const Operand& src); + void cmpw(Register dst, Register src) { cmpw(Operand(dst), src); } + void cmpw(const Operand& dst, Register src); + void cmp(Register reg, int32_t imm32); + void cmp(Register reg, Handle handle); + void cmp(Register reg0, Register reg1) { cmp(reg0, Operand(reg1)); } + void cmp(Register reg, const Operand& op); + void cmp(Register reg, const Immediate& imm) { cmp(Operand(reg), imm); } + void cmp(const Operand& op, Register reg); + void cmp(const Operand& op, const Immediate& imm); + void cmp(const Operand& op, Handle handle); + + void dec_b(Register dst); + void dec_b(const Operand& dst); + + void dec(Register dst); + void dec(const Operand& dst); + + void cdq(); + + void idiv(Register src) { idiv(Operand(src)); } + void idiv(const Operand& src); + void div(Register src) { div(Operand(src)); } + void div(const Operand& src); + + // Signed multiply instructions. + void imul(Register src); // edx:eax = eax * src. + void imul(Register dst, Register src) { imul(dst, Operand(src)); } + void imul(Register dst, const Operand& src); // dst = dst * src. + void imul(Register dst, Register src, int32_t imm32); // dst = src * imm32. + void imul(Register dst, const Operand& src, int32_t imm32); + + void inc(Register dst); + void inc(const Operand& dst); + + void lea(Register dst, const Operand& src); + + // Unsigned multiply instruction. + void mul(Register src); // edx:eax = eax * reg. + + void neg(Register dst); + void neg(const Operand& dst); + + void not_(Register dst); + void not_(const Operand& dst); + + void or_(Register dst, int32_t imm32); + void or_(Register dst, Register src) { or_(dst, Operand(src)); } + void or_(Register dst, const Operand& src); + void or_(const Operand& dst, Register src); + void or_(Register dst, const Immediate& imm) { or_(Operand(dst), imm); } + void or_(const Operand& dst, const Immediate& x); + + void rcl(Register dst, uint8_t imm8); + void rcr(Register dst, uint8_t imm8); + + void ror(Register dst, uint8_t imm8) { ror(Operand(dst), imm8); } + void ror(const Operand& dst, uint8_t imm8); + void ror_cl(Register dst) { ror_cl(Operand(dst)); } + void ror_cl(const Operand& dst); + + void sar(Register dst, uint8_t imm8) { sar(Operand(dst), imm8); } + void sar(const Operand& dst, uint8_t imm8); + void sar_cl(Register dst) { sar_cl(Operand(dst)); } + void sar_cl(const Operand& dst); + + void sbb(Register dst, const Operand& src); + + void shl(Register dst, uint8_t imm8) { shl(Operand(dst), imm8); } + void shl(const Operand& dst, uint8_t imm8); + void shl_cl(Register dst) { shl_cl(Operand(dst)); } + void shl_cl(const Operand& dst); + void shld(Register dst, Register src, uint8_t shift); + void shld_cl(Register dst, Register src); + + void shr(Register dst, uint8_t imm8) { shr(Operand(dst), imm8); } + void shr(const Operand& dst, uint8_t imm8); + void shr_cl(Register dst) { shr_cl(Operand(dst)); } + void shr_cl(const Operand& dst); + void shrd(Register dst, Register src, uint8_t shift); + void shrd_cl(Register dst, Register src) { shrd_cl(Operand(dst), src); } + void shrd_cl(const Operand& dst, Register src); + + void sub(Register dst, const Immediate& imm) { sub(Operand(dst), imm); } + void sub(const Operand& dst, const Immediate& x); + void sub(Register dst, Register src) { sub(dst, Operand(src)); } + void sub(Register dst, const Operand& src); + void sub(const Operand& dst, Register src); + + void test(Register reg, const Immediate& imm); + void test(Register reg0, Register reg1) { test(reg0, Operand(reg1)); } + void test(Register reg, const Operand& op); + void test(const Operand& op, const Immediate& imm); + void test(const Operand& op, Register reg) { test(reg, op); } + void test_b(Register reg, const Operand& op); + void test_b(Register reg, Immediate imm8); + void test_b(const Operand& op, Immediate imm8); + void test_b(const Operand& op, Register reg) { test_b(reg, op); } + void test_b(Register dst, Register src) { test_b(dst, Operand(src)); } + void test_w(Register reg, const Operand& op); + void test_w(Register reg, Immediate imm16); + void test_w(const Operand& op, Immediate imm16); + void test_w(const Operand& op, Register reg) { test_w(reg, op); } + void test_w(Register dst, Register src) { test_w(dst, Operand(src)); } + + void xor_(Register dst, int32_t imm32); + void xor_(Register dst, Register src) { xor_(dst, Operand(src)); } + void xor_(Register dst, const Operand& src); + void xor_(const Operand& dst, Register src); + void xor_(Register dst, const Immediate& imm) { xor_(Operand(dst), imm); } + void xor_(const Operand& dst, const Immediate& x); + + // Bit operations. + void bt(const Operand& dst, Register src); + void bts(Register dst, Register src) { bts(Operand(dst), src); } + void bts(const Operand& dst, Register src); + void bsr(Register dst, Register src) { bsr(dst, Operand(src)); } + void bsr(Register dst, const Operand& src); + void bsf(Register dst, Register src) { bsf(dst, Operand(src)); } + void bsf(Register dst, const Operand& src); + + // Miscellaneous + void hlt(); + void int3(); + void nop(); + void ret(int imm16); + void ud2(); + + // Label operations & relative jumps (PPUM Appendix D) + // + // Takes a branch opcode (cc) and a label (L) and generates + // either a backward branch or a forward branch and links it + // to the label fixup chain. Usage: + // + // Label L; // unbound label + // j(cc, &L); // forward branch to unbound label + // bind(&L); // bind label to the current pc + // j(cc, &L); // backward branch to bound label + // bind(&L); // illegal: a label may be bound only once + // + // Note: The same Label can be used for forward and backward branches + // but it may be bound only once. + + void bind(Label* L); // binds an unbound label L to the current code position + + // Calls + void call(Label* L); + void call(byte* entry, RelocInfo::Mode rmode); + int CallSize(const Operand& adr); + void call(Register reg) { call(Operand(reg)); } + void call(const Operand& adr); + int CallSize(Handle code, RelocInfo::Mode mode); + void call(Handle code, RelocInfo::Mode rmode); + void call(CodeStub* stub); + + // Jumps + // unconditional jump to L + void jmp(Label* L, Label::Distance distance = Label::kFar); + void jmp(byte* entry, RelocInfo::Mode rmode); + void jmp(Register reg) { jmp(Operand(reg)); } + void jmp(const Operand& adr); + void jmp(Handle code, RelocInfo::Mode rmode); + + // Conditional jumps + void j(Condition cc, + Label* L, + Label::Distance distance = Label::kFar); + void j(Condition cc, byte* entry, RelocInfo::Mode rmode); + void j(Condition cc, Handle code, + RelocInfo::Mode rmode = RelocInfo::CODE_TARGET); + + // Floating-point operations + void fld(int i); + void fstp(int i); + + void fld1(); + void fldz(); + void fldpi(); + void fldln2(); + + void fld_s(const Operand& adr); + void fld_d(const Operand& adr); + + void fstp_s(const Operand& adr); + void fst_s(const Operand& adr); + void fstp_d(const Operand& adr); + void fst_d(const Operand& adr); + + void fild_s(const Operand& adr); + void fild_d(const Operand& adr); + + void fist_s(const Operand& adr); + + void fistp_s(const Operand& adr); + void fistp_d(const Operand& adr); + + // The fisttp instructions require SSE3. + void fisttp_s(const Operand& adr); + void fisttp_d(const Operand& adr); + + void fabs(); + void fchs(); + void fsqrt(); + void fcos(); + void fsin(); + void fptan(); + void fyl2x(); + void f2xm1(); + void fscale(); + void fninit(); + + void fadd(int i); + void fadd_i(int i); + void fadd_d(const Operand& adr); + void fsub(int i); + void fsub_i(int i); + void fsub_d(const Operand& adr); + void fsubr_d(const Operand& adr); + void fmul(int i); + void fmul_d(const Operand& adr); + void fmul_i(int i); + void fdiv(int i); + void fdiv_d(const Operand& adr); + void fdivr_d(const Operand& adr); + void fdiv_i(int i); + + void fisub_s(const Operand& adr); + + void faddp(int i = 1); + void fsubp(int i = 1); + void fsubr(int i = 1); + void fsubrp(int i = 1); + void fmulp(int i = 1); + void fdivp(int i = 1); + void fprem(); + void fprem1(); + + void fxch(int i = 1); + void fincstp(); + void ffree(int i = 0); + + void ftst(); + void fxam(); + void fucomp(int i); + void fucompp(); + void fucomi(int i); + void fucomip(); + void fcompp(); + void fnstsw_ax(); + void fldcw(const Operand& adr); + void fnstcw(const Operand& adr); + void fwait(); + void fnclex(); + void fnsave(const Operand& adr); + void frstor(const Operand& adr); + + void frndint(); + + void sahf(); + void setcc(Condition cc, Register reg); + + void cpuid(); + + // TODO(lrn): Need SFENCE for movnt? + + // Check the code size generated from label to here. + int SizeOfCodeGeneratedSince(Label* label) { + return pc_offset() - label->pos(); + } + + // Mark address of a debug break slot. + void RecordDebugBreakSlot(RelocInfo::Mode mode); + + // Record a comment relocation entry that can be used by a disassembler. + // Use --code-comments to enable. + void RecordComment(const char* msg); + + // Record a deoptimization reason that can be used by a log or cpu profiler. + // Use --trace-deopt to enable. + void RecordDeoptReason(DeoptimizeReason reason, SourcePosition position, + int id); + + // Writes a single byte or word of data in the code stream. Used for + // inline tables, e.g., jump-tables. + void db(uint8_t data); + void dd(uint32_t data); + void dq(uint64_t data); + void dp(uintptr_t data) { dd(data); } + void dd(Label* label); + + // Check if there is less than kGap bytes available in the buffer. + // If this is the case, we need to grow the buffer before emitting + // an instruction or relocation information. + inline bool buffer_overflow() const { + return pc_ >= reloc_info_writer.pos() - kGap; + } + + // Get the number of bytes available in the buffer. + inline int available_space() const { return reloc_info_writer.pos() - pc_; } + + static bool IsNop(Address addr); + + int relocation_writer_size() { + return (buffer_ + buffer_size_) - reloc_info_writer.pos(); + } + + // Avoid overflows for displacements etc. + static const int kMaximalBufferSize = 512*MB; + + byte byte_at(int pos) { return buffer_[pos]; } + void set_byte_at(int pos, byte value) { buffer_[pos] = value; } + + void PatchConstantPoolAccessInstruction(int pc_offset, int offset, + ConstantPoolEntry::Access access, + ConstantPoolEntry::Type type) { + // No embedded constant pool support. + UNREACHABLE(); + } + + protected: + byte* addr_at(int pos) { return buffer_ + pos; } + + + private: + uint32_t long_at(int pos) { + return *reinterpret_cast(addr_at(pos)); + } + void long_at_put(int pos, uint32_t x) { + *reinterpret_cast(addr_at(pos)) = x; + } + + // code emission + void GrowBuffer(); + inline void emit(uint32_t x); + inline void emit(Handle handle); + inline void emit(uint32_t x, RelocInfo::Mode rmode); + inline void emit(Handle code, RelocInfo::Mode rmode); + inline void emit(const Immediate& x); + inline void emit_b(Immediate x); + inline void emit_w(const Immediate& x); + inline void emit_q(uint64_t x); + + // Emit the code-object-relative offset of the label's position + inline void emit_code_relative_offset(Label* label); + + // instruction generation + void emit_arith_b(int op1, int op2, Register dst, int imm8); + + // Emit a basic arithmetic instruction (i.e. first byte of the family is 0x81) + // with a given destination expression and an immediate operand. It attempts + // to use the shortest encoding possible. + // sel specifies the /n in the modrm byte (see the Intel PRM). + void emit_arith(int sel, Operand dst, const Immediate& x); + + void emit_operand(Register reg, const Operand& adr); + + void emit_label(Label* label); + + void emit_farith(int b1, int b2, int i); + + // labels + void print(Label* L); + void bind_to(Label* L, int pos); + + // displacements + inline Displacement disp_at(Label* L); + inline void disp_at_put(Label* L, Displacement disp); + inline void emit_disp(Label* L, Displacement::Type type); + inline void emit_near_disp(Label* L); + + // record reloc info for current pc_ + void RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data = 0); + + friend class CodePatcher; + friend class EnsureSpace; + + // Internal reference positions, required for (potential) patching in + // GrowBuffer(); contains only those internal references whose labels + // are already bound. + std::deque internal_reference_positions_; + + // code generation + RelocInfoWriter reloc_info_writer; + + // The following functions help with avoiding allocations of embedded heap + // objects during the code assembly phase. {RequestHeapObject} records the + // need for a future heap number allocation or code stub generation. After + // code assembly, {AllocateAndInstallRequestedHeapObjects} will allocate these + // objects and place them where they are expected (determined by the pc offset + // associated with each request). That is, for each request, it will patch the + // dummy heap object handle that we emitted during code assembly with the + // actual heap object handle. + void RequestHeapObject(HeapObjectRequest request); + void AllocateAndInstallRequestedHeapObjects(Isolate* isolate); + + std::forward_list heap_object_requests_; +}; + + +// Helper class that ensures that there is enough space for generating +// instructions and relocation information. The constructor makes +// sure that there is enough space and (in debug mode) the destructor +// checks that we did not generate too much. +class EnsureSpace BASE_EMBEDDED { + public: + explicit EnsureSpace(Assembler* assembler) : assembler_(assembler) { + if (assembler_->buffer_overflow()) assembler_->GrowBuffer(); +#ifdef DEBUG + space_before_ = assembler_->available_space(); +#endif + } + +#ifdef DEBUG + ~EnsureSpace() { + int bytes_generated = space_before_ - assembler_->available_space(); + DCHECK(bytes_generated < assembler_->kGap); + } +#endif + + private: + Assembler* assembler_; +#ifdef DEBUG + int space_before_; +#endif +}; + +} // namespace internal +} // namespace v8 + +#endif // V8_X87_ASSEMBLER_X87_H_ diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/x87/assembler-x87-inl.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/x87/assembler-x87-inl.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/x87/assembler-x87-inl.h 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/x87/assembler-x87-inl.h 2017-12-28 01:52:55.041936208 +0100 @@ -0,0 +1,528 @@ +// Copyright (c) 1994-2006 Sun Microsystems Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// - Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// - Redistribution in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// - Neither the name of Sun Microsystems or the names of contributors may +// be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// The original source code covered by the above license above has been +// modified significantly by Google Inc. +// Copyright 2012 the V8 project authors. All rights reserved. + +// A light-weight IA32 Assembler. + +#ifndef V8_X87_ASSEMBLER_X87_INL_H_ +#define V8_X87_ASSEMBLER_X87_INL_H_ + +#include "src/x87/assembler-x87.h" + +#include "src/assembler.h" +#include "src/debug/debug.h" +#include "src/objects-inl.h" + +namespace v8 { +namespace internal { + +bool CpuFeatures::SupportsCrankshaft() { return true; } + +bool CpuFeatures::SupportsWasmSimd128() { return false; } + +static const byte kCallOpcode = 0xE8; +static const int kNoCodeAgeSequenceLength = 5; + + +// The modes possibly affected by apply must be in kApplyMask. +void RelocInfo::apply(intptr_t delta) { + if (IsRuntimeEntry(rmode_) || IsCodeTarget(rmode_)) { + int32_t* p = reinterpret_cast(pc_); + *p -= delta; // Relocate entry. + } else if (IsCodeAgeSequence(rmode_)) { + if (*pc_ == kCallOpcode) { + int32_t* p = reinterpret_cast(pc_ + 1); + *p -= delta; // Relocate entry. + } + } else if (IsDebugBreakSlot(rmode_) && IsPatchedDebugBreakSlotSequence()) { + // Special handling of a debug break slot when a break point is set (call + // instruction has been inserted). + int32_t* p = reinterpret_cast( + pc_ + Assembler::kPatchDebugBreakSlotAddressOffset); + *p -= delta; // Relocate entry. + } else if (IsInternalReference(rmode_)) { + // absolute code pointer inside code object moves with the code object. + int32_t* p = reinterpret_cast(pc_); + *p += delta; // Relocate entry. + } +} + + +Address RelocInfo::target_address() { + DCHECK(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)); + return Assembler::target_address_at(pc_, host_); +} + +Address RelocInfo::target_address_address() { + DCHECK(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_) + || rmode_ == EMBEDDED_OBJECT + || rmode_ == EXTERNAL_REFERENCE); + return reinterpret_cast
(pc_); +} + + +Address RelocInfo::constant_pool_entry_address() { + UNREACHABLE(); +} + + +int RelocInfo::target_address_size() { + return Assembler::kSpecialTargetSize; +} + +HeapObject* RelocInfo::target_object() { + DCHECK(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); + return HeapObject::cast(Memory::Object_at(pc_)); +} + +Handle RelocInfo::target_object_handle(Assembler* origin) { + DCHECK(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); + return Handle::cast(Memory::Object_Handle_at(pc_)); +} + +void RelocInfo::set_target_object(HeapObject* target, + WriteBarrierMode write_barrier_mode, + ICacheFlushMode icache_flush_mode) { + DCHECK(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); + Memory::Object_at(pc_) = target; + if (icache_flush_mode != SKIP_ICACHE_FLUSH) { + Assembler::FlushICache(target->GetIsolate(), pc_, sizeof(Address)); + } + if (write_barrier_mode == UPDATE_WRITE_BARRIER && host() != nullptr) { + host()->GetHeap()->RecordWriteIntoCode(host(), this, target); + host()->GetHeap()->incremental_marking()->RecordWriteIntoCode(host(), this, + target); + } +} + + +Address RelocInfo::target_external_reference() { + DCHECK(rmode_ == RelocInfo::EXTERNAL_REFERENCE); + return Memory::Address_at(pc_); +} + + +Address RelocInfo::target_internal_reference() { + DCHECK(rmode_ == INTERNAL_REFERENCE); + return Memory::Address_at(pc_); +} + + +Address RelocInfo::target_internal_reference_address() { + DCHECK(rmode_ == INTERNAL_REFERENCE); + return reinterpret_cast
(pc_); +} + + +Address RelocInfo::target_runtime_entry(Assembler* origin) { + DCHECK(IsRuntimeEntry(rmode_)); + return reinterpret_cast
(*reinterpret_cast(pc_)); +} + +void RelocInfo::set_target_runtime_entry(Isolate* isolate, Address target, + WriteBarrierMode write_barrier_mode, + ICacheFlushMode icache_flush_mode) { + DCHECK(IsRuntimeEntry(rmode_)); + if (target_address() != target) { + set_target_address(isolate, target, write_barrier_mode, icache_flush_mode); + } +} + + +Handle RelocInfo::target_cell_handle() { + DCHECK(rmode_ == RelocInfo::CELL); + Address address = Memory::Address_at(pc_); + return Handle(reinterpret_cast(address)); +} + + +Cell* RelocInfo::target_cell() { + DCHECK(rmode_ == RelocInfo::CELL); + return Cell::FromValueAddress(Memory::Address_at(pc_)); +} + + +void RelocInfo::set_target_cell(Cell* cell, + WriteBarrierMode write_barrier_mode, + ICacheFlushMode icache_flush_mode) { + DCHECK(cell->IsCell()); + DCHECK(rmode_ == RelocInfo::CELL); + Address address = cell->address() + Cell::kValueOffset; + Memory::Address_at(pc_) = address; + if (icache_flush_mode != SKIP_ICACHE_FLUSH) { + Assembler::FlushICache(cell->GetIsolate(), pc_, sizeof(Address)); + } + if (write_barrier_mode == UPDATE_WRITE_BARRIER && host() != NULL) { + host()->GetHeap()->incremental_marking()->RecordWriteIntoCode(host(), this, + cell); + } +} + +Handle RelocInfo::code_age_stub_handle(Assembler* origin) { + DCHECK(rmode_ == RelocInfo::CODE_AGE_SEQUENCE); + DCHECK(*pc_ == kCallOpcode); + return Handle::cast(Memory::Object_Handle_at(pc_ + 1)); +} + + +Code* RelocInfo::code_age_stub() { + DCHECK(rmode_ == RelocInfo::CODE_AGE_SEQUENCE); + DCHECK(*pc_ == kCallOpcode); + return Code::GetCodeFromTargetAddress( + Assembler::target_address_at(pc_ + 1, host_)); +} + + +void RelocInfo::set_code_age_stub(Code* stub, + ICacheFlushMode icache_flush_mode) { + DCHECK(*pc_ == kCallOpcode); + DCHECK(rmode_ == RelocInfo::CODE_AGE_SEQUENCE); + Assembler::set_target_address_at(stub->GetIsolate(), pc_ + 1, host_, + stub->instruction_start(), + icache_flush_mode); +} + + +Address RelocInfo::debug_call_address() { + DCHECK(IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()); + Address location = pc_ + Assembler::kPatchDebugBreakSlotAddressOffset; + return Assembler::target_address_at(location, host_); +} + +void RelocInfo::set_debug_call_address(Isolate* isolate, Address target) { + DCHECK(IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()); + Address location = pc_ + Assembler::kPatchDebugBreakSlotAddressOffset; + Assembler::set_target_address_at(isolate, location, host_, target); + if (host() != NULL) { + Code* target_code = Code::GetCodeFromTargetAddress(target); + host()->GetHeap()->incremental_marking()->RecordWriteIntoCode(host(), this, + target_code); + } +} + +void RelocInfo::WipeOut(Isolate* isolate) { + if (IsEmbeddedObject(rmode_) || IsExternalReference(rmode_) || + IsInternalReference(rmode_)) { + Memory::Address_at(pc_) = NULL; + } else if (IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)) { + // Effectively write zero into the relocation. + Assembler::set_target_address_at(isolate, pc_, host_, + pc_ + sizeof(int32_t)); + } else { + UNREACHABLE(); + } +} + +template +void RelocInfo::Visit(Isolate* isolate, ObjectVisitor* visitor) { + RelocInfo::Mode mode = rmode(); + if (mode == RelocInfo::EMBEDDED_OBJECT) { + visitor->VisitEmbeddedPointer(host(), this); + Assembler::FlushICache(isolate, pc_, sizeof(Address)); + } else if (RelocInfo::IsCodeTarget(mode)) { + visitor->VisitCodeTarget(host(), this); + } else if (mode == RelocInfo::CELL) { + visitor->VisitCellPointer(host(), this); + } else if (mode == RelocInfo::EXTERNAL_REFERENCE) { + visitor->VisitExternalReference(host(), this); + } else if (mode == RelocInfo::INTERNAL_REFERENCE) { + visitor->VisitInternalReference(host(), this); + } else if (RelocInfo::IsCodeAgeSequence(mode)) { + visitor->VisitCodeAgeSequence(host(), this); + } else if (RelocInfo::IsDebugBreakSlot(mode) && + IsPatchedDebugBreakSlotSequence()) { + visitor->VisitDebugTarget(host(), this); + } else if (IsRuntimeEntry(mode)) { + visitor->VisitRuntimeEntry(host(), this); + } +} + + +template +void RelocInfo::Visit(Heap* heap) { + RelocInfo::Mode mode = rmode(); + if (mode == RelocInfo::EMBEDDED_OBJECT) { + StaticVisitor::VisitEmbeddedPointer(heap, this); + Assembler::FlushICache(heap->isolate(), pc_, sizeof(Address)); + } else if (RelocInfo::IsCodeTarget(mode)) { + StaticVisitor::VisitCodeTarget(heap, this); + } else if (mode == RelocInfo::CELL) { + StaticVisitor::VisitCell(heap, this); + } else if (mode == RelocInfo::EXTERNAL_REFERENCE) { + StaticVisitor::VisitExternalReference(this); + } else if (mode == RelocInfo::INTERNAL_REFERENCE) { + StaticVisitor::VisitInternalReference(this); + } else if (RelocInfo::IsCodeAgeSequence(mode)) { + StaticVisitor::VisitCodeAgeSequence(heap, this); + } else if (RelocInfo::IsDebugBreakSlot(mode) && + IsPatchedDebugBreakSlotSequence()) { + StaticVisitor::VisitDebugTarget(heap, this); + } else if (IsRuntimeEntry(mode)) { + StaticVisitor::VisitRuntimeEntry(this); + } +} + + + +Immediate::Immediate(int x) { + value_.immediate = x; + rmode_ = RelocInfo::NONE32; +} + +Immediate::Immediate(Address x, RelocInfo::Mode rmode) { + value_.immediate = reinterpret_cast(x); + rmode_ = rmode; +} + +Immediate::Immediate(const ExternalReference& ext) { + value_.immediate = reinterpret_cast(ext.address()); + rmode_ = RelocInfo::EXTERNAL_REFERENCE; +} + + +Immediate::Immediate(Label* internal_offset) { + value_.immediate = reinterpret_cast(internal_offset); + rmode_ = RelocInfo::INTERNAL_REFERENCE; +} + + +Immediate::Immediate(Handle handle) { + value_.immediate = reinterpret_cast(handle.address()); + rmode_ = RelocInfo::EMBEDDED_OBJECT; +} + + +Immediate::Immediate(Smi* value) { + value_.immediate = reinterpret_cast(value); + rmode_ = RelocInfo::NONE32; +} + + +Immediate::Immediate(Address addr) { + value_.immediate = reinterpret_cast(addr); + rmode_ = RelocInfo::NONE32; +} + + +void Assembler::emit(uint32_t x) { + *reinterpret_cast(pc_) = x; + pc_ += sizeof(uint32_t); +} + + +void Assembler::emit_q(uint64_t x) { + *reinterpret_cast(pc_) = x; + pc_ += sizeof(uint64_t); +} + + +void Assembler::emit(Handle handle) { + emit(reinterpret_cast(handle.address()), + RelocInfo::EMBEDDED_OBJECT); +} + + +void Assembler::emit(uint32_t x, RelocInfo::Mode rmode) { + if (!RelocInfo::IsNone(rmode) && rmode != RelocInfo::CODE_AGE_SEQUENCE) { + RecordRelocInfo(rmode); + } + emit(x); +} + + +void Assembler::emit(Handle code, RelocInfo::Mode rmode) { + emit(reinterpret_cast(code.address()), rmode); +} + + +void Assembler::emit(const Immediate& x) { + if (x.rmode_ == RelocInfo::INTERNAL_REFERENCE) { + Label* label = reinterpret_cast(x.immediate()); + emit_code_relative_offset(label); + return; + } + if (!RelocInfo::IsNone(x.rmode_)) RecordRelocInfo(x.rmode_); + if (x.is_heap_object_request()) { + RequestHeapObject(x.heap_object_request()); + emit(0); + } else { + emit(x.immediate()); + } +} + + +void Assembler::emit_code_relative_offset(Label* label) { + if (label->is_bound()) { + int32_t pos; + pos = label->pos() + Code::kHeaderSize - kHeapObjectTag; + emit(pos); + } else { + emit_disp(label, Displacement::CODE_RELATIVE); + } +} + +void Assembler::emit_b(Immediate x) { + DCHECK(x.is_int8() || x.is_uint8()); + uint8_t value = static_cast(x.immediate()); + *pc_++ = value; +} + +void Assembler::emit_w(const Immediate& x) { + DCHECK(RelocInfo::IsNone(x.rmode_)); + uint16_t value = static_cast(x.immediate()); + reinterpret_cast(pc_)[0] = value; + pc_ += sizeof(uint16_t); +} + + +Address Assembler::target_address_at(Address pc, Address constant_pool) { + return pc + sizeof(int32_t) + *reinterpret_cast(pc); +} + + +void Assembler::set_target_address_at(Isolate* isolate, Address pc, + Address constant_pool, Address target, + ICacheFlushMode icache_flush_mode) { + DCHECK_IMPLIES(isolate == nullptr, icache_flush_mode == SKIP_ICACHE_FLUSH); + int32_t* p = reinterpret_cast(pc); + *p = target - (pc + sizeof(int32_t)); + if (icache_flush_mode != SKIP_ICACHE_FLUSH) { + Assembler::FlushICache(isolate, p, sizeof(int32_t)); + } +} + +Address Assembler::target_address_at(Address pc, Code* code) { + Address constant_pool = code ? code->constant_pool() : NULL; + return target_address_at(pc, constant_pool); +} + +void Assembler::set_target_address_at(Isolate* isolate, Address pc, Code* code, + Address target, + ICacheFlushMode icache_flush_mode) { + Address constant_pool = code ? code->constant_pool() : NULL; + set_target_address_at(isolate, pc, constant_pool, target, icache_flush_mode); +} + +Address Assembler::target_address_from_return_address(Address pc) { + return pc - kCallTargetAddressOffset; +} + + +Displacement Assembler::disp_at(Label* L) { + return Displacement(long_at(L->pos())); +} + + +void Assembler::disp_at_put(Label* L, Displacement disp) { + long_at_put(L->pos(), disp.data()); +} + + +void Assembler::emit_disp(Label* L, Displacement::Type type) { + Displacement disp(L, type); + L->link_to(pc_offset()); + emit(static_cast(disp.data())); +} + + +void Assembler::emit_near_disp(Label* L) { + byte disp = 0x00; + if (L->is_near_linked()) { + int offset = L->near_link_pos() - pc_offset(); + DCHECK(is_int8(offset)); + disp = static_cast(offset & 0xFF); + } + L->link_to(pc_offset(), Label::kNear); + *pc_++ = disp; +} + + +void Assembler::deserialization_set_target_internal_reference_at( + Isolate* isolate, Address pc, Address target, RelocInfo::Mode mode) { + Memory::Address_at(pc) = target; +} + + +void Operand::set_modrm(int mod, Register rm) { + DCHECK((mod & -4) == 0); + buf_[0] = mod << 6 | rm.code(); + len_ = 1; +} + + +void Operand::set_sib(ScaleFactor scale, Register index, Register base) { + DCHECK(len_ == 1); + DCHECK((scale & -4) == 0); + // Use SIB with no index register only for base esp. + DCHECK(!index.is(esp) || base.is(esp)); + buf_[1] = scale << 6 | index.code() << 3 | base.code(); + len_ = 2; +} + + +void Operand::set_disp8(int8_t disp) { + DCHECK(len_ == 1 || len_ == 2); + *reinterpret_cast(&buf_[len_++]) = disp; +} + + +void Operand::set_dispr(int32_t disp, RelocInfo::Mode rmode) { + DCHECK(len_ == 1 || len_ == 2); + int32_t* p = reinterpret_cast(&buf_[len_]); + *p = disp; + len_ += sizeof(int32_t); + rmode_ = rmode; +} + +Operand::Operand(Register reg) { + // reg + set_modrm(3, reg); +} + + +Operand::Operand(int32_t disp, RelocInfo::Mode rmode) { + // [disp/r] + set_modrm(0, ebp); + set_dispr(disp, rmode); +} + + +Operand::Operand(Immediate imm) { + // [disp/r] + set_modrm(0, ebp); + set_dispr(imm.immediate(), imm.rmode_); +} +} // namespace internal +} // namespace v8 + +#endif // V8_X87_ASSEMBLER_X87_INL_H_ diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/x87/codegen-x87.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/x87/codegen-x87.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/x87/codegen-x87.cc 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/x87/codegen-x87.cc 2017-12-28 04:52:57.450203610 +0100 @@ -0,0 +1,381 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/x87/codegen-x87.h" + +#if V8_TARGET_ARCH_X87 + +#include "src/codegen.h" +#include "src/heap/heap.h" +#include "src/macro-assembler.h" + +namespace v8 { +namespace internal { + + +// ------------------------------------------------------------------------- +// Platform-specific RuntimeCallHelper functions. + +void StubRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const { + masm->EnterFrame(StackFrame::INTERNAL); + DCHECK(!masm->has_frame()); + masm->set_has_frame(true); +} + + +void StubRuntimeCallHelper::AfterCall(MacroAssembler* masm) const { + masm->LeaveFrame(StackFrame::INTERNAL); + DCHECK(masm->has_frame()); + masm->set_has_frame(false); +} + + +#define __ masm. + + +UnaryMathFunctionWithIsolate CreateSqrtFunction(Isolate* isolate) { + size_t actual_size; + // Allocate buffer in executable space. + byte* buffer = + static_cast(base::OS::Allocate(1 * KB, &actual_size, true)); + if (buffer == nullptr) return nullptr; + + MacroAssembler masm(isolate, buffer, static_cast(actual_size), + CodeObjectRequired::kNo); + // Load double input into registers. + __ fld_d(MemOperand(esp, 4)); + __ X87SetFPUCW(0x027F); + __ fsqrt(); + __ X87SetFPUCW(0x037F); + __ Ret(); + + CodeDesc desc; + masm.GetCode(isolate, &desc); + DCHECK(!RelocInfo::RequiresRelocation(isolate, desc)); + + Assembler::FlushICache(isolate, buffer, actual_size); + base::OS::ProtectCode(buffer, actual_size); + return FUNCTION_CAST(buffer); +} + + +// Helper functions for CreateMemMoveFunction. +#undef __ +#define __ ACCESS_MASM(masm) + +enum Direction { FORWARD, BACKWARD }; +enum Alignment { MOVE_ALIGNED, MOVE_UNALIGNED }; + + +void MemMoveEmitPopAndReturn(MacroAssembler* masm) { + __ pop(esi); + __ pop(edi); + __ ret(0); +} + + +#undef __ +#define __ masm. + + +class LabelConverter { + public: + explicit LabelConverter(byte* buffer) : buffer_(buffer) {} + int32_t address(Label* l) const { + return reinterpret_cast(buffer_) + l->pos(); + } + private: + byte* buffer_; +}; + + +MemMoveFunction CreateMemMoveFunction(Isolate* isolate) { + size_t actual_size; + // Allocate buffer in executable space. + byte* buffer = + static_cast(base::OS::Allocate(1 * KB, &actual_size, true)); + if (buffer == nullptr) return nullptr; + MacroAssembler masm(isolate, buffer, static_cast(actual_size), + CodeObjectRequired::kNo); + LabelConverter conv(buffer); + + // Generated code is put into a fixed, unmovable buffer, and not into + // the V8 heap. We can't, and don't, refer to any relocatable addresses + // (e.g. the JavaScript nan-object). + + // 32-bit C declaration function calls pass arguments on stack. + + // Stack layout: + // esp[12]: Third argument, size. + // esp[8]: Second argument, source pointer. + // esp[4]: First argument, destination pointer. + // esp[0]: return address + + const int kDestinationOffset = 1 * kPointerSize; + const int kSourceOffset = 2 * kPointerSize; + const int kSizeOffset = 3 * kPointerSize; + + int stack_offset = 0; // Update if we change the stack height. + + Label backward, backward_much_overlap; + Label forward_much_overlap, small_size, medium_size, pop_and_return; + __ push(edi); + __ push(esi); + stack_offset += 2 * kPointerSize; + Register dst = edi; + Register src = esi; + Register count = ecx; + __ mov(dst, Operand(esp, stack_offset + kDestinationOffset)); + __ mov(src, Operand(esp, stack_offset + kSourceOffset)); + __ mov(count, Operand(esp, stack_offset + kSizeOffset)); + + __ cmp(dst, src); + __ j(equal, &pop_and_return); + + // No SSE2. + Label forward; + __ cmp(count, 0); + __ j(equal, &pop_and_return); + __ cmp(dst, src); + __ j(above, &backward); + __ jmp(&forward); + { + // Simple forward copier. + Label forward_loop_1byte, forward_loop_4byte; + __ bind(&forward_loop_4byte); + __ mov(eax, Operand(src, 0)); + __ sub(count, Immediate(4)); + __ add(src, Immediate(4)); + __ mov(Operand(dst, 0), eax); + __ add(dst, Immediate(4)); + __ bind(&forward); // Entry point. + __ cmp(count, 3); + __ j(above, &forward_loop_4byte); + __ bind(&forward_loop_1byte); + __ cmp(count, 0); + __ j(below_equal, &pop_and_return); + __ mov_b(eax, Operand(src, 0)); + __ dec(count); + __ inc(src); + __ mov_b(Operand(dst, 0), eax); + __ inc(dst); + __ jmp(&forward_loop_1byte); + } + { + // Simple backward copier. + Label backward_loop_1byte, backward_loop_4byte, entry_shortcut; + __ bind(&backward); + __ add(src, count); + __ add(dst, count); + __ cmp(count, 3); + __ j(below_equal, &entry_shortcut); + + __ bind(&backward_loop_4byte); + __ sub(src, Immediate(4)); + __ sub(count, Immediate(4)); + __ mov(eax, Operand(src, 0)); + __ sub(dst, Immediate(4)); + __ mov(Operand(dst, 0), eax); + __ cmp(count, 3); + __ j(above, &backward_loop_4byte); + __ bind(&backward_loop_1byte); + __ cmp(count, 0); + __ j(below_equal, &pop_and_return); + __ bind(&entry_shortcut); + __ dec(src); + __ dec(count); + __ mov_b(eax, Operand(src, 0)); + __ dec(dst); + __ mov_b(Operand(dst, 0), eax); + __ jmp(&backward_loop_1byte); + } + + __ bind(&pop_and_return); + MemMoveEmitPopAndReturn(&masm); + + CodeDesc desc; + masm.GetCode(isolate, &desc); + DCHECK(!RelocInfo::RequiresRelocation(isolate, desc)); + Assembler::FlushICache(isolate, buffer, actual_size); + base::OS::ProtectCode(buffer, actual_size); + // TODO(jkummerow): It would be nice to register this code creation event + // with the PROFILE / GDBJIT system. + return FUNCTION_CAST(buffer); +} + + +#undef __ + +// ------------------------------------------------------------------------- +// Code generators + +#define __ ACCESS_MASM(masm) + +void StringCharLoadGenerator::Generate(MacroAssembler* masm, + Factory* factory, + Register string, + Register index, + Register result, + Label* call_runtime) { + Label indirect_string_loaded; + __ bind(&indirect_string_loaded); + + // Fetch the instance type of the receiver into result register. + __ mov(result, FieldOperand(string, HeapObject::kMapOffset)); + __ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset)); + + // We need special handling for indirect strings. + Label check_sequential; + __ test(result, Immediate(kIsIndirectStringMask)); + __ j(zero, &check_sequential, Label::kNear); + + // Dispatch on the indirect string shape: slice or cons. + Label cons_string, thin_string; + __ and_(result, Immediate(kStringRepresentationMask)); + __ cmp(result, Immediate(kConsStringTag)); + __ j(equal, &cons_string, Label::kNear); + __ cmp(result, Immediate(kThinStringTag)); + __ j(equal, &thin_string, Label::kNear); + + // Handle slices. + __ mov(result, FieldOperand(string, SlicedString::kOffsetOffset)); + __ SmiUntag(result); + __ add(index, result); + __ mov(string, FieldOperand(string, SlicedString::kParentOffset)); + __ jmp(&indirect_string_loaded); + + // Handle thin strings. + __ bind(&thin_string); + __ mov(string, FieldOperand(string, ThinString::kActualOffset)); + __ jmp(&indirect_string_loaded); + + // Handle cons strings. + // Check whether the right hand side is the empty string (i.e. if + // this is really a flat string in a cons string). If that is not + // the case we would rather go to the runtime system now to flatten + // the string. + __ bind(&cons_string); + __ cmp(FieldOperand(string, ConsString::kSecondOffset), + Immediate(factory->empty_string())); + __ j(not_equal, call_runtime); + __ mov(string, FieldOperand(string, ConsString::kFirstOffset)); + __ jmp(&indirect_string_loaded); + + // Distinguish sequential and external strings. Only these two string + // representations can reach here (slices and flat cons strings have been + // reduced to the underlying sequential or external string). + Label seq_string; + __ bind(&check_sequential); + STATIC_ASSERT(kSeqStringTag == 0); + __ test(result, Immediate(kStringRepresentationMask)); + __ j(zero, &seq_string, Label::kNear); + + // Handle external strings. + Label one_byte_external, done; + if (FLAG_debug_code) { + // Assert that we do not have a cons or slice (indirect strings) here. + // Sequential strings have already been ruled out. + __ test(result, Immediate(kIsIndirectStringMask)); + __ Assert(zero, kExternalStringExpectedButNotFound); + } + // Rule out short external strings. + STATIC_ASSERT(kShortExternalStringTag != 0); + __ test_b(result, Immediate(kShortExternalStringMask)); + __ j(not_zero, call_runtime); + // Check encoding. + STATIC_ASSERT(kTwoByteStringTag == 0); + __ test_b(result, Immediate(kStringEncodingMask)); + __ mov(result, FieldOperand(string, ExternalString::kResourceDataOffset)); + __ j(not_equal, &one_byte_external, Label::kNear); + // Two-byte string. + __ movzx_w(result, Operand(result, index, times_2, 0)); + __ jmp(&done, Label::kNear); + __ bind(&one_byte_external); + // One-byte string. + __ movzx_b(result, Operand(result, index, times_1, 0)); + __ jmp(&done, Label::kNear); + + // Dispatch on the encoding: one-byte or two-byte. + Label one_byte; + __ bind(&seq_string); + STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0); + STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); + __ test(result, Immediate(kStringEncodingMask)); + __ j(not_zero, &one_byte, Label::kNear); + + // Two-byte string. + // Load the two-byte character code into the result register. + __ movzx_w(result, FieldOperand(string, + index, + times_2, + SeqTwoByteString::kHeaderSize)); + __ jmp(&done, Label::kNear); + + // One-byte string. + // Load the byte into the result register. + __ bind(&one_byte); + __ movzx_b(result, FieldOperand(string, + index, + times_1, + SeqOneByteString::kHeaderSize)); + __ bind(&done); +} + + +#undef __ + + +CodeAgingHelper::CodeAgingHelper(Isolate* isolate) { + USE(isolate); + DCHECK(young_sequence_.length() == kNoCodeAgeSequenceLength); + CodePatcher patcher(isolate, young_sequence_.start(), + young_sequence_.length()); + patcher.masm()->push(ebp); + patcher.masm()->mov(ebp, esp); + patcher.masm()->push(esi); + patcher.masm()->push(edi); +} + + +#ifdef DEBUG +bool CodeAgingHelper::IsOld(byte* candidate) const { + return *candidate == kCallOpcode; +} +#endif + + +bool Code::IsYoungSequence(Isolate* isolate, byte* sequence) { + bool result = isolate->code_aging_helper()->IsYoung(sequence); + DCHECK(result || isolate->code_aging_helper()->IsOld(sequence)); + return result; +} + +Code::Age Code::GetCodeAge(Isolate* isolate, byte* sequence) { + if (IsYoungSequence(isolate, sequence)) return kNoAgeCodeAge; + + sequence++; // Skip the kCallOpcode byte + Address target_address = sequence + *reinterpret_cast(sequence) + + Assembler::kCallTargetAddressOffset; + Code* stub = GetCodeFromTargetAddress(target_address); + return GetAgeOfCodeAgeStub(stub); +} + +void Code::PatchPlatformCodeAge(Isolate* isolate, byte* sequence, + Code::Age age) { + uint32_t young_length = isolate->code_aging_helper()->young_sequence_length(); + if (age == kNoAgeCodeAge) { + isolate->code_aging_helper()->CopyYoungSequenceTo(sequence); + Assembler::FlushICache(isolate, sequence, young_length); + } else { + Code* stub = GetCodeAgeStub(isolate, age); + CodePatcher patcher(isolate, sequence, young_length); + patcher.masm()->call(stub->instruction_start(), RelocInfo::NONE32); + } +} + + +} // namespace internal +} // namespace v8 + +#endif // V8_TARGET_ARCH_X87 diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/x87/codegen-x87.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/x87/codegen-x87.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/x87/codegen-x87.h 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/x87/codegen-x87.h 2017-12-25 17:42:57.221465559 +0100 @@ -0,0 +1,33 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_X87_CODEGEN_X87_H_ +#define V8_X87_CODEGEN_X87_H_ + +#include "src/macro-assembler.h" + +namespace v8 { +namespace internal { + + +class StringCharLoadGenerator : public AllStatic { + public: + // Generates the code for handling different string types and loading the + // indexed character into |result|. We expect |index| as untagged input and + // |result| as untagged output. + static void Generate(MacroAssembler* masm, + Factory* factory, + Register string, + Register index, + Register result, + Label* call_runtime); + + private: + DISALLOW_COPY_AND_ASSIGN(StringCharLoadGenerator); +}; + +} // namespace internal +} // namespace v8 + +#endif // V8_X87_CODEGEN_X87_H_ diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/x87/code-stubs-x87.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/x87/code-stubs-x87.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/x87/code-stubs-x87.cc 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/x87/code-stubs-x87.cc 2017-12-28 04:50:57.144052671 +0100 @@ -0,0 +1,2668 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#if V8_TARGET_ARCH_X87 + +#include "src/code-stubs.h" +#include "src/api-arguments.h" +#include "src/base/bits.h" +#include "src/bootstrapper.h" +#include "src/codegen.h" +#include "src/ic/handler-compiler.h" +#include "src/ic/ic.h" +#include "src/ic/stub-cache.h" +#include "src/isolate.h" +#include "src/regexp/jsregexp.h" +#include "src/regexp/regexp-macro-assembler.h" +#include "src/runtime/runtime.h" +#include "src/x87/code-stubs-x87.h" +#include "src/x87/frames-x87.h" + +namespace v8 { +namespace internal { + +#define __ ACCESS_MASM(masm) + +void ArrayNArgumentsConstructorStub::Generate(MacroAssembler* masm) { + __ pop(ecx); + __ mov(MemOperand(esp, eax, times_4, 0), edi); + __ push(edi); + __ push(ebx); + __ push(ecx); + __ add(eax, Immediate(3)); + __ TailCallRuntime(Runtime::kNewArray); +} + + +void StoreBufferOverflowStub::Generate(MacroAssembler* masm) { + // We don't allow a GC during a store buffer overflow so there is no need to + // store the registers in any particular way, but we do have to store and + // restore them. + __ pushad(); + if (save_doubles()) { + // Save FPU stat in m108byte. + __ sub(esp, Immediate(108)); + __ fnsave(Operand(esp, 0)); + } + const int argument_count = 1; + + AllowExternalCallThatCantCauseGC scope(masm); + __ PrepareCallCFunction(argument_count, ecx); + __ mov(Operand(esp, 0 * kPointerSize), + Immediate(ExternalReference::isolate_address(isolate()))); + __ CallCFunction( + ExternalReference::store_buffer_overflow_function(isolate()), + argument_count); + if (save_doubles()) { + // Restore FPU stat in m108byte. + __ frstor(Operand(esp, 0)); + __ add(esp, Immediate(108)); + } + __ popad(); + __ ret(0); +} + + +class FloatingPointHelper : public AllStatic { + public: + enum ArgLocation { + ARGS_ON_STACK, + ARGS_IN_REGISTERS + }; + + // Code pattern for loading a floating point value. Input value must + // be either a smi or a heap number object (fp value). Requirements: + // operand in register number. Returns operand as floating point number + // on FPU stack. + static void LoadFloatOperand(MacroAssembler* masm, Register number); + + // Test if operands are smi or number objects (fp). Requirements: + // operand_1 in eax, operand_2 in edx; falls through on float + // operands, jumps to the non_float label otherwise. + static void CheckFloatOperands(MacroAssembler* masm, + Label* non_float, + Register scratch); +}; + + +void DoubleToIStub::Generate(MacroAssembler* masm) { + Register input_reg = this->source(); + Register final_result_reg = this->destination(); + DCHECK(is_truncating()); + + Label check_negative, process_64_bits, done, done_no_stash; + + int double_offset = offset(); + + // Account for return address and saved regs if input is esp. + if (input_reg.is(esp)) double_offset += 3 * kPointerSize; + + MemOperand mantissa_operand(MemOperand(input_reg, double_offset)); + MemOperand exponent_operand(MemOperand(input_reg, + double_offset + kDoubleSize / 2)); + + Register scratch1; + { + Register scratch_candidates[3] = { ebx, edx, edi }; + for (int i = 0; i < 3; i++) { + scratch1 = scratch_candidates[i]; + if (!final_result_reg.is(scratch1) && !input_reg.is(scratch1)) break; + } + } + // Since we must use ecx for shifts below, use some other register (eax) + // to calculate the result if ecx is the requested return register. + Register result_reg = final_result_reg.is(ecx) ? eax : final_result_reg; + // Save ecx if it isn't the return register and therefore volatile, or if it + // is the return register, then save the temp register we use in its stead for + // the result. + Register save_reg = final_result_reg.is(ecx) ? eax : ecx; + __ push(scratch1); + __ push(save_reg); + + bool stash_exponent_copy = !input_reg.is(esp); + __ mov(scratch1, mantissa_operand); + __ mov(ecx, exponent_operand); + if (stash_exponent_copy) __ push(ecx); + + __ and_(ecx, HeapNumber::kExponentMask); + __ shr(ecx, HeapNumber::kExponentShift); + __ lea(result_reg, MemOperand(ecx, -HeapNumber::kExponentBias)); + __ cmp(result_reg, Immediate(HeapNumber::kMantissaBits)); + __ j(below, &process_64_bits); + + // Result is entirely in lower 32-bits of mantissa + int delta = HeapNumber::kExponentBias + Double::kPhysicalSignificandSize; + __ sub(ecx, Immediate(delta)); + __ xor_(result_reg, result_reg); + __ cmp(ecx, Immediate(31)); + __ j(above, &done); + __ shl_cl(scratch1); + __ jmp(&check_negative); + + __ bind(&process_64_bits); + // Result must be extracted from shifted 32-bit mantissa + __ sub(ecx, Immediate(delta)); + __ neg(ecx); + if (stash_exponent_copy) { + __ mov(result_reg, MemOperand(esp, 0)); + } else { + __ mov(result_reg, exponent_operand); + } + __ and_(result_reg, + Immediate(static_cast(Double::kSignificandMask >> 32))); + __ add(result_reg, + Immediate(static_cast(Double::kHiddenBit >> 32))); + __ shrd_cl(scratch1, result_reg); + __ shr_cl(result_reg); + __ test(ecx, Immediate(32)); + { + Label skip_mov; + __ j(equal, &skip_mov, Label::kNear); + __ mov(scratch1, result_reg); + __ bind(&skip_mov); + } + + // If the double was negative, negate the integer result. + __ bind(&check_negative); + __ mov(result_reg, scratch1); + __ neg(result_reg); + if (stash_exponent_copy) { + __ cmp(MemOperand(esp, 0), Immediate(0)); + } else { + __ cmp(exponent_operand, Immediate(0)); + } + { + Label skip_mov; + __ j(less_equal, &skip_mov, Label::kNear); + __ mov(result_reg, scratch1); + __ bind(&skip_mov); + } + + // Restore registers + __ bind(&done); + if (stash_exponent_copy) { + __ add(esp, Immediate(kDoubleSize / 2)); + } + __ bind(&done_no_stash); + if (!final_result_reg.is(result_reg)) { + DCHECK(final_result_reg.is(ecx)); + __ mov(final_result_reg, result_reg); + } + __ pop(save_reg); + __ pop(scratch1); + __ ret(0); +} + + +void FloatingPointHelper::LoadFloatOperand(MacroAssembler* masm, + Register number) { + Label load_smi, done; + + __ JumpIfSmi(number, &load_smi, Label::kNear); + __ fld_d(FieldOperand(number, HeapNumber::kValueOffset)); + __ jmp(&done, Label::kNear); + + __ bind(&load_smi); + __ SmiUntag(number); + __ push(number); + __ fild_s(Operand(esp, 0)); + __ pop(number); + + __ bind(&done); +} + + +void FloatingPointHelper::CheckFloatOperands(MacroAssembler* masm, + Label* non_float, + Register scratch) { + Label test_other, done; + // Test if both operands are floats or smi -> scratch=k_is_float; + // Otherwise scratch = k_not_float. + __ JumpIfSmi(edx, &test_other, Label::kNear); + __ mov(scratch, FieldOperand(edx, HeapObject::kMapOffset)); + Factory* factory = masm->isolate()->factory(); + __ cmp(scratch, factory->heap_number_map()); + __ j(not_equal, non_float); // argument in edx is not a number -> NaN + + __ bind(&test_other); + __ JumpIfSmi(eax, &done, Label::kNear); + __ mov(scratch, FieldOperand(eax, HeapObject::kMapOffset)); + __ cmp(scratch, factory->heap_number_map()); + __ j(not_equal, non_float); // argument in eax is not a number -> NaN + + // Fall-through: Both operands are numbers. + __ bind(&done); +} + + +void MathPowStub::Generate(MacroAssembler* masm) { + const Register scratch = ecx; + + // Load the double_exponent into x87 FPU + __ fld_d(Operand(esp, 0 * kDoubleSize + 4)); + // Load the double_base into x87 FPU + __ fld_d(Operand(esp, 1 * kDoubleSize + 4)); + + // Call ieee754 runtime directly. + { + AllowExternalCallThatCantCauseGC scope(masm); + __ PrepareCallCFunction(4, scratch); + // Put the double_base parameter in call stack + __ fstp_d(Operand(esp, 0 * kDoubleSize)); + // Put the double_exponent parameter in call stack + __ fstp_d(Operand(esp, 1 * kDoubleSize)); + __ CallCFunction(ExternalReference::power_double_double_function(isolate()), + 4); + } + // Return value is in st(0) on ia32. + __ ret(0); +} + + +static int NegativeComparisonResult(Condition cc) { + DCHECK(cc != equal); + DCHECK((cc == less) || (cc == less_equal) + || (cc == greater) || (cc == greater_equal)); + return (cc == greater || cc == greater_equal) ? LESS : GREATER; +} + + +static void CheckInputType(MacroAssembler* masm, Register input, + CompareICState::State expected, Label* fail) { + Label ok; + if (expected == CompareICState::SMI) { + __ JumpIfNotSmi(input, fail); + } else if (expected == CompareICState::NUMBER) { + __ JumpIfSmi(input, &ok); + __ cmp(FieldOperand(input, HeapObject::kMapOffset), + Immediate(masm->isolate()->factory()->heap_number_map())); + __ j(not_equal, fail); + } + // We could be strict about internalized/non-internalized here, but as long as + // hydrogen doesn't care, the stub doesn't have to care either. + __ bind(&ok); +} + + +static void BranchIfNotInternalizedString(MacroAssembler* masm, + Label* label, + Register object, + Register scratch) { + __ JumpIfSmi(object, label); + __ mov(scratch, FieldOperand(object, HeapObject::kMapOffset)); + __ movzx_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset)); + STATIC_ASSERT(kInternalizedTag == 0 && kStringTag == 0); + __ test(scratch, Immediate(kIsNotStringMask | kIsNotInternalizedMask)); + __ j(not_zero, label); +} + + +void CompareICStub::GenerateGeneric(MacroAssembler* masm) { + Label runtime_call, check_unequal_objects; + Condition cc = GetCondition(); + + Label miss; + CheckInputType(masm, edx, left(), &miss); + CheckInputType(masm, eax, right(), &miss); + + // Compare two smis. + Label non_smi, smi_done; + __ mov(ecx, edx); + __ or_(ecx, eax); + __ JumpIfNotSmi(ecx, &non_smi, Label::kNear); + __ sub(edx, eax); // Return on the result of the subtraction. + __ j(no_overflow, &smi_done, Label::kNear); + __ not_(edx); // Correct sign in case of overflow. edx is never 0 here. + __ bind(&smi_done); + __ mov(eax, edx); + __ ret(0); + __ bind(&non_smi); + + // NOTICE! This code is only reached after a smi-fast-case check, so + // it is certain that at least one operand isn't a smi. + + // Identical objects can be compared fast, but there are some tricky cases + // for NaN and undefined. + Label generic_heap_number_comparison; + { + Label not_identical; + __ cmp(eax, edx); + __ j(not_equal, ¬_identical); + + if (cc != equal) { + // Check for undefined. undefined OP undefined is false even though + // undefined == undefined. + __ cmp(edx, isolate()->factory()->undefined_value()); + Label check_for_nan; + __ j(not_equal, &check_for_nan, Label::kNear); + __ Move(eax, Immediate(Smi::FromInt(NegativeComparisonResult(cc)))); + __ ret(0); + __ bind(&check_for_nan); + } + + // Test for NaN. Compare heap numbers in a general way, + // to handle NaNs correctly. + __ cmp(FieldOperand(edx, HeapObject::kMapOffset), + Immediate(isolate()->factory()->heap_number_map())); + __ j(equal, &generic_heap_number_comparison, Label::kNear); + if (cc != equal) { + __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset)); + __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset)); + // Call runtime on identical JSObjects. Otherwise return equal. + __ cmpb(ecx, Immediate(FIRST_JS_RECEIVER_TYPE)); + __ j(above_equal, &runtime_call, Label::kFar); + // Call runtime on identical symbols since we need to throw a TypeError. + __ cmpb(ecx, Immediate(SYMBOL_TYPE)); + __ j(equal, &runtime_call, Label::kFar); + } + __ Move(eax, Immediate(Smi::FromInt(EQUAL))); + __ ret(0); + + + __ bind(¬_identical); + } + + // Strict equality can quickly decide whether objects are equal. + // Non-strict object equality is slower, so it is handled later in the stub. + if (cc == equal && strict()) { + Label slow; // Fallthrough label. + Label not_smis; + // If we're doing a strict equality comparison, we don't have to do + // type conversion, so we generate code to do fast comparison for objects + // and oddballs. Non-smi numbers and strings still go through the usual + // slow-case code. + // If either is a Smi (we know that not both are), then they can only + // be equal if the other is a HeapNumber. If so, use the slow case. + STATIC_ASSERT(kSmiTag == 0); + DCHECK_EQ(static_cast(0), Smi::kZero); + __ mov(ecx, Immediate(kSmiTagMask)); + __ and_(ecx, eax); + __ test(ecx, edx); + __ j(not_zero, ¬_smis, Label::kNear); + // One operand is a smi. + + // Check whether the non-smi is a heap number. + STATIC_ASSERT(kSmiTagMask == 1); + // ecx still holds eax & kSmiTag, which is either zero or one. + __ sub(ecx, Immediate(0x01)); + __ mov(ebx, edx); + __ xor_(ebx, eax); + __ and_(ebx, ecx); // ebx holds either 0 or eax ^ edx. + __ xor_(ebx, eax); + // if eax was smi, ebx is now edx, else eax. + + // Check if the non-smi operand is a heap number. + __ cmp(FieldOperand(ebx, HeapObject::kMapOffset), + Immediate(isolate()->factory()->heap_number_map())); + // If heap number, handle it in the slow case. + __ j(equal, &slow, Label::kNear); + // Return non-equal (ebx is not zero) + __ mov(eax, ebx); + __ ret(0); + + __ bind(¬_smis); + // If either operand is a JSObject or an oddball value, then they are not + // equal since their pointers are different + // There is no test for undetectability in strict equality. + + // Get the type of the first operand. + // If the first object is a JS object, we have done pointer comparison. + Label first_non_object; + STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE); + __ CmpObjectType(eax, FIRST_JS_RECEIVER_TYPE, ecx); + __ j(below, &first_non_object, Label::kNear); + + // Return non-zero (eax is not zero) + Label return_not_equal; + STATIC_ASSERT(kHeapObjectTag != 0); + __ bind(&return_not_equal); + __ ret(0); + + __ bind(&first_non_object); + // Check for oddballs: true, false, null, undefined. + __ CmpInstanceType(ecx, ODDBALL_TYPE); + __ j(equal, &return_not_equal); + + __ CmpObjectType(edx, FIRST_JS_RECEIVER_TYPE, ecx); + __ j(above_equal, &return_not_equal); + + // Check for oddballs: true, false, null, undefined. + __ CmpInstanceType(ecx, ODDBALL_TYPE); + __ j(equal, &return_not_equal); + + // Fall through to the general case. + __ bind(&slow); + } + + // Generate the number comparison code. + Label non_number_comparison; + Label unordered; + __ bind(&generic_heap_number_comparison); + FloatingPointHelper::CheckFloatOperands( + masm, &non_number_comparison, ebx); + FloatingPointHelper::LoadFloatOperand(masm, eax); + FloatingPointHelper::LoadFloatOperand(masm, edx); + __ FCmp(); + + // Don't base result on EFLAGS when a NaN is involved. + __ j(parity_even, &unordered, Label::kNear); + + Label below_label, above_label; + // Return a result of -1, 0, or 1, based on EFLAGS. + __ j(below, &below_label, Label::kNear); + __ j(above, &above_label, Label::kNear); + + __ Move(eax, Immediate(0)); + __ ret(0); + + __ bind(&below_label); + __ mov(eax, Immediate(Smi::FromInt(-1))); + __ ret(0); + + __ bind(&above_label); + __ mov(eax, Immediate(Smi::FromInt(1))); + __ ret(0); + + // If one of the numbers was NaN, then the result is always false. + // The cc is never not-equal. + __ bind(&unordered); + DCHECK(cc != not_equal); + if (cc == less || cc == less_equal) { + __ mov(eax, Immediate(Smi::FromInt(1))); + } else { + __ mov(eax, Immediate(Smi::FromInt(-1))); + } + __ ret(0); + + // The number comparison code did not provide a valid result. + __ bind(&non_number_comparison); + + // Fast negative check for internalized-to-internalized equality. + Label check_for_strings; + if (cc == equal) { + BranchIfNotInternalizedString(masm, &check_for_strings, eax, ecx); + BranchIfNotInternalizedString(masm, &check_for_strings, edx, ecx); + + // We've already checked for object identity, so if both operands + // are internalized they aren't equal. Register eax already holds a + // non-zero value, which indicates not equal, so just return. + __ ret(0); + } + + __ bind(&check_for_strings); + + __ JumpIfNotBothSequentialOneByteStrings(edx, eax, ecx, ebx, + &check_unequal_objects); + + // Inline comparison of one-byte strings. + if (cc == equal) { + StringHelper::GenerateFlatOneByteStringEquals(masm, edx, eax, ecx, ebx); + } else { + StringHelper::GenerateCompareFlatOneByteStrings(masm, edx, eax, ecx, ebx, + edi); + } +#ifdef DEBUG + __ Abort(kUnexpectedFallThroughFromStringComparison); +#endif + + __ bind(&check_unequal_objects); + if (cc == equal && !strict()) { + // Non-strict equality. Objects are unequal if + // they are both JSObjects and not undetectable, + // and their pointers are different. + Label return_equal, return_unequal, undetectable; + // At most one is a smi, so we can test for smi by adding the two. + // A smi plus a heap object has the low bit set, a heap object plus + // a heap object has the low bit clear. + STATIC_ASSERT(kSmiTag == 0); + STATIC_ASSERT(kSmiTagMask == 1); + __ lea(ecx, Operand(eax, edx, times_1, 0)); + __ test(ecx, Immediate(kSmiTagMask)); + __ j(not_zero, &runtime_call); + + __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset)); + __ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset)); + + __ test_b(FieldOperand(ebx, Map::kBitFieldOffset), + Immediate(1 << Map::kIsUndetectable)); + __ j(not_zero, &undetectable, Label::kNear); + __ test_b(FieldOperand(ecx, Map::kBitFieldOffset), + Immediate(1 << Map::kIsUndetectable)); + __ j(not_zero, &return_unequal, Label::kNear); + + __ CmpInstanceType(ebx, FIRST_JS_RECEIVER_TYPE); + __ j(below, &runtime_call, Label::kNear); + __ CmpInstanceType(ecx, FIRST_JS_RECEIVER_TYPE); + __ j(below, &runtime_call, Label::kNear); + + __ bind(&return_unequal); + // Return non-equal by returning the non-zero object pointer in eax. + __ ret(0); // eax, edx were pushed + + __ bind(&undetectable); + __ test_b(FieldOperand(ecx, Map::kBitFieldOffset), + Immediate(1 << Map::kIsUndetectable)); + __ j(zero, &return_unequal, Label::kNear); + + // If both sides are JSReceivers, then the result is false according to + // the HTML specification, which says that only comparisons with null or + // undefined are affected by special casing for document.all. + __ CmpInstanceType(ebx, ODDBALL_TYPE); + __ j(zero, &return_equal, Label::kNear); + __ CmpInstanceType(ecx, ODDBALL_TYPE); + __ j(not_zero, &return_unequal, Label::kNear); + + __ bind(&return_equal); + __ Move(eax, Immediate(EQUAL)); + __ ret(0); // eax, edx were pushed + } + __ bind(&runtime_call); + + if (cc == equal) { + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ Push(esi); + __ Call(strict() ? isolate()->builtins()->StrictEqual() + : isolate()->builtins()->Equal(), + RelocInfo::CODE_TARGET); + __ Pop(esi); + } + // Turn true into 0 and false into some non-zero value. + STATIC_ASSERT(EQUAL == 0); + __ sub(eax, Immediate(isolate()->factory()->true_value())); + __ Ret(); + } else { + // Push arguments below the return address. + __ pop(ecx); + __ push(edx); + __ push(eax); + __ push(Immediate(Smi::FromInt(NegativeComparisonResult(cc)))); + + // Restore return address on the stack. + __ push(ecx); + // Call the native; it returns -1 (less), 0 (equal), or 1 (greater) + // tagged as a small integer. + __ TailCallRuntime(Runtime::kCompare); + } + + __ bind(&miss); + GenerateMiss(masm); +} + + +static void CallStubInRecordCallTarget(MacroAssembler* masm, CodeStub* stub) { + // eax : number of arguments to the construct function + // ebx : feedback vector + // edx : slot in feedback vector (Smi) + // edi : the function to call + + { + FrameScope scope(masm, StackFrame::INTERNAL); + + // Number-of-arguments register must be smi-tagged to call out. + __ SmiTag(eax); + __ push(eax); + __ push(edi); + __ push(edx); + __ push(ebx); + __ push(esi); + + __ CallStub(stub); + + __ pop(esi); + __ pop(ebx); + __ pop(edx); + __ pop(edi); + __ pop(eax); + __ SmiUntag(eax); + } +} + + +static void GenerateRecordCallTarget(MacroAssembler* masm) { + // Cache the called function in a feedback vector slot. Cache states + // are uninitialized, monomorphic (indicated by a JSFunction), and + // megamorphic. + // eax : number of arguments to the construct function + // ebx : feedback vector + // edx : slot in feedback vector (Smi) + // edi : the function to call + Isolate* isolate = masm->isolate(); + Label initialize, done, miss, megamorphic, not_array_function; + + // Load the cache state into ecx. + __ mov(ecx, FieldOperand(ebx, edx, times_half_pointer_size, + FixedArray::kHeaderSize)); + + // A monomorphic cache hit or an already megamorphic state: invoke the + // function without changing the state. + // We don't know if ecx is a WeakCell or a Symbol, but it's harmless to read + // at this position in a symbol (see static asserts in feedback-vector.h). + Label check_allocation_site; + __ cmp(edi, FieldOperand(ecx, WeakCell::kValueOffset)); + __ j(equal, &done, Label::kFar); + __ CompareRoot(ecx, Heap::kmegamorphic_symbolRootIndex); + __ j(equal, &done, Label::kFar); + __ CompareRoot(FieldOperand(ecx, HeapObject::kMapOffset), + Heap::kWeakCellMapRootIndex); + __ j(not_equal, &check_allocation_site); + + // If the weak cell is cleared, we have a new chance to become monomorphic. + __ JumpIfSmi(FieldOperand(ecx, WeakCell::kValueOffset), &initialize); + __ jmp(&megamorphic); + + __ bind(&check_allocation_site); + // If we came here, we need to see if we are the array function. + // If we didn't have a matching function, and we didn't find the megamorph + // sentinel, then we have in the slot either some other function or an + // AllocationSite. + __ CompareRoot(FieldOperand(ecx, 0), Heap::kAllocationSiteMapRootIndex); + __ j(not_equal, &miss); + + // Make sure the function is the Array() function + __ LoadGlobalFunction(Context::ARRAY_FUNCTION_INDEX, ecx); + __ cmp(edi, ecx); + __ j(not_equal, &megamorphic); + __ jmp(&done, Label::kFar); + + __ bind(&miss); + + // A monomorphic miss (i.e, here the cache is not uninitialized) goes + // megamorphic. + __ CompareRoot(ecx, Heap::kuninitialized_symbolRootIndex); + __ j(equal, &initialize); + // MegamorphicSentinel is an immortal immovable object (undefined) so no + // write-barrier is needed. + __ bind(&megamorphic); + __ mov( + FieldOperand(ebx, edx, times_half_pointer_size, FixedArray::kHeaderSize), + Immediate(FeedbackVector::MegamorphicSentinel(isolate))); + __ jmp(&done, Label::kFar); + + // An uninitialized cache is patched with the function or sentinel to + // indicate the ElementsKind if function is the Array constructor. + __ bind(&initialize); + // Make sure the function is the Array() function + __ LoadGlobalFunction(Context::ARRAY_FUNCTION_INDEX, ecx); + __ cmp(edi, ecx); + __ j(not_equal, ¬_array_function); + + // The target function is the Array constructor, + // Create an AllocationSite if we don't already have it, store it in the + // slot. + CreateAllocationSiteStub create_stub(isolate); + CallStubInRecordCallTarget(masm, &create_stub); + __ jmp(&done); + + __ bind(¬_array_function); + CreateWeakCellStub weak_cell_stub(isolate); + CallStubInRecordCallTarget(masm, &weak_cell_stub); + + __ bind(&done); + // Increment the call count for all function calls. + __ add(FieldOperand(ebx, edx, times_half_pointer_size, + FixedArray::kHeaderSize + kPointerSize), + Immediate(Smi::FromInt(1))); +} + + +void CallConstructStub::Generate(MacroAssembler* masm) { + // eax : number of arguments + // ebx : feedback vector + // edx : slot in feedback vector (Smi, for RecordCallTarget) + // edi : constructor function + + Label non_function; + // Check that function is not a smi. + __ JumpIfSmi(edi, &non_function); + // Check that function is a JSFunction. + __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx); + __ j(not_equal, &non_function); + + GenerateRecordCallTarget(masm); + + Label feedback_register_initialized; + // Put the AllocationSite from the feedback vector into ebx, or undefined. + __ mov(ebx, FieldOperand(ebx, edx, times_half_pointer_size, + FixedArray::kHeaderSize)); + Handle allocation_site_map = isolate()->factory()->allocation_site_map(); + __ cmp(FieldOperand(ebx, 0), Immediate(allocation_site_map)); + __ j(equal, &feedback_register_initialized); + __ mov(ebx, isolate()->factory()->undefined_value()); + __ bind(&feedback_register_initialized); + + __ AssertUndefinedOrAllocationSite(ebx); + + // Pass new target to construct stub. + __ mov(edx, edi); + + // Tail call to the function-specific construct stub (still in the caller + // context at this point). + __ mov(ecx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); + __ mov(ecx, FieldOperand(ecx, SharedFunctionInfo::kConstructStubOffset)); + __ lea(ecx, FieldOperand(ecx, Code::kHeaderSize)); + __ jmp(ecx); + + __ bind(&non_function); + __ mov(edx, edi); + __ Jump(isolate()->builtins()->Construct(), RelocInfo::CODE_TARGET); +} + + +bool CEntryStub::NeedsImmovableCode() { + return false; +} + + +void CodeStub::GenerateStubsAheadOfTime(Isolate* isolate) { + CEntryStub::GenerateAheadOfTime(isolate); + StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime(isolate); + // It is important that the store buffer overflow stubs are generated first. + CommonArrayConstructorStub::GenerateStubsAheadOfTime(isolate); + CreateAllocationSiteStub::GenerateAheadOfTime(isolate); + CreateWeakCellStub::GenerateAheadOfTime(isolate); + StoreFastElementStub::GenerateAheadOfTime(isolate); +} + + +void CodeStub::GenerateFPStubs(Isolate* isolate) { + CEntryStub save_doubles(isolate, 1, kSaveFPRegs); + // Stubs might already be in the snapshot, detect that and don't regenerate, + // which would lead to code stub initialization state being messed up. + Code* save_doubles_code; + if (!save_doubles.FindCodeInCache(&save_doubles_code)) { + save_doubles_code = *(save_doubles.GetCode()); + } +} + + +void CEntryStub::GenerateAheadOfTime(Isolate* isolate) { + CEntryStub stub(isolate, 1, kDontSaveFPRegs); + stub.GetCode(); +} + + +void CEntryStub::Generate(MacroAssembler* masm) { + // eax: number of arguments including receiver + // ebx: pointer to C function (C callee-saved) + // ebp: frame pointer (restored after C call) + // esp: stack pointer (restored after C call) + // esi: current context (C callee-saved) + // edi: JS function of the caller (C callee-saved) + // + // If argv_in_register(): + // ecx: pointer to the first argument + + ProfileEntryHookStub::MaybeCallEntryHook(masm); + + // Reserve space on the stack for the three arguments passed to the call. If + // result size is greater than can be returned in registers, also reserve + // space for the hidden argument for the result location, and space for the + // result itself. + int arg_stack_space = result_size() < 3 ? 3 : 4 + result_size(); + + // Enter the exit frame that transitions from JavaScript to C++. + if (argv_in_register()) { + DCHECK(!save_doubles()); + DCHECK(!is_builtin_exit()); + __ EnterApiExitFrame(arg_stack_space); + + // Move argc and argv into the correct registers. + __ mov(esi, ecx); + __ mov(edi, eax); + } else { + __ EnterExitFrame( + arg_stack_space, save_doubles(), + is_builtin_exit() ? StackFrame::BUILTIN_EXIT : StackFrame::EXIT); + } + + // ebx: pointer to C function (C callee-saved) + // ebp: frame pointer (restored after C call) + // esp: stack pointer (restored after C call) + // edi: number of arguments including receiver (C callee-saved) + // esi: pointer to the first argument (C callee-saved) + + // Result returned in eax, or eax+edx if result size is 2. + + // Check stack alignment. + if (FLAG_debug_code) { + __ CheckStackAlignment(); + } + // Call C function. + if (result_size() <= 2) { + __ mov(Operand(esp, 0 * kPointerSize), edi); // argc. + __ mov(Operand(esp, 1 * kPointerSize), esi); // argv. + __ mov(Operand(esp, 2 * kPointerSize), + Immediate(ExternalReference::isolate_address(isolate()))); + } else { + DCHECK_EQ(3, result_size()); + // Pass a pointer to the result location as the first argument. + __ lea(eax, Operand(esp, 4 * kPointerSize)); + __ mov(Operand(esp, 0 * kPointerSize), eax); + __ mov(Operand(esp, 1 * kPointerSize), edi); // argc. + __ mov(Operand(esp, 2 * kPointerSize), esi); // argv. + __ mov(Operand(esp, 3 * kPointerSize), + Immediate(ExternalReference::isolate_address(isolate()))); + } + __ call(ebx); + + if (result_size() > 2) { + DCHECK_EQ(3, result_size()); +#ifndef _WIN32 + // Restore the "hidden" argument on the stack which was popped by caller. + __ sub(esp, Immediate(kPointerSize)); +#endif + // Read result values stored on stack. Result is stored above the arguments. + __ mov(kReturnRegister0, Operand(esp, 4 * kPointerSize)); + __ mov(kReturnRegister1, Operand(esp, 5 * kPointerSize)); + __ mov(kReturnRegister2, Operand(esp, 6 * kPointerSize)); + } + // Result is in eax, edx:eax or edi:edx:eax - do not destroy these registers! + + // Check result for exception sentinel. + Label exception_returned; + __ cmp(eax, isolate()->factory()->exception()); + __ j(equal, &exception_returned); + + // Check that there is no pending exception, otherwise we + // should have returned the exception sentinel. + if (FLAG_debug_code) { + __ push(edx); + __ mov(edx, Immediate(isolate()->factory()->the_hole_value())); + Label okay; + ExternalReference pending_exception_address( + IsolateAddressId::kPendingExceptionAddress, isolate()); + __ cmp(edx, Operand::StaticVariable(pending_exception_address)); + // Cannot use check here as it attempts to generate call into runtime. + __ j(equal, &okay, Label::kNear); + __ int3(); + __ bind(&okay); + __ pop(edx); + } + + // Exit the JavaScript to C++ exit frame. + __ LeaveExitFrame(save_doubles(), !argv_in_register()); + __ ret(0); + + // Handling of exception. + __ bind(&exception_returned); + + ExternalReference pending_handler_context_address( + IsolateAddressId::kPendingHandlerContextAddress, isolate()); + ExternalReference pending_handler_code_address( + IsolateAddressId::kPendingHandlerCodeAddress, isolate()); + ExternalReference pending_handler_offset_address( + IsolateAddressId::kPendingHandlerOffsetAddress, isolate()); + ExternalReference pending_handler_fp_address( + IsolateAddressId::kPendingHandlerFPAddress, isolate()); + ExternalReference pending_handler_sp_address( + IsolateAddressId::kPendingHandlerSPAddress, isolate()); + + // Ask the runtime for help to determine the handler. This will set eax to + // contain the current pending exception, don't clobber it. + ExternalReference find_handler(Runtime::kUnwindAndFindExceptionHandler, + isolate()); + { + FrameScope scope(masm, StackFrame::MANUAL); + __ PrepareCallCFunction(3, eax); + __ mov(Operand(esp, 0 * kPointerSize), Immediate(0)); // argc. + __ mov(Operand(esp, 1 * kPointerSize), Immediate(0)); // argv. + __ mov(Operand(esp, 2 * kPointerSize), + Immediate(ExternalReference::isolate_address(isolate()))); + __ CallCFunction(find_handler, 3); + } + + // Retrieve the handler context, SP and FP. + __ mov(esi, Operand::StaticVariable(pending_handler_context_address)); + __ mov(esp, Operand::StaticVariable(pending_handler_sp_address)); + __ mov(ebp, Operand::StaticVariable(pending_handler_fp_address)); + + // If the handler is a JS frame, restore the context to the frame. Note that + // the context will be set to (esi == 0) for non-JS frames. + Label skip; + __ test(esi, esi); + __ j(zero, &skip, Label::kNear); + __ mov(Operand(ebp, StandardFrameConstants::kContextOffset), esi); + __ bind(&skip); + + // Compute the handler entry address and jump to it. + __ mov(edi, Operand::StaticVariable(pending_handler_code_address)); + __ mov(edx, Operand::StaticVariable(pending_handler_offset_address)); + // Check whether it's a turbofanned exception handler code before jump to it. + Label not_turbo; + __ push(eax); + __ mov(eax, Operand(edi, Code::kKindSpecificFlags1Offset - kHeapObjectTag)); + __ and_(eax, Immediate(1 << Code::kIsTurbofannedBit)); + __ j(zero, ¬_turbo); + __ fninit(); + __ fld1(); + __ bind(¬_turbo); + __ pop(eax); + __ lea(edi, FieldOperand(edi, edx, times_1, Code::kHeaderSize)); + __ jmp(edi); +} + + +void JSEntryStub::Generate(MacroAssembler* masm) { + Label invoke, handler_entry, exit; + Label not_outermost_js, not_outermost_js_2; + + ProfileEntryHookStub::MaybeCallEntryHook(masm); + + // Set up frame. + __ push(ebp); + __ mov(ebp, esp); + + // Push marker in two places. + int marker = type(); + __ push(Immediate(Smi::FromInt(marker))); // marker + ExternalReference context_address(IsolateAddressId::kContextAddress, + isolate()); + __ push(Operand::StaticVariable(context_address)); // context + // Save callee-saved registers (C calling conventions). + __ push(edi); + __ push(esi); + __ push(ebx); + + // Save copies of the top frame descriptor on the stack. + ExternalReference c_entry_fp(IsolateAddressId::kCEntryFPAddress, isolate()); + __ push(Operand::StaticVariable(c_entry_fp)); + + // If this is the outermost JS call, set js_entry_sp value. + ExternalReference js_entry_sp(IsolateAddressId::kJSEntrySPAddress, isolate()); + __ cmp(Operand::StaticVariable(js_entry_sp), Immediate(0)); + __ j(not_equal, ¬_outermost_js, Label::kNear); + __ mov(Operand::StaticVariable(js_entry_sp), ebp); + __ push(Immediate(Smi::FromInt(StackFrame::OUTERMOST_JSENTRY_FRAME))); + __ jmp(&invoke, Label::kNear); + __ bind(¬_outermost_js); + __ push(Immediate(Smi::FromInt(StackFrame::INNER_JSENTRY_FRAME))); + + // Jump to a faked try block that does the invoke, with a faked catch + // block that sets the pending exception. + __ jmp(&invoke); + __ bind(&handler_entry); + handler_offset_ = handler_entry.pos(); + // Caught exception: Store result (exception) in the pending exception + // field in the JSEnv and return a failure sentinel. + ExternalReference pending_exception( + IsolateAddressId::kPendingExceptionAddress, isolate()); + __ mov(Operand::StaticVariable(pending_exception), eax); + __ mov(eax, Immediate(isolate()->factory()->exception())); + __ jmp(&exit); + + // Invoke: Link this frame into the handler chain. + __ bind(&invoke); + __ PushStackHandler(); + + // Fake a receiver (NULL). + __ push(Immediate(0)); // receiver + + // Invoke the function by calling through JS entry trampoline builtin and + // pop the faked function when we return. Notice that we cannot store a + // reference to the trampoline code directly in this stub, because the + // builtin stubs may not have been generated yet. + if (type() == StackFrame::ENTRY_CONSTRUCT) { + ExternalReference construct_entry(Builtins::kJSConstructEntryTrampoline, + isolate()); + __ mov(edx, Immediate(construct_entry)); + } else { + ExternalReference entry(Builtins::kJSEntryTrampoline, isolate()); + __ mov(edx, Immediate(entry)); + } + __ mov(edx, Operand(edx, 0)); // deref address + __ lea(edx, FieldOperand(edx, Code::kHeaderSize)); + __ call(edx); + + // Unlink this frame from the handler chain. + __ PopStackHandler(); + + __ bind(&exit); + // Check if the current stack frame is marked as the outermost JS frame. + __ pop(ebx); + __ cmp(ebx, Immediate(Smi::FromInt(StackFrame::OUTERMOST_JSENTRY_FRAME))); + __ j(not_equal, ¬_outermost_js_2); + __ mov(Operand::StaticVariable(js_entry_sp), Immediate(0)); + __ bind(¬_outermost_js_2); + + // Restore the top frame descriptor from the stack. + __ pop(Operand::StaticVariable( + ExternalReference(IsolateAddressId::kCEntryFPAddress, isolate()))); + + // Restore callee-saved registers (C calling conventions). + __ pop(ebx); + __ pop(esi); + __ pop(edi); + __ add(esp, Immediate(2 * kPointerSize)); // remove markers + + // Restore frame pointer and return. + __ pop(ebp); + __ ret(0); +} + + +// ------------------------------------------------------------------------- +// StringCharCodeAtGenerator + +void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) { + // If the receiver is a smi trigger the non-string case. + if (check_mode_ == RECEIVER_IS_UNKNOWN) { + __ JumpIfSmi(object_, receiver_not_string_); + + // Fetch the instance type of the receiver into result register. + __ mov(result_, FieldOperand(object_, HeapObject::kMapOffset)); + __ movzx_b(result_, FieldOperand(result_, Map::kInstanceTypeOffset)); + // If the receiver is not a string trigger the non-string case. + __ test(result_, Immediate(kIsNotStringMask)); + __ j(not_zero, receiver_not_string_); + } + + // If the index is non-smi trigger the non-smi case. + __ JumpIfNotSmi(index_, &index_not_smi_); + __ bind(&got_smi_index_); + + // Check for index out of range. + __ cmp(index_, FieldOperand(object_, String::kLengthOffset)); + __ j(above_equal, index_out_of_range_); + + __ SmiUntag(index_); + + Factory* factory = masm->isolate()->factory(); + StringCharLoadGenerator::Generate( + masm, factory, object_, index_, result_, &call_runtime_); + + __ SmiTag(result_); + __ bind(&exit_); +} + + +void StringCharCodeAtGenerator::GenerateSlow( + MacroAssembler* masm, EmbedMode embed_mode, + const RuntimeCallHelper& call_helper) { + __ Abort(kUnexpectedFallthroughToCharCodeAtSlowCase); + + // Index is not a smi. + __ bind(&index_not_smi_); + // If index is a heap number, try converting it to an integer. + __ CheckMap(index_, + masm->isolate()->factory()->heap_number_map(), + index_not_number_, + DONT_DO_SMI_CHECK); + call_helper.BeforeCall(masm); + if (embed_mode == PART_OF_IC_HANDLER) { + __ push(LoadWithVectorDescriptor::VectorRegister()); + __ push(LoadDescriptor::SlotRegister()); + } + __ push(object_); + __ push(index_); // Consumed by runtime conversion function. + __ CallRuntime(Runtime::kNumberToSmi); + if (!index_.is(eax)) { + // Save the conversion result before the pop instructions below + // have a chance to overwrite it. + __ mov(index_, eax); + } + __ pop(object_); + if (embed_mode == PART_OF_IC_HANDLER) { + __ pop(LoadDescriptor::SlotRegister()); + __ pop(LoadWithVectorDescriptor::VectorRegister()); + } + // Reload the instance type. + __ mov(result_, FieldOperand(object_, HeapObject::kMapOffset)); + __ movzx_b(result_, FieldOperand(result_, Map::kInstanceTypeOffset)); + call_helper.AfterCall(masm); + // If index is still not a smi, it must be out of range. + STATIC_ASSERT(kSmiTag == 0); + __ JumpIfNotSmi(index_, index_out_of_range_); + // Otherwise, return to the fast path. + __ jmp(&got_smi_index_); + + // Call runtime. We get here when the receiver is a string and the + // index is a number, but the code of getting the actual character + // is too complex (e.g., when the string needs to be flattened). + __ bind(&call_runtime_); + call_helper.BeforeCall(masm); + __ push(object_); + __ SmiTag(index_); + __ push(index_); + __ CallRuntime(Runtime::kStringCharCodeAtRT); + if (!result_.is(eax)) { + __ mov(result_, eax); + } + call_helper.AfterCall(masm); + __ jmp(&exit_); + + __ Abort(kUnexpectedFallthroughFromCharCodeAtSlowCase); +} + +void StringHelper::GenerateFlatOneByteStringEquals(MacroAssembler* masm, + Register left, + Register right, + Register scratch1, + Register scratch2) { + Register length = scratch1; + + // Compare lengths. + Label strings_not_equal, check_zero_length; + __ mov(length, FieldOperand(left, String::kLengthOffset)); + __ cmp(length, FieldOperand(right, String::kLengthOffset)); + __ j(equal, &check_zero_length, Label::kNear); + __ bind(&strings_not_equal); + __ Move(eax, Immediate(Smi::FromInt(NOT_EQUAL))); + __ ret(0); + + // Check if the length is zero. + Label compare_chars; + __ bind(&check_zero_length); + STATIC_ASSERT(kSmiTag == 0); + __ test(length, length); + __ j(not_zero, &compare_chars, Label::kNear); + __ Move(eax, Immediate(Smi::FromInt(EQUAL))); + __ ret(0); + + // Compare characters. + __ bind(&compare_chars); + GenerateOneByteCharsCompareLoop(masm, left, right, length, scratch2, + &strings_not_equal, Label::kNear); + + // Characters are equal. + __ Move(eax, Immediate(Smi::FromInt(EQUAL))); + __ ret(0); +} + + +void StringHelper::GenerateCompareFlatOneByteStrings( + MacroAssembler* masm, Register left, Register right, Register scratch1, + Register scratch2, Register scratch3) { + Counters* counters = masm->isolate()->counters(); + __ IncrementCounter(counters->string_compare_native(), 1); + + // Find minimum length. + Label left_shorter; + __ mov(scratch1, FieldOperand(left, String::kLengthOffset)); + __ mov(scratch3, scratch1); + __ sub(scratch3, FieldOperand(right, String::kLengthOffset)); + + Register length_delta = scratch3; + + __ j(less_equal, &left_shorter, Label::kNear); + // Right string is shorter. Change scratch1 to be length of right string. + __ sub(scratch1, length_delta); + __ bind(&left_shorter); + + Register min_length = scratch1; + + // If either length is zero, just compare lengths. + Label compare_lengths; + __ test(min_length, min_length); + __ j(zero, &compare_lengths, Label::kNear); + + // Compare characters. + Label result_not_equal; + GenerateOneByteCharsCompareLoop(masm, left, right, min_length, scratch2, + &result_not_equal, Label::kNear); + + // Compare lengths - strings up to min-length are equal. + __ bind(&compare_lengths); + __ test(length_delta, length_delta); + Label length_not_equal; + __ j(not_zero, &length_not_equal, Label::kNear); + + // Result is EQUAL. + STATIC_ASSERT(EQUAL == 0); + STATIC_ASSERT(kSmiTag == 0); + __ Move(eax, Immediate(Smi::FromInt(EQUAL))); + __ ret(0); + + Label result_greater; + Label result_less; + __ bind(&length_not_equal); + __ j(greater, &result_greater, Label::kNear); + __ jmp(&result_less, Label::kNear); + __ bind(&result_not_equal); + __ j(above, &result_greater, Label::kNear); + __ bind(&result_less); + + // Result is LESS. + __ Move(eax, Immediate(Smi::FromInt(LESS))); + __ ret(0); + + // Result is GREATER. + __ bind(&result_greater); + __ Move(eax, Immediate(Smi::FromInt(GREATER))); + __ ret(0); +} + + +void StringHelper::GenerateOneByteCharsCompareLoop( + MacroAssembler* masm, Register left, Register right, Register length, + Register scratch, Label* chars_not_equal, + Label::Distance chars_not_equal_near) { + // Change index to run from -length to -1 by adding length to string + // start. This means that loop ends when index reaches zero, which + // doesn't need an additional compare. + __ SmiUntag(length); + __ lea(left, + FieldOperand(left, length, times_1, SeqOneByteString::kHeaderSize)); + __ lea(right, + FieldOperand(right, length, times_1, SeqOneByteString::kHeaderSize)); + __ neg(length); + Register index = length; // index = -length; + + // Compare loop. + Label loop; + __ bind(&loop); + __ mov_b(scratch, Operand(left, index, times_1, 0)); + __ cmpb(scratch, Operand(right, index, times_1, 0)); + __ j(not_equal, chars_not_equal, chars_not_equal_near); + __ inc(index); + __ j(not_zero, &loop); +} + + +void CompareICStub::GenerateBooleans(MacroAssembler* masm) { + DCHECK_EQ(CompareICState::BOOLEAN, state()); + Label miss; + Label::Distance const miss_distance = + masm->emit_debug_code() ? Label::kFar : Label::kNear; + + __ JumpIfSmi(edx, &miss, miss_distance); + __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset)); + __ JumpIfSmi(eax, &miss, miss_distance); + __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset)); + __ JumpIfNotRoot(ecx, Heap::kBooleanMapRootIndex, &miss, miss_distance); + __ JumpIfNotRoot(ebx, Heap::kBooleanMapRootIndex, &miss, miss_distance); + if (!Token::IsEqualityOp(op())) { + __ mov(eax, FieldOperand(eax, Oddball::kToNumberOffset)); + __ AssertSmi(eax); + __ mov(edx, FieldOperand(edx, Oddball::kToNumberOffset)); + __ AssertSmi(edx); + __ xchg(eax, edx); + } + __ sub(eax, edx); + __ Ret(); + + __ bind(&miss); + GenerateMiss(masm); +} + + +void CompareICStub::GenerateSmis(MacroAssembler* masm) { + DCHECK(state() == CompareICState::SMI); + Label miss; + __ mov(ecx, edx); + __ or_(ecx, eax); + __ JumpIfNotSmi(ecx, &miss, Label::kNear); + + if (GetCondition() == equal) { + // For equality we do not care about the sign of the result. + __ sub(eax, edx); + } else { + Label done; + __ sub(edx, eax); + __ j(no_overflow, &done, Label::kNear); + // Correct sign of result in case of overflow. + __ not_(edx); + __ bind(&done); + __ mov(eax, edx); + } + __ ret(0); + + __ bind(&miss); + GenerateMiss(masm); +} + + +void CompareICStub::GenerateNumbers(MacroAssembler* masm) { + DCHECK(state() == CompareICState::NUMBER); + + Label generic_stub, check_left; + Label unordered, maybe_undefined1, maybe_undefined2; + Label miss; + + if (left() == CompareICState::SMI) { + __ JumpIfNotSmi(edx, &miss); + } + if (right() == CompareICState::SMI) { + __ JumpIfNotSmi(eax, &miss); + } + + // Inlining the double comparison and falling back to the general compare + // stub if NaN is involved or SSE2 or CMOV is unsupported. + __ JumpIfSmi(eax, &check_left, Label::kNear); + __ cmp(FieldOperand(eax, HeapObject::kMapOffset), + isolate()->factory()->heap_number_map()); + __ j(not_equal, &maybe_undefined1, Label::kNear); + + __ bind(&check_left); + __ JumpIfSmi(edx, &generic_stub, Label::kNear); + __ cmp(FieldOperand(edx, HeapObject::kMapOffset), + isolate()->factory()->heap_number_map()); + __ j(not_equal, &maybe_undefined2, Label::kNear); + + __ bind(&unordered); + __ bind(&generic_stub); + CompareICStub stub(isolate(), op(), CompareICState::GENERIC, + CompareICState::GENERIC, CompareICState::GENERIC); + __ jmp(stub.GetCode(), RelocInfo::CODE_TARGET); + + __ bind(&maybe_undefined1); + if (Token::IsOrderedRelationalCompareOp(op())) { + __ cmp(eax, Immediate(isolate()->factory()->undefined_value())); + __ j(not_equal, &miss); + __ JumpIfSmi(edx, &unordered); + __ CmpObjectType(edx, HEAP_NUMBER_TYPE, ecx); + __ j(not_equal, &maybe_undefined2, Label::kNear); + __ jmp(&unordered); + } + + __ bind(&maybe_undefined2); + if (Token::IsOrderedRelationalCompareOp(op())) { + __ cmp(edx, Immediate(isolate()->factory()->undefined_value())); + __ j(equal, &unordered); + } + + __ bind(&miss); + GenerateMiss(masm); +} + + +void CompareICStub::GenerateInternalizedStrings(MacroAssembler* masm) { + DCHECK(state() == CompareICState::INTERNALIZED_STRING); + DCHECK(GetCondition() == equal); + + // Registers containing left and right operands respectively. + Register left = edx; + Register right = eax; + Register tmp1 = ecx; + Register tmp2 = ebx; + + // Check that both operands are heap objects. + Label miss; + __ mov(tmp1, left); + STATIC_ASSERT(kSmiTag == 0); + __ and_(tmp1, right); + __ JumpIfSmi(tmp1, &miss, Label::kNear); + + // Check that both operands are internalized strings. + __ mov(tmp1, FieldOperand(left, HeapObject::kMapOffset)); + __ mov(tmp2, FieldOperand(right, HeapObject::kMapOffset)); + __ movzx_b(tmp1, FieldOperand(tmp1, Map::kInstanceTypeOffset)); + __ movzx_b(tmp2, FieldOperand(tmp2, Map::kInstanceTypeOffset)); + STATIC_ASSERT(kInternalizedTag == 0 && kStringTag == 0); + __ or_(tmp1, tmp2); + __ test(tmp1, Immediate(kIsNotStringMask | kIsNotInternalizedMask)); + __ j(not_zero, &miss, Label::kNear); + + // Internalized strings are compared by identity. + Label done; + __ cmp(left, right); + // Make sure eax is non-zero. At this point input operands are + // guaranteed to be non-zero. + DCHECK(right.is(eax)); + __ j(not_equal, &done, Label::kNear); + STATIC_ASSERT(EQUAL == 0); + STATIC_ASSERT(kSmiTag == 0); + __ Move(eax, Immediate(Smi::FromInt(EQUAL))); + __ bind(&done); + __ ret(0); + + __ bind(&miss); + GenerateMiss(masm); +} + + +void CompareICStub::GenerateUniqueNames(MacroAssembler* masm) { + DCHECK(state() == CompareICState::UNIQUE_NAME); + DCHECK(GetCondition() == equal); + + // Registers containing left and right operands respectively. + Register left = edx; + Register right = eax; + Register tmp1 = ecx; + Register tmp2 = ebx; + + // Check that both operands are heap objects. + Label miss; + __ mov(tmp1, left); + STATIC_ASSERT(kSmiTag == 0); + __ and_(tmp1, right); + __ JumpIfSmi(tmp1, &miss, Label::kNear); + + // Check that both operands are unique names. This leaves the instance + // types loaded in tmp1 and tmp2. + __ mov(tmp1, FieldOperand(left, HeapObject::kMapOffset)); + __ mov(tmp2, FieldOperand(right, HeapObject::kMapOffset)); + __ movzx_b(tmp1, FieldOperand(tmp1, Map::kInstanceTypeOffset)); + __ movzx_b(tmp2, FieldOperand(tmp2, Map::kInstanceTypeOffset)); + + __ JumpIfNotUniqueNameInstanceType(tmp1, &miss, Label::kNear); + __ JumpIfNotUniqueNameInstanceType(tmp2, &miss, Label::kNear); + + // Unique names are compared by identity. + Label done; + __ cmp(left, right); + // Make sure eax is non-zero. At this point input operands are + // guaranteed to be non-zero. + DCHECK(right.is(eax)); + __ j(not_equal, &done, Label::kNear); + STATIC_ASSERT(EQUAL == 0); + STATIC_ASSERT(kSmiTag == 0); + __ Move(eax, Immediate(Smi::FromInt(EQUAL))); + __ bind(&done); + __ ret(0); + + __ bind(&miss); + GenerateMiss(masm); +} + + +void CompareICStub::GenerateStrings(MacroAssembler* masm) { + DCHECK(state() == CompareICState::STRING); + Label miss; + + bool equality = Token::IsEqualityOp(op()); + + // Registers containing left and right operands respectively. + Register left = edx; + Register right = eax; + Register tmp1 = ecx; + Register tmp2 = ebx; + Register tmp3 = edi; + + // Check that both operands are heap objects. + __ mov(tmp1, left); + STATIC_ASSERT(kSmiTag == 0); + __ and_(tmp1, right); + __ JumpIfSmi(tmp1, &miss); + + // Check that both operands are strings. This leaves the instance + // types loaded in tmp1 and tmp2. + __ mov(tmp1, FieldOperand(left, HeapObject::kMapOffset)); + __ mov(tmp2, FieldOperand(right, HeapObject::kMapOffset)); + __ movzx_b(tmp1, FieldOperand(tmp1, Map::kInstanceTypeOffset)); + __ movzx_b(tmp2, FieldOperand(tmp2, Map::kInstanceTypeOffset)); + __ mov(tmp3, tmp1); + STATIC_ASSERT(kNotStringTag != 0); + __ or_(tmp3, tmp2); + __ test(tmp3, Immediate(kIsNotStringMask)); + __ j(not_zero, &miss); + + // Fast check for identical strings. + Label not_same; + __ cmp(left, right); + __ j(not_equal, ¬_same, Label::kNear); + STATIC_ASSERT(EQUAL == 0); + STATIC_ASSERT(kSmiTag == 0); + __ Move(eax, Immediate(Smi::FromInt(EQUAL))); + __ ret(0); + + // Handle not identical strings. + __ bind(¬_same); + + // Check that both strings are internalized. If they are, we're done + // because we already know they are not identical. But in the case of + // non-equality compare, we still need to determine the order. We + // also know they are both strings. + if (equality) { + Label do_compare; + STATIC_ASSERT(kInternalizedTag == 0); + __ or_(tmp1, tmp2); + __ test(tmp1, Immediate(kIsNotInternalizedMask)); + __ j(not_zero, &do_compare, Label::kNear); + // Make sure eax is non-zero. At this point input operands are + // guaranteed to be non-zero. + DCHECK(right.is(eax)); + __ ret(0); + __ bind(&do_compare); + } + + // Check that both strings are sequential one-byte. + Label runtime; + __ JumpIfNotBothSequentialOneByteStrings(left, right, tmp1, tmp2, &runtime); + + // Compare flat one byte strings. Returns when done. + if (equality) { + StringHelper::GenerateFlatOneByteStringEquals(masm, left, right, tmp1, + tmp2); + } else { + StringHelper::GenerateCompareFlatOneByteStrings(masm, left, right, tmp1, + tmp2, tmp3); + } + + // Handle more complex cases in runtime. + __ bind(&runtime); + if (equality) { + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ Push(left); + __ Push(right); + __ CallRuntime(Runtime::kStringEqual); + } + __ sub(eax, Immediate(masm->isolate()->factory()->true_value())); + __ Ret(); + } else { + __ pop(tmp1); // Return address. + __ push(left); + __ push(right); + __ push(tmp1); + __ TailCallRuntime(Runtime::kStringCompare); + } + + __ bind(&miss); + GenerateMiss(masm); +} + + +void CompareICStub::GenerateReceivers(MacroAssembler* masm) { + DCHECK_EQ(CompareICState::RECEIVER, state()); + Label miss; + __ mov(ecx, edx); + __ and_(ecx, eax); + __ JumpIfSmi(ecx, &miss, Label::kNear); + + STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE); + __ CmpObjectType(eax, FIRST_JS_RECEIVER_TYPE, ecx); + __ j(below, &miss, Label::kNear); + __ CmpObjectType(edx, FIRST_JS_RECEIVER_TYPE, ecx); + __ j(below, &miss, Label::kNear); + + DCHECK_EQ(equal, GetCondition()); + __ sub(eax, edx); + __ ret(0); + + __ bind(&miss); + GenerateMiss(masm); +} + + +void CompareICStub::GenerateKnownReceivers(MacroAssembler* masm) { + Label miss; + Handle cell = Map::WeakCellForMap(known_map_); + __ mov(ecx, edx); + __ and_(ecx, eax); + __ JumpIfSmi(ecx, &miss, Label::kNear); + + __ GetWeakValue(edi, cell); + __ cmp(edi, FieldOperand(eax, HeapObject::kMapOffset)); + __ j(not_equal, &miss, Label::kNear); + __ cmp(edi, FieldOperand(edx, HeapObject::kMapOffset)); + __ j(not_equal, &miss, Label::kNear); + + if (Token::IsEqualityOp(op())) { + __ sub(eax, edx); + __ ret(0); + } else { + __ PopReturnAddressTo(ecx); + __ Push(edx); + __ Push(eax); + __ Push(Immediate(Smi::FromInt(NegativeComparisonResult(GetCondition())))); + __ PushReturnAddressFrom(ecx); + __ TailCallRuntime(Runtime::kCompare); + } + + __ bind(&miss); + GenerateMiss(masm); +} + + +void CompareICStub::GenerateMiss(MacroAssembler* masm) { + { + // Call the runtime system in a fresh internal frame. + FrameScope scope(masm, StackFrame::INTERNAL); + __ push(edx); // Preserve edx and eax. + __ push(eax); + __ push(edx); // And also use them as the arguments. + __ push(eax); + __ push(Immediate(Smi::FromInt(op()))); + __ CallRuntime(Runtime::kCompareIC_Miss); + // Compute the entry point of the rewritten stub. + __ lea(edi, FieldOperand(eax, Code::kHeaderSize)); + __ pop(eax); + __ pop(edx); + } + + // Do a tail call to the rewritten stub. + __ jmp(edi); +} + + +// Helper function used to check that the dictionary doesn't contain +// the property. This function may return false negatives, so miss_label +// must always call a backup property check that is complete. +// This function is safe to call if the receiver has fast properties. +// Name must be a unique name and receiver must be a heap object. +void NameDictionaryLookupStub::GenerateNegativeLookup(MacroAssembler* masm, + Label* miss, + Label* done, + Register properties, + Handle name, + Register r0) { + DCHECK(name->IsUniqueName()); + + // If names of slots in range from 1 to kProbes - 1 for the hash value are + // not equal to the name and kProbes-th slot is not used (its name is the + // undefined value), it guarantees the hash table doesn't contain the + // property. It's true even if some slots represent deleted properties + // (their names are the hole value). + for (int i = 0; i < kInlinedProbes; i++) { + // Compute the masked index: (hash + i + i * i) & mask. + Register index = r0; + // Capacity is smi 2^n. + __ mov(index, FieldOperand(properties, kCapacityOffset)); + __ dec(index); + __ and_(index, + Immediate(Smi::FromInt(name->Hash() + + NameDictionary::GetProbeOffset(i)))); + + // Scale the index by multiplying by the entry size. + STATIC_ASSERT(NameDictionary::kEntrySize == 3); + __ lea(index, Operand(index, index, times_2, 0)); // index *= 3. + Register entity_name = r0; + // Having undefined at this place means the name is not contained. + STATIC_ASSERT(kSmiTagSize == 1); + __ mov(entity_name, Operand(properties, index, times_half_pointer_size, + kElementsStartOffset - kHeapObjectTag)); + __ cmp(entity_name, masm->isolate()->factory()->undefined_value()); + __ j(equal, done); + + // Stop if found the property. + __ cmp(entity_name, Handle(name)); + __ j(equal, miss); + + Label good; + // Check for the hole and skip. + __ cmp(entity_name, masm->isolate()->factory()->the_hole_value()); + __ j(equal, &good, Label::kNear); + + // Check if the entry name is not a unique name. + __ mov(entity_name, FieldOperand(entity_name, HeapObject::kMapOffset)); + __ JumpIfNotUniqueNameInstanceType( + FieldOperand(entity_name, Map::kInstanceTypeOffset), miss); + __ bind(&good); + } + + NameDictionaryLookupStub stub(masm->isolate(), properties, r0, r0, + NEGATIVE_LOOKUP); + __ push(Immediate(name)); + __ push(Immediate(name->Hash())); + __ CallStub(&stub); + __ test(r0, r0); + __ j(not_zero, miss); + __ jmp(done); +} + +void NameDictionaryLookupStub::Generate(MacroAssembler* masm) { + // This stub overrides SometimesSetsUpAFrame() to return false. That means + // we cannot call anything that could cause a GC from this stub. + // Stack frame on entry: + // esp[0 * kPointerSize]: return address. + // esp[1 * kPointerSize]: key's hash. + // esp[2 * kPointerSize]: key. + // Registers: + // dictionary_: NameDictionary to probe. + // result_: used as scratch. + // index_: will hold an index of entry if lookup is successful. + // might alias with result_. + // Returns: + // result_ is zero if lookup failed, non zero otherwise. + + Label in_dictionary, maybe_in_dictionary, not_in_dictionary; + + Register scratch = result(); + + __ mov(scratch, FieldOperand(dictionary(), kCapacityOffset)); + __ dec(scratch); + __ SmiUntag(scratch); + __ push(scratch); + + // If names of slots in range from 1 to kProbes - 1 for the hash value are + // not equal to the name and kProbes-th slot is not used (its name is the + // undefined value), it guarantees the hash table doesn't contain the + // property. It's true even if some slots represent deleted properties + // (their names are the null value). + for (int i = kInlinedProbes; i < kTotalProbes; i++) { + // Compute the masked index: (hash + i + i * i) & mask. + __ mov(scratch, Operand(esp, 2 * kPointerSize)); + if (i > 0) { + __ add(scratch, Immediate(NameDictionary::GetProbeOffset(i))); + } + __ and_(scratch, Operand(esp, 0)); + + // Scale the index by multiplying by the entry size. + STATIC_ASSERT(NameDictionary::kEntrySize == 3); + __ lea(index(), Operand(scratch, scratch, times_2, 0)); // index *= 3. + + // Having undefined at this place means the name is not contained. + STATIC_ASSERT(kSmiTagSize == 1); + __ mov(scratch, Operand(dictionary(), index(), times_pointer_size, + kElementsStartOffset - kHeapObjectTag)); + __ cmp(scratch, isolate()->factory()->undefined_value()); + __ j(equal, ¬_in_dictionary); + + // Stop if found the property. + __ cmp(scratch, Operand(esp, 3 * kPointerSize)); + __ j(equal, &in_dictionary); + + if (i != kTotalProbes - 1 && mode() == NEGATIVE_LOOKUP) { + // If we hit a key that is not a unique name during negative + // lookup we have to bailout as this key might be equal to the + // key we are looking for. + + // Check if the entry name is not a unique name. + __ mov(scratch, FieldOperand(scratch, HeapObject::kMapOffset)); + __ JumpIfNotUniqueNameInstanceType( + FieldOperand(scratch, Map::kInstanceTypeOffset), + &maybe_in_dictionary); + } + } + + __ bind(&maybe_in_dictionary); + // If we are doing negative lookup then probing failure should be + // treated as a lookup success. For positive lookup probing failure + // should be treated as lookup failure. + if (mode() == POSITIVE_LOOKUP) { + __ mov(result(), Immediate(0)); + __ Drop(1); + __ ret(2 * kPointerSize); + } + + __ bind(&in_dictionary); + __ mov(result(), Immediate(1)); + __ Drop(1); + __ ret(2 * kPointerSize); + + __ bind(¬_in_dictionary); + __ mov(result(), Immediate(0)); + __ Drop(1); + __ ret(2 * kPointerSize); +} + + +void StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime( + Isolate* isolate) { + StoreBufferOverflowStub stub(isolate, kDontSaveFPRegs); + stub.GetCode(); + StoreBufferOverflowStub stub2(isolate, kSaveFPRegs); + stub2.GetCode(); +} + + +// Takes the input in 3 registers: address_ value_ and object_. A pointer to +// the value has just been written into the object, now this stub makes sure +// we keep the GC informed. The word in the object where the value has been +// written is in the address register. +void RecordWriteStub::Generate(MacroAssembler* masm) { + Label skip_to_incremental_noncompacting; + Label skip_to_incremental_compacting; + + // The first two instructions are generated with labels so as to get the + // offset fixed up correctly by the bind(Label*) call. We patch it back and + // forth between a compare instructions (a nop in this position) and the + // real branch when we start and stop incremental heap marking. + __ jmp(&skip_to_incremental_noncompacting, Label::kNear); + __ jmp(&skip_to_incremental_compacting, Label::kFar); + + if (remembered_set_action() == EMIT_REMEMBERED_SET) { + __ RememberedSetHelper(object(), address(), value(), save_fp_regs_mode(), + MacroAssembler::kReturnAtEnd); + } else { + __ ret(0); + } + + __ bind(&skip_to_incremental_noncompacting); + GenerateIncremental(masm, INCREMENTAL); + + __ bind(&skip_to_incremental_compacting); + GenerateIncremental(masm, INCREMENTAL_COMPACTION); + + // Initial mode of the stub is expected to be STORE_BUFFER_ONLY. + // Will be checked in IncrementalMarking::ActivateGeneratedStub. + masm->set_byte_at(0, kTwoByteNopInstruction); + masm->set_byte_at(2, kFiveByteNopInstruction); +} + + +void RecordWriteStub::GenerateIncremental(MacroAssembler* masm, Mode mode) { + regs_.Save(masm); + + if (remembered_set_action() == EMIT_REMEMBERED_SET) { + Label dont_need_remembered_set; + + __ mov(regs_.scratch0(), Operand(regs_.address(), 0)); + __ JumpIfNotInNewSpace(regs_.scratch0(), // Value. + regs_.scratch0(), + &dont_need_remembered_set); + + __ JumpIfInNewSpace(regs_.object(), regs_.scratch0(), + &dont_need_remembered_set); + + // First notify the incremental marker if necessary, then update the + // remembered set. + CheckNeedsToInformIncrementalMarker( + masm, + kUpdateRememberedSetOnNoNeedToInformIncrementalMarker, + mode); + InformIncrementalMarker(masm); + regs_.Restore(masm); + __ RememberedSetHelper(object(), address(), value(), save_fp_regs_mode(), + MacroAssembler::kReturnAtEnd); + + __ bind(&dont_need_remembered_set); + } + + CheckNeedsToInformIncrementalMarker( + masm, + kReturnOnNoNeedToInformIncrementalMarker, + mode); + InformIncrementalMarker(masm); + regs_.Restore(masm); + __ ret(0); +} + + +void RecordWriteStub::InformIncrementalMarker(MacroAssembler* masm) { + regs_.SaveCallerSaveRegisters(masm, save_fp_regs_mode()); + int argument_count = 3; + __ PrepareCallCFunction(argument_count, regs_.scratch0()); + __ mov(Operand(esp, 0 * kPointerSize), regs_.object()); + __ mov(Operand(esp, 1 * kPointerSize), regs_.address()); // Slot. + __ mov(Operand(esp, 2 * kPointerSize), + Immediate(ExternalReference::isolate_address(isolate()))); + + AllowExternalCallThatCantCauseGC scope(masm); + __ CallCFunction( + ExternalReference::incremental_marking_record_write_function(isolate()), + argument_count); + + regs_.RestoreCallerSaveRegisters(masm, save_fp_regs_mode()); +} + + +void RecordWriteStub::CheckNeedsToInformIncrementalMarker( + MacroAssembler* masm, + OnNoNeedToInformIncrementalMarker on_no_need, + Mode mode) { + Label need_incremental, need_incremental_pop_object; + +#ifndef V8_CONCURRENT_MARKING + Label object_is_black; + // Let's look at the color of the object: If it is not black we don't have + // to inform the incremental marker. + __ JumpIfBlack(regs_.object(), + regs_.scratch0(), + regs_.scratch1(), + &object_is_black, + Label::kNear); + + regs_.Restore(masm); + if (on_no_need == kUpdateRememberedSetOnNoNeedToInformIncrementalMarker) { + __ RememberedSetHelper(object(), address(), value(), save_fp_regs_mode(), + MacroAssembler::kReturnAtEnd); + } else { + __ ret(0); + } + + __ bind(&object_is_black); +#endif + + // Get the value from the slot. + __ mov(regs_.scratch0(), Operand(regs_.address(), 0)); + + if (mode == INCREMENTAL_COMPACTION) { + Label ensure_not_white; + + __ CheckPageFlag(regs_.scratch0(), // Contains value. + regs_.scratch1(), // Scratch. + MemoryChunk::kEvacuationCandidateMask, + zero, + &ensure_not_white, + Label::kNear); + + __ CheckPageFlag(regs_.object(), + regs_.scratch1(), // Scratch. + MemoryChunk::kSkipEvacuationSlotsRecordingMask, + not_zero, + &ensure_not_white, + Label::kNear); + + __ jmp(&need_incremental); + + __ bind(&ensure_not_white); + } + + // We need an extra register for this, so we push the object register + // temporarily. + __ push(regs_.object()); + __ JumpIfWhite(regs_.scratch0(), // The value. + regs_.scratch1(), // Scratch. + regs_.object(), // Scratch. + &need_incremental_pop_object, Label::kNear); + __ pop(regs_.object()); + + regs_.Restore(masm); + if (on_no_need == kUpdateRememberedSetOnNoNeedToInformIncrementalMarker) { + __ RememberedSetHelper(object(), address(), value(), save_fp_regs_mode(), + MacroAssembler::kReturnAtEnd); + } else { + __ ret(0); + } + + __ bind(&need_incremental_pop_object); + __ pop(regs_.object()); + + __ bind(&need_incremental); + + // Fall through when we need to inform the incremental marker. +} + + +void ProfileEntryHookStub::MaybeCallEntryHookDelayed(TurboAssembler* tasm, + Zone* zone) { + if (tasm->isolate()->function_entry_hook() != NULL) { + tasm->CallStubDelayed(new (zone) ProfileEntryHookStub(nullptr)); + } +} + +void ProfileEntryHookStub::MaybeCallEntryHook(MacroAssembler* masm) { + if (masm->isolate()->function_entry_hook() != NULL) { + ProfileEntryHookStub stub(masm->isolate()); + masm->CallStub(&stub); + } +} + +void ProfileEntryHookStub::Generate(MacroAssembler* masm) { + // Save volatile registers. + const int kNumSavedRegisters = 3; + __ push(eax); + __ push(ecx); + __ push(edx); + + // Calculate and push the original stack pointer. + __ lea(eax, Operand(esp, (kNumSavedRegisters + 1) * kPointerSize)); + __ push(eax); + + // Retrieve our return address and use it to calculate the calling + // function's address. + __ mov(eax, Operand(esp, (kNumSavedRegisters + 1) * kPointerSize)); + __ sub(eax, Immediate(Assembler::kCallInstructionLength)); + __ push(eax); + + // Call the entry hook. + DCHECK(isolate()->function_entry_hook() != NULL); + __ call(FUNCTION_ADDR(isolate()->function_entry_hook()), + RelocInfo::RUNTIME_ENTRY); + __ add(esp, Immediate(2 * kPointerSize)); + + // Restore ecx. + __ pop(edx); + __ pop(ecx); + __ pop(eax); + + __ ret(0); +} + +template +static void CreateArrayDispatch(MacroAssembler* masm, + AllocationSiteOverrideMode mode) { + if (mode == DISABLE_ALLOCATION_SITES) { + T stub(masm->isolate(), GetInitialFastElementsKind(), mode); + __ TailCallStub(&stub); + } else if (mode == DONT_OVERRIDE) { + int last_index = + GetSequenceIndexFromFastElementsKind(TERMINAL_FAST_ELEMENTS_KIND); + for (int i = 0; i <= last_index; ++i) { + Label next; + ElementsKind kind = GetFastElementsKindFromSequenceIndex(i); + __ cmp(edx, kind); + __ j(not_equal, &next); + T stub(masm->isolate(), kind); + __ TailCallStub(&stub); + __ bind(&next); + } + + // If we reached this point there is a problem. + __ Abort(kUnexpectedElementsKindInArrayConstructor); + } else { + UNREACHABLE(); + } +} + +static void CreateArrayDispatchOneArgument(MacroAssembler* masm, + AllocationSiteOverrideMode mode) { + // ebx - allocation site (if mode != DISABLE_ALLOCATION_SITES) + // edx - kind (if mode != DISABLE_ALLOCATION_SITES) + // eax - number of arguments + // edi - constructor? + // esp[0] - return address + // esp[4] - last argument + STATIC_ASSERT(PACKED_SMI_ELEMENTS == 0); + STATIC_ASSERT(HOLEY_SMI_ELEMENTS == 1); + STATIC_ASSERT(PACKED_ELEMENTS == 2); + STATIC_ASSERT(HOLEY_ELEMENTS == 3); + STATIC_ASSERT(PACKED_DOUBLE_ELEMENTS == 4); + STATIC_ASSERT(HOLEY_DOUBLE_ELEMENTS == 5); + + if (mode == DISABLE_ALLOCATION_SITES) { + ElementsKind initial = GetInitialFastElementsKind(); + ElementsKind holey_initial = GetHoleyElementsKind(initial); + + ArraySingleArgumentConstructorStub stub_holey( + masm->isolate(), holey_initial, DISABLE_ALLOCATION_SITES); + __ TailCallStub(&stub_holey); + } else if (mode == DONT_OVERRIDE) { + // is the low bit set? If so, we are holey and that is good. + Label normal_sequence; + __ test_b(edx, Immediate(1)); + __ j(not_zero, &normal_sequence); + + // We are going to create a holey array, but our kind is non-holey. + // Fix kind and retry. + __ inc(edx); + + if (FLAG_debug_code) { + Handle allocation_site_map = + masm->isolate()->factory()->allocation_site_map(); + __ cmp(FieldOperand(ebx, 0), Immediate(allocation_site_map)); + __ Assert(equal, kExpectedAllocationSite); + } + + // Save the resulting elements kind in type info. We can't just store r3 + // in the AllocationSite::transition_info field because elements kind is + // restricted to a portion of the field...upper bits need to be left alone. + STATIC_ASSERT(AllocationSite::ElementsKindBits::kShift == 0); + __ add( + FieldOperand(ebx, AllocationSite::kTransitionInfoOrBoilerplateOffset), + Immediate(Smi::FromInt(kFastElementsKindPackedToHoley))); + + __ bind(&normal_sequence); + int last_index = + GetSequenceIndexFromFastElementsKind(TERMINAL_FAST_ELEMENTS_KIND); + for (int i = 0; i <= last_index; ++i) { + Label next; + ElementsKind kind = GetFastElementsKindFromSequenceIndex(i); + __ cmp(edx, kind); + __ j(not_equal, &next); + ArraySingleArgumentConstructorStub stub(masm->isolate(), kind); + __ TailCallStub(&stub); + __ bind(&next); + } + + // If we reached this point there is a problem. + __ Abort(kUnexpectedElementsKindInArrayConstructor); + } else { + UNREACHABLE(); + } +} + +template +static void ArrayConstructorStubAheadOfTimeHelper(Isolate* isolate) { + int to_index = + GetSequenceIndexFromFastElementsKind(TERMINAL_FAST_ELEMENTS_KIND); + for (int i = 0; i <= to_index; ++i) { + ElementsKind kind = GetFastElementsKindFromSequenceIndex(i); + T stub(isolate, kind); + stub.GetCode(); + if (AllocationSite::ShouldTrack(kind)) { + T stub1(isolate, kind, DISABLE_ALLOCATION_SITES); + stub1.GetCode(); + } + } +} + +void CommonArrayConstructorStub::GenerateStubsAheadOfTime(Isolate* isolate) { + ArrayConstructorStubAheadOfTimeHelper( + isolate); + ArrayConstructorStubAheadOfTimeHelper( + isolate); + ArrayNArgumentsConstructorStub stub(isolate); + stub.GetCode(); + + ElementsKind kinds[2] = {PACKED_ELEMENTS, HOLEY_ELEMENTS}; + for (int i = 0; i < 2; i++) { + // For internal arrays we only need a few things + InternalArrayNoArgumentConstructorStub stubh1(isolate, kinds[i]); + stubh1.GetCode(); + InternalArraySingleArgumentConstructorStub stubh2(isolate, kinds[i]); + stubh2.GetCode(); + } +} + +void ArrayConstructorStub::GenerateDispatchToArrayStub( + MacroAssembler* masm, AllocationSiteOverrideMode mode) { + Label not_zero_case, not_one_case; + __ test(eax, eax); + __ j(not_zero, ¬_zero_case); + CreateArrayDispatch(masm, mode); + + __ bind(¬_zero_case); + __ cmp(eax, 1); + __ j(greater, ¬_one_case); + CreateArrayDispatchOneArgument(masm, mode); + + __ bind(¬_one_case); + ArrayNArgumentsConstructorStub stub(masm->isolate()); + __ TailCallStub(&stub); +} + +void ArrayConstructorStub::Generate(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- eax : argc (only if argument_count() is ANY or MORE_THAN_ONE) + // -- ebx : AllocationSite or undefined + // -- edi : constructor + // -- edx : Original constructor + // -- esp[0] : return address + // -- esp[4] : last argument + // ----------------------------------- + if (FLAG_debug_code) { + // The array construct code is only set for the global and natives + // builtin Array functions which always have maps. + + // Initial map for the builtin Array function should be a map. + __ mov(ecx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset)); + // Will both indicate a NULL and a Smi. + __ test(ecx, Immediate(kSmiTagMask)); + __ Assert(not_zero, kUnexpectedInitialMapForArrayFunction); + __ CmpObjectType(ecx, MAP_TYPE, ecx); + __ Assert(equal, kUnexpectedInitialMapForArrayFunction); + + // We should either have undefined in ebx or a valid AllocationSite + __ AssertUndefinedOrAllocationSite(ebx); + } + + Label subclassing; + + // Enter the context of the Array function. + __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); + + __ cmp(edx, edi); + __ j(not_equal, &subclassing); + + Label no_info; + // If the feedback vector is the undefined value call an array constructor + // that doesn't use AllocationSites. + __ cmp(ebx, isolate()->factory()->undefined_value()); + __ j(equal, &no_info); + + // Only look at the lower 16 bits of the transition info. + __ mov(edx, + FieldOperand(ebx, AllocationSite::kTransitionInfoOrBoilerplateOffset)); + __ SmiUntag(edx); + STATIC_ASSERT(AllocationSite::ElementsKindBits::kShift == 0); + __ and_(edx, Immediate(AllocationSite::ElementsKindBits::kMask)); + GenerateDispatchToArrayStub(masm, DONT_OVERRIDE); + + __ bind(&no_info); + GenerateDispatchToArrayStub(masm, DISABLE_ALLOCATION_SITES); + + // Subclassing. + __ bind(&subclassing); + __ mov(Operand(esp, eax, times_pointer_size, kPointerSize), edi); + __ add(eax, Immediate(3)); + __ PopReturnAddressTo(ecx); + __ Push(edx); + __ Push(ebx); + __ PushReturnAddressFrom(ecx); + __ JumpToExternalReference(ExternalReference(Runtime::kNewArray, isolate())); +} + +void InternalArrayConstructorStub::GenerateCase(MacroAssembler* masm, + ElementsKind kind) { + Label not_zero_case, not_one_case; + Label normal_sequence; + + __ test(eax, eax); + __ j(not_zero, ¬_zero_case); + InternalArrayNoArgumentConstructorStub stub0(isolate(), kind); + __ TailCallStub(&stub0); + + __ bind(¬_zero_case); + __ cmp(eax, 1); + __ j(greater, ¬_one_case); + + if (IsFastPackedElementsKind(kind)) { + // We might need to create a holey array + // look at the first argument + __ mov(ecx, Operand(esp, kPointerSize)); + __ test(ecx, ecx); + __ j(zero, &normal_sequence); + + InternalArraySingleArgumentConstructorStub stub1_holey( + isolate(), GetHoleyElementsKind(kind)); + __ TailCallStub(&stub1_holey); + } + + __ bind(&normal_sequence); + InternalArraySingleArgumentConstructorStub stub1(isolate(), kind); + __ TailCallStub(&stub1); + + __ bind(¬_one_case); + ArrayNArgumentsConstructorStub stubN(isolate()); + __ TailCallStub(&stubN); +} + +void InternalArrayConstructorStub::Generate(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- eax : argc + // -- edi : constructor + // -- esp[0] : return address + // -- esp[4] : last argument + // ----------------------------------- + + if (FLAG_debug_code) { + // The array construct code is only set for the global and natives + // builtin Array functions which always have maps. + + // Initial map for the builtin Array function should be a map. + __ mov(ecx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset)); + // Will both indicate a NULL and a Smi. + __ test(ecx, Immediate(kSmiTagMask)); + __ Assert(not_zero, kUnexpectedInitialMapForArrayFunction); + __ CmpObjectType(ecx, MAP_TYPE, ecx); + __ Assert(equal, kUnexpectedInitialMapForArrayFunction); + } + + // Figure out the right elements kind + __ mov(ecx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset)); + + // Load the map's "bit field 2" into |result|. We only need the first byte, + // but the following masking takes care of that anyway. + __ mov(ecx, FieldOperand(ecx, Map::kBitField2Offset)); + // Retrieve elements_kind from bit field 2. + __ DecodeField(ecx); + + if (FLAG_debug_code) { + Label done; + __ cmp(ecx, Immediate(PACKED_ELEMENTS)); + __ j(equal, &done); + __ cmp(ecx, Immediate(HOLEY_ELEMENTS)); + __ Assert(equal, kInvalidElementsKindForInternalArrayOrInternalPackedArray); + __ bind(&done); + } + + Label fast_elements_case; + __ cmp(ecx, Immediate(PACKED_ELEMENTS)); + __ j(equal, &fast_elements_case); + GenerateCase(masm, HOLEY_ELEMENTS); + + __ bind(&fast_elements_case); + GenerateCase(masm, PACKED_ELEMENTS); +} + +// Generates an Operand for saving parameters after PrepareCallApiFunction. +static Operand ApiParameterOperand(int index) { + return Operand(esp, index * kPointerSize); +} + + +// Prepares stack to put arguments (aligns and so on). Reserves +// space for return value if needed (assumes the return value is a handle). +// Arguments must be stored in ApiParameterOperand(0), ApiParameterOperand(1) +// etc. Saves context (esi). If space was reserved for return value then +// stores the pointer to the reserved slot into esi. +static void PrepareCallApiFunction(MacroAssembler* masm, int argc) { + __ EnterApiExitFrame(argc); + if (__ emit_debug_code()) { + __ mov(esi, Immediate(bit_cast(kZapValue))); + } +} + + +// Calls an API function. Allocates HandleScope, extracts returned value +// from handle and propagates exceptions. Clobbers ebx, edi and +// caller-save registers. Restores context. On return removes +// stack_space * kPointerSize (GCed). +static void CallApiFunctionAndReturn(MacroAssembler* masm, + Register function_address, + ExternalReference thunk_ref, + Operand thunk_last_arg, int stack_space, + Operand* stack_space_operand, + Operand return_value_operand, + Operand* context_restore_operand) { + Isolate* isolate = masm->isolate(); + + ExternalReference next_address = + ExternalReference::handle_scope_next_address(isolate); + ExternalReference limit_address = + ExternalReference::handle_scope_limit_address(isolate); + ExternalReference level_address = + ExternalReference::handle_scope_level_address(isolate); + + DCHECK(edx.is(function_address)); + // Allocate HandleScope in callee-save registers. + __ mov(ebx, Operand::StaticVariable(next_address)); + __ mov(edi, Operand::StaticVariable(limit_address)); + __ add(Operand::StaticVariable(level_address), Immediate(1)); + + if (FLAG_log_timer_events) { + FrameScope frame(masm, StackFrame::MANUAL); + __ PushSafepointRegisters(); + __ PrepareCallCFunction(1, eax); + __ mov(Operand(esp, 0), + Immediate(ExternalReference::isolate_address(isolate))); + __ CallCFunction(ExternalReference::log_enter_external_function(isolate), + 1); + __ PopSafepointRegisters(); + } + + + Label profiler_disabled; + Label end_profiler_check; + __ mov(eax, Immediate(ExternalReference::is_profiling_address(isolate))); + __ cmpb(Operand(eax, 0), Immediate(0)); + __ j(zero, &profiler_disabled); + + // Additional parameter is the address of the actual getter function. + __ mov(thunk_last_arg, function_address); + // Call the api function. + __ mov(eax, Immediate(thunk_ref)); + __ call(eax); + __ jmp(&end_profiler_check); + + __ bind(&profiler_disabled); + // Call the api function. + __ call(function_address); + __ bind(&end_profiler_check); + + if (FLAG_log_timer_events) { + FrameScope frame(masm, StackFrame::MANUAL); + __ PushSafepointRegisters(); + __ PrepareCallCFunction(1, eax); + __ mov(Operand(esp, 0), + Immediate(ExternalReference::isolate_address(isolate))); + __ CallCFunction(ExternalReference::log_leave_external_function(isolate), + 1); + __ PopSafepointRegisters(); + } + + Label prologue; + // Load the value from ReturnValue + __ mov(eax, return_value_operand); + + Label promote_scheduled_exception; + Label delete_allocated_handles; + Label leave_exit_frame; + + __ bind(&prologue); + // No more valid handles (the result handle was the last one). Restore + // previous handle scope. + __ mov(Operand::StaticVariable(next_address), ebx); + __ sub(Operand::StaticVariable(level_address), Immediate(1)); + __ Assert(above_equal, kInvalidHandleScopeLevel); + __ cmp(edi, Operand::StaticVariable(limit_address)); + __ j(not_equal, &delete_allocated_handles); + + // Leave the API exit frame. + __ bind(&leave_exit_frame); + bool restore_context = context_restore_operand != NULL; + if (restore_context) { + __ mov(esi, *context_restore_operand); + } + if (stack_space_operand != nullptr) { + __ mov(ebx, *stack_space_operand); + } + __ LeaveApiExitFrame(!restore_context); + + // Check if the function scheduled an exception. + ExternalReference scheduled_exception_address = + ExternalReference::scheduled_exception_address(isolate); + __ cmp(Operand::StaticVariable(scheduled_exception_address), + Immediate(isolate->factory()->the_hole_value())); + __ j(not_equal, &promote_scheduled_exception); + +#if DEBUG + // Check if the function returned a valid JavaScript value. + Label ok; + Register return_value = eax; + Register map = ecx; + + __ JumpIfSmi(return_value, &ok, Label::kNear); + __ mov(map, FieldOperand(return_value, HeapObject::kMapOffset)); + + __ CmpInstanceType(map, LAST_NAME_TYPE); + __ j(below_equal, &ok, Label::kNear); + + __ CmpInstanceType(map, FIRST_JS_RECEIVER_TYPE); + __ j(above_equal, &ok, Label::kNear); + + __ cmp(map, isolate->factory()->heap_number_map()); + __ j(equal, &ok, Label::kNear); + + __ cmp(return_value, isolate->factory()->undefined_value()); + __ j(equal, &ok, Label::kNear); + + __ cmp(return_value, isolate->factory()->true_value()); + __ j(equal, &ok, Label::kNear); + + __ cmp(return_value, isolate->factory()->false_value()); + __ j(equal, &ok, Label::kNear); + + __ cmp(return_value, isolate->factory()->null_value()); + __ j(equal, &ok, Label::kNear); + + __ Abort(kAPICallReturnedInvalidObject); + + __ bind(&ok); +#endif + + if (stack_space_operand != nullptr) { + DCHECK_EQ(0, stack_space); + __ pop(ecx); + __ add(esp, ebx); + __ jmp(ecx); + } else { + __ ret(stack_space * kPointerSize); + } + + // Re-throw by promoting a scheduled exception. + __ bind(&promote_scheduled_exception); + __ TailCallRuntime(Runtime::kPromoteScheduledException); + + // HandleScope limit has changed. Delete allocated extensions. + ExternalReference delete_extensions = + ExternalReference::delete_handle_scope_extensions(isolate); + __ bind(&delete_allocated_handles); + __ mov(Operand::StaticVariable(limit_address), edi); + __ mov(edi, eax); + __ mov(Operand(esp, 0), + Immediate(ExternalReference::isolate_address(isolate))); + __ mov(eax, Immediate(delete_extensions)); + __ call(eax); + __ mov(eax, edi); + __ jmp(&leave_exit_frame); +} + +void CallApiCallbackStub::Generate(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- edi : callee + // -- ebx : call_data + // -- ecx : holder + // -- edx : api_function_address + // -- esi : context + // -- + // -- esp[0] : return address + // -- esp[4] : last argument + // -- ... + // -- esp[argc * 4] : first argument + // -- esp[(argc + 1) * 4] : receiver + // ----------------------------------- + + Register callee = edi; + Register call_data = ebx; + Register holder = ecx; + Register api_function_address = edx; + Register context = esi; + Register return_address = eax; + + typedef FunctionCallbackArguments FCA; + + STATIC_ASSERT(FCA::kContextSaveIndex == 6); + STATIC_ASSERT(FCA::kCalleeIndex == 5); + STATIC_ASSERT(FCA::kDataIndex == 4); + STATIC_ASSERT(FCA::kReturnValueOffset == 3); + STATIC_ASSERT(FCA::kReturnValueDefaultValueIndex == 2); + STATIC_ASSERT(FCA::kIsolateIndex == 1); + STATIC_ASSERT(FCA::kHolderIndex == 0); + STATIC_ASSERT(FCA::kNewTargetIndex == 7); + STATIC_ASSERT(FCA::kArgsLength == 8); + + __ pop(return_address); + + // new target + __ PushRoot(Heap::kUndefinedValueRootIndex); + + // context save. + __ push(context); + + // callee + __ push(callee); + + // call data + __ push(call_data); + + // return value + __ push(Immediate(masm->isolate()->factory()->undefined_value())); + // return value default + __ push(Immediate(masm->isolate()->factory()->undefined_value())); + // isolate + __ push(Immediate(reinterpret_cast(masm->isolate()))); + // holder + __ push(holder); + + Register scratch = call_data; + __ mov(scratch, esp); + + // push return address + __ push(return_address); + + if (!is_lazy()) { + // load context from callee + __ mov(context, FieldOperand(callee, JSFunction::kContextOffset)); + } + + // API function gets reference to the v8::Arguments. If CPU profiler + // is enabled wrapper function will be called and we need to pass + // address of the callback as additional parameter, always allocate + // space for it. + const int kApiArgc = 1 + 1; + + // Allocate the v8::Arguments structure in the arguments' space since + // it's not controlled by GC. + const int kApiStackSpace = 3; + + PrepareCallApiFunction(masm, kApiArgc + kApiStackSpace); + + // FunctionCallbackInfo::implicit_args_. + __ mov(ApiParameterOperand(2), scratch); + __ add(scratch, Immediate((argc() + FCA::kArgsLength - 1) * kPointerSize)); + // FunctionCallbackInfo::values_. + __ mov(ApiParameterOperand(3), scratch); + // FunctionCallbackInfo::length_. + __ Move(ApiParameterOperand(4), Immediate(argc())); + + // v8::InvocationCallback's argument. + __ lea(scratch, ApiParameterOperand(2)); + __ mov(ApiParameterOperand(0), scratch); + + ExternalReference thunk_ref = + ExternalReference::invoke_function_callback(masm->isolate()); + + Operand context_restore_operand(ebp, + (2 + FCA::kContextSaveIndex) * kPointerSize); + // Stores return the first js argument + int return_value_offset = 0; + if (is_store()) { + return_value_offset = 2 + FCA::kArgsLength; + } else { + return_value_offset = 2 + FCA::kReturnValueOffset; + } + Operand return_value_operand(ebp, return_value_offset * kPointerSize); + int stack_space = 0; + Operand length_operand = ApiParameterOperand(4); + Operand* stack_space_operand = &length_operand; + stack_space = argc() + FCA::kArgsLength + 1; + stack_space_operand = nullptr; + CallApiFunctionAndReturn(masm, api_function_address, thunk_ref, + ApiParameterOperand(1), stack_space, + stack_space_operand, return_value_operand, + &context_restore_operand); +} + + +void CallApiGetterStub::Generate(MacroAssembler* masm) { + // Build v8::PropertyCallbackInfo::args_ array on the stack and push property + // name below the exit frame to make GC aware of them. + STATIC_ASSERT(PropertyCallbackArguments::kShouldThrowOnErrorIndex == 0); + STATIC_ASSERT(PropertyCallbackArguments::kHolderIndex == 1); + STATIC_ASSERT(PropertyCallbackArguments::kIsolateIndex == 2); + STATIC_ASSERT(PropertyCallbackArguments::kReturnValueDefaultValueIndex == 3); + STATIC_ASSERT(PropertyCallbackArguments::kReturnValueOffset == 4); + STATIC_ASSERT(PropertyCallbackArguments::kDataIndex == 5); + STATIC_ASSERT(PropertyCallbackArguments::kThisIndex == 6); + STATIC_ASSERT(PropertyCallbackArguments::kArgsLength == 7); + + Register receiver = ApiGetterDescriptor::ReceiverRegister(); + Register holder = ApiGetterDescriptor::HolderRegister(); + Register callback = ApiGetterDescriptor::CallbackRegister(); + Register scratch = ebx; + DCHECK(!AreAliased(receiver, holder, callback, scratch)); + + __ pop(scratch); // Pop return address to extend the frame. + __ push(receiver); + __ push(FieldOperand(callback, AccessorInfo::kDataOffset)); + __ PushRoot(Heap::kUndefinedValueRootIndex); // ReturnValue + // ReturnValue default value + __ PushRoot(Heap::kUndefinedValueRootIndex); + __ push(Immediate(ExternalReference::isolate_address(isolate()))); + __ push(holder); + __ push(Immediate(Smi::kZero)); // should_throw_on_error -> false + __ push(FieldOperand(callback, AccessorInfo::kNameOffset)); + __ push(scratch); // Restore return address. + + // v8::PropertyCallbackInfo::args_ array and name handle. + const int kStackUnwindSpace = PropertyCallbackArguments::kArgsLength + 1; + + // Allocate v8::PropertyCallbackInfo object, arguments for callback and + // space for optional callback address parameter (in case CPU profiler is + // active) in non-GCed stack space. + const int kApiArgc = 3 + 1; + + // Load address of v8::PropertyAccessorInfo::args_ array. + __ lea(scratch, Operand(esp, 2 * kPointerSize)); + + PrepareCallApiFunction(masm, kApiArgc); + // Create v8::PropertyCallbackInfo object on the stack and initialize + // it's args_ field. + Operand info_object = ApiParameterOperand(3); + __ mov(info_object, scratch); + + // Name as handle. + __ sub(scratch, Immediate(kPointerSize)); + __ mov(ApiParameterOperand(0), scratch); + // Arguments pointer. + __ lea(scratch, info_object); + __ mov(ApiParameterOperand(1), scratch); + // Reserve space for optional callback address parameter. + Operand thunk_last_arg = ApiParameterOperand(2); + + ExternalReference thunk_ref = + ExternalReference::invoke_accessor_getter_callback(isolate()); + + __ mov(scratch, FieldOperand(callback, AccessorInfo::kJsGetterOffset)); + Register function_address = edx; + __ mov(function_address, + FieldOperand(scratch, Foreign::kForeignAddressOffset)); + // +3 is to skip prolog, return address and name handle. + Operand return_value_operand( + ebp, (PropertyCallbackArguments::kReturnValueOffset + 3) * kPointerSize); + CallApiFunctionAndReturn(masm, function_address, thunk_ref, thunk_last_arg, + kStackUnwindSpace, nullptr, return_value_operand, + NULL); +} + +#undef __ + +} // namespace internal +} // namespace v8 + +#endif // V8_TARGET_ARCH_X87 diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/x87/code-stubs-x87.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/x87/code-stubs-x87.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/x87/code-stubs-x87.h 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/x87/code-stubs-x87.h 2017-12-25 17:42:57.221465559 +0100 @@ -0,0 +1,351 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_X87_CODE_STUBS_X87_H_ +#define V8_X87_CODE_STUBS_X87_H_ + +namespace v8 { +namespace internal { + + +void ArrayNativeCode(MacroAssembler* masm, + bool construct_call, + Label* call_generic_code); + + +class StringHelper : public AllStatic { + public: + // Compares two flat one byte strings and returns result in eax. + static void GenerateCompareFlatOneByteStrings(MacroAssembler* masm, + Register left, Register right, + Register scratch1, + Register scratch2, + Register scratch3); + + // Compares two flat one byte strings for equality and returns result in eax. + static void GenerateFlatOneByteStringEquals(MacroAssembler* masm, + Register left, Register right, + Register scratch1, + Register scratch2); + + private: + static void GenerateOneByteCharsCompareLoop( + MacroAssembler* masm, Register left, Register right, Register length, + Register scratch, Label* chars_not_equal, + Label::Distance chars_not_equal_near = Label::kFar); + + DISALLOW_IMPLICIT_CONSTRUCTORS(StringHelper); +}; + + +class NameDictionaryLookupStub: public PlatformCodeStub { + public: + enum LookupMode { POSITIVE_LOOKUP, NEGATIVE_LOOKUP }; + + NameDictionaryLookupStub(Isolate* isolate, Register dictionary, + Register result, Register index, LookupMode mode) + : PlatformCodeStub(isolate) { + minor_key_ = DictionaryBits::encode(dictionary.code()) | + ResultBits::encode(result.code()) | + IndexBits::encode(index.code()) | LookupModeBits::encode(mode); + } + + static void GenerateNegativeLookup(MacroAssembler* masm, + Label* miss, + Label* done, + Register properties, + Handle name, + Register r0); + + bool SometimesSetsUpAFrame() override { return false; } + + private: + static const int kInlinedProbes = 4; + static const int kTotalProbes = 20; + + static const int kCapacityOffset = + NameDictionary::kHeaderSize + + NameDictionary::kCapacityIndex * kPointerSize; + + static const int kElementsStartOffset = + NameDictionary::kHeaderSize + + NameDictionary::kElementsStartIndex * kPointerSize; + + Register dictionary() const { + return Register::from_code(DictionaryBits::decode(minor_key_)); + } + + Register result() const { + return Register::from_code(ResultBits::decode(minor_key_)); + } + + Register index() const { + return Register::from_code(IndexBits::decode(minor_key_)); + } + + LookupMode mode() const { return LookupModeBits::decode(minor_key_); } + + class DictionaryBits: public BitField {}; + class ResultBits: public BitField {}; + class IndexBits: public BitField {}; + class LookupModeBits: public BitField {}; + + DEFINE_NULL_CALL_INTERFACE_DESCRIPTOR(); + DEFINE_PLATFORM_CODE_STUB(NameDictionaryLookup, PlatformCodeStub); +}; + + +class RecordWriteStub: public PlatformCodeStub { + public: + RecordWriteStub(Isolate* isolate, Register object, Register value, + Register address, RememberedSetAction remembered_set_action, + SaveFPRegsMode fp_mode) + : PlatformCodeStub(isolate), + regs_(object, // An input reg. + address, // An input reg. + value) { // One scratch reg. + minor_key_ = ObjectBits::encode(object.code()) | + ValueBits::encode(value.code()) | + AddressBits::encode(address.code()) | + RememberedSetActionBits::encode(remembered_set_action) | + SaveFPRegsModeBits::encode(fp_mode); + } + + RecordWriteStub(uint32_t key, Isolate* isolate) + : PlatformCodeStub(key, isolate), regs_(object(), address(), value()) {} + + enum Mode { + STORE_BUFFER_ONLY, + INCREMENTAL, + INCREMENTAL_COMPACTION + }; + + bool SometimesSetsUpAFrame() override { return false; } + + static const byte kTwoByteNopInstruction = 0x3c; // Cmpb al, #imm8. + static const byte kTwoByteJumpInstruction = 0xeb; // Jmp #imm8. + + static const byte kFiveByteNopInstruction = 0x3d; // Cmpl eax, #imm32. + static const byte kFiveByteJumpInstruction = 0xe9; // Jmp #imm32. + + static Mode GetMode(Code* stub) { + byte first_instruction = stub->instruction_start()[0]; + byte second_instruction = stub->instruction_start()[2]; + + if (first_instruction == kTwoByteJumpInstruction) { + return INCREMENTAL; + } + + DCHECK(first_instruction == kTwoByteNopInstruction); + + if (second_instruction == kFiveByteJumpInstruction) { + return INCREMENTAL_COMPACTION; + } + + DCHECK(second_instruction == kFiveByteNopInstruction); + + return STORE_BUFFER_ONLY; + } + + static void Patch(Code* stub, Mode mode) { + switch (mode) { + case STORE_BUFFER_ONLY: + DCHECK(GetMode(stub) == INCREMENTAL || + GetMode(stub) == INCREMENTAL_COMPACTION); + stub->instruction_start()[0] = kTwoByteNopInstruction; + stub->instruction_start()[2] = kFiveByteNopInstruction; + break; + case INCREMENTAL: + DCHECK(GetMode(stub) == STORE_BUFFER_ONLY); + stub->instruction_start()[0] = kTwoByteJumpInstruction; + break; + case INCREMENTAL_COMPACTION: + DCHECK(GetMode(stub) == STORE_BUFFER_ONLY); + stub->instruction_start()[0] = kTwoByteNopInstruction; + stub->instruction_start()[2] = kFiveByteJumpInstruction; + break; + } + DCHECK(GetMode(stub) == mode); + Assembler::FlushICache(stub->GetIsolate(), stub->instruction_start(), 7); + } + + DEFINE_NULL_CALL_INTERFACE_DESCRIPTOR(); + + private: + // This is a helper class for freeing up 3 scratch registers, where the third + // is always ecx (needed for shift operations). The input is two registers + // that must be preserved and one scratch register provided by the caller. + class RegisterAllocation { + public: + RegisterAllocation(Register object, + Register address, + Register scratch0) + : object_orig_(object), + address_orig_(address), + scratch0_orig_(scratch0), + object_(object), + address_(address), + scratch0_(scratch0) { + DCHECK(!AreAliased(scratch0, object, address, no_reg)); + scratch1_ = GetRegThatIsNotEcxOr(object_, address_, scratch0_); + if (scratch0.is(ecx)) { + scratch0_ = GetRegThatIsNotEcxOr(object_, address_, scratch1_); + } + if (object.is(ecx)) { + object_ = GetRegThatIsNotEcxOr(address_, scratch0_, scratch1_); + } + if (address.is(ecx)) { + address_ = GetRegThatIsNotEcxOr(object_, scratch0_, scratch1_); + } + DCHECK(!AreAliased(scratch0_, object_, address_, ecx)); + } + + void Save(MacroAssembler* masm) { + DCHECK(!address_orig_.is(object_)); + DCHECK(object_.is(object_orig_) || address_.is(address_orig_)); + DCHECK(!AreAliased(object_, address_, scratch1_, scratch0_)); + DCHECK(!AreAliased(object_orig_, address_, scratch1_, scratch0_)); + DCHECK(!AreAliased(object_, address_orig_, scratch1_, scratch0_)); + // We don't have to save scratch0_orig_ because it was given to us as + // a scratch register. But if we had to switch to a different reg then + // we should save the new scratch0_. + if (!scratch0_.is(scratch0_orig_)) masm->push(scratch0_); + if (!ecx.is(scratch0_orig_) && + !ecx.is(object_orig_) && + !ecx.is(address_orig_)) { + masm->push(ecx); + } + masm->push(scratch1_); + if (!address_.is(address_orig_)) { + masm->push(address_); + masm->mov(address_, address_orig_); + } + if (!object_.is(object_orig_)) { + masm->push(object_); + masm->mov(object_, object_orig_); + } + } + + void Restore(MacroAssembler* masm) { + // These will have been preserved the entire time, so we just need to move + // them back. Only in one case is the orig_ reg different from the plain + // one, since only one of them can alias with ecx. + if (!object_.is(object_orig_)) { + masm->mov(object_orig_, object_); + masm->pop(object_); + } + if (!address_.is(address_orig_)) { + masm->mov(address_orig_, address_); + masm->pop(address_); + } + masm->pop(scratch1_); + if (!ecx.is(scratch0_orig_) && + !ecx.is(object_orig_) && + !ecx.is(address_orig_)) { + masm->pop(ecx); + } + if (!scratch0_.is(scratch0_orig_)) masm->pop(scratch0_); + } + + // If we have to call into C then we need to save and restore all caller- + // saved registers that were not already preserved. The caller saved + // registers are eax, ecx and edx. The three scratch registers (incl. ecx) + // will be restored by other means so we don't bother pushing them here. + void SaveCallerSaveRegisters(MacroAssembler* masm, SaveFPRegsMode mode) { + masm->PushCallerSaved(mode, ecx, scratch0_, scratch1_); + } + + inline void RestoreCallerSaveRegisters(MacroAssembler* masm, + SaveFPRegsMode mode) { + masm->PopCallerSaved(mode, ecx, scratch0_, scratch1_); + } + + inline Register object() { return object_; } + inline Register address() { return address_; } + inline Register scratch0() { return scratch0_; } + inline Register scratch1() { return scratch1_; } + + private: + Register object_orig_; + Register address_orig_; + Register scratch0_orig_; + Register object_; + Register address_; + Register scratch0_; + Register scratch1_; + // Third scratch register is always ecx. + + Register GetRegThatIsNotEcxOr(Register r1, + Register r2, + Register r3) { + for (int i = 0; i < Register::kNumRegisters; i++) { + if (RegisterConfiguration::Crankshaft()->IsAllocatableGeneralCode(i)) { + Register candidate = Register::from_code(i); + if (candidate.is(ecx)) continue; + if (candidate.is(r1)) continue; + if (candidate.is(r2)) continue; + if (candidate.is(r3)) continue; + return candidate; + } + } + UNREACHABLE(); + } + friend class RecordWriteStub; + }; + + enum OnNoNeedToInformIncrementalMarker { + kReturnOnNoNeedToInformIncrementalMarker, + kUpdateRememberedSetOnNoNeedToInformIncrementalMarker + }; + + inline Major MajorKey() const final { return RecordWrite; } + + void Generate(MacroAssembler* masm) override; + void GenerateIncremental(MacroAssembler* masm, Mode mode); + void CheckNeedsToInformIncrementalMarker( + MacroAssembler* masm, + OnNoNeedToInformIncrementalMarker on_no_need, + Mode mode); + void InformIncrementalMarker(MacroAssembler* masm); + + void Activate(Code* code) override { + code->GetHeap()->incremental_marking()->ActivateGeneratedStub(code); + } + + Register object() const { + return Register::from_code(ObjectBits::decode(minor_key_)); + } + + Register value() const { + return Register::from_code(ValueBits::decode(minor_key_)); + } + + Register address() const { + return Register::from_code(AddressBits::decode(minor_key_)); + } + + RememberedSetAction remembered_set_action() const { + return RememberedSetActionBits::decode(minor_key_); + } + + SaveFPRegsMode save_fp_regs_mode() const { + return SaveFPRegsModeBits::decode(minor_key_); + } + + class ObjectBits: public BitField {}; + class ValueBits: public BitField {}; + class AddressBits: public BitField {}; + class RememberedSetActionBits: public BitField {}; + class SaveFPRegsModeBits : public BitField {}; + + RegisterAllocation regs_; + + DISALLOW_COPY_AND_ASSIGN(RecordWriteStub); +}; + + +} // namespace internal +} // namespace v8 + +#endif // V8_X87_CODE_STUBS_X87_H_ diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/x87/cpu-x87.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/x87/cpu-x87.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/x87/cpu-x87.cc 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/x87/cpu-x87.cc 2017-12-25 17:42:57.221465559 +0100 @@ -0,0 +1,43 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// CPU specific code for ia32 independent of OS goes here. + +#ifdef __GNUC__ +#include "src/third_party/valgrind/valgrind.h" +#endif + +#if V8_TARGET_ARCH_X87 + +#include "src/assembler.h" +#include "src/macro-assembler.h" + +namespace v8 { +namespace internal { + +void CpuFeatures::FlushICache(void* start, size_t size) { + // No need to flush the instruction cache on Intel. On Intel instruction + // cache flushing is only necessary when multiple cores running the same + // code simultaneously. V8 (and JavaScript) is single threaded and when code + // is patched on an intel CPU the core performing the patching will have its + // own instruction cache updated automatically. + + // If flushing of the instruction cache becomes necessary Windows has the + // API function FlushInstructionCache. + + // By default, valgrind only checks the stack for writes that might need to + // invalidate already cached translated code. This leads to random + // instability when code patches or moves are sometimes unnoticed. One + // solution is to run valgrind with --smc-check=all, but this comes at a big + // performance cost. We can notify valgrind to invalidate its cache. +#ifdef VALGRIND_DISCARD_TRANSLATIONS + unsigned res = VALGRIND_DISCARD_TRANSLATIONS(start, size); + USE(res); +#endif +} + +} // namespace internal +} // namespace v8 + +#endif // V8_TARGET_ARCH_X87 diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/x87/deoptimizer-x87.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/x87/deoptimizer-x87.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/x87/deoptimizer-x87.cc 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/x87/deoptimizer-x87.cc 2017-12-28 04:55:52.791508679 +0100 @@ -0,0 +1,412 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#if V8_TARGET_ARCH_X87 + +#include "src/codegen.h" +#include "src/deoptimizer.h" +#include "src/full-codegen/full-codegen.h" +#include "src/register-configuration.h" +#include "src/safepoint-table.h" +#include "src/x87/frames-x87.h" + +namespace v8 { +namespace internal { + +const int Deoptimizer::table_entry_size_ = 10; + + +int Deoptimizer::patch_size() { + return Assembler::kCallInstructionLength; +} + + +void Deoptimizer::EnsureRelocSpaceForLazyDeoptimization(Handle code) { + Isolate* isolate = code->GetIsolate(); + HandleScope scope(isolate); + + // Compute the size of relocation information needed for the code + // patching in Deoptimizer::PatchCodeForDeoptimization below. + int min_reloc_size = 0; + int prev_pc_offset = 0; + DeoptimizationInputData* deopt_data = + DeoptimizationInputData::cast(code->deoptimization_data()); + for (int i = 0; i < deopt_data->DeoptCount(); i++) { + int pc_offset = deopt_data->Pc(i)->value(); + if (pc_offset == -1) continue; + pc_offset = pc_offset + 1; // We will encode the pc offset after the call. + DCHECK_GE(pc_offset, prev_pc_offset); + int pc_delta = pc_offset - prev_pc_offset; + // We use RUNTIME_ENTRY reloc info which has a size of 2 bytes + // if encodable with small pc delta encoding and up to 6 bytes + // otherwise. + if (pc_delta <= RelocInfo::kMaxSmallPCDelta) { + min_reloc_size += 2; + } else { + min_reloc_size += 6; + } + prev_pc_offset = pc_offset; + } + + // If the relocation information is not big enough we create a new + // relocation info object that is padded with comments to make it + // big enough for lazy doptimization. + int reloc_length = code->relocation_info()->length(); + if (min_reloc_size > reloc_length) { + int comment_reloc_size = RelocInfo::kMinRelocCommentSize; + // Padding needed. + int min_padding = min_reloc_size - reloc_length; + // Number of comments needed to take up at least that much space. + int additional_comments = + (min_padding + comment_reloc_size - 1) / comment_reloc_size; + // Actual padding size. + int padding = additional_comments * comment_reloc_size; + // Allocate new relocation info and copy old relocation to the end + // of the new relocation info array because relocation info is + // written and read backwards. + Factory* factory = isolate->factory(); + Handle new_reloc = + factory->NewByteArray(reloc_length + padding, TENURED); + MemCopy(new_reloc->GetDataStartAddress() + padding, + code->relocation_info()->GetDataStartAddress(), reloc_length); + // Create a relocation writer to write the comments in the padding + // space. Use position 0 for everything to ensure short encoding. + RelocInfoWriter reloc_info_writer( + new_reloc->GetDataStartAddress() + padding, 0); + intptr_t comment_string + = reinterpret_cast(RelocInfo::kFillerCommentString); + RelocInfo rinfo(0, RelocInfo::COMMENT, comment_string, NULL); + for (int i = 0; i < additional_comments; ++i) { +#ifdef DEBUG + byte* pos_before = reloc_info_writer.pos(); +#endif + reloc_info_writer.Write(&rinfo); + DCHECK(RelocInfo::kMinRelocCommentSize == + pos_before - reloc_info_writer.pos()); + } + // Replace relocation information on the code object. + code->set_relocation_info(*new_reloc); + } +} + + +void Deoptimizer::PatchCodeForDeoptimization(Isolate* isolate, Code* code) { + Address code_start_address = code->instruction_start(); + + // Fail hard and early if we enter this code object again. + byte* pointer = code->FindCodeAgeSequence(); + if (pointer != NULL) { + pointer += kNoCodeAgeSequenceLength; + } else { + pointer = code->instruction_start(); + } + CodePatcher patcher(isolate, pointer, 1); + patcher.masm()->int3(); + + DeoptimizationInputData* data = + DeoptimizationInputData::cast(code->deoptimization_data()); + int osr_offset = data->OsrPcOffset()->value(); + if (osr_offset > 0) { + CodePatcher osr_patcher(isolate, code_start_address + osr_offset, 1); + osr_patcher.masm()->int3(); + } + + // We will overwrite the code's relocation info in-place. Relocation info + // is written backward. The relocation info is the payload of a byte + // array. Later on we will slide this to the start of the byte array and + // create a filler object in the remaining space. + ByteArray* reloc_info = code->relocation_info(); + Address reloc_end_address = reloc_info->address() + reloc_info->Size(); + RelocInfoWriter reloc_info_writer(reloc_end_address, code_start_address); + + // Since the call is a relative encoding, write new + // reloc info. We do not need any of the existing reloc info because the + // existing code will not be used again (we zap it in debug builds). + // + // Emit call to lazy deoptimization at all lazy deopt points. + DeoptimizationInputData* deopt_data = + DeoptimizationInputData::cast(code->deoptimization_data()); +#ifdef DEBUG + Address prev_call_address = NULL; +#endif + // For each LLazyBailout instruction insert a call to the corresponding + // deoptimization entry. + for (int i = 0; i < deopt_data->DeoptCount(); i++) { + if (deopt_data->Pc(i)->value() == -1) continue; + // Patch lazy deoptimization entry. + Address call_address = code_start_address + deopt_data->Pc(i)->value(); + CodePatcher patcher(isolate, call_address, patch_size()); + Address deopt_entry = GetDeoptimizationEntry(isolate, i, LAZY); + patcher.masm()->call(deopt_entry, RelocInfo::NONE32); + // We use RUNTIME_ENTRY for deoptimization bailouts. + RelocInfo rinfo(call_address + 1, // 1 after the call opcode. + RelocInfo::RUNTIME_ENTRY, + reinterpret_cast(deopt_entry), NULL); + reloc_info_writer.Write(&rinfo); + DCHECK_GE(reloc_info_writer.pos(), + reloc_info->address() + ByteArray::kHeaderSize); + DCHECK(prev_call_address == NULL || + call_address >= prev_call_address + patch_size()); + DCHECK(call_address + patch_size() <= code->instruction_end()); +#ifdef DEBUG + prev_call_address = call_address; +#endif + } + + // Move the relocation info to the beginning of the byte array. + const int new_reloc_length = reloc_end_address - reloc_info_writer.pos(); + MemMove(code->relocation_start(), reloc_info_writer.pos(), new_reloc_length); + + // Right trim the relocation info to free up remaining space. + const int delta = reloc_info->length() - new_reloc_length; + if (delta > 0) { + isolate->heap()->RightTrimFixedArray(reloc_info, delta); + } +} + + +#define __ masm()-> + +void Deoptimizer::TableEntryGenerator::Generate() { + GeneratePrologue(); + + // Save all general purpose registers before messing with them. + const int kNumberOfRegisters = Register::kNumRegisters; + + const int kDoubleRegsSize = kDoubleSize * X87Register::kMaxNumRegisters; + + // Reserve space for x87 fp registers. + __ sub(esp, Immediate(kDoubleRegsSize)); + + __ pushad(); + + ExternalReference c_entry_fp_address(IsolateAddressId::kCEntryFPAddress, + isolate()); + __ mov(Operand::StaticVariable(c_entry_fp_address), ebp); + + // GP registers are safe to use now. + // Save used x87 fp registers in correct position of previous reserve space. + Label loop, done; + // Get the layout of x87 stack. + __ sub(esp, Immediate(kPointerSize)); + __ fistp_s(MemOperand(esp, 0)); + __ pop(eax); + // Preserve stack layout in edi + __ mov(edi, eax); + // Get the x87 stack depth, the first 3 bits. + __ mov(ecx, eax); + __ and_(ecx, 0x7); + __ j(zero, &done, Label::kNear); + + __ bind(&loop); + __ shr(eax, 0x3); + __ mov(ebx, eax); + __ and_(ebx, 0x7); // Extract the st_x index into ebx. + // Pop TOS to the correct position. The disp(0x20) is due to pushad. + // The st_i should be saved to (esp + ebx * kDoubleSize + 0x20). + __ fstp_d(Operand(esp, ebx, times_8, 0x20)); + __ dec(ecx); // Decrease stack depth. + __ j(not_zero, &loop, Label::kNear); + __ bind(&done); + + const int kSavedRegistersAreaSize = + kNumberOfRegisters * kPointerSize + kDoubleRegsSize; + + // Get the bailout id from the stack. + __ mov(ebx, Operand(esp, kSavedRegistersAreaSize)); + + // Get the address of the location in the code object + // and compute the fp-to-sp delta in register edx. + __ mov(ecx, Operand(esp, kSavedRegistersAreaSize + 1 * kPointerSize)); + __ lea(edx, Operand(esp, kSavedRegistersAreaSize + 2 * kPointerSize)); + + __ sub(edx, ebp); + __ neg(edx); + + __ push(edi); + // Allocate a new deoptimizer object. + __ PrepareCallCFunction(6, eax); + __ mov(eax, Immediate(0)); + Label context_check; + __ mov(edi, Operand(ebp, CommonFrameConstants::kContextOrFrameTypeOffset)); + __ JumpIfSmi(edi, &context_check); + __ mov(eax, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); + __ bind(&context_check); + __ mov(Operand(esp, 0 * kPointerSize), eax); // Function. + __ mov(Operand(esp, 1 * kPointerSize), Immediate(type())); // Bailout type. + __ mov(Operand(esp, 2 * kPointerSize), ebx); // Bailout id. + __ mov(Operand(esp, 3 * kPointerSize), ecx); // Code address or 0. + __ mov(Operand(esp, 4 * kPointerSize), edx); // Fp-to-sp delta. + __ mov(Operand(esp, 5 * kPointerSize), + Immediate(ExternalReference::isolate_address(isolate()))); + { + AllowExternalCallThatCantCauseGC scope(masm()); + __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate()), 6); + } + + __ pop(edi); + + // Preserve deoptimizer object in register eax and get the input + // frame descriptor pointer. + __ mov(ebx, Operand(eax, Deoptimizer::input_offset())); + + // Fill in the input registers. + for (int i = kNumberOfRegisters - 1; i >= 0; i--) { + int offset = (i * kPointerSize) + FrameDescription::registers_offset(); + __ pop(Operand(ebx, offset)); + } + + int double_regs_offset = FrameDescription::double_registers_offset(); + const RegisterConfiguration* config = RegisterConfiguration::Crankshaft(); + // Fill in the double input registers. + for (int i = 0; i < X87Register::kMaxNumAllocatableRegisters; ++i) { + int code = config->GetAllocatableDoubleCode(i); + int dst_offset = code * kDoubleSize + double_regs_offset; + int src_offset = code * kDoubleSize; + __ fld_d(Operand(esp, src_offset)); + __ fstp_d(Operand(ebx, dst_offset)); + } + + // Clear FPU all exceptions. + // TODO(ulan): Find out why the TOP register is not zero here in some cases, + // and check that the generated code never deoptimizes with unbalanced stack. + __ fnclex(); + + // Remove the bailout id, return address and the double registers. + __ add(esp, Immediate(kDoubleRegsSize + 2 * kPointerSize)); + + // Compute a pointer to the unwinding limit in register ecx; that is + // the first stack slot not part of the input frame. + __ mov(ecx, Operand(ebx, FrameDescription::frame_size_offset())); + __ add(ecx, esp); + + // Unwind the stack down to - but not including - the unwinding + // limit and copy the contents of the activation frame to the input + // frame description. + __ lea(edx, Operand(ebx, FrameDescription::frame_content_offset())); + Label pop_loop_header; + __ jmp(&pop_loop_header); + Label pop_loop; + __ bind(&pop_loop); + __ pop(Operand(edx, 0)); + __ add(edx, Immediate(sizeof(uint32_t))); + __ bind(&pop_loop_header); + __ cmp(ecx, esp); + __ j(not_equal, &pop_loop); + + // Compute the output frame in the deoptimizer. + __ push(edi); + __ push(eax); + __ PrepareCallCFunction(1, ebx); + __ mov(Operand(esp, 0 * kPointerSize), eax); + { + AllowExternalCallThatCantCauseGC scope(masm()); + __ CallCFunction( + ExternalReference::compute_output_frames_function(isolate()), 1); + } + __ pop(eax); + __ pop(edi); + __ mov(esp, Operand(eax, Deoptimizer::caller_frame_top_offset())); + + // Replace the current (input) frame with the output frames. + Label outer_push_loop, inner_push_loop, + outer_loop_header, inner_loop_header; + // Outer loop state: eax = current FrameDescription**, edx = one past the + // last FrameDescription**. + __ mov(edx, Operand(eax, Deoptimizer::output_count_offset())); + __ mov(eax, Operand(eax, Deoptimizer::output_offset())); + __ lea(edx, Operand(eax, edx, times_4, 0)); + __ jmp(&outer_loop_header); + __ bind(&outer_push_loop); + // Inner loop state: ebx = current FrameDescription*, ecx = loop index. + __ mov(ebx, Operand(eax, 0)); + __ mov(ecx, Operand(ebx, FrameDescription::frame_size_offset())); + __ jmp(&inner_loop_header); + __ bind(&inner_push_loop); + __ sub(ecx, Immediate(sizeof(uint32_t))); + __ push(Operand(ebx, ecx, times_1, FrameDescription::frame_content_offset())); + __ bind(&inner_loop_header); + __ test(ecx, ecx); + __ j(not_zero, &inner_push_loop); + __ add(eax, Immediate(kPointerSize)); + __ bind(&outer_loop_header); + __ cmp(eax, edx); + __ j(below, &outer_push_loop); + + + // In case of a failed STUB, we have to restore the x87 stack. + // x87 stack layout is in edi. + Label loop2, done2; + // Get the x87 stack depth, the first 3 bits. + __ mov(ecx, edi); + __ and_(ecx, 0x7); + __ j(zero, &done2, Label::kNear); + + __ lea(ecx, Operand(ecx, ecx, times_2, 0)); + __ bind(&loop2); + __ mov(eax, edi); + __ shr_cl(eax); + __ and_(eax, 0x7); + __ fld_d(Operand(ebx, eax, times_8, double_regs_offset)); + __ sub(ecx, Immediate(0x3)); + __ j(not_zero, &loop2, Label::kNear); + __ bind(&done2); + + // Push state, pc, and continuation from the last output frame. + __ push(Operand(ebx, FrameDescription::state_offset())); + __ push(Operand(ebx, FrameDescription::pc_offset())); + __ push(Operand(ebx, FrameDescription::continuation_offset())); + + + // Push the registers from the last output frame. + for (int i = 0; i < kNumberOfRegisters; i++) { + int offset = (i * kPointerSize) + FrameDescription::registers_offset(); + __ push(Operand(ebx, offset)); + } + + // Restore the registers from the stack. + __ popad(); + + // Return to the continuation point. + __ ret(0); +} + + +void Deoptimizer::TableEntryGenerator::GeneratePrologue() { + // Create a sequence of deoptimization entries. + Label done; + for (int i = 0; i < count(); i++) { + int start = masm()->pc_offset(); + USE(start); + __ push_imm32(i); + __ jmp(&done); + DCHECK(masm()->pc_offset() - start == table_entry_size_); + } + __ bind(&done); +} + + +void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) { + SetFrameSlot(offset, value); +} + + +void FrameDescription::SetCallerFp(unsigned offset, intptr_t value) { + SetFrameSlot(offset, value); +} + + +void FrameDescription::SetCallerConstantPool(unsigned offset, intptr_t value) { + // No embedded constant pool support. + UNREACHABLE(); +} + + +#undef __ + + +} // namespace internal +} // namespace v8 + +#endif // V8_TARGET_ARCH_X87 diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/x87/disasm-x87.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/x87/disasm-x87.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/x87/disasm-x87.cc 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/x87/disasm-x87.cc 2017-12-25 17:42:57.222465544 +0100 @@ -0,0 +1,1874 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include +#include + +#if V8_TARGET_ARCH_X87 + +#include "src/base/compiler-specific.h" +#include "src/disasm.h" + +namespace disasm { + +enum OperandOrder { + UNSET_OP_ORDER = 0, + REG_OPER_OP_ORDER, + OPER_REG_OP_ORDER +}; + + +//------------------------------------------------------------------ +// Tables +//------------------------------------------------------------------ +struct ByteMnemonic { + int b; // -1 terminates, otherwise must be in range (0..255) + const char* mnem; + OperandOrder op_order_; +}; + +static const ByteMnemonic two_operands_instr[] = { + {0x01, "add", OPER_REG_OP_ORDER}, {0x03, "add", REG_OPER_OP_ORDER}, + {0x09, "or", OPER_REG_OP_ORDER}, {0x0B, "or", REG_OPER_OP_ORDER}, + {0x13, "adc", REG_OPER_OP_ORDER}, {0x1B, "sbb", REG_OPER_OP_ORDER}, + {0x21, "and", OPER_REG_OP_ORDER}, {0x23, "and", REG_OPER_OP_ORDER}, + {0x29, "sub", OPER_REG_OP_ORDER}, {0x2A, "subb", REG_OPER_OP_ORDER}, + {0x2B, "sub", REG_OPER_OP_ORDER}, {0x31, "xor", OPER_REG_OP_ORDER}, + {0x33, "xor", REG_OPER_OP_ORDER}, {0x38, "cmpb", OPER_REG_OP_ORDER}, + {0x39, "cmp", OPER_REG_OP_ORDER}, {0x3A, "cmpb", REG_OPER_OP_ORDER}, + {0x3B, "cmp", REG_OPER_OP_ORDER}, {0x84, "test_b", REG_OPER_OP_ORDER}, + {0x85, "test", REG_OPER_OP_ORDER}, {0x86, "xchg_b", REG_OPER_OP_ORDER}, + {0x87, "xchg", REG_OPER_OP_ORDER}, {0x8A, "mov_b", REG_OPER_OP_ORDER}, + {0x8B, "mov", REG_OPER_OP_ORDER}, {0x8D, "lea", REG_OPER_OP_ORDER}, + {-1, "", UNSET_OP_ORDER}}; + +static const ByteMnemonic zero_operands_instr[] = { + {0xC3, "ret", UNSET_OP_ORDER}, + {0xC9, "leave", UNSET_OP_ORDER}, + {0x90, "nop", UNSET_OP_ORDER}, + {0xF4, "hlt", UNSET_OP_ORDER}, + {0xCC, "int3", UNSET_OP_ORDER}, + {0x60, "pushad", UNSET_OP_ORDER}, + {0x61, "popad", UNSET_OP_ORDER}, + {0x9C, "pushfd", UNSET_OP_ORDER}, + {0x9D, "popfd", UNSET_OP_ORDER}, + {0x9E, "sahf", UNSET_OP_ORDER}, + {0x99, "cdq", UNSET_OP_ORDER}, + {0x9B, "fwait", UNSET_OP_ORDER}, + {0xFC, "cld", UNSET_OP_ORDER}, + {0xAB, "stos", UNSET_OP_ORDER}, + {-1, "", UNSET_OP_ORDER} +}; + + +static const ByteMnemonic call_jump_instr[] = { + {0xE8, "call", UNSET_OP_ORDER}, + {0xE9, "jmp", UNSET_OP_ORDER}, + {-1, "", UNSET_OP_ORDER} +}; + + +static const ByteMnemonic short_immediate_instr[] = { + {0x05, "add", UNSET_OP_ORDER}, + {0x0D, "or", UNSET_OP_ORDER}, + {0x15, "adc", UNSET_OP_ORDER}, + {0x25, "and", UNSET_OP_ORDER}, + {0x2D, "sub", UNSET_OP_ORDER}, + {0x35, "xor", UNSET_OP_ORDER}, + {0x3D, "cmp", UNSET_OP_ORDER}, + {-1, "", UNSET_OP_ORDER} +}; + + +// Generally we don't want to generate these because they are subject to partial +// register stalls. They are included for completeness and because the cmp +// variant is used by the RecordWrite stub. Because it does not update the +// register it is not subject to partial register stalls. +static ByteMnemonic byte_immediate_instr[] = { + {0x0c, "or", UNSET_OP_ORDER}, + {0x24, "and", UNSET_OP_ORDER}, + {0x34, "xor", UNSET_OP_ORDER}, + {0x3c, "cmp", UNSET_OP_ORDER}, + {-1, "", UNSET_OP_ORDER} +}; + + +static const char* const jump_conditional_mnem[] = { + /*0*/ "jo", "jno", "jc", "jnc", + /*4*/ "jz", "jnz", "jna", "ja", + /*8*/ "js", "jns", "jpe", "jpo", + /*12*/ "jl", "jnl", "jng", "jg" +}; + + +static const char* const set_conditional_mnem[] = { + /*0*/ "seto", "setno", "setc", "setnc", + /*4*/ "setz", "setnz", "setna", "seta", + /*8*/ "sets", "setns", "setpe", "setpo", + /*12*/ "setl", "setnl", "setng", "setg" +}; + + +static const char* const conditional_move_mnem[] = { + /*0*/ "cmovo", "cmovno", "cmovc", "cmovnc", + /*4*/ "cmovz", "cmovnz", "cmovna", "cmova", + /*8*/ "cmovs", "cmovns", "cmovpe", "cmovpo", + /*12*/ "cmovl", "cmovnl", "cmovng", "cmovg" +}; + + +enum InstructionType { + NO_INSTR, + ZERO_OPERANDS_INSTR, + TWO_OPERANDS_INSTR, + JUMP_CONDITIONAL_SHORT_INSTR, + REGISTER_INSTR, + MOVE_REG_INSTR, + CALL_JUMP_INSTR, + SHORT_IMMEDIATE_INSTR, + BYTE_IMMEDIATE_INSTR +}; + + +struct InstructionDesc { + const char* mnem; + InstructionType type; + OperandOrder op_order_; +}; + + +class InstructionTable { + public: + InstructionTable(); + const InstructionDesc& Get(byte x) const { return instructions_[x]; } + static InstructionTable* get_instance() { + static InstructionTable table; + return &table; + } + + private: + InstructionDesc instructions_[256]; + void Clear(); + void Init(); + void CopyTable(const ByteMnemonic bm[], InstructionType type); + void SetTableRange(InstructionType type, + byte start, + byte end, + const char* mnem); + void AddJumpConditionalShort(); +}; + + +InstructionTable::InstructionTable() { + Clear(); + Init(); +} + + +void InstructionTable::Clear() { + for (int i = 0; i < 256; i++) { + instructions_[i].mnem = ""; + instructions_[i].type = NO_INSTR; + instructions_[i].op_order_ = UNSET_OP_ORDER; + } +} + + +void InstructionTable::Init() { + CopyTable(two_operands_instr, TWO_OPERANDS_INSTR); + CopyTable(zero_operands_instr, ZERO_OPERANDS_INSTR); + CopyTable(call_jump_instr, CALL_JUMP_INSTR); + CopyTable(short_immediate_instr, SHORT_IMMEDIATE_INSTR); + CopyTable(byte_immediate_instr, BYTE_IMMEDIATE_INSTR); + AddJumpConditionalShort(); + SetTableRange(REGISTER_INSTR, 0x40, 0x47, "inc"); + SetTableRange(REGISTER_INSTR, 0x48, 0x4F, "dec"); + SetTableRange(REGISTER_INSTR, 0x50, 0x57, "push"); + SetTableRange(REGISTER_INSTR, 0x58, 0x5F, "pop"); + SetTableRange(REGISTER_INSTR, 0x91, 0x97, "xchg eax,"); // 0x90 is nop. + SetTableRange(MOVE_REG_INSTR, 0xB8, 0xBF, "mov"); +} + + +void InstructionTable::CopyTable(const ByteMnemonic bm[], + InstructionType type) { + for (int i = 0; bm[i].b >= 0; i++) { + InstructionDesc* id = &instructions_[bm[i].b]; + id->mnem = bm[i].mnem; + id->op_order_ = bm[i].op_order_; + DCHECK_EQ(NO_INSTR, id->type); // Information not already entered. + id->type = type; + } +} + + +void InstructionTable::SetTableRange(InstructionType type, + byte start, + byte end, + const char* mnem) { + for (byte b = start; b <= end; b++) { + InstructionDesc* id = &instructions_[b]; + DCHECK_EQ(NO_INSTR, id->type); // Information not already entered. + id->mnem = mnem; + id->type = type; + } +} + + +void InstructionTable::AddJumpConditionalShort() { + for (byte b = 0x70; b <= 0x7F; b++) { + InstructionDesc* id = &instructions_[b]; + DCHECK_EQ(NO_INSTR, id->type); // Information not already entered. + id->mnem = jump_conditional_mnem[b & 0x0F]; + id->type = JUMP_CONDITIONAL_SHORT_INSTR; + } +} + + +// The X87 disassembler implementation. +class DisassemblerX87 { + public: + DisassemblerX87(const NameConverter& converter, + bool abort_on_unimplemented = true) + : converter_(converter), + instruction_table_(InstructionTable::get_instance()), + tmp_buffer_pos_(0), + abort_on_unimplemented_(abort_on_unimplemented) { + tmp_buffer_[0] = '\0'; + } + + virtual ~DisassemblerX87() {} + + // Writes one disassembled instruction into 'buffer' (0-terminated). + // Returns the length of the disassembled machine instruction in bytes. + int InstructionDecode(v8::internal::Vector buffer, byte* instruction); + + private: + const NameConverter& converter_; + InstructionTable* instruction_table_; + v8::internal::EmbeddedVector tmp_buffer_; + unsigned int tmp_buffer_pos_; + bool abort_on_unimplemented_; + + enum { + eax = 0, + ecx = 1, + edx = 2, + ebx = 3, + esp = 4, + ebp = 5, + esi = 6, + edi = 7 + }; + + + enum ShiftOpcodeExtension { + kROL = 0, + kROR = 1, + kRCL = 2, + kRCR = 3, + kSHL = 4, + KSHR = 5, + kSAR = 7 + }; + + + const char* NameOfCPURegister(int reg) const { + return converter_.NameOfCPURegister(reg); + } + + + const char* NameOfByteCPURegister(int reg) const { + return converter_.NameOfByteCPURegister(reg); + } + + + const char* NameOfXMMRegister(int reg) const { + return converter_.NameOfXMMRegister(reg); + } + + + const char* NameOfAddress(byte* addr) const { + return converter_.NameOfAddress(addr); + } + + + // Disassembler helper functions. + static void get_modrm(byte data, int* mod, int* regop, int* rm) { + *mod = (data >> 6) & 3; + *regop = (data & 0x38) >> 3; + *rm = data & 7; + } + + + static void get_sib(byte data, int* scale, int* index, int* base) { + *scale = (data >> 6) & 3; + *index = (data >> 3) & 7; + *base = data & 7; + } + + typedef const char* (DisassemblerX87::*RegisterNameMapping)(int reg) const; + + int PrintRightOperandHelper(byte* modrmp, RegisterNameMapping register_name); + int PrintRightOperand(byte* modrmp); + int PrintRightByteOperand(byte* modrmp); + int PrintRightXMMOperand(byte* modrmp); + int PrintOperands(const char* mnem, OperandOrder op_order, byte* data); + int PrintImmediateOp(byte* data); + int F7Instruction(byte* data); + int D1D3C1Instruction(byte* data); + int JumpShort(byte* data); + int JumpConditional(byte* data, const char* comment); + int JumpConditionalShort(byte* data, const char* comment); + int SetCC(byte* data); + int CMov(byte* data); + int FPUInstruction(byte* data); + int MemoryFPUInstruction(int escape_opcode, int regop, byte* modrm_start); + int RegisterFPUInstruction(int escape_opcode, byte modrm_byte); + PRINTF_FORMAT(2, 3) void AppendToBuffer(const char* format, ...); + + void UnimplementedInstruction() { + if (abort_on_unimplemented_) { + UNIMPLEMENTED(); + } else { + AppendToBuffer("'Unimplemented Instruction'"); + } + } +}; + + +void DisassemblerX87::AppendToBuffer(const char* format, ...) { + v8::internal::Vector buf = tmp_buffer_ + tmp_buffer_pos_; + va_list args; + va_start(args, format); + int result = v8::internal::VSNPrintF(buf, format, args); + va_end(args); + tmp_buffer_pos_ += result; +} + +int DisassemblerX87::PrintRightOperandHelper( + byte* modrmp, + RegisterNameMapping direct_register_name) { + int mod, regop, rm; + get_modrm(*modrmp, &mod, ®op, &rm); + RegisterNameMapping register_name = (mod == 3) ? direct_register_name : + &DisassemblerX87::NameOfCPURegister; + switch (mod) { + case 0: + if (rm == ebp) { + int32_t disp = *reinterpret_cast(modrmp+1); + AppendToBuffer("[0x%x]", disp); + return 5; + } else if (rm == esp) { + byte sib = *(modrmp + 1); + int scale, index, base; + get_sib(sib, &scale, &index, &base); + if (index == esp && base == esp && scale == 0 /*times_1*/) { + AppendToBuffer("[%s]", (this->*register_name)(rm)); + return 2; + } else if (base == ebp) { + int32_t disp = *reinterpret_cast(modrmp + 2); + AppendToBuffer("[%s*%d%s0x%x]", + (this->*register_name)(index), + 1 << scale, + disp < 0 ? "-" : "+", + disp < 0 ? -disp : disp); + return 6; + } else if (index != esp && base != ebp) { + // [base+index*scale] + AppendToBuffer("[%s+%s*%d]", + (this->*register_name)(base), + (this->*register_name)(index), + 1 << scale); + return 2; + } else { + UnimplementedInstruction(); + return 1; + } + } else { + AppendToBuffer("[%s]", (this->*register_name)(rm)); + return 1; + } + break; + case 1: // fall through + case 2: + if (rm == esp) { + byte sib = *(modrmp + 1); + int scale, index, base; + get_sib(sib, &scale, &index, &base); + int disp = mod == 2 ? *reinterpret_cast(modrmp + 2) + : *reinterpret_cast(modrmp + 2); + if (index == base && index == rm /*esp*/ && scale == 0 /*times_1*/) { + AppendToBuffer("[%s%s0x%x]", + (this->*register_name)(rm), + disp < 0 ? "-" : "+", + disp < 0 ? -disp : disp); + } else { + AppendToBuffer("[%s+%s*%d%s0x%x]", + (this->*register_name)(base), + (this->*register_name)(index), + 1 << scale, + disp < 0 ? "-" : "+", + disp < 0 ? -disp : disp); + } + return mod == 2 ? 6 : 3; + } else { + // No sib. + int disp = mod == 2 ? *reinterpret_cast(modrmp + 1) + : *reinterpret_cast(modrmp + 1); + AppendToBuffer("[%s%s0x%x]", + (this->*register_name)(rm), + disp < 0 ? "-" : "+", + disp < 0 ? -disp : disp); + return mod == 2 ? 5 : 2; + } + break; + case 3: + AppendToBuffer("%s", (this->*register_name)(rm)); + return 1; + default: + UnimplementedInstruction(); + return 1; + } + UNREACHABLE(); +} + + +int DisassemblerX87::PrintRightOperand(byte* modrmp) { + return PrintRightOperandHelper(modrmp, &DisassemblerX87::NameOfCPURegister); +} + + +int DisassemblerX87::PrintRightByteOperand(byte* modrmp) { + return PrintRightOperandHelper(modrmp, + &DisassemblerX87::NameOfByteCPURegister); +} + + +int DisassemblerX87::PrintRightXMMOperand(byte* modrmp) { + return PrintRightOperandHelper(modrmp, + &DisassemblerX87::NameOfXMMRegister); +} + + +// Returns number of bytes used including the current *data. +// Writes instruction's mnemonic, left and right operands to 'tmp_buffer_'. +int DisassemblerX87::PrintOperands(const char* mnem, + OperandOrder op_order, + byte* data) { + byte modrm = *data; + int mod, regop, rm; + get_modrm(modrm, &mod, ®op, &rm); + int advance = 0; + switch (op_order) { + case REG_OPER_OP_ORDER: { + AppendToBuffer("%s %s,", mnem, NameOfCPURegister(regop)); + advance = PrintRightOperand(data); + break; + } + case OPER_REG_OP_ORDER: { + AppendToBuffer("%s ", mnem); + advance = PrintRightOperand(data); + AppendToBuffer(",%s", NameOfCPURegister(regop)); + break; + } + default: + UNREACHABLE(); + break; + } + return advance; +} + + +// Returns number of bytes used by machine instruction, including *data byte. +// Writes immediate instructions to 'tmp_buffer_'. +int DisassemblerX87::PrintImmediateOp(byte* data) { + bool sign_extension_bit = (*data & 0x02) != 0; + byte modrm = *(data+1); + int mod, regop, rm; + get_modrm(modrm, &mod, ®op, &rm); + const char* mnem = "Imm???"; + switch (regop) { + case 0: mnem = "add"; break; + case 1: mnem = "or"; break; + case 2: mnem = "adc"; break; + case 4: mnem = "and"; break; + case 5: mnem = "sub"; break; + case 6: mnem = "xor"; break; + case 7: mnem = "cmp"; break; + default: UnimplementedInstruction(); + } + AppendToBuffer("%s ", mnem); + int count = PrintRightOperand(data+1); + if (sign_extension_bit) { + AppendToBuffer(",0x%x", *(data + 1 + count)); + return 1 + count + 1 /*int8*/; + } else { + AppendToBuffer(",0x%x", *reinterpret_cast(data + 1 + count)); + return 1 + count + 4 /*int32_t*/; + } +} + + +// Returns number of bytes used, including *data. +int DisassemblerX87::F7Instruction(byte* data) { + DCHECK_EQ(0xF7, *data); + byte modrm = *++data; + int mod, regop, rm; + get_modrm(modrm, &mod, ®op, &rm); + const char* mnem = NULL; + switch (regop) { + case 0: + mnem = "test"; + break; + case 2: + mnem = "not"; + break; + case 3: + mnem = "neg"; + break; + case 4: + mnem = "mul"; + break; + case 5: + mnem = "imul"; + break; + case 6: + mnem = "div"; + break; + case 7: + mnem = "idiv"; + break; + default: + UnimplementedInstruction(); + } + AppendToBuffer("%s ", mnem); + int count = PrintRightOperand(data); + if (regop == 0) { + AppendToBuffer(",0x%x", *reinterpret_cast(data + count)); + count += 4; + } + return 1 + count; +} + + +int DisassemblerX87::D1D3C1Instruction(byte* data) { + byte op = *data; + DCHECK(op == 0xD1 || op == 0xD3 || op == 0xC1); + byte modrm = *++data; + int mod, regop, rm; + get_modrm(modrm, &mod, ®op, &rm); + int imm8 = -1; + const char* mnem = NULL; + switch (regop) { + case kROL: + mnem = "rol"; + break; + case kROR: + mnem = "ror"; + break; + case kRCL: + mnem = "rcl"; + break; + case kRCR: + mnem = "rcr"; + break; + case kSHL: + mnem = "shl"; + break; + case KSHR: + mnem = "shr"; + break; + case kSAR: + mnem = "sar"; + break; + default: + UnimplementedInstruction(); + } + AppendToBuffer("%s ", mnem); + int count = PrintRightOperand(data); + if (op == 0xD1) { + imm8 = 1; + } else if (op == 0xC1) { + imm8 = *(data + 1); + count++; + } else if (op == 0xD3) { + // Shift/rotate by cl. + } + if (imm8 >= 0) { + AppendToBuffer(",%d", imm8); + } else { + AppendToBuffer(",cl"); + } + return 1 + count; +} + + +// Returns number of bytes used, including *data. +int DisassemblerX87::JumpShort(byte* data) { + DCHECK_EQ(0xEB, *data); + byte b = *(data+1); + byte* dest = data + static_cast(b) + 2; + AppendToBuffer("jmp %s", NameOfAddress(dest)); + return 2; +} + + +// Returns number of bytes used, including *data. +int DisassemblerX87::JumpConditional(byte* data, const char* comment) { + DCHECK_EQ(0x0F, *data); + byte cond = *(data+1) & 0x0F; + byte* dest = data + *reinterpret_cast(data+2) + 6; + const char* mnem = jump_conditional_mnem[cond]; + AppendToBuffer("%s %s", mnem, NameOfAddress(dest)); + if (comment != NULL) { + AppendToBuffer(", %s", comment); + } + return 6; // includes 0x0F +} + + +// Returns number of bytes used, including *data. +int DisassemblerX87::JumpConditionalShort(byte* data, const char* comment) { + byte cond = *data & 0x0F; + byte b = *(data+1); + byte* dest = data + static_cast(b) + 2; + const char* mnem = jump_conditional_mnem[cond]; + AppendToBuffer("%s %s", mnem, NameOfAddress(dest)); + if (comment != NULL) { + AppendToBuffer(", %s", comment); + } + return 2; +} + + +// Returns number of bytes used, including *data. +int DisassemblerX87::SetCC(byte* data) { + DCHECK_EQ(0x0F, *data); + byte cond = *(data+1) & 0x0F; + const char* mnem = set_conditional_mnem[cond]; + AppendToBuffer("%s ", mnem); + PrintRightByteOperand(data+2); + return 3; // Includes 0x0F. +} + + +// Returns number of bytes used, including *data. +int DisassemblerX87::CMov(byte* data) { + DCHECK_EQ(0x0F, *data); + byte cond = *(data + 1) & 0x0F; + const char* mnem = conditional_move_mnem[cond]; + int op_size = PrintOperands(mnem, REG_OPER_OP_ORDER, data + 2); + return 2 + op_size; // includes 0x0F +} + + +// Returns number of bytes used, including *data. +int DisassemblerX87::FPUInstruction(byte* data) { + byte escape_opcode = *data; + DCHECK_EQ(0xD8, escape_opcode & 0xF8); + byte modrm_byte = *(data+1); + + if (modrm_byte >= 0xC0) { + return RegisterFPUInstruction(escape_opcode, modrm_byte); + } else { + return MemoryFPUInstruction(escape_opcode, modrm_byte, data+1); + } +} + +int DisassemblerX87::MemoryFPUInstruction(int escape_opcode, + int modrm_byte, + byte* modrm_start) { + const char* mnem = "?"; + int regop = (modrm_byte >> 3) & 0x7; // reg/op field of modrm byte. + switch (escape_opcode) { + case 0xD9: switch (regop) { + case 0: mnem = "fld_s"; break; + case 2: mnem = "fst_s"; break; + case 3: mnem = "fstp_s"; break; + case 5: + mnem = "fldcw"; + break; + case 7: + mnem = "fnstcw"; + break; + default: UnimplementedInstruction(); + } + break; + + case 0xDB: switch (regop) { + case 0: mnem = "fild_s"; break; + case 1: mnem = "fisttp_s"; break; + case 2: mnem = "fist_s"; break; + case 3: mnem = "fistp_s"; break; + default: UnimplementedInstruction(); + } + break; + + case 0xDC: + switch (regop) { + case 0: + mnem = "fadd_d"; + break; + case 1: + mnem = "fmul_d"; + break; + case 4: + mnem = "fsub_d"; + break; + case 5: + mnem = "fsubr_d"; + break; + case 6: + mnem = "fdiv_d"; + break; + case 7: + mnem = "fdivr_d"; + break; + default: + UnimplementedInstruction(); + } + break; + + case 0xDD: switch (regop) { + case 0: mnem = "fld_d"; break; + case 1: mnem = "fisttp_d"; break; + case 2: mnem = "fst_d"; break; + case 3: mnem = "fstp_d"; break; + case 4: + mnem = "frstor"; + break; + case 6: + mnem = "fnsave"; + break; + default: UnimplementedInstruction(); + } + break; + + case 0xDF: switch (regop) { + case 5: mnem = "fild_d"; break; + case 7: mnem = "fistp_d"; break; + default: UnimplementedInstruction(); + } + break; + + default: UnimplementedInstruction(); + } + AppendToBuffer("%s ", mnem); + int count = PrintRightOperand(modrm_start); + return count + 1; +} + +int DisassemblerX87::RegisterFPUInstruction(int escape_opcode, + byte modrm_byte) { + bool has_register = false; // Is the FPU register encoded in modrm_byte? + const char* mnem = "?"; + + switch (escape_opcode) { + case 0xD8: + has_register = true; + switch (modrm_byte & 0xF8) { + case 0xC0: mnem = "fadd_i"; break; + case 0xE0: mnem = "fsub_i"; break; + case 0xC8: mnem = "fmul_i"; break; + case 0xF0: mnem = "fdiv_i"; break; + default: UnimplementedInstruction(); + } + break; + + case 0xD9: + switch (modrm_byte & 0xF8) { + case 0xC0: + mnem = "fld"; + has_register = true; + break; + case 0xC8: + mnem = "fxch"; + has_register = true; + break; + default: + switch (modrm_byte) { + case 0xE0: mnem = "fchs"; break; + case 0xE1: mnem = "fabs"; break; + case 0xE4: mnem = "ftst"; break; + case 0xE8: mnem = "fld1"; break; + case 0xEB: mnem = "fldpi"; break; + case 0xED: mnem = "fldln2"; break; + case 0xEE: mnem = "fldz"; break; + case 0xF0: mnem = "f2xm1"; break; + case 0xF1: mnem = "fyl2x"; break; + case 0xF4: mnem = "fxtract"; break; + case 0xF5: mnem = "fprem1"; break; + case 0xF7: mnem = "fincstp"; break; + case 0xF8: mnem = "fprem"; break; + case 0xFC: mnem = "frndint"; break; + case 0xFD: mnem = "fscale"; break; + case 0xFE: mnem = "fsin"; break; + case 0xFF: mnem = "fcos"; break; + default: UnimplementedInstruction(); + } + } + break; + + case 0xDA: + if (modrm_byte == 0xE9) { + mnem = "fucompp"; + } else { + UnimplementedInstruction(); + } + break; + + case 0xDB: + if ((modrm_byte & 0xF8) == 0xE8) { + mnem = "fucomi"; + has_register = true; + } else if (modrm_byte == 0xE2) { + mnem = "fclex"; + } else if (modrm_byte == 0xE3) { + mnem = "fninit"; + } else { + UnimplementedInstruction(); + } + break; + + case 0xDC: + has_register = true; + switch (modrm_byte & 0xF8) { + case 0xC0: mnem = "fadd"; break; + case 0xE8: mnem = "fsub"; break; + case 0xC8: mnem = "fmul"; break; + case 0xF8: mnem = "fdiv"; break; + default: UnimplementedInstruction(); + } + break; + + case 0xDD: + has_register = true; + switch (modrm_byte & 0xF8) { + case 0xC0: mnem = "ffree"; break; + case 0xD0: mnem = "fst"; break; + case 0xD8: mnem = "fstp"; break; + default: UnimplementedInstruction(); + } + break; + + case 0xDE: + if (modrm_byte == 0xD9) { + mnem = "fcompp"; + } else { + has_register = true; + switch (modrm_byte & 0xF8) { + case 0xC0: mnem = "faddp"; break; + case 0xE8: mnem = "fsubp"; break; + case 0xC8: mnem = "fmulp"; break; + case 0xF8: mnem = "fdivp"; break; + default: UnimplementedInstruction(); + } + } + break; + + case 0xDF: + if (modrm_byte == 0xE0) { + mnem = "fnstsw_ax"; + } else if ((modrm_byte & 0xF8) == 0xE8) { + mnem = "fucomip"; + has_register = true; + } + break; + + default: UnimplementedInstruction(); + } + + if (has_register) { + AppendToBuffer("%s st%d", mnem, modrm_byte & 0x7); + } else { + AppendToBuffer("%s", mnem); + } + return 2; +} + + +// Mnemonics for instructions 0xF0 byte. +// Returns NULL if the instruction is not handled here. +static const char* F0Mnem(byte f0byte) { + switch (f0byte) { + case 0x0B: + return "ud2"; + case 0x18: + return "prefetch"; + case 0xA2: + return "cpuid"; + case 0xBE: + return "movsx_b"; + case 0xBF: + return "movsx_w"; + case 0xB6: + return "movzx_b"; + case 0xB7: + return "movzx_w"; + case 0xAF: + return "imul"; + case 0xA4: + return "shld"; + case 0xA5: + return "shld"; + case 0xAD: + return "shrd"; + case 0xAC: + return "shrd"; // 3-operand version. + case 0xAB: + return "bts"; + case 0xB0: + return "cmpxchg_b"; + case 0xB1: + return "cmpxchg"; + case 0xBC: + return "bsf"; + case 0xBD: + return "bsr"; + default: return NULL; + } +} + + +// Disassembled instruction '*instr' and writes it into 'out_buffer'. +int DisassemblerX87::InstructionDecode(v8::internal::Vector out_buffer, + byte* instr) { + tmp_buffer_pos_ = 0; // starting to write as position 0 + byte* data = instr; + // Check for hints. + const char* branch_hint = NULL; + // We use these two prefixes only with branch prediction + if (*data == 0x3E /*ds*/) { + branch_hint = "predicted taken"; + data++; + } else if (*data == 0x2E /*cs*/) { + branch_hint = "predicted not taken"; + data++; + } else if (*data == 0xF0 /*lock*/) { + AppendToBuffer("lock "); + data++; + } + + bool processed = true; // Will be set to false if the current instruction + // is not in 'instructions' table. + const InstructionDesc& idesc = instruction_table_->Get(*data); + switch (idesc.type) { + case ZERO_OPERANDS_INSTR: + AppendToBuffer("%s", idesc.mnem); + data++; + break; + + case TWO_OPERANDS_INSTR: + data++; + data += PrintOperands(idesc.mnem, idesc.op_order_, data); + break; + + case JUMP_CONDITIONAL_SHORT_INSTR: + data += JumpConditionalShort(data, branch_hint); + break; + + case REGISTER_INSTR: + AppendToBuffer("%s %s", idesc.mnem, NameOfCPURegister(*data & 0x07)); + data++; + break; + + case MOVE_REG_INSTR: { + byte* addr = reinterpret_cast(*reinterpret_cast(data+1)); + AppendToBuffer("mov %s,%s", + NameOfCPURegister(*data & 0x07), + NameOfAddress(addr)); + data += 5; + break; + } + + case CALL_JUMP_INSTR: { + byte* addr = data + *reinterpret_cast(data+1) + 5; + AppendToBuffer("%s %s", idesc.mnem, NameOfAddress(addr)); + data += 5; + break; + } + + case SHORT_IMMEDIATE_INSTR: { + byte* addr = reinterpret_cast(*reinterpret_cast(data+1)); + AppendToBuffer("%s eax,%s", idesc.mnem, NameOfAddress(addr)); + data += 5; + break; + } + + case BYTE_IMMEDIATE_INSTR: { + AppendToBuffer("%s al,0x%x", idesc.mnem, data[1]); + data += 2; + break; + } + + case NO_INSTR: + processed = false; + break; + + default: + UNIMPLEMENTED(); // This type is not implemented. + } + //---------------------------- + if (!processed) { + switch (*data) { + case 0xC2: + AppendToBuffer("ret 0x%x", *reinterpret_cast(data+1)); + data += 3; + break; + + case 0x6B: { + data++; + data += PrintOperands("imul", REG_OPER_OP_ORDER, data); + AppendToBuffer(",%d", *data); + data++; + } break; + + case 0x69: { + data++; + data += PrintOperands("imul", REG_OPER_OP_ORDER, data); + AppendToBuffer(",%d", *reinterpret_cast(data)); + data += 4; + } + break; + + case 0xF6: + { data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + if (regop == eax) { + AppendToBuffer("test_b "); + data += PrintRightByteOperand(data); + int32_t imm = *data; + AppendToBuffer(",0x%x", imm); + data++; + } else { + UnimplementedInstruction(); + } + } + break; + + case 0x81: // fall through + case 0x83: // 0x81 with sign extension bit set + data += PrintImmediateOp(data); + break; + + case 0x0F: + { byte f0byte = data[1]; + const char* f0mnem = F0Mnem(f0byte); + if (f0byte == 0x18) { + data += 2; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + const char* suffix[] = {"nta", "1", "2", "3"}; + AppendToBuffer("%s%s ", f0mnem, suffix[regop & 0x03]); + data += PrintRightOperand(data); + } else if (f0byte == 0x1F && data[2] == 0) { + AppendToBuffer("nop"); // 3 byte nop. + data += 3; + } else if (f0byte == 0x1F && data[2] == 0x40 && data[3] == 0) { + AppendToBuffer("nop"); // 4 byte nop. + data += 4; + } else if (f0byte == 0x1F && data[2] == 0x44 && data[3] == 0 && + data[4] == 0) { + AppendToBuffer("nop"); // 5 byte nop. + data += 5; + } else if (f0byte == 0x1F && data[2] == 0x80 && data[3] == 0 && + data[4] == 0 && data[5] == 0 && data[6] == 0) { + AppendToBuffer("nop"); // 7 byte nop. + data += 7; + } else if (f0byte == 0x1F && data[2] == 0x84 && data[3] == 0 && + data[4] == 0 && data[5] == 0 && data[6] == 0 && + data[7] == 0) { + AppendToBuffer("nop"); // 8 byte nop. + data += 8; + } else if (f0byte == 0x0B || f0byte == 0xA2 || f0byte == 0x31) { + AppendToBuffer("%s", f0mnem); + data += 2; + } else if (f0byte == 0x28) { + data += 2; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("movaps %s,%s", + NameOfXMMRegister(regop), + NameOfXMMRegister(rm)); + data++; + } else if (f0byte >= 0x53 && f0byte <= 0x5F) { + const char* const pseudo_op[] = { + "rcpps", + "andps", + "andnps", + "orps", + "xorps", + "addps", + "mulps", + "cvtps2pd", + "cvtdq2ps", + "subps", + "minps", + "divps", + "maxps", + }; + + data += 2; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("%s %s,", + pseudo_op[f0byte - 0x53], + NameOfXMMRegister(regop)); + data += PrintRightXMMOperand(data); + } else if (f0byte == 0x50) { + data += 2; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("movmskps %s,%s", + NameOfCPURegister(regop), + NameOfXMMRegister(rm)); + data++; + } else if (f0byte== 0xC6) { + // shufps xmm, xmm/m128, imm8 + data += 2; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + int8_t imm8 = static_cast(data[1]); + AppendToBuffer("shufps %s,%s,%d", + NameOfXMMRegister(rm), + NameOfXMMRegister(regop), + static_cast(imm8)); + data += 2; + } else if ((f0byte & 0xF0) == 0x80) { + data += JumpConditional(data, branch_hint); + } else if (f0byte == 0xBE || f0byte == 0xBF || f0byte == 0xB6 || + f0byte == 0xB7 || f0byte == 0xAF) { + data += 2; + data += PrintOperands(f0mnem, REG_OPER_OP_ORDER, data); + } else if ((f0byte & 0xF0) == 0x90) { + data += SetCC(data); + } else if ((f0byte & 0xF0) == 0x40) { + data += CMov(data); + } else if (f0byte == 0xA4 || f0byte == 0xAC) { + // shld, shrd + data += 2; + AppendToBuffer("%s ", f0mnem); + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + int8_t imm8 = static_cast(data[1]); + data += 2; + AppendToBuffer("%s,%s,%d", NameOfCPURegister(rm), + NameOfCPURegister(regop), static_cast(imm8)); + } else if (f0byte == 0xAB || f0byte == 0xA5 || f0byte == 0xAD) { + // shrd_cl, shld_cl, bts + data += 2; + AppendToBuffer("%s ", f0mnem); + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + data += PrintRightOperand(data); + if (f0byte == 0xAB) { + AppendToBuffer(",%s", NameOfCPURegister(regop)); + } else { + AppendToBuffer(",%s,cl", NameOfCPURegister(regop)); + } + } else if (f0byte == 0xB0) { + // cmpxchg_b + data += 2; + AppendToBuffer("%s ", f0mnem); + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + data += PrintRightOperand(data); + AppendToBuffer(",%s", NameOfByteCPURegister(regop)); + } else if (f0byte == 0xB1) { + // cmpxchg + data += 2; + data += PrintOperands(f0mnem, OPER_REG_OP_ORDER, data); + } else if (f0byte == 0xBC) { + data += 2; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("%s %s,", f0mnem, NameOfCPURegister(regop)); + data += PrintRightOperand(data); + } else if (f0byte == 0xBD) { + data += 2; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("%s %s,", f0mnem, NameOfCPURegister(regop)); + data += PrintRightOperand(data); + } else { + UnimplementedInstruction(); + } + } + break; + + case 0x8F: + { data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + if (regop == eax) { + AppendToBuffer("pop "); + data += PrintRightOperand(data); + } + } + break; + + case 0xFF: + { data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + const char* mnem = NULL; + switch (regop) { + case esi: mnem = "push"; break; + case eax: mnem = "inc"; break; + case ecx: mnem = "dec"; break; + case edx: mnem = "call"; break; + case esp: mnem = "jmp"; break; + default: mnem = "???"; + } + AppendToBuffer("%s ", mnem); + data += PrintRightOperand(data); + } + break; + + case 0xC7: // imm32, fall through + case 0xC6: // imm8 + { bool is_byte = *data == 0xC6; + data++; + if (is_byte) { + AppendToBuffer("%s ", "mov_b"); + data += PrintRightByteOperand(data); + int32_t imm = *data; + AppendToBuffer(",0x%x", imm); + data++; + } else { + AppendToBuffer("%s ", "mov"); + data += PrintRightOperand(data); + int32_t imm = *reinterpret_cast(data); + AppendToBuffer(",0x%x", imm); + data += 4; + } + } + break; + + case 0x80: + { data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + const char* mnem = NULL; + switch (regop) { + case 5: mnem = "subb"; break; + case 7: mnem = "cmpb"; break; + default: UnimplementedInstruction(); + } + AppendToBuffer("%s ", mnem); + data += PrintRightByteOperand(data); + int32_t imm = *data; + AppendToBuffer(",0x%x", imm); + data++; + } + break; + + case 0x88: // 8bit, fall through + case 0x89: // 32bit + { bool is_byte = *data == 0x88; + int mod, regop, rm; + data++; + get_modrm(*data, &mod, ®op, &rm); + if (is_byte) { + AppendToBuffer("%s ", "mov_b"); + data += PrintRightByteOperand(data); + AppendToBuffer(",%s", NameOfByteCPURegister(regop)); + } else { + AppendToBuffer("%s ", "mov"); + data += PrintRightOperand(data); + AppendToBuffer(",%s", NameOfCPURegister(regop)); + } + } + break; + + case 0x66: // prefix + while (*data == 0x66) data++; + if (*data == 0xf && data[1] == 0x1f) { + AppendToBuffer("nop"); // 0x66 prefix + } else if (*data == 0x39) { + data++; + data += PrintOperands("cmpw", OPER_REG_OP_ORDER, data); + } else if (*data == 0x3B) { + data++; + data += PrintOperands("cmpw", REG_OPER_OP_ORDER, data); + } else if (*data == 0x81) { + data++; + AppendToBuffer("cmpw "); + data += PrintRightOperand(data); + int imm = *reinterpret_cast(data); + AppendToBuffer(",0x%x", imm); + data += 2; + } else if (*data == 0x87) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("xchg_w %s,", NameOfCPURegister(regop)); + data += PrintRightOperand(data); + } else if (*data == 0x89) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("mov_w "); + data += PrintRightOperand(data); + AppendToBuffer(",%s", NameOfCPURegister(regop)); + } else if (*data == 0x8B) { + data++; + data += PrintOperands("mov_w", REG_OPER_OP_ORDER, data); + } else if (*data == 0x90) { + AppendToBuffer("nop"); // 0x66 prefix + } else if (*data == 0xC7) { + data++; + AppendToBuffer("%s ", "mov_w"); + data += PrintRightOperand(data); + int imm = *reinterpret_cast(data); + AppendToBuffer(",0x%x", imm); + data += 2; + } else if (*data == 0xF7) { + data++; + AppendToBuffer("%s ", "test_w"); + data += PrintRightOperand(data); + int imm = *reinterpret_cast(data); + AppendToBuffer(",0x%x", imm); + data += 2; + } else if (*data == 0x0F) { + data++; + if (*data == 0x38) { + data++; + if (*data == 0x17) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("ptest %s,%s", + NameOfXMMRegister(regop), + NameOfXMMRegister(rm)); + data++; + } else if (*data == 0x2A) { + // movntdqa + UnimplementedInstruction(); + } else { + UnimplementedInstruction(); + } + } else if (*data == 0x3A) { + data++; + if (*data == 0x0B) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + int8_t imm8 = static_cast(data[1]); + AppendToBuffer("roundsd %s,%s,%d", + NameOfXMMRegister(regop), + NameOfXMMRegister(rm), + static_cast(imm8)); + data += 2; + } else if (*data == 0x16) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, &rm, ®op); + int8_t imm8 = static_cast(data[1]); + AppendToBuffer("pextrd %s,%s,%d", + NameOfCPURegister(regop), + NameOfXMMRegister(rm), + static_cast(imm8)); + data += 2; + } else if (*data == 0x17) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + int8_t imm8 = static_cast(data[1]); + AppendToBuffer("extractps %s,%s,%d", + NameOfCPURegister(rm), + NameOfXMMRegister(regop), + static_cast(imm8)); + data += 2; + } else if (*data == 0x22) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + int8_t imm8 = static_cast(data[1]); + AppendToBuffer("pinsrd %s,%s,%d", + NameOfXMMRegister(regop), + NameOfCPURegister(rm), + static_cast(imm8)); + data += 2; + } else { + UnimplementedInstruction(); + } + } else if (*data == 0x2E || *data == 0x2F) { + const char* mnem = (*data == 0x2E) ? "ucomisd" : "comisd"; + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + if (mod == 0x3) { + AppendToBuffer("%s %s,%s", mnem, + NameOfXMMRegister(regop), + NameOfXMMRegister(rm)); + data++; + } else { + AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop)); + data += PrintRightOperand(data); + } + } else if (*data == 0x50) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("movmskpd %s,%s", + NameOfCPURegister(regop), + NameOfXMMRegister(rm)); + data++; + } else if (*data == 0x54) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("andpd %s,%s", + NameOfXMMRegister(regop), + NameOfXMMRegister(rm)); + data++; + } else if (*data == 0x56) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("orpd %s,%s", + NameOfXMMRegister(regop), + NameOfXMMRegister(rm)); + data++; + } else if (*data == 0x57) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("xorpd %s,%s", + NameOfXMMRegister(regop), + NameOfXMMRegister(rm)); + data++; + } else if (*data == 0x6E) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("movd %s,", NameOfXMMRegister(regop)); + data += PrintRightOperand(data); + } else if (*data == 0x6F) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("movdqa %s,", NameOfXMMRegister(regop)); + data += PrintRightXMMOperand(data); + } else if (*data == 0x70) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + int8_t imm8 = static_cast(data[1]); + AppendToBuffer("pshufd %s,%s,%d", + NameOfXMMRegister(regop), + NameOfXMMRegister(rm), + static_cast(imm8)); + data += 2; + } else if (*data == 0x76) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("pcmpeqd %s,%s", + NameOfXMMRegister(regop), + NameOfXMMRegister(rm)); + data++; + } else if (*data == 0x90) { + data++; + AppendToBuffer("nop"); // 2 byte nop. + } else if (*data == 0xF3) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("psllq %s,%s", + NameOfXMMRegister(regop), + NameOfXMMRegister(rm)); + data++; + } else if (*data == 0x73) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + int8_t imm8 = static_cast(data[1]); + DCHECK(regop == esi || regop == edx); + AppendToBuffer("%s %s,%d", + (regop == esi) ? "psllq" : "psrlq", + NameOfXMMRegister(rm), + static_cast(imm8)); + data += 2; + } else if (*data == 0xD3) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("psrlq %s,%s", + NameOfXMMRegister(regop), + NameOfXMMRegister(rm)); + data++; + } else if (*data == 0x7F) { + AppendToBuffer("movdqa "); + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + data += PrintRightXMMOperand(data); + AppendToBuffer(",%s", NameOfXMMRegister(regop)); + } else if (*data == 0x7E) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("movd "); + data += PrintRightOperand(data); + AppendToBuffer(",%s", NameOfXMMRegister(regop)); + } else if (*data == 0xDB) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("pand %s,%s", + NameOfXMMRegister(regop), + NameOfXMMRegister(rm)); + data++; + } else if (*data == 0xE7) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + if (mod == 3) { + // movntdq + UnimplementedInstruction(); + } else { + UnimplementedInstruction(); + } + } else if (*data == 0xEF) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("pxor %s,%s", + NameOfXMMRegister(regop), + NameOfXMMRegister(rm)); + data++; + } else if (*data == 0xEB) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("por %s,%s", + NameOfXMMRegister(regop), + NameOfXMMRegister(rm)); + data++; + } else if (*data == 0xB1) { + data++; + data += PrintOperands("cmpxchg_w", OPER_REG_OP_ORDER, data); + } else { + UnimplementedInstruction(); + } + } else { + UnimplementedInstruction(); + } + break; + + case 0xFE: + { data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + if (regop == ecx) { + AppendToBuffer("dec_b "); + data += PrintRightOperand(data); + } else { + UnimplementedInstruction(); + } + } + break; + + case 0x68: + AppendToBuffer("push 0x%x", *reinterpret_cast(data+1)); + data += 5; + break; + + case 0x6A: + AppendToBuffer("push 0x%x", *reinterpret_cast(data + 1)); + data += 2; + break; + + case 0xA8: + AppendToBuffer("test al,0x%x", *reinterpret_cast(data+1)); + data += 2; + break; + + case 0xA9: + AppendToBuffer("test eax,0x%x", *reinterpret_cast(data+1)); + data += 5; + break; + + case 0xD1: // fall through + case 0xD3: // fall through + case 0xC1: + data += D1D3C1Instruction(data); + break; + + case 0xD8: // fall through + case 0xD9: // fall through + case 0xDA: // fall through + case 0xDB: // fall through + case 0xDC: // fall through + case 0xDD: // fall through + case 0xDE: // fall through + case 0xDF: + data += FPUInstruction(data); + break; + + case 0xEB: + data += JumpShort(data); + break; + + case 0xF2: + if (*(data+1) == 0x0F) { + byte b2 = *(data+2); + if (b2 == 0x11) { + AppendToBuffer("movsd "); + data += 3; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + data += PrintRightXMMOperand(data); + AppendToBuffer(",%s", NameOfXMMRegister(regop)); + } else if (b2 == 0x10) { + data += 3; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("movsd %s,", NameOfXMMRegister(regop)); + data += PrintRightXMMOperand(data); + } else if (b2 == 0x5A) { + data += 3; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("cvtsd2ss %s,", NameOfXMMRegister(regop)); + data += PrintRightXMMOperand(data); + } else { + const char* mnem = "?"; + switch (b2) { + case 0x2A: mnem = "cvtsi2sd"; break; + case 0x2C: mnem = "cvttsd2si"; break; + case 0x2D: mnem = "cvtsd2si"; break; + case 0x51: mnem = "sqrtsd"; break; + case 0x58: mnem = "addsd"; break; + case 0x59: mnem = "mulsd"; break; + case 0x5C: mnem = "subsd"; break; + case 0x5E: mnem = "divsd"; break; + } + data += 3; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + if (b2 == 0x2A) { + AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop)); + data += PrintRightOperand(data); + } else if (b2 == 0x2C || b2 == 0x2D) { + AppendToBuffer("%s %s,", mnem, NameOfCPURegister(regop)); + data += PrintRightXMMOperand(data); + } else if (b2 == 0xC2) { + // Intel manual 2A, Table 3-18. + const char* const pseudo_op[] = { + "cmpeqsd", + "cmpltsd", + "cmplesd", + "cmpunordsd", + "cmpneqsd", + "cmpnltsd", + "cmpnlesd", + "cmpordsd" + }; + AppendToBuffer("%s %s,%s", + pseudo_op[data[1]], + NameOfXMMRegister(regop), + NameOfXMMRegister(rm)); + data += 2; + } else { + AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop)); + data += PrintRightXMMOperand(data); + } + } + } else { + UnimplementedInstruction(); + } + break; + + case 0xF3: + if (*(data+1) == 0x0F) { + byte b2 = *(data+2); + if (b2 == 0x11) { + AppendToBuffer("movss "); + data += 3; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + data += PrintRightXMMOperand(data); + AppendToBuffer(",%s", NameOfXMMRegister(regop)); + } else if (b2 == 0x10) { + data += 3; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("movss %s,", NameOfXMMRegister(regop)); + data += PrintRightXMMOperand(data); + } else if (b2 == 0x2C) { + data += 3; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("cvttss2si %s,", NameOfCPURegister(regop)); + data += PrintRightXMMOperand(data); + } else if (b2 == 0x5A) { + data += 3; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("cvtss2sd %s,", NameOfXMMRegister(regop)); + data += PrintRightXMMOperand(data); + } else if (b2 == 0x6F) { + data += 3; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("movdqu %s,", NameOfXMMRegister(regop)); + data += PrintRightXMMOperand(data); + } else if (b2 == 0x7F) { + AppendToBuffer("movdqu "); + data += 3; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + data += PrintRightXMMOperand(data); + AppendToBuffer(",%s", NameOfXMMRegister(regop)); + } else { + UnimplementedInstruction(); + } + } else if (*(data+1) == 0xA5) { + data += 2; + AppendToBuffer("rep_movs"); + } else if (*(data+1) == 0xAB) { + data += 2; + AppendToBuffer("rep_stos"); + } else { + UnimplementedInstruction(); + } + break; + + case 0xF7: + data += F7Instruction(data); + break; + + default: + UnimplementedInstruction(); + } + } + + if (tmp_buffer_pos_ < sizeof tmp_buffer_) { + tmp_buffer_[tmp_buffer_pos_] = '\0'; + } + + int instr_len = data - instr; + if (instr_len == 0) { + printf("%02x", *data); + } + DCHECK(instr_len > 0); // Ensure progress. + + int outp = 0; + // Instruction bytes. + for (byte* bp = instr; bp < data; bp++) { + outp += v8::internal::SNPrintF(out_buffer + outp, "%02x", *bp); + } + for (int i = 6 - instr_len; i >= 0; i--) { + outp += v8::internal::SNPrintF(out_buffer + outp, " "); + } + + outp += v8::internal::SNPrintF(out_buffer + outp, " %s", tmp_buffer_.start()); + return instr_len; +} // NOLINT (function is too long) + + +//------------------------------------------------------------------------------ + + +static const char* const cpu_regs[8] = { + "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi" +}; + + +static const char* const byte_cpu_regs[8] = { + "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh" +}; + + +static const char* const xmm_regs[8] = { + "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7" +}; + + +const char* NameConverter::NameOfAddress(byte* addr) const { + v8::internal::SNPrintF(tmp_buffer_, "%p", static_cast(addr)); + return tmp_buffer_.start(); +} + + +const char* NameConverter::NameOfConstant(byte* addr) const { + return NameOfAddress(addr); +} + + +const char* NameConverter::NameOfCPURegister(int reg) const { + if (0 <= reg && reg < 8) return cpu_regs[reg]; + return "noreg"; +} + + +const char* NameConverter::NameOfByteCPURegister(int reg) const { + if (0 <= reg && reg < 8) return byte_cpu_regs[reg]; + return "noreg"; +} + + +const char* NameConverter::NameOfXMMRegister(int reg) const { + if (0 <= reg && reg < 8) return xmm_regs[reg]; + return "noxmmreg"; +} + + +const char* NameConverter::NameInCode(byte* addr) const { + // X87 does not embed debug strings at the moment. + UNREACHABLE(); +} + + +//------------------------------------------------------------------------------ + +Disassembler::Disassembler(const NameConverter& converter) + : converter_(converter) {} + + +Disassembler::~Disassembler() {} + + +int Disassembler::InstructionDecode(v8::internal::Vector buffer, + byte* instruction) { + DisassemblerX87 d(converter_, false /*do not crash if unimplemented*/); + return d.InstructionDecode(buffer, instruction); +} + + +// The IA-32 assembler does not currently use constant pools. +int Disassembler::ConstantPoolSizeAt(byte* instruction) { return -1; } + + +/*static*/ void Disassembler::Disassemble(FILE* f, byte* begin, byte* end) { + NameConverter converter; + Disassembler d(converter); + for (byte* pc = begin; pc < end;) { + v8::internal::EmbeddedVector buffer; + buffer[0] = '\0'; + byte* prev_pc = pc; + pc += d.InstructionDecode(buffer, pc); + fprintf(f, "%p", static_cast(prev_pc)); + fprintf(f, " "); + + for (byte* bp = prev_pc; bp < pc; bp++) { + fprintf(f, "%02x", *bp); + } + for (int i = 6 - (pc - prev_pc); i >= 0; i--) { + fprintf(f, " "); + } + fprintf(f, " %s\n", buffer.start()); + } +} + + +} // namespace disasm + +#endif // V8_TARGET_ARCH_X87 diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/x87/frames-x87.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/x87/frames-x87.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/x87/frames-x87.cc 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/x87/frames-x87.cc 2017-12-25 17:42:57.222465544 +0100 @@ -0,0 +1,27 @@ +// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#if V8_TARGET_ARCH_X87 + +#include "src/assembler.h" +#include "src/frames.h" +#include "src/x87/assembler-x87-inl.h" +#include "src/x87/assembler-x87.h" +#include "src/x87/frames-x87.h" + +namespace v8 { +namespace internal { + + +Register JavaScriptFrame::fp_register() { return ebp; } +Register JavaScriptFrame::context_register() { return esi; } +Register JavaScriptFrame::constant_pool_pointer_register() { + UNREACHABLE(); +} + + +} // namespace internal +} // namespace v8 + +#endif // V8_TARGET_ARCH_X87 diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/x87/frames-x87.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/x87/frames-x87.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/x87/frames-x87.h 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/x87/frames-x87.h 2017-12-25 17:42:57.222465544 +0100 @@ -0,0 +1,78 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_X87_FRAMES_X87_H_ +#define V8_X87_FRAMES_X87_H_ + +namespace v8 { +namespace internal { + + +// Register lists +// Note that the bit values must match those used in actual instruction encoding +const int kNumRegs = 8; + + +// Caller-saved registers +const RegList kJSCallerSaved = + 1 << 0 | // eax + 1 << 1 | // ecx + 1 << 2 | // edx + 1 << 3 | // ebx - used as a caller-saved register in JavaScript code + 1 << 7; // edi - callee function + +const int kNumJSCallerSaved = 5; + + +// Number of registers for which space is reserved in safepoints. +const int kNumSafepointRegisters = 8; + +// ---------------------------------------------------- + + +class EntryFrameConstants : public AllStatic { + public: + static const int kCallerFPOffset = -6 * kPointerSize; + + static const int kNewTargetArgOffset = +2 * kPointerSize; + static const int kFunctionArgOffset = +3 * kPointerSize; + static const int kReceiverArgOffset = +4 * kPointerSize; + static const int kArgcOffset = +5 * kPointerSize; + static const int kArgvOffset = +6 * kPointerSize; +}; + +class ExitFrameConstants : public TypedFrameConstants { + public: + static const int kSPOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(0); + static const int kCodeOffset = TYPED_FRAME_PUSHED_VALUE_OFFSET(1); + DEFINE_TYPED_FRAME_SIZES(2); + + static const int kCallerFPOffset = 0 * kPointerSize; + static const int kCallerPCOffset = +1 * kPointerSize; + + // FP-relative displacement of the caller's SP. It points just + // below the saved PC. + static const int kCallerSPDisplacement = +2 * kPointerSize; + + static const int kConstantPoolOffset = 0; // Not used +}; + + +class JavaScriptFrameConstants : public AllStatic { + public: + // FP-relative. + static const int kLocal0Offset = StandardFrameConstants::kExpressionsOffset; + static const int kLastParameterOffset = +2 * kPointerSize; + static const int kFunctionOffset = StandardFrameConstants::kFunctionOffset; + + // Caller SP-relative. + static const int kParam0Offset = -2 * kPointerSize; + static const int kReceiverOffset = -1 * kPointerSize; +}; + + +} // namespace internal +} // namespace v8 + +#endif // V8_X87_FRAMES_X87_H_ diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/x87/interface-descriptors-x87.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/x87/interface-descriptors-x87.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/x87/interface-descriptors-x87.cc 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/x87/interface-descriptors-x87.cc 2017-12-29 01:59:03.000000000 +0100 @@ -0,0 +1,450 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#if V8_TARGET_ARCH_X87 + +#include "src/interface-descriptors.h" + +namespace v8 { +namespace internal { + +const Register CallInterfaceDescriptor::ContextRegister() { return esi; } + +void CallInterfaceDescriptor::DefaultInitializePlatformSpecific( + CallInterfaceDescriptorData* data, int register_parameter_count) { + const Register default_stub_registers[] = {eax, ebx, ecx, edx, edi}; + CHECK_LE(static_cast(register_parameter_count), + arraysize(default_stub_registers)); + data->InitializePlatformSpecific(register_parameter_count, + default_stub_registers); +} + +const Register FastNewFunctionContextDescriptor::FunctionRegister() { + return edi; +} +const Register FastNewFunctionContextDescriptor::SlotsRegister() { return eax; } + +const Register LoadDescriptor::ReceiverRegister() { return edx; } +const Register LoadDescriptor::NameRegister() { return ecx; } +const Register LoadDescriptor::SlotRegister() { return eax; } + +const Register LoadWithVectorDescriptor::VectorRegister() { return ebx; } + +const Register LoadICProtoArrayDescriptor::HandlerRegister() { return edi; } + +const Register StoreDescriptor::ReceiverRegister() { return edx; } +const Register StoreDescriptor::NameRegister() { return ecx; } +const Register StoreDescriptor::ValueRegister() { return eax; } +const Register StoreDescriptor::SlotRegister() { return edi; } + +const Register StoreWithVectorDescriptor::VectorRegister() { return ebx; } + +const Register StoreTransitionDescriptor::SlotRegister() { return no_reg; } +const Register StoreTransitionDescriptor::VectorRegister() { return ebx; } +const Register StoreTransitionDescriptor::MapRegister() { return edi; } + +const Register StringCompareDescriptor::LeftRegister() { return edx; } +const Register StringCompareDescriptor::RightRegister() { return eax; } + +const Register StringConcatDescriptor::ArgumentsCountRegister() { return eax; } + +const Register ApiGetterDescriptor::HolderRegister() { return ecx; } +const Register ApiGetterDescriptor::CallbackRegister() { return eax; } + +const Register MathPowTaggedDescriptor::exponent() { return eax; } + +const Register MathPowIntegerDescriptor::exponent() { + return MathPowTaggedDescriptor::exponent(); +} + + +const Register GrowArrayElementsDescriptor::ObjectRegister() { return eax; } +const Register GrowArrayElementsDescriptor::KeyRegister() { return ebx; } + + +void FastNewClosureDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // SharedFunctionInfo, vector, slot index. + Register registers[] = {ebx, ecx, edx}; + data->InitializePlatformSpecific(arraysize(registers), registers, NULL); +} + +// static +const Register TypeConversionDescriptor::ArgumentRegister() { return eax; } + +void TypeofDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = {ebx}; + data->InitializePlatformSpecific(arraysize(registers), registers, NULL); +} + + +void FastCloneRegExpDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = {edi, eax, ecx, edx}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + + +void FastCloneShallowArrayDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = {eax, ebx, ecx}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + + +void FastCloneShallowObjectDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = {eax, ebx, ecx, edx}; + data->InitializePlatformSpecific(arraysize(registers), registers, NULL); +} + + +void CreateAllocationSiteDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = {ebx, edx}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + + +void CreateWeakCellDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = {ebx, edx, edi}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + + +void CallFunctionDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = {edi}; + data->InitializePlatformSpecific(arraysize(registers), registers, NULL); +} + +void CallICTrampolineDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = {edi, eax, edx}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void CallICDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = {edi, eax, edx, ebx}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + + +void CallConstructDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // eax : number of arguments + // ebx : feedback vector + // ecx : new target (for IsSuperConstructorCall) + // edx : slot in feedback vector (Smi, for RecordCallTarget) + // edi : constructor function + // TODO(turbofan): So far we don't gather type feedback and hence skip the + // slot parameter, but ArrayConstructStub needs the vector to be undefined. + Register registers[] = {eax, edi, ecx, ebx}; + data->InitializePlatformSpecific(arraysize(registers), registers, NULL); +} + + +void CallTrampolineDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // eax : number of arguments + // edi : the target to call + Register registers[] = {edi, eax}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void CallVarargsDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // eax : number of arguments (on the stack, not including receiver) + // edi : the target to call + // ebx : arguments list (FixedArray) + // ecx : arguments list length (untagged) + Register registers[] = {edi, eax, ebx, ecx}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void CallForwardVarargsDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // eax : number of arguments + // ecx : start index (to support rest parameters) + // edi : the target to call + Register registers[] = {edi, eax, ecx}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void CallWithSpreadDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // eax : number of arguments (on the stack, not including receiver) + // edi : the target to call + // ebx : the object to spread + Register registers[] = {edi, eax, ebx}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void CallWithArrayLikeDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // edi : the target to call + // ebx : the arguments list + Register registers[] = {edi, ebx}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void ConstructVarargsDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // eax : number of arguments (on the stack, not including receiver) + // edi : the target to call + // edx : the new target + // ebx : arguments list (FixedArray) + // ecx : arguments list length (untagged) + Register registers[] = {edi, edx, eax, ebx, ecx}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void ConstructForwardVarargsDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // eax : number of arguments + // edx : the new target + // ecx : start index (to support rest parameters) + // edi : the target to call + Register registers[] = {edi, edx, eax, ecx}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void ConstructWithSpreadDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // eax : number of arguments (on the stack, not including receiver) + // edi : the target to call + // edx : the new target + // ebx : the object to spread + Register registers[] = {edi, edx, eax, ebx}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void ConstructWithArrayLikeDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // edi : the target to call + // edx : the new target + // ebx : the arguments list + Register registers[] = {edi, edx, ebx}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void ConstructStubDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // eax : number of arguments + // edx : the new target + // edi : the target to call + // ebx : allocation site or undefined + Register registers[] = {edi, edx, eax, ebx}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + + +void ConstructTrampolineDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // eax : number of arguments + // edx : the new target + // edi : the target to call + Register registers[] = {edi, edx, eax}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + + +void TransitionElementsKindDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = {eax, ebx}; + data->InitializePlatformSpecific(arraysize(registers), registers, NULL); +} + + +void AllocateHeapNumberDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // register state + data->InitializePlatformSpecific(0, nullptr, nullptr); +} + +void ArrayConstructorDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // kTarget, kNewTarget, kActualArgumentsCount, kAllocationSite + Register registers[] = {edi, edx, eax, ebx}; + data->InitializePlatformSpecific(arraysize(registers), registers, NULL); +} + +void ArrayNoArgumentConstructorDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // register state + // eax -- number of arguments + // edi -- function + // ebx -- allocation site with elements kind + Register registers[] = {edi, ebx, eax}; + data->InitializePlatformSpecific(arraysize(registers), registers, NULL); +} + +void ArraySingleArgumentConstructorDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // register state + // eax -- number of arguments + // edi -- function + // ebx -- allocation site with elements kind + Register registers[] = {edi, ebx, eax}; + data->InitializePlatformSpecific(arraysize(registers), registers, NULL); +} + +void ArrayNArgumentsConstructorDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // register state + // eax -- number of arguments + // edi -- function + // ebx -- allocation site with elements kind + Register registers[] = {edi, ebx, eax}; + data->InitializePlatformSpecific(arraysize(registers), registers, NULL); +} + +void VarArgFunctionDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // stack param count needs (arg count) + Register registers[] = {eax}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void CompareDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = {edx, eax}; + data->InitializePlatformSpecific(arraysize(registers), registers, NULL); +} + + +void BinaryOpDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = {edx, eax}; + data->InitializePlatformSpecific(arraysize(registers), registers, NULL); +} + + +void BinaryOpWithAllocationSiteDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = {ecx, edx, eax}; + data->InitializePlatformSpecific(arraysize(registers), registers, NULL); +} + +void BinaryOpWithVectorDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + // register state + // edx -- lhs + // eax -- rhs + // edi -- slot id + // ebx -- vector + Register registers[] = {edx, eax, edi, ebx}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void CountOpDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = {eax}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void StringAddDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = {edx, eax}; + data->InitializePlatformSpecific(arraysize(registers), registers, NULL); +} + +void ArgumentAdaptorDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = { + edi, // JSFunction + edx, // the new target + eax, // actual number of arguments + ebx, // expected number of arguments + }; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void ApiCallbackDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = { + edi, // callee + ebx, // call_data + ecx, // holder + edx, // api_function_address + }; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void InterpreterDispatchDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = { + kInterpreterAccumulatorRegister, kInterpreterBytecodeOffsetRegister, + kInterpreterBytecodeArrayRegister, kInterpreterDispatchTableRegister}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void InterpreterPushArgsThenCallDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = { + eax, // argument count (not including receiver) + ebx, // address of first argument + edi // the target callable to be call + }; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void InterpreterPushArgsThenConstructDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = { + eax, // argument count (not including receiver) + edx, // new target + edi, // constructor + ebx, // allocation site feedback + ecx, // address of first argument + }; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void InterpreterPushArgsThenConstructArrayDescriptor:: + InitializePlatformSpecific(CallInterfaceDescriptorData* data) { + Register registers[] = { + eax, // argument count (not including receiver) + edx, // target to the call. It is checked to be Array function. + ebx, // allocation site feedback + ecx, // address of first argument + }; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void InterpreterCEntryDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = { + eax, // argument count (argc) + ecx, // address of first argument (argv) + ebx // the runtime function to call + }; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void ResumeGeneratorDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = { + eax, // the value to pass to the generator + ebx, // the JSGeneratorObject to resume + edx // the resume mode (tagged) + }; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +void FrameDropperTrampolineDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = { + ebx, // loaded new FP + }; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + +} // namespace internal +} // namespace v8 + +#endif // V8_TARGET_ARCH_X87 diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/x87/macro-assembler-x87.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/x87/macro-assembler-x87.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/x87/macro-assembler-x87.cc 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/x87/macro-assembler-x87.cc 2017-12-29 02:00:17.837154172 +0100 @@ -0,0 +1,2584 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#if V8_TARGET_ARCH_X87 + +#include "src/base/bits.h" +#include "src/base/division-by-constant.h" +#include "src/base/utils/random-number-generator.h" +#include "src/bootstrapper.h" +#include "src/codegen.h" +#include "src/debug/debug.h" +#include "src/runtime/runtime.h" +#include "src/x87/frames-x87.h" +#include "src/x87/macro-assembler-x87.h" + +namespace v8 { +namespace internal { + +// ------------------------------------------------------------------------- +// MacroAssembler implementation. + +MacroAssembler::MacroAssembler(Isolate* isolate, void* buffer, int size, + CodeObjectRequired create_code_object) + : TurboAssembler(isolate, buffer, size, create_code_object), + jit_cookie_(0) { + if (FLAG_mask_constants_with_cookie) { + jit_cookie_ = isolate->random_number_generator()->NextInt(); + } +} + + +void MacroAssembler::Load(Register dst, const Operand& src, Representation r) { + DCHECK(!r.IsDouble()); + if (r.IsInteger8()) { + movsx_b(dst, src); + } else if (r.IsUInteger8()) { + movzx_b(dst, src); + } else if (r.IsInteger16()) { + movsx_w(dst, src); + } else if (r.IsUInteger16()) { + movzx_w(dst, src); + } else { + mov(dst, src); + } +} + + +void MacroAssembler::Store(Register src, const Operand& dst, Representation r) { + DCHECK(!r.IsDouble()); + if (r.IsInteger8() || r.IsUInteger8()) { + mov_b(dst, src); + } else if (r.IsInteger16() || r.IsUInteger16()) { + mov_w(dst, src); + } else { + if (r.IsHeapObject()) { + AssertNotSmi(src); + } else if (r.IsSmi()) { + AssertSmi(src); + } + mov(dst, src); + } +} + + +void MacroAssembler::LoadRoot(Register destination, Heap::RootListIndex index) { + if (isolate()->heap()->RootCanBeTreatedAsConstant(index)) { + Handle object = isolate()->heap()->root_handle(index); + if (object->IsHeapObject()) { + mov(destination, Handle::cast(object)); + } else { + mov(destination, Immediate(Smi::cast(*object))); + } + return; + } + ExternalReference roots_array_start = + ExternalReference::roots_array_start(isolate()); + mov(destination, Immediate(index)); + mov(destination, Operand::StaticArray(destination, + times_pointer_size, + roots_array_start)); +} + + +void MacroAssembler::StoreRoot(Register source, + Register scratch, + Heap::RootListIndex index) { + DCHECK(Heap::RootCanBeWrittenAfterInitialization(index)); + ExternalReference roots_array_start = + ExternalReference::roots_array_start(isolate()); + mov(scratch, Immediate(index)); + mov(Operand::StaticArray(scratch, times_pointer_size, roots_array_start), + source); +} + + +void MacroAssembler::CompareRoot(Register with, + Register scratch, + Heap::RootListIndex index) { + ExternalReference roots_array_start = + ExternalReference::roots_array_start(isolate()); + mov(scratch, Immediate(index)); + cmp(with, Operand::StaticArray(scratch, + times_pointer_size, + roots_array_start)); +} + + +void MacroAssembler::CompareRoot(Register with, Heap::RootListIndex index) { + DCHECK(isolate()->heap()->RootCanBeTreatedAsConstant(index)); + Handle object = isolate()->heap()->root_handle(index); + if (object->IsHeapObject()) { + cmp(with, Handle::cast(object)); + } else { + cmp(with, Immediate(Smi::cast(*object))); + } +} + + +void MacroAssembler::CompareRoot(const Operand& with, + Heap::RootListIndex index) { + DCHECK(isolate()->heap()->RootCanBeTreatedAsConstant(index)); + Handle object = isolate()->heap()->root_handle(index); + if (object->IsHeapObject()) { + cmp(with, Handle::cast(object)); + } else { + cmp(with, Immediate(Smi::cast(*object))); + } +} + + +void MacroAssembler::PushRoot(Heap::RootListIndex index) { + DCHECK(isolate()->heap()->RootCanBeTreatedAsConstant(index)); + PushObject(isolate()->heap()->root_handle(index)); +} + +#define REG(Name) \ + { Register::kCode_##Name } + +static const Register saved_regs[] = {REG(eax), REG(ecx), REG(edx)}; + +#undef REG + +static const int kNumberOfSavedRegs = sizeof(saved_regs) / sizeof(Register); + +void MacroAssembler::PushCallerSaved(SaveFPRegsMode fp_mode, + Register exclusion1, Register exclusion2, + Register exclusion3) { + // We don't allow a GC during a store buffer overflow so there is no need to + // store the registers in any particular way, but we do have to store and + // restore them. + for (int i = 0; i < kNumberOfSavedRegs; i++) { + Register reg = saved_regs[i]; + if (!reg.is(exclusion1) && !reg.is(exclusion2) && !reg.is(exclusion3)) { + push(reg); + } + } + if (fp_mode == kSaveFPRegs) { + // Save FPU state in m108byte. + sub(esp, Immediate(108)); + fnsave(Operand(esp, 0)); + } +} + +void MacroAssembler::PopCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1, + Register exclusion2, Register exclusion3) { + if (fp_mode == kSaveFPRegs) { + // Restore FPU state in m108byte. + frstor(Operand(esp, 0)); + add(esp, Immediate(108)); + } + + for (int i = kNumberOfSavedRegs - 1; i >= 0; i--) { + Register reg = saved_regs[i]; + if (!reg.is(exclusion1) && !reg.is(exclusion2) && !reg.is(exclusion3)) { + pop(reg); + } + } +} + +void MacroAssembler::InNewSpace(Register object, Register scratch, Condition cc, + Label* condition_met, + Label::Distance distance) { + CheckPageFlag(object, scratch, MemoryChunk::kIsInNewSpaceMask, cc, + condition_met, distance); +} + + +void MacroAssembler::RememberedSetHelper( + Register object, // Only used for debug checks. + Register addr, Register scratch, SaveFPRegsMode save_fp, + MacroAssembler::RememberedSetFinalAction and_then) { + Label done; + if (emit_debug_code()) { + Label ok; + JumpIfNotInNewSpace(object, scratch, &ok, Label::kNear); + int3(); + bind(&ok); + } + // Load store buffer top. + ExternalReference store_buffer = + ExternalReference::store_buffer_top(isolate()); + mov(scratch, Operand::StaticVariable(store_buffer)); + // Store pointer to buffer. + mov(Operand(scratch, 0), addr); + // Increment buffer top. + add(scratch, Immediate(kPointerSize)); + // Write back new top of buffer. + mov(Operand::StaticVariable(store_buffer), scratch); + // Call stub on end of buffer. + // Check for end of buffer. + test(scratch, Immediate(StoreBuffer::kStoreBufferMask)); + if (and_then == kReturnAtEnd) { + Label buffer_overflowed; + j(equal, &buffer_overflowed, Label::kNear); + ret(0); + bind(&buffer_overflowed); + } else { + DCHECK(and_then == kFallThroughAtEnd); + j(not_equal, &done, Label::kNear); + } + StoreBufferOverflowStub store_buffer_overflow(isolate(), save_fp); + CallStub(&store_buffer_overflow); + if (and_then == kReturnAtEnd) { + ret(0); + } else { + DCHECK(and_then == kFallThroughAtEnd); + bind(&done); + } +} + + +void MacroAssembler::ClampTOSToUint8(Register result_reg) { + Label done, conv_failure; + sub(esp, Immediate(kPointerSize)); + fnclex(); + fist_s(Operand(esp, 0)); + pop(result_reg); + X87CheckIA(); + j(equal, &conv_failure, Label::kNear); + test(result_reg, Immediate(0xFFFFFF00)); + j(zero, &done, Label::kNear); + setcc(sign, result_reg); + sub(result_reg, Immediate(1)); + and_(result_reg, Immediate(255)); + jmp(&done, Label::kNear); + bind(&conv_failure); + fnclex(); + fldz(); + fld(1); + FCmp(); + setcc(below, result_reg); // 1 if negative, 0 if positive. + dec_b(result_reg); // 0 if negative, 255 if positive. + bind(&done); +} + + +void MacroAssembler::ClampUint8(Register reg) { + Label done; + test(reg, Immediate(0xFFFFFF00)); + j(zero, &done, Label::kNear); + setcc(negative, reg); // 1 if negative, 0 if positive. + dec_b(reg); // 0 if negative, 255 if positive. + bind(&done); +} + + +void TurboAssembler::SlowTruncateToIDelayed(Zone* zone, Register result_reg, + Register input_reg, int offset) { + CallStubDelayed( + new (zone) DoubleToIStub(nullptr, input_reg, result_reg, offset, true)); +} + +void MacroAssembler::SlowTruncateToI(Register result_reg, + Register input_reg, + int offset) { + DoubleToIStub stub(isolate(), input_reg, result_reg, offset, true); + CallStub(&stub); +} + + +void TurboAssembler::TruncateX87TOSToI(Zone* zone, Register result_reg) { + sub(esp, Immediate(kDoubleSize)); + fst_d(MemOperand(esp, 0)); + SlowTruncateToIDelayed(zone, result_reg, esp, 0); + add(esp, Immediate(kDoubleSize)); +} + + +void MacroAssembler::X87TOSToI(Register result_reg, + MinusZeroMode minus_zero_mode, + Label* lost_precision, Label* is_nan, + Label* minus_zero, Label::Distance dst) { + Label done; + sub(esp, Immediate(kPointerSize)); + fld(0); + fist_s(MemOperand(esp, 0)); + fild_s(MemOperand(esp, 0)); + pop(result_reg); + FCmp(); + j(not_equal, lost_precision, dst); + j(parity_even, is_nan, dst); + if (minus_zero_mode == FAIL_ON_MINUS_ZERO) { + test(result_reg, Operand(result_reg)); + j(not_zero, &done, Label::kNear); + // To check for minus zero, we load the value again as float, and check + // if that is still 0. + sub(esp, Immediate(kPointerSize)); + fst_s(MemOperand(esp, 0)); + pop(result_reg); + test(result_reg, Operand(result_reg)); + j(not_zero, minus_zero, dst); + } + bind(&done); +} + + +void MacroAssembler::TruncateHeapNumberToI(Register result_reg, + Register input_reg) { + Label done, slow_case; + + SlowTruncateToI(result_reg, input_reg); + bind(&done); +} + + +void TurboAssembler::LoadUint32NoSSE2(const Operand& src) { + Label done; + push(src); + fild_s(Operand(esp, 0)); + cmp(src, Immediate(0)); + j(not_sign, &done, Label::kNear); + ExternalReference uint32_bias = + ExternalReference::address_of_uint32_bias(); + fld_d(Operand::StaticVariable(uint32_bias)); + faddp(1); + bind(&done); + add(esp, Immediate(kPointerSize)); +} + + +void MacroAssembler::RecordWriteField( + Register object, int offset, Register value, Register dst, + SaveFPRegsMode save_fp, RememberedSetAction remembered_set_action, + SmiCheck smi_check, PointersToHereCheck pointers_to_here_check_for_value) { + // First, check if a write barrier is even needed. The tests below + // catch stores of Smis. + Label done; + + // Skip barrier if writing a smi. + if (smi_check == INLINE_SMI_CHECK) { + JumpIfSmi(value, &done, Label::kNear); + } + + // Although the object register is tagged, the offset is relative to the start + // of the object, so so offset must be a multiple of kPointerSize. + DCHECK(IsAligned(offset, kPointerSize)); + + lea(dst, FieldOperand(object, offset)); + if (emit_debug_code()) { + Label ok; + test_b(dst, Immediate(kPointerSize - 1)); + j(zero, &ok, Label::kNear); + int3(); + bind(&ok); + } + + RecordWrite(object, dst, value, save_fp, remembered_set_action, + OMIT_SMI_CHECK, pointers_to_here_check_for_value); + + bind(&done); + + // Clobber clobbered input registers when running with the debug-code flag + // turned on to provoke errors. + if (emit_debug_code()) { + mov(value, Immediate(bit_cast(kZapValue))); + mov(dst, Immediate(bit_cast(kZapValue))); + } +} + + +void MacroAssembler::RecordWriteForMap(Register object, Handle map, + Register scratch1, Register scratch2, + SaveFPRegsMode save_fp) { + Label done; + + Register address = scratch1; + Register value = scratch2; + if (emit_debug_code()) { + Label ok; + lea(address, FieldOperand(object, HeapObject::kMapOffset)); + test_b(address, Immediate(kPointerSize - 1)); + j(zero, &ok, Label::kNear); + int3(); + bind(&ok); + } + + DCHECK(!object.is(value)); + DCHECK(!object.is(address)); + DCHECK(!value.is(address)); + AssertNotSmi(object); + + if (!FLAG_incremental_marking) { + return; + } + + // Compute the address. + lea(address, FieldOperand(object, HeapObject::kMapOffset)); + + // A single check of the map's pages interesting flag suffices, since it is + // only set during incremental collection, and then it's also guaranteed that + // the from object's page's interesting flag is also set. This optimization + // relies on the fact that maps can never be in new space. + DCHECK(!isolate()->heap()->InNewSpace(*map)); + CheckPageFlagForMap(map, + MemoryChunk::kPointersToHereAreInterestingMask, + zero, + &done, + Label::kNear); + + RecordWriteStub stub(isolate(), object, value, address, OMIT_REMEMBERED_SET, + save_fp); + CallStub(&stub); + + bind(&done); + + // Count number of write barriers in generated code. + isolate()->counters()->write_barriers_static()->Increment(); + IncrementCounter(isolate()->counters()->write_barriers_dynamic(), 1); + + // Clobber clobbered input registers when running with the debug-code flag + // turned on to provoke errors. + if (emit_debug_code()) { + mov(value, Immediate(bit_cast(kZapValue))); + mov(scratch1, Immediate(bit_cast(kZapValue))); + mov(scratch2, Immediate(bit_cast(kZapValue))); + } +} + + +void MacroAssembler::RecordWrite( + Register object, Register address, Register value, SaveFPRegsMode fp_mode, + RememberedSetAction remembered_set_action, SmiCheck smi_check, + PointersToHereCheck pointers_to_here_check_for_value) { + DCHECK(!object.is(value)); + DCHECK(!object.is(address)); + DCHECK(!value.is(address)); + AssertNotSmi(object); + + if (remembered_set_action == OMIT_REMEMBERED_SET && + !FLAG_incremental_marking) { + return; + } + + if (emit_debug_code()) { + Label ok; + cmp(value, Operand(address, 0)); + j(equal, &ok, Label::kNear); + int3(); + bind(&ok); + } + + // First, check if a write barrier is even needed. The tests below + // catch stores of Smis and stores into young gen. + Label done; + + if (smi_check == INLINE_SMI_CHECK) { + // Skip barrier if writing a smi. + JumpIfSmi(value, &done, Label::kNear); + } + + if (pointers_to_here_check_for_value != kPointersToHereAreAlwaysInteresting) { + CheckPageFlag(value, + value, // Used as scratch. + MemoryChunk::kPointersToHereAreInterestingMask, + zero, + &done, + Label::kNear); + } + CheckPageFlag(object, + value, // Used as scratch. + MemoryChunk::kPointersFromHereAreInterestingMask, + zero, + &done, + Label::kNear); + + RecordWriteStub stub(isolate(), object, value, address, remembered_set_action, + fp_mode); + CallStub(&stub); + + bind(&done); + + // Count number of write barriers in generated code. + isolate()->counters()->write_barriers_static()->Increment(); + IncrementCounter(isolate()->counters()->write_barriers_dynamic(), 1); + + // Clobber clobbered registers when running with the debug-code flag + // turned on to provoke errors. + if (emit_debug_code()) { + mov(address, Immediate(bit_cast(kZapValue))); + mov(value, Immediate(bit_cast(kZapValue))); + } +} + +void MacroAssembler::RecordWriteCodeEntryField(Register js_function, + Register code_entry, + Register scratch) { + const int offset = JSFunction::kCodeEntryOffset; + + // Since a code entry (value) is always in old space, we don't need to update + // remembered set. If incremental marking is off, there is nothing for us to + // do. + if (!FLAG_incremental_marking) return; + + DCHECK(!js_function.is(code_entry)); + DCHECK(!js_function.is(scratch)); + DCHECK(!code_entry.is(scratch)); + AssertNotSmi(js_function); + + if (emit_debug_code()) { + Label ok; + lea(scratch, FieldOperand(js_function, offset)); + cmp(code_entry, Operand(scratch, 0)); + j(equal, &ok, Label::kNear); + int3(); + bind(&ok); + } + + // First, check if a write barrier is even needed. The tests below + // catch stores of Smis and stores into young gen. + Label done; + + CheckPageFlag(code_entry, scratch, + MemoryChunk::kPointersToHereAreInterestingMask, zero, &done, + Label::kNear); + CheckPageFlag(js_function, scratch, + MemoryChunk::kPointersFromHereAreInterestingMask, zero, &done, + Label::kNear); + + // Save input registers. + push(js_function); + push(code_entry); + + const Register dst = scratch; + lea(dst, FieldOperand(js_function, offset)); + + // Save caller-saved registers. + PushCallerSaved(kDontSaveFPRegs, js_function, code_entry); + + int argument_count = 3; + PrepareCallCFunction(argument_count, code_entry); + mov(Operand(esp, 0 * kPointerSize), js_function); + mov(Operand(esp, 1 * kPointerSize), dst); // Slot. + mov(Operand(esp, 2 * kPointerSize), + Immediate(ExternalReference::isolate_address(isolate()))); + + { + AllowExternalCallThatCantCauseGC scope(this); + CallCFunction( + ExternalReference::incremental_marking_record_write_code_entry_function( + isolate()), + argument_count); + } + + // Restore caller-saved registers. + PopCallerSaved(kDontSaveFPRegs, js_function, code_entry); + + // Restore input registers. + pop(code_entry); + pop(js_function); + + bind(&done); +} + +void MacroAssembler::MaybeDropFrames() { + // Check whether we need to drop frames to restart a function on the stack. + ExternalReference restart_fp = + ExternalReference::debug_restart_fp_address(isolate()); + mov(ebx, Operand::StaticVariable(restart_fp)); + test(ebx, ebx); + j(not_zero, isolate()->builtins()->FrameDropperTrampoline(), + RelocInfo::CODE_TARGET); +} + +void TurboAssembler::ShlPair(Register high, Register low, uint8_t shift) { + if (shift >= 32) { + mov(high, low); + shl(high, shift - 32); + xor_(low, low); + } else { + shld(high, low, shift); + shl(low, shift); + } +} + +void TurboAssembler::ShlPair_cl(Register high, Register low) { + shld_cl(high, low); + shl_cl(low); + Label done; + test(ecx, Immediate(0x20)); + j(equal, &done, Label::kNear); + mov(high, low); + xor_(low, low); + bind(&done); +} + +void TurboAssembler::ShrPair(Register high, Register low, uint8_t shift) { + if (shift >= 32) { + mov(low, high); + shr(low, shift - 32); + xor_(high, high); + } else { + shrd(high, low, shift); + shr(high, shift); + } +} + +void TurboAssembler::ShrPair_cl(Register high, Register low) { + shrd_cl(low, high); + shr_cl(high); + Label done; + test(ecx, Immediate(0x20)); + j(equal, &done, Label::kNear); + mov(low, high); + xor_(high, high); + bind(&done); +} + +void TurboAssembler::SarPair(Register high, Register low, uint8_t shift) { + if (shift >= 32) { + mov(low, high); + sar(low, shift - 32); + sar(high, 31); + } else { + shrd(high, low, shift); + sar(high, shift); + } +} + +void TurboAssembler::SarPair_cl(Register high, Register low) { + shrd_cl(low, high); + sar_cl(high); + Label done; + test(ecx, Immediate(0x20)); + j(equal, &done, Label::kNear); + mov(low, high); + sar(high, 31); + bind(&done); +} + +bool MacroAssembler::IsUnsafeImmediate(const Immediate& x) { + static const int kMaxImmediateBits = 17; + if (!RelocInfo::IsNone(x.rmode_)) return false; + return !is_intn(x.immediate(), kMaxImmediateBits); +} + + +void MacroAssembler::SafeMove(Register dst, const Immediate& x) { + if (IsUnsafeImmediate(x) && jit_cookie() != 0) { + Move(dst, Immediate(x.immediate() ^ jit_cookie())); + xor_(dst, jit_cookie()); + } else { + Move(dst, x); + } +} + + +void MacroAssembler::SafePush(const Immediate& x) { + if (IsUnsafeImmediate(x) && jit_cookie() != 0) { + push(Immediate(x.immediate() ^ jit_cookie())); + xor_(Operand(esp, 0), Immediate(jit_cookie())); + } else { + push(x); + } +} + + +void MacroAssembler::CmpObjectType(Register heap_object, + InstanceType type, + Register map) { + mov(map, FieldOperand(heap_object, HeapObject::kMapOffset)); + CmpInstanceType(map, type); +} + + +void MacroAssembler::CmpInstanceType(Register map, InstanceType type) { + cmpb(FieldOperand(map, Map::kInstanceTypeOffset), Immediate(type)); +} + +void MacroAssembler::CompareMap(Register obj, Handle map) { + cmp(FieldOperand(obj, HeapObject::kMapOffset), map); +} + + +void MacroAssembler::CheckMap(Register obj, + Handle map, + Label* fail, + SmiCheckType smi_check_type) { + if (smi_check_type == DO_SMI_CHECK) { + JumpIfSmi(obj, fail); + } + + CompareMap(obj, map); + j(not_equal, fail); +} + + +Condition MacroAssembler::IsObjectStringType(Register heap_object, + Register map, + Register instance_type) { + mov(map, FieldOperand(heap_object, HeapObject::kMapOffset)); + movzx_b(instance_type, FieldOperand(map, Map::kInstanceTypeOffset)); + STATIC_ASSERT(kNotStringTag != 0); + test(instance_type, Immediate(kIsNotStringMask)); + return zero; +} + + +void TurboAssembler::FCmp() { + fucompp(); + push(eax); + fnstsw_ax(); + sahf(); + pop(eax); +} + + +void MacroAssembler::FXamMinusZero() { + fxam(); + push(eax); + fnstsw_ax(); + and_(eax, Immediate(0x4700)); + // For minus zero, C3 == 1 && C1 == 1. + cmp(eax, Immediate(0x4200)); + pop(eax); + fstp(0); +} + + +void MacroAssembler::FXamSign() { + fxam(); + push(eax); + fnstsw_ax(); + // For negative value (including -0.0), C1 == 1. + and_(eax, Immediate(0x0200)); + pop(eax); + fstp(0); +} + + +void MacroAssembler::X87CheckIA() { + push(eax); + fnstsw_ax(); + // For #IA, IE == 1 && SF == 0. + and_(eax, Immediate(0x0041)); + cmp(eax, Immediate(0x0001)); + pop(eax); +} + + +// rc=00B, round to nearest. +// rc=01B, round down. +// rc=10B, round up. +// rc=11B, round toward zero. +void TurboAssembler::X87SetRC(int rc) { + sub(esp, Immediate(kPointerSize)); + fnstcw(MemOperand(esp, 0)); + and_(MemOperand(esp, 0), Immediate(0xF3FF)); + or_(MemOperand(esp, 0), Immediate(rc)); + fldcw(MemOperand(esp, 0)); + add(esp, Immediate(kPointerSize)); +} + + +void TurboAssembler::X87SetFPUCW(int cw) { + RecordComment("-- X87SetFPUCW start --"); + push(Immediate(cw)); + fldcw(MemOperand(esp, 0)); + add(esp, Immediate(kPointerSize)); + RecordComment("-- X87SetFPUCW end--"); +} + + +void MacroAssembler::AssertSmi(Register object) { + if (emit_debug_code()) { + test(object, Immediate(kSmiTagMask)); + Check(equal, kOperandIsNotASmi); + } +} + + +void MacroAssembler::AssertFixedArray(Register object) { + if (emit_debug_code()) { + test(object, Immediate(kSmiTagMask)); + Check(not_equal, kOperandIsASmiAndNotAFixedArray); + Push(object); + CmpObjectType(object, FIXED_ARRAY_TYPE, object); + Pop(object); + Check(equal, kOperandIsNotAFixedArray); + } +} + + +void MacroAssembler::AssertFunction(Register object) { + if (emit_debug_code()) { + test(object, Immediate(kSmiTagMask)); + Check(not_equal, kOperandIsASmiAndNotAFunction); + Push(object); + CmpObjectType(object, JS_FUNCTION_TYPE, object); + Pop(object); + Check(equal, kOperandIsNotAFunction); + } +} + + +void MacroAssembler::AssertBoundFunction(Register object) { + if (emit_debug_code()) { + test(object, Immediate(kSmiTagMask)); + Check(not_equal, kOperandIsASmiAndNotABoundFunction); + Push(object); + CmpObjectType(object, JS_BOUND_FUNCTION_TYPE, object); + Pop(object); + Check(equal, kOperandIsNotABoundFunction); + } +} + +void MacroAssembler::AssertGeneratorObject(Register object) { + if (emit_debug_code()) { + test(object, Immediate(kSmiTagMask)); + Check(not_equal, kOperandIsASmiAndNotAGeneratorObject); + Push(object); + CmpObjectType(object, JS_GENERATOR_OBJECT_TYPE, object); + Pop(object); + Check(equal, kOperandIsNotAGeneratorObject); + } +} + +void MacroAssembler::AssertUndefinedOrAllocationSite(Register object) { + if (emit_debug_code()) { + Label done_checking; + AssertNotSmi(object); + cmp(object, isolate()->factory()->undefined_value()); + j(equal, &done_checking); + cmp(FieldOperand(object, 0), + Immediate(isolate()->factory()->allocation_site_map())); + Assert(equal, kExpectedUndefinedOrCell); + bind(&done_checking); + } +} + + +void MacroAssembler::AssertNotSmi(Register object) { + if (emit_debug_code()) { + test(object, Immediate(kSmiTagMask)); + Check(not_equal, kOperandIsASmi); + } +} + +void TurboAssembler::StubPrologue(StackFrame::Type type) { + push(ebp); // Caller's frame pointer. + mov(ebp, esp); + push(Immediate(Smi::FromInt(type))); +} + + +void TurboAssembler::Prologue(bool code_pre_aging) { + PredictableCodeSizeScope predictible_code_size_scope(this, + kNoCodeAgeSequenceLength); + if (code_pre_aging) { + // Pre-age the code. + call(isolate()->builtins()->MarkCodeAsExecutedOnce(), + RelocInfo::CODE_AGE_SEQUENCE); + Nop(kNoCodeAgeSequenceLength - Assembler::kCallInstructionLength); + } else { + push(ebp); // Caller's frame pointer. + mov(ebp, esp); + push(esi); // Callee's context. + push(edi); // Callee's JS function. + } +} + +void MacroAssembler::EmitLoadFeedbackVector(Register vector) { + mov(vector, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset)); + mov(vector, FieldOperand(vector, JSFunction::kFeedbackVectorOffset)); + mov(vector, FieldOperand(vector, Cell::kValueOffset)); +} + + +void TurboAssembler::EnterFrame(StackFrame::Type type) { + push(ebp); + mov(ebp, esp); + push(Immediate(Smi::FromInt(type))); + if (type == StackFrame::INTERNAL) { + push(Immediate(CodeObject())); + } + if (emit_debug_code()) { + cmp(Operand(esp, 0), Immediate(isolate()->factory()->undefined_value())); + Check(not_equal, kCodeObjectNotProperlyPatched); + } +} + + +void TurboAssembler::LeaveFrame(StackFrame::Type type) { + if (emit_debug_code()) { + cmp(Operand(ebp, CommonFrameConstants::kContextOrFrameTypeOffset), + Immediate(Smi::FromInt(type))); + Check(equal, kStackFrameTypesMustMatch); + } + leave(); +} + +void MacroAssembler::EnterBuiltinFrame(Register context, Register target, + Register argc) { + Push(ebp); + Move(ebp, esp); + Push(context); + Push(target); + Push(argc); +} + +void MacroAssembler::LeaveBuiltinFrame(Register context, Register target, + Register argc) { + Pop(argc); + Pop(target); + Pop(context); + leave(); +} + +void MacroAssembler::EnterExitFramePrologue(StackFrame::Type frame_type) { + DCHECK(frame_type == StackFrame::EXIT || + frame_type == StackFrame::BUILTIN_EXIT); + + // Set up the frame structure on the stack. + DCHECK_EQ(+2 * kPointerSize, ExitFrameConstants::kCallerSPDisplacement); + DCHECK_EQ(+1 * kPointerSize, ExitFrameConstants::kCallerPCOffset); + DCHECK_EQ(0 * kPointerSize, ExitFrameConstants::kCallerFPOffset); + push(ebp); + mov(ebp, esp); + + // Reserve room for entry stack pointer and push the code object. + push(Immediate(Smi::FromInt(frame_type))); + DCHECK_EQ(-2 * kPointerSize, ExitFrameConstants::kSPOffset); + push(Immediate(0)); // Saved entry sp, patched before call. + DCHECK_EQ(-3 * kPointerSize, ExitFrameConstants::kCodeOffset); + push(Immediate(CodeObject())); // Accessed from ExitFrame::code_slot. + + // Save the frame pointer and the context in top. + ExternalReference c_entry_fp_address(IsolateAddressId::kCEntryFPAddress, + isolate()); + ExternalReference context_address(IsolateAddressId::kContextAddress, + isolate()); + ExternalReference c_function_address(IsolateAddressId::kCFunctionAddress, + isolate()); + mov(Operand::StaticVariable(c_entry_fp_address), ebp); + mov(Operand::StaticVariable(context_address), esi); + mov(Operand::StaticVariable(c_function_address), ebx); +} + + +void MacroAssembler::EnterExitFrameEpilogue(int argc, bool save_doubles) { + // Optionally save FPU state. + if (save_doubles) { + // Store FPU state to m108byte. + int space = 108 + argc * kPointerSize; + sub(esp, Immediate(space)); + const int offset = -ExitFrameConstants::kFixedFrameSizeFromFp; + fnsave(MemOperand(ebp, offset - 108)); + } else { + sub(esp, Immediate(argc * kPointerSize)); + } + + // Get the required frame alignment for the OS. + const int kFrameAlignment = base::OS::ActivationFrameAlignment(); + if (kFrameAlignment > 0) { + DCHECK(base::bits::IsPowerOfTwo(kFrameAlignment)); + and_(esp, -kFrameAlignment); + } + + // Patch the saved entry sp. + mov(Operand(ebp, ExitFrameConstants::kSPOffset), esp); +} + +void MacroAssembler::EnterExitFrame(int argc, bool save_doubles, + StackFrame::Type frame_type) { + EnterExitFramePrologue(frame_type); + + // Set up argc and argv in callee-saved registers. + int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize; + mov(edi, eax); + lea(esi, Operand(ebp, eax, times_4, offset)); + + // Reserve space for argc, argv and isolate. + EnterExitFrameEpilogue(argc, save_doubles); +} + + +void MacroAssembler::EnterApiExitFrame(int argc) { + EnterExitFramePrologue(StackFrame::EXIT); + EnterExitFrameEpilogue(argc, false); +} + + +void MacroAssembler::LeaveExitFrame(bool save_doubles, bool pop_arguments) { + // Optionally restore FPU state. + if (save_doubles) { + const int offset = -ExitFrameConstants::kFixedFrameSizeFromFp; + frstor(MemOperand(ebp, offset - 108)); + } + + if (pop_arguments) { + // Get the return address from the stack and restore the frame pointer. + mov(ecx, Operand(ebp, 1 * kPointerSize)); + mov(ebp, Operand(ebp, 0 * kPointerSize)); + + // Pop the arguments and the receiver from the caller stack. + lea(esp, Operand(esi, 1 * kPointerSize)); + + // Push the return address to get ready to return. + push(ecx); + } else { + // Otherwise just leave the exit frame. + leave(); + } + + LeaveExitFrameEpilogue(true); +} + + +void MacroAssembler::LeaveExitFrameEpilogue(bool restore_context) { + // Restore current context from top and clear it in debug mode. + ExternalReference context_address(IsolateAddressId::kContextAddress, + isolate()); + if (restore_context) { + mov(esi, Operand::StaticVariable(context_address)); + } +#ifdef DEBUG + mov(Operand::StaticVariable(context_address), Immediate(0)); +#endif + + // Clear the top frame. + ExternalReference c_entry_fp_address(IsolateAddressId::kCEntryFPAddress, + isolate()); + mov(Operand::StaticVariable(c_entry_fp_address), Immediate(0)); +} + + +void MacroAssembler::LeaveApiExitFrame(bool restore_context) { + mov(esp, ebp); + pop(ebp); + + LeaveExitFrameEpilogue(restore_context); +} + + +void MacroAssembler::PushStackHandler() { + // Adjust this code if not the case. + STATIC_ASSERT(StackHandlerConstants::kSize == 1 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); + + // Link the current handler as the next handler. + ExternalReference handler_address(IsolateAddressId::kHandlerAddress, + isolate()); + push(Operand::StaticVariable(handler_address)); + + // Set this new handler as the current one. + mov(Operand::StaticVariable(handler_address), esp); +} + + +void MacroAssembler::PopStackHandler() { + STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); + ExternalReference handler_address(IsolateAddressId::kHandlerAddress, + isolate()); + pop(Operand::StaticVariable(handler_address)); + add(esp, Immediate(StackHandlerConstants::kSize - kPointerSize)); +} + + +// Compute the hash code from the untagged key. This must be kept in sync with +// ComputeIntegerHash in utils.h and KeyedLoadGenericStub in +// code-stub-hydrogen.cc +// +// Note: r0 will contain hash code +void MacroAssembler::GetNumberHash(Register r0, Register scratch) { + // Xor original key with a seed. + if (serializer_enabled()) { + ExternalReference roots_array_start = + ExternalReference::roots_array_start(isolate()); + mov(scratch, Immediate(Heap::kHashSeedRootIndex)); + mov(scratch, + Operand::StaticArray(scratch, times_pointer_size, roots_array_start)); + SmiUntag(scratch); + xor_(r0, scratch); + } else { + int32_t seed = isolate()->heap()->HashSeed(); + xor_(r0, Immediate(seed)); + } + + // hash = ~hash + (hash << 15); + mov(scratch, r0); + not_(r0); + shl(scratch, 15); + add(r0, scratch); + // hash = hash ^ (hash >> 12); + mov(scratch, r0); + shr(scratch, 12); + xor_(r0, scratch); + // hash = hash + (hash << 2); + lea(r0, Operand(r0, r0, times_4, 0)); + // hash = hash ^ (hash >> 4); + mov(scratch, r0); + shr(scratch, 4); + xor_(r0, scratch); + // hash = hash * 2057; + imul(r0, r0, 2057); + // hash = hash ^ (hash >> 16); + mov(scratch, r0); + shr(scratch, 16); + xor_(r0, scratch); + and_(r0, 0x3fffffff); +} + +void MacroAssembler::LoadAllocationTopHelper(Register result, + Register scratch, + AllocationFlags flags) { + ExternalReference allocation_top = + AllocationUtils::GetAllocationTopReference(isolate(), flags); + + // Just return if allocation top is already known. + if ((flags & RESULT_CONTAINS_TOP) != 0) { + // No use of scratch if allocation top is provided. + DCHECK(scratch.is(no_reg)); +#ifdef DEBUG + // Assert that result actually contains top on entry. + cmp(result, Operand::StaticVariable(allocation_top)); + Check(equal, kUnexpectedAllocationTop); +#endif + return; + } + + // Move address of new object to result. Use scratch register if available. + if (scratch.is(no_reg)) { + mov(result, Operand::StaticVariable(allocation_top)); + } else { + mov(scratch, Immediate(allocation_top)); + mov(result, Operand(scratch, 0)); + } +} + + +void MacroAssembler::UpdateAllocationTopHelper(Register result_end, + Register scratch, + AllocationFlags flags) { + if (emit_debug_code()) { + test(result_end, Immediate(kObjectAlignmentMask)); + Check(zero, kUnalignedAllocationInNewSpace); + } + + ExternalReference allocation_top = + AllocationUtils::GetAllocationTopReference(isolate(), flags); + + // Update new top. Use scratch if available. + if (scratch.is(no_reg)) { + mov(Operand::StaticVariable(allocation_top), result_end); + } else { + mov(Operand(scratch, 0), result_end); + } +} + + +void MacroAssembler::Allocate(int object_size, + Register result, + Register result_end, + Register scratch, + Label* gc_required, + AllocationFlags flags) { + DCHECK((flags & (RESULT_CONTAINS_TOP | SIZE_IN_WORDS)) == 0); + DCHECK(object_size <= kMaxRegularHeapObjectSize); + if (!FLAG_inline_new) { + if (emit_debug_code()) { + // Trash the registers to simulate an allocation failure. + mov(result, Immediate(0x7091)); + if (result_end.is_valid()) { + mov(result_end, Immediate(0x7191)); + } + if (scratch.is_valid()) { + mov(scratch, Immediate(0x7291)); + } + } + jmp(gc_required); + return; + } + DCHECK(!result.is(result_end)); + + // Load address of new object into result. + LoadAllocationTopHelper(result, scratch, flags); + + ExternalReference allocation_limit = + AllocationUtils::GetAllocationLimitReference(isolate(), flags); + + // Align the next allocation. Storing the filler map without checking top is + // safe in new-space because the limit of the heap is aligned there. + if ((flags & DOUBLE_ALIGNMENT) != 0) { + DCHECK(kPointerAlignment * 2 == kDoubleAlignment); + Label aligned; + test(result, Immediate(kDoubleAlignmentMask)); + j(zero, &aligned, Label::kNear); + if ((flags & PRETENURE) != 0) { + cmp(result, Operand::StaticVariable(allocation_limit)); + j(above_equal, gc_required); + } + mov(Operand(result, 0), + Immediate(isolate()->factory()->one_pointer_filler_map())); + add(result, Immediate(kDoubleSize / 2)); + bind(&aligned); + } + + // Calculate new top and bail out if space is exhausted. + Register top_reg = result_end.is_valid() ? result_end : result; + + if (!top_reg.is(result)) { + mov(top_reg, result); + } + add(top_reg, Immediate(object_size)); + cmp(top_reg, Operand::StaticVariable(allocation_limit)); + j(above, gc_required); + + UpdateAllocationTopHelper(top_reg, scratch, flags); + + if (top_reg.is(result)) { + sub(result, Immediate(object_size - kHeapObjectTag)); + } else { + // Tag the result. + DCHECK(kHeapObjectTag == 1); + inc(result); + } +} + + +void MacroAssembler::Allocate(int header_size, + ScaleFactor element_size, + Register element_count, + RegisterValueType element_count_type, + Register result, + Register result_end, + Register scratch, + Label* gc_required, + AllocationFlags flags) { + DCHECK((flags & SIZE_IN_WORDS) == 0); + if (!FLAG_inline_new) { + if (emit_debug_code()) { + // Trash the registers to simulate an allocation failure. + mov(result, Immediate(0x7091)); + mov(result_end, Immediate(0x7191)); + if (scratch.is_valid()) { + mov(scratch, Immediate(0x7291)); + } + // Register element_count is not modified by the function. + } + jmp(gc_required); + return; + } + DCHECK(!result.is(result_end)); + + // Load address of new object into result. + LoadAllocationTopHelper(result, scratch, flags); + + ExternalReference allocation_limit = + AllocationUtils::GetAllocationLimitReference(isolate(), flags); + + // Align the next allocation. Storing the filler map without checking top is + // safe in new-space because the limit of the heap is aligned there. + if ((flags & DOUBLE_ALIGNMENT) != 0) { + DCHECK(kPointerAlignment * 2 == kDoubleAlignment); + Label aligned; + test(result, Immediate(kDoubleAlignmentMask)); + j(zero, &aligned, Label::kNear); + if ((flags & PRETENURE) != 0) { + cmp(result, Operand::StaticVariable(allocation_limit)); + j(above_equal, gc_required); + } + mov(Operand(result, 0), + Immediate(isolate()->factory()->one_pointer_filler_map())); + add(result, Immediate(kDoubleSize / 2)); + bind(&aligned); + } + + // Calculate new top and bail out if space is exhausted. + // We assume that element_count*element_size + header_size does not + // overflow. + if (element_count_type == REGISTER_VALUE_IS_SMI) { + STATIC_ASSERT(static_cast(times_2 - 1) == times_1); + STATIC_ASSERT(static_cast(times_4 - 1) == times_2); + STATIC_ASSERT(static_cast(times_8 - 1) == times_4); + DCHECK(element_size >= times_2); + DCHECK(kSmiTagSize == 1); + element_size = static_cast(element_size - 1); + } else { + DCHECK(element_count_type == REGISTER_VALUE_IS_INT32); + } + lea(result_end, Operand(element_count, element_size, header_size)); + add(result_end, result); + j(carry, gc_required); + cmp(result_end, Operand::StaticVariable(allocation_limit)); + j(above, gc_required); + + // Tag result. + DCHECK(kHeapObjectTag == 1); + inc(result); + + // Update allocation top. + UpdateAllocationTopHelper(result_end, scratch, flags); +} + +void MacroAssembler::Allocate(Register object_size, + Register result, + Register result_end, + Register scratch, + Label* gc_required, + AllocationFlags flags) { + DCHECK((flags & (RESULT_CONTAINS_TOP | SIZE_IN_WORDS)) == 0); + if (!FLAG_inline_new) { + if (emit_debug_code()) { + // Trash the registers to simulate an allocation failure. + mov(result, Immediate(0x7091)); + mov(result_end, Immediate(0x7191)); + if (scratch.is_valid()) { + mov(scratch, Immediate(0x7291)); + } + // object_size is left unchanged by this function. + } + jmp(gc_required); + return; + } + DCHECK(!result.is(result_end)); + + // Load address of new object into result. + LoadAllocationTopHelper(result, scratch, flags); + + ExternalReference allocation_limit = + AllocationUtils::GetAllocationLimitReference(isolate(), flags); + + // Align the next allocation. Storing the filler map without checking top is + // safe in new-space because the limit of the heap is aligned there. + if ((flags & DOUBLE_ALIGNMENT) != 0) { + DCHECK(kPointerAlignment * 2 == kDoubleAlignment); + Label aligned; + test(result, Immediate(kDoubleAlignmentMask)); + j(zero, &aligned, Label::kNear); + if ((flags & PRETENURE) != 0) { + cmp(result, Operand::StaticVariable(allocation_limit)); + j(above_equal, gc_required); + } + mov(Operand(result, 0), + Immediate(isolate()->factory()->one_pointer_filler_map())); + add(result, Immediate(kDoubleSize / 2)); + bind(&aligned); + } + + // Calculate new top and bail out if space is exhausted. + if (!object_size.is(result_end)) { + mov(result_end, object_size); + } + add(result_end, result); + cmp(result_end, Operand::StaticVariable(allocation_limit)); + j(above, gc_required); + + // Tag result. + DCHECK(kHeapObjectTag == 1); + inc(result); + + UpdateAllocationTopHelper(result_end, scratch, flags); +} + +void MacroAssembler::AllocateHeapNumber(Register result, + Register scratch1, + Register scratch2, + Label* gc_required, + MutableMode mode) { + // Allocate heap number in new space. + Allocate(HeapNumber::kSize, result, scratch1, scratch2, gc_required, + NO_ALLOCATION_FLAGS); + + Handle map = mode == MUTABLE + ? isolate()->factory()->mutable_heap_number_map() + : isolate()->factory()->heap_number_map(); + + // Set the map. + mov(FieldOperand(result, HeapObject::kMapOffset), Immediate(map)); +} + +void MacroAssembler::AllocateJSValue(Register result, Register constructor, + Register value, Register scratch, + Label* gc_required) { + DCHECK(!result.is(constructor)); + DCHECK(!result.is(scratch)); + DCHECK(!result.is(value)); + + // Allocate JSValue in new space. + Allocate(JSValue::kSize, result, scratch, no_reg, gc_required, + NO_ALLOCATION_FLAGS); + + // Initialize the JSValue. + LoadGlobalFunctionInitialMap(constructor, scratch); + mov(FieldOperand(result, HeapObject::kMapOffset), scratch); + LoadRoot(scratch, Heap::kEmptyFixedArrayRootIndex); + mov(FieldOperand(result, JSObject::kPropertiesOrHashOffset), scratch); + mov(FieldOperand(result, JSObject::kElementsOffset), scratch); + mov(FieldOperand(result, JSValue::kValueOffset), value); + STATIC_ASSERT(JSValue::kSize == 4 * kPointerSize); +} + +void MacroAssembler::InitializeFieldsWithFiller(Register current_address, + Register end_address, + Register filler) { + Label loop, entry; + jmp(&entry, Label::kNear); + bind(&loop); + mov(Operand(current_address, 0), filler); + add(current_address, Immediate(kPointerSize)); + bind(&entry); + cmp(current_address, end_address); + j(below, &loop, Label::kNear); +} + + +void MacroAssembler::BooleanBitTest(Register object, + int field_offset, + int bit_index) { + bit_index += kSmiTagSize + kSmiShiftSize; + DCHECK(base::bits::IsPowerOfTwo(kBitsPerByte)); + int byte_index = bit_index / kBitsPerByte; + int byte_bit_index = bit_index & (kBitsPerByte - 1); + test_b(FieldOperand(object, field_offset + byte_index), + Immediate(1 << byte_bit_index)); +} + +void MacroAssembler::GetMapConstructor(Register result, Register map, + Register temp) { + Label done, loop; + mov(result, FieldOperand(map, Map::kConstructorOrBackPointerOffset)); + bind(&loop); + JumpIfSmi(result, &done, Label::kNear); + CmpObjectType(result, MAP_TYPE, temp); + j(not_equal, &done, Label::kNear); + mov(result, FieldOperand(result, Map::kConstructorOrBackPointerOffset)); + jmp(&loop); + bind(&done); +} + +void MacroAssembler::CallStub(CodeStub* stub) { + DCHECK(AllowThisStubCall(stub)); // Calls are not allowed in some stubs. + call(stub->GetCode(), RelocInfo::CODE_TARGET); +} + +void TurboAssembler::CallStubDelayed(CodeStub* stub) { + DCHECK(AllowThisStubCall(stub)); // Calls are not allowed in some stubs. + call(stub); +} + +void MacroAssembler::TailCallStub(CodeStub* stub) { + jmp(stub->GetCode(), RelocInfo::CODE_TARGET); +} + +bool TurboAssembler::AllowThisStubCall(CodeStub* stub) { + return has_frame() || !stub->SometimesSetsUpAFrame(); +} + +void MacroAssembler::CallRuntime(const Runtime::Function* f, int num_arguments, + SaveFPRegsMode save_doubles) { + // If the expected number of arguments of the runtime function is + // constant, we check that the actual number of arguments match the + // expectation. + CHECK(f->nargs < 0 || f->nargs == num_arguments); + + // TODO(1236192): Most runtime routines don't need the number of + // arguments passed in because it is constant. At some point we + // should remove this need and make the runtime routine entry code + // smarter. + Move(eax, Immediate(num_arguments)); + mov(ebx, Immediate(ExternalReference(f, isolate()))); + CEntryStub ces(isolate(), 1, save_doubles); + CallStub(&ces); +} + +void TurboAssembler::CallRuntimeDelayed(Zone* zone, Runtime::FunctionId fid, + SaveFPRegsMode save_doubles) { + const Runtime::Function* f = Runtime::FunctionForId(fid); + // TODO(1236192): Most runtime routines don't need the number of + // arguments passed in because it is constant. At some point we + // should remove this need and make the runtime routine entry code + // smarter. + Move(eax, Immediate(f->nargs)); + mov(ebx, Immediate(ExternalReference(f, isolate()))); + CallStubDelayed(new (zone) CEntryStub(nullptr, 1, save_doubles)); +} + +void MacroAssembler::CallExternalReference(ExternalReference ref, + int num_arguments) { + mov(eax, Immediate(num_arguments)); + mov(ebx, Immediate(ref)); + + CEntryStub stub(isolate(), 1); + CallStub(&stub); +} + + +void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid) { + // ----------- S t a t e ------------- + // -- esp[0] : return address + // -- esp[8] : argument num_arguments - 1 + // ... + // -- esp[8 * num_arguments] : argument 0 (receiver) + // + // For runtime functions with variable arguments: + // -- eax : number of arguments + // ----------------------------------- + + const Runtime::Function* function = Runtime::FunctionForId(fid); + DCHECK_EQ(1, function->result_size); + if (function->nargs >= 0) { + // TODO(1236192): Most runtime routines don't need the number of + // arguments passed in because it is constant. At some point we + // should remove this need and make the runtime routine entry code + // smarter. + mov(eax, Immediate(function->nargs)); + } + JumpToExternalReference(ExternalReference(fid, isolate())); +} + +void MacroAssembler::JumpToExternalReference(const ExternalReference& ext, + bool builtin_exit_frame) { + // Set the entry point and jump to the C entry runtime stub. + mov(ebx, Immediate(ext)); + CEntryStub ces(isolate(), 1, kDontSaveFPRegs, kArgvOnStack, + builtin_exit_frame); + jmp(ces.GetCode(), RelocInfo::CODE_TARGET); +} + +void TurboAssembler::PrepareForTailCall( + const ParameterCount& callee_args_count, Register caller_args_count_reg, + Register scratch0, Register scratch1, ReturnAddressState ra_state, + int number_of_temp_values_after_return_address) { +#if DEBUG + if (callee_args_count.is_reg()) { + DCHECK(!AreAliased(callee_args_count.reg(), caller_args_count_reg, scratch0, + scratch1)); + } else { + DCHECK(!AreAliased(caller_args_count_reg, scratch0, scratch1)); + } + DCHECK(ra_state != ReturnAddressState::kNotOnStack || + number_of_temp_values_after_return_address == 0); +#endif + + // Calculate the destination address where we will put the return address + // after we drop current frame. + Register new_sp_reg = scratch0; + if (callee_args_count.is_reg()) { + sub(caller_args_count_reg, callee_args_count.reg()); + lea(new_sp_reg, + Operand(ebp, caller_args_count_reg, times_pointer_size, + StandardFrameConstants::kCallerPCOffset - + number_of_temp_values_after_return_address * kPointerSize)); + } else { + lea(new_sp_reg, Operand(ebp, caller_args_count_reg, times_pointer_size, + StandardFrameConstants::kCallerPCOffset - + (callee_args_count.immediate() + + number_of_temp_values_after_return_address) * + kPointerSize)); + } + + if (FLAG_debug_code) { + cmp(esp, new_sp_reg); + Check(below, kStackAccessBelowStackPointer); + } + + // Copy return address from caller's frame to current frame's return address + // to avoid its trashing and let the following loop copy it to the right + // place. + Register tmp_reg = scratch1; + if (ra_state == ReturnAddressState::kOnStack) { + mov(tmp_reg, Operand(ebp, StandardFrameConstants::kCallerPCOffset)); + mov(Operand(esp, number_of_temp_values_after_return_address * kPointerSize), + tmp_reg); + } else { + DCHECK(ReturnAddressState::kNotOnStack == ra_state); + DCHECK_EQ(0, number_of_temp_values_after_return_address); + Push(Operand(ebp, StandardFrameConstants::kCallerPCOffset)); + } + + // Restore caller's frame pointer now as it could be overwritten by + // the copying loop. + mov(ebp, Operand(ebp, StandardFrameConstants::kCallerFPOffset)); + + // +2 here is to copy both receiver and return address. + Register count_reg = caller_args_count_reg; + if (callee_args_count.is_reg()) { + lea(count_reg, Operand(callee_args_count.reg(), + 2 + number_of_temp_values_after_return_address)); + } else { + mov(count_reg, Immediate(callee_args_count.immediate() + 2 + + number_of_temp_values_after_return_address)); + // TODO(ishell): Unroll copying loop for small immediate values. + } + + // Now copy callee arguments to the caller frame going backwards to avoid + // callee arguments corruption (source and destination areas could overlap). + Label loop, entry; + jmp(&entry, Label::kNear); + bind(&loop); + dec(count_reg); + mov(tmp_reg, Operand(esp, count_reg, times_pointer_size, 0)); + mov(Operand(new_sp_reg, count_reg, times_pointer_size, 0), tmp_reg); + bind(&entry); + cmp(count_reg, Immediate(0)); + j(not_equal, &loop, Label::kNear); + + // Leave current frame. + mov(esp, new_sp_reg); +} + +void MacroAssembler::InvokePrologue(const ParameterCount& expected, + const ParameterCount& actual, + Label* done, + bool* definitely_mismatches, + InvokeFlag flag, + Label::Distance done_near, + const CallWrapper& call_wrapper) { + bool definitely_matches = false; + *definitely_mismatches = false; + Label invoke; + if (expected.is_immediate()) { + DCHECK(actual.is_immediate()); + mov(eax, actual.immediate()); + if (expected.immediate() == actual.immediate()) { + definitely_matches = true; + } else { + const int sentinel = SharedFunctionInfo::kDontAdaptArgumentsSentinel; + if (expected.immediate() == sentinel) { + // Don't worry about adapting arguments for builtins that + // don't want that done. Skip adaption code by making it look + // like we have a match between expected and actual number of + // arguments. + definitely_matches = true; + } else { + *definitely_mismatches = true; + mov(ebx, expected.immediate()); + } + } + } else { + if (actual.is_immediate()) { + // Expected is in register, actual is immediate. This is the + // case when we invoke function values without going through the + // IC mechanism. + mov(eax, actual.immediate()); + cmp(expected.reg(), actual.immediate()); + j(equal, &invoke); + DCHECK(expected.reg().is(ebx)); + } else if (!expected.reg().is(actual.reg())) { + // Both expected and actual are in (different) registers. This + // is the case when we invoke functions using call and apply. + cmp(expected.reg(), actual.reg()); + j(equal, &invoke); + DCHECK(actual.reg().is(eax)); + DCHECK(expected.reg().is(ebx)); + } else { + definitely_matches = true; + Move(eax, actual.reg()); + } + } + + if (!definitely_matches) { + Handle adaptor = + isolate()->builtins()->ArgumentsAdaptorTrampoline(); + if (flag == CALL_FUNCTION) { + call_wrapper.BeforeCall(CallSize(adaptor, RelocInfo::CODE_TARGET)); + call(adaptor, RelocInfo::CODE_TARGET); + call_wrapper.AfterCall(); + if (!*definitely_mismatches) { + jmp(done, done_near); + } + } else { + jmp(adaptor, RelocInfo::CODE_TARGET); + } + bind(&invoke); + } +} + +void MacroAssembler::CheckDebugHook(Register fun, Register new_target, + const ParameterCount& expected, + const ParameterCount& actual) { + Label skip_hook; + ExternalReference debug_hook_active = + ExternalReference::debug_hook_on_function_call_address(isolate()); + cmpb(Operand::StaticVariable(debug_hook_active), Immediate(0)); + j(equal, &skip_hook); + { + FrameScope frame(this, + has_frame() ? StackFrame::NONE : StackFrame::INTERNAL); + if (expected.is_reg()) { + SmiTag(expected.reg()); + Push(expected.reg()); + } + if (actual.is_reg()) { + SmiTag(actual.reg()); + Push(actual.reg()); + } + if (new_target.is_valid()) { + Push(new_target); + } + Push(fun); + Push(fun); + CallRuntime(Runtime::kDebugOnFunctionCall); + Pop(fun); + if (new_target.is_valid()) { + Pop(new_target); + } + if (actual.is_reg()) { + Pop(actual.reg()); + SmiUntag(actual.reg()); + } + if (expected.is_reg()) { + Pop(expected.reg()); + SmiUntag(expected.reg()); + } + } + bind(&skip_hook); +} + + +void MacroAssembler::InvokeFunctionCode(Register function, Register new_target, + const ParameterCount& expected, + const ParameterCount& actual, + InvokeFlag flag, + const CallWrapper& call_wrapper) { + // You can't call a function without a valid frame. + DCHECK(flag == JUMP_FUNCTION || has_frame()); + DCHECK(function.is(edi)); + DCHECK_IMPLIES(new_target.is_valid(), new_target.is(edx)); + + if (call_wrapper.NeedsDebugHookCheck()) { + CheckDebugHook(function, new_target, expected, actual); + } + + // Clear the new.target register if not given. + if (!new_target.is_valid()) { + mov(edx, isolate()->factory()->undefined_value()); + } + + Label done; + bool definitely_mismatches = false; + InvokePrologue(expected, actual, &done, &definitely_mismatches, flag, + Label::kNear, call_wrapper); + if (!definitely_mismatches) { + // We call indirectly through the code field in the function to + // allow recompilation to take effect without changing any of the + // call sites. + Operand code = FieldOperand(function, JSFunction::kCodeEntryOffset); + if (flag == CALL_FUNCTION) { + call_wrapper.BeforeCall(CallSize(code)); + call(code); + call_wrapper.AfterCall(); + } else { + DCHECK(flag == JUMP_FUNCTION); + jmp(code); + } + bind(&done); + } +} + + +void MacroAssembler::InvokeFunction(Register fun, Register new_target, + const ParameterCount& actual, + InvokeFlag flag, + const CallWrapper& call_wrapper) { + // You can't call a function without a valid frame. + DCHECK(flag == JUMP_FUNCTION || has_frame()); + + DCHECK(fun.is(edi)); + mov(ebx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); + mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); + mov(ebx, FieldOperand(ebx, SharedFunctionInfo::kFormalParameterCountOffset)); + + ParameterCount expected(ebx); + InvokeFunctionCode(edi, new_target, expected, actual, flag, call_wrapper); +} + + +void MacroAssembler::InvokeFunction(Register fun, + const ParameterCount& expected, + const ParameterCount& actual, + InvokeFlag flag, + const CallWrapper& call_wrapper) { + // You can't call a function without a valid frame. + DCHECK(flag == JUMP_FUNCTION || has_frame()); + + DCHECK(fun.is(edi)); + mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); + + InvokeFunctionCode(edi, no_reg, expected, actual, flag, call_wrapper); +} + + +void MacroAssembler::InvokeFunction(Handle function, + const ParameterCount& expected, + const ParameterCount& actual, + InvokeFlag flag, + const CallWrapper& call_wrapper) { + Move(edi, function); + InvokeFunction(edi, expected, actual, flag, call_wrapper); +} + + +void MacroAssembler::LoadContext(Register dst, int context_chain_length) { + if (context_chain_length > 0) { + // Move up the chain of contexts to the context containing the slot. + mov(dst, Operand(esi, Context::SlotOffset(Context::PREVIOUS_INDEX))); + for (int i = 1; i < context_chain_length; i++) { + mov(dst, Operand(dst, Context::SlotOffset(Context::PREVIOUS_INDEX))); + } + } else { + // Slot is in the current function context. Move it into the + // destination register in case we store into it (the write barrier + // cannot be allowed to destroy the context in esi). + mov(dst, esi); + } + + // We should not have found a with context by walking the context chain + // (i.e., the static scope chain and runtime context chain do not agree). + // A variable occurring in such a scope should have slot type LOOKUP and + // not CONTEXT. + if (emit_debug_code()) { + cmp(FieldOperand(dst, HeapObject::kMapOffset), + isolate()->factory()->with_context_map()); + Check(not_equal, kVariableResolvedToWithContext); + } +} + + +void MacroAssembler::LoadGlobalProxy(Register dst) { + mov(dst, NativeContextOperand()); + mov(dst, ContextOperand(dst, Context::GLOBAL_PROXY_INDEX)); +} + +void MacroAssembler::LoadGlobalFunction(int index, Register function) { + // Load the native context from the current context. + mov(function, NativeContextOperand()); + // Load the function from the native context. + mov(function, ContextOperand(function, index)); +} + + +void MacroAssembler::LoadGlobalFunctionInitialMap(Register function, + Register map) { + // Load the initial map. The global functions all have initial maps. + mov(map, FieldOperand(function, JSFunction::kPrototypeOrInitialMapOffset)); + if (emit_debug_code()) { + Label ok, fail; + CheckMap(map, isolate()->factory()->meta_map(), &fail, DO_SMI_CHECK); + jmp(&ok); + bind(&fail); + Abort(kGlobalFunctionsMustHaveInitialMap); + bind(&ok); + } +} + + +// Store the value in register src in the safepoint register stack +// slot for register dst. +void MacroAssembler::StoreToSafepointRegisterSlot(Register dst, Register src) { + mov(SafepointRegisterSlot(dst), src); +} + + +void MacroAssembler::StoreToSafepointRegisterSlot(Register dst, Immediate src) { + mov(SafepointRegisterSlot(dst), src); +} + + +void MacroAssembler::LoadFromSafepointRegisterSlot(Register dst, Register src) { + mov(dst, SafepointRegisterSlot(src)); +} + + +Operand MacroAssembler::SafepointRegisterSlot(Register reg) { + return Operand(esp, SafepointRegisterStackIndex(reg.code()) * kPointerSize); +} + + +int MacroAssembler::SafepointRegisterStackIndex(int reg_code) { + // The registers are pushed starting with the lowest encoding, + // which means that lowest encodings are furthest away from + // the stack pointer. + DCHECK(reg_code >= 0 && reg_code < kNumSafepointRegisters); + return kNumSafepointRegisters - reg_code - 1; +} + + +void MacroAssembler::CmpHeapObject(Register reg, Handle object) { + cmp(reg, object); +} + +void MacroAssembler::PushObject(Handle object) { + if (object->IsHeapObject()) { + Push(Handle::cast(object)); + } else { + Push(Smi::cast(*object)); + } +} + +void MacroAssembler::GetWeakValue(Register value, Handle cell) { + mov(value, cell); + mov(value, FieldOperand(value, WeakCell::kValueOffset)); +} + + +void MacroAssembler::LoadWeakValue(Register value, Handle cell, + Label* miss) { + GetWeakValue(value, cell); + JumpIfSmi(value, miss); +} + +void TurboAssembler::Ret() { ret(0); } + +void TurboAssembler::Ret(int bytes_dropped, Register scratch) { + if (is_uint16(bytes_dropped)) { + ret(bytes_dropped); + } else { + pop(scratch); + add(esp, Immediate(bytes_dropped)); + push(scratch); + ret(0); + } +} + + +void TurboAssembler::VerifyX87StackDepth(uint32_t depth) { + // Turn off the stack depth check when serializer is enabled to reduce the + // code size. + if (serializer_enabled()) return; + // Make sure the floating point stack is either empty or has depth items. + DCHECK(depth <= 7); + // This is very expensive. + DCHECK(FLAG_debug_code && FLAG_enable_slow_asserts); + + // The top-of-stack (tos) is 7 if there is one item pushed. + int tos = (8 - depth) % 8; + const int kTopMask = 0x3800; + push(eax); + fwait(); + fnstsw_ax(); + and_(eax, kTopMask); + shr(eax, 11); + cmp(eax, Immediate(tos)); + Check(equal, kUnexpectedFPUStackDepthAfterInstruction); + fnclex(); + pop(eax); +} + + +void MacroAssembler::Drop(int stack_elements) { + if (stack_elements > 0) { + add(esp, Immediate(stack_elements * kPointerSize)); + } +} + + +void TurboAssembler::Move(Register dst, Register src) { + if (!dst.is(src)) { + mov(dst, src); + } +} + + +void TurboAssembler::Move(Register dst, const Immediate& x) { + if (!x.is_heap_object_request() && x.is_zero() && + RelocInfo::IsNone(x.rmode())) { + xor_(dst, dst); // Shorter than mov of 32-bit immediate 0. + } else { + mov(dst, x); + } +} + + +void TurboAssembler::Move(const Operand& dst, const Immediate& x) { + mov(dst, x); +} + + +void TurboAssembler::Move(Register dst, Handle object) { + mov(dst, object); +} + + +void TurboAssembler::Lzcnt(Register dst, const Operand& src) { + // TODO(intel): Add support for LZCNT (with ABM/BMI1). + Label not_zero_src; + bsr(dst, src); + j(not_zero, ¬_zero_src, Label::kNear); + Move(dst, Immediate(63)); // 63^31 == 32 + bind(¬_zero_src); + xor_(dst, Immediate(31)); // for x in [0..31], 31^x == 31-x. +} + + +void TurboAssembler::Tzcnt(Register dst, const Operand& src) { + // TODO(intel): Add support for TZCNT (with ABM/BMI1). + Label not_zero_src; + bsf(dst, src); + j(not_zero, ¬_zero_src, Label::kNear); + Move(dst, Immediate(32)); // The result of tzcnt is 32 if src = 0. + bind(¬_zero_src); +} + + +void TurboAssembler::Popcnt(Register dst, const Operand& src) { + // TODO(intel): Add support for POPCNT (with POPCNT) + // if (CpuFeatures::IsSupported(POPCNT)) { + // CpuFeatureScope scope(this, POPCNT); + // popcnt(dst, src); + // return; + // } + UNREACHABLE(); +} + + +void MacroAssembler::SetCounter(StatsCounter* counter, int value) { + if (FLAG_native_code_counters && counter->Enabled()) { + mov(Operand::StaticVariable(ExternalReference(counter)), Immediate(value)); + } +} + + +void MacroAssembler::IncrementCounter(StatsCounter* counter, int value) { + DCHECK(value > 0); + if (FLAG_native_code_counters && counter->Enabled()) { + Operand operand = Operand::StaticVariable(ExternalReference(counter)); + if (value == 1) { + inc(operand); + } else { + add(operand, Immediate(value)); + } + } +} + + +void MacroAssembler::DecrementCounter(StatsCounter* counter, int value) { + DCHECK(value > 0); + if (FLAG_native_code_counters && counter->Enabled()) { + Operand operand = Operand::StaticVariable(ExternalReference(counter)); + if (value == 1) { + dec(operand); + } else { + sub(operand, Immediate(value)); + } + } +} + + +void MacroAssembler::IncrementCounter(Condition cc, + StatsCounter* counter, + int value) { + DCHECK(value > 0); + if (FLAG_native_code_counters && counter->Enabled()) { + Label skip; + j(NegateCondition(cc), &skip); + pushfd(); + IncrementCounter(counter, value); + popfd(); + bind(&skip); + } +} + + +void MacroAssembler::DecrementCounter(Condition cc, + StatsCounter* counter, + int value) { + DCHECK(value > 0); + if (FLAG_native_code_counters && counter->Enabled()) { + Label skip; + j(NegateCondition(cc), &skip); + pushfd(); + DecrementCounter(counter, value); + popfd(); + bind(&skip); + } +} + + +void TurboAssembler::Assert(Condition cc, BailoutReason reason) { + if (emit_debug_code()) Check(cc, reason); +} + +void TurboAssembler::AssertUnreachable(BailoutReason reason) { + if (emit_debug_code()) Abort(reason); +} + + + +void TurboAssembler::Check(Condition cc, BailoutReason reason) { + Label L; + j(cc, &L); + Abort(reason); + // will not return here + bind(&L); +} + + +void TurboAssembler::CheckStackAlignment() { + int frame_alignment = base::OS::ActivationFrameAlignment(); + int frame_alignment_mask = frame_alignment - 1; + if (frame_alignment > kPointerSize) { + DCHECK(base::bits::IsPowerOfTwo(frame_alignment)); + Label alignment_as_expected; + test(esp, Immediate(frame_alignment_mask)); + j(zero, &alignment_as_expected); + // Abort if stack is not aligned. + int3(); + bind(&alignment_as_expected); + } +} + + +void TurboAssembler::Abort(BailoutReason reason) { +#ifdef DEBUG + const char* msg = GetBailoutReason(reason); + if (msg != NULL) { + RecordComment("Abort message: "); + RecordComment(msg); + } + + if (FLAG_trap_on_abort) { + int3(); + return; + } +#endif + + Move(edx, Smi::FromInt(static_cast(reason))); + + // Disable stub call restrictions to always allow calls to abort. + if (!has_frame()) { + // We don't actually want to generate a pile of code for this, so just + // claim there is a stack frame, without generating one. + FrameScope scope(this, StackFrame::NONE); + Call(isolate()->builtins()->Abort(), RelocInfo::CODE_TARGET); + } else { + Call(isolate()->builtins()->Abort(), RelocInfo::CODE_TARGET); + } + // will not return here + int3(); +} + + +void MacroAssembler::LoadInstanceDescriptors(Register map, + Register descriptors) { + mov(descriptors, FieldOperand(map, Map::kDescriptorsOffset)); +} + + +void MacroAssembler::NumberOfOwnDescriptors(Register dst, Register map) { + mov(dst, FieldOperand(map, Map::kBitField3Offset)); + DecodeField(dst); +} + + +void MacroAssembler::LoadAccessor(Register dst, Register holder, + int accessor_index, + AccessorComponent accessor) { + mov(dst, FieldOperand(holder, HeapObject::kMapOffset)); + LoadInstanceDescriptors(dst, dst); + mov(dst, FieldOperand(dst, DescriptorArray::GetValueOffset(accessor_index))); + int offset = accessor == ACCESSOR_GETTER ? AccessorPair::kGetterOffset + : AccessorPair::kSetterOffset; + mov(dst, FieldOperand(dst, offset)); +} + +void MacroAssembler::JumpIfNotBothSequentialOneByteStrings(Register object1, + Register object2, + Register scratch1, + Register scratch2, + Label* failure) { + // Check that both objects are not smis. + STATIC_ASSERT(kSmiTag == 0); + mov(scratch1, object1); + and_(scratch1, object2); + JumpIfSmi(scratch1, failure); + + // Load instance type for both strings. + mov(scratch1, FieldOperand(object1, HeapObject::kMapOffset)); + mov(scratch2, FieldOperand(object2, HeapObject::kMapOffset)); + movzx_b(scratch1, FieldOperand(scratch1, Map::kInstanceTypeOffset)); + movzx_b(scratch2, FieldOperand(scratch2, Map::kInstanceTypeOffset)); + + // Check that both are flat one-byte strings. + const int kFlatOneByteStringMask = + kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask; + const int kFlatOneByteStringTag = + kStringTag | kOneByteStringTag | kSeqStringTag; + // Interleave bits from both instance types and compare them in one check. + const int kShift = 8; + DCHECK_EQ(0, kFlatOneByteStringMask & (kFlatOneByteStringMask << kShift)); + and_(scratch1, kFlatOneByteStringMask); + and_(scratch2, kFlatOneByteStringMask); + shl(scratch2, kShift); + or_(scratch1, scratch2); + cmp(scratch1, kFlatOneByteStringTag | (kFlatOneByteStringTag << kShift)); + j(not_equal, failure); +} + + +void MacroAssembler::JumpIfNotUniqueNameInstanceType(Operand operand, + Label* not_unique_name, + Label::Distance distance) { + STATIC_ASSERT(kInternalizedTag == 0 && kStringTag == 0); + Label succeed; + test(operand, Immediate(kIsNotStringMask | kIsNotInternalizedMask)); + j(zero, &succeed); + cmpb(operand, Immediate(SYMBOL_TYPE)); + j(not_equal, not_unique_name, distance); + + bind(&succeed); +} + + +void MacroAssembler::EmitSeqStringSetCharCheck(Register string, + Register index, + Register value, + uint32_t encoding_mask) { + Label is_object; + JumpIfNotSmi(string, &is_object, Label::kNear); + Abort(kNonObject); + bind(&is_object); + + push(value); + mov(value, FieldOperand(string, HeapObject::kMapOffset)); + movzx_b(value, FieldOperand(value, Map::kInstanceTypeOffset)); + + and_(value, Immediate(kStringRepresentationMask | kStringEncodingMask)); + cmp(value, Immediate(encoding_mask)); + pop(value); + Check(equal, kUnexpectedStringType); + + // The index is assumed to be untagged coming in, tag it to compare with the + // string length without using a temp register, it is restored at the end of + // this function. + SmiTag(index); + Check(no_overflow, kIndexIsTooLarge); + + cmp(index, FieldOperand(string, String::kLengthOffset)); + Check(less, kIndexIsTooLarge); + + cmp(index, Immediate(Smi::kZero)); + Check(greater_equal, kIndexIsNegative); + + // Restore the index + SmiUntag(index); +} + + +void TurboAssembler::PrepareCallCFunction(int num_arguments, Register scratch) { + int frame_alignment = base::OS::ActivationFrameAlignment(); + if (frame_alignment != 0) { + // Make stack end at alignment and make room for num_arguments words + // and the original value of esp. + mov(scratch, esp); + sub(esp, Immediate((num_arguments + 1) * kPointerSize)); + DCHECK(base::bits::IsPowerOfTwo(frame_alignment)); + and_(esp, -frame_alignment); + mov(Operand(esp, num_arguments * kPointerSize), scratch); + } else { + sub(esp, Immediate(num_arguments * kPointerSize)); + } +} + + +void TurboAssembler::CallCFunction(ExternalReference function, + int num_arguments) { + // Trashing eax is ok as it will be the return value. + mov(eax, Immediate(function)); + CallCFunction(eax, num_arguments); +} + + +void TurboAssembler::CallCFunction(Register function, int num_arguments) { + DCHECK(has_frame()); + // Check stack alignment. + if (emit_debug_code()) { + CheckStackAlignment(); + } + + call(function); + if (base::OS::ActivationFrameAlignment() != 0) { + mov(esp, Operand(esp, num_arguments * kPointerSize)); + } else { + add(esp, Immediate(num_arguments * kPointerSize)); + } +} + + +#ifdef DEBUG +bool AreAliased(Register reg1, + Register reg2, + Register reg3, + Register reg4, + Register reg5, + Register reg6, + Register reg7, + Register reg8) { + int n_of_valid_regs = reg1.is_valid() + reg2.is_valid() + + reg3.is_valid() + reg4.is_valid() + reg5.is_valid() + reg6.is_valid() + + reg7.is_valid() + reg8.is_valid(); + + RegList regs = 0; + if (reg1.is_valid()) regs |= reg1.bit(); + if (reg2.is_valid()) regs |= reg2.bit(); + if (reg3.is_valid()) regs |= reg3.bit(); + if (reg4.is_valid()) regs |= reg4.bit(); + if (reg5.is_valid()) regs |= reg5.bit(); + if (reg6.is_valid()) regs |= reg6.bit(); + if (reg7.is_valid()) regs |= reg7.bit(); + if (reg8.is_valid()) regs |= reg8.bit(); + int n_of_non_aliasing_regs = NumRegs(regs); + + return n_of_valid_regs != n_of_non_aliasing_regs; +} +#endif + + +CodePatcher::CodePatcher(Isolate* isolate, byte* address, int size) + : address_(address), + size_(size), + masm_(isolate, address, size + Assembler::kGap, CodeObjectRequired::kNo) { + // Create a new macro assembler pointing to the address of the code to patch. + // The size is adjusted with kGap on order for the assembler to generate size + // bytes of instructions without failing with buffer size constraints. + DCHECK(masm_.reloc_info_writer.pos() == address_ + size_ + Assembler::kGap); +} + + +CodePatcher::~CodePatcher() { + // Indicate that code has changed. + Assembler::FlushICache(masm_.isolate(), address_, size_); + + // Check that the code was patched as expected. + DCHECK(masm_.pc_ == address_ + size_); + DCHECK(masm_.reloc_info_writer.pos() == address_ + size_ + Assembler::kGap); +} + + +void TurboAssembler::CheckPageFlag(Register object, Register scratch, int mask, + Condition cc, Label* condition_met, + Label::Distance condition_met_distance) { + DCHECK(cc == zero || cc == not_zero); + if (scratch.is(object)) { + and_(scratch, Immediate(~Page::kPageAlignmentMask)); + } else { + mov(scratch, Immediate(~Page::kPageAlignmentMask)); + and_(scratch, object); + } + if (mask < (1 << kBitsPerByte)) { + test_b(Operand(scratch, MemoryChunk::kFlagsOffset), Immediate(mask)); + } else { + test(Operand(scratch, MemoryChunk::kFlagsOffset), Immediate(mask)); + } + j(cc, condition_met, condition_met_distance); +} + + +void MacroAssembler::CheckPageFlagForMap( + Handle map, + int mask, + Condition cc, + Label* condition_met, + Label::Distance condition_met_distance) { + DCHECK(cc == zero || cc == not_zero); + Page* page = Page::FromAddress(map->address()); + DCHECK(!serializer_enabled()); // Serializer cannot match page_flags. + ExternalReference reference(ExternalReference::page_flags(page)); + // The inlined static address check of the page's flags relies + // on maps never being compacted. + DCHECK(!isolate()->heap()->mark_compact_collector()-> + IsOnEvacuationCandidate(*map)); + if (mask < (1 << kBitsPerByte)) { + test_b(Operand::StaticVariable(reference), Immediate(mask)); + } else { + test(Operand::StaticVariable(reference), Immediate(mask)); + } + j(cc, condition_met, condition_met_distance); +} + + +void MacroAssembler::JumpIfBlack(Register object, + Register scratch0, + Register scratch1, + Label* on_black, + Label::Distance on_black_near) { + HasColor(object, scratch0, scratch1, on_black, on_black_near, 1, + 1); // kBlackBitPattern. + DCHECK(strcmp(Marking::kBlackBitPattern, "11") == 0); +} + + +void MacroAssembler::HasColor(Register object, + Register bitmap_scratch, + Register mask_scratch, + Label* has_color, + Label::Distance has_color_distance, + int first_bit, + int second_bit) { + DCHECK(!AreAliased(object, bitmap_scratch, mask_scratch, ecx)); + + GetMarkBits(object, bitmap_scratch, mask_scratch); + + Label other_color, word_boundary; + test(mask_scratch, Operand(bitmap_scratch, MemoryChunk::kHeaderSize)); + j(first_bit == 1 ? zero : not_zero, &other_color, Label::kNear); + add(mask_scratch, mask_scratch); // Shift left 1 by adding. + j(zero, &word_boundary, Label::kNear); + test(mask_scratch, Operand(bitmap_scratch, MemoryChunk::kHeaderSize)); + j(second_bit == 1 ? not_zero : zero, has_color, has_color_distance); + jmp(&other_color, Label::kNear); + + bind(&word_boundary); + test_b(Operand(bitmap_scratch, MemoryChunk::kHeaderSize + kPointerSize), + Immediate(1)); + + j(second_bit == 1 ? not_zero : zero, has_color, has_color_distance); + bind(&other_color); +} + + +void MacroAssembler::GetMarkBits(Register addr_reg, + Register bitmap_reg, + Register mask_reg) { + DCHECK(!AreAliased(addr_reg, mask_reg, bitmap_reg, ecx)); + mov(bitmap_reg, Immediate(~Page::kPageAlignmentMask)); + and_(bitmap_reg, addr_reg); + mov(ecx, addr_reg); + int shift = + Bitmap::kBitsPerCellLog2 + kPointerSizeLog2 - Bitmap::kBytesPerCellLog2; + shr(ecx, shift); + and_(ecx, + (Page::kPageAlignmentMask >> shift) & ~(Bitmap::kBytesPerCell - 1)); + + add(bitmap_reg, ecx); + mov(ecx, addr_reg); + shr(ecx, kPointerSizeLog2); + and_(ecx, (1 << Bitmap::kBitsPerCellLog2) - 1); + mov(mask_reg, Immediate(1)); + shl_cl(mask_reg); +} + + +void MacroAssembler::JumpIfWhite(Register value, Register bitmap_scratch, + Register mask_scratch, Label* value_is_white, + Label::Distance distance) { + DCHECK(!AreAliased(value, bitmap_scratch, mask_scratch, ecx)); + GetMarkBits(value, bitmap_scratch, mask_scratch); + + // If the value is black or grey we don't need to do anything. + DCHECK(strcmp(Marking::kWhiteBitPattern, "00") == 0); + DCHECK(strcmp(Marking::kBlackBitPattern, "11") == 0); + DCHECK(strcmp(Marking::kGreyBitPattern, "10") == 0); + DCHECK(strcmp(Marking::kImpossibleBitPattern, "01") == 0); + + // Since both black and grey have a 1 in the first position and white does + // not have a 1 there we only need to check one bit. + test(mask_scratch, Operand(bitmap_scratch, MemoryChunk::kHeaderSize)); + j(zero, value_is_white, Label::kNear); +} + + +void MacroAssembler::EnumLength(Register dst, Register map) { + STATIC_ASSERT(Map::EnumLengthBits::kShift == 0); + mov(dst, FieldOperand(map, Map::kBitField3Offset)); + and_(dst, Immediate(Map::EnumLengthBits::kMask)); + SmiTag(dst); +} + + +void MacroAssembler::CheckEnumCache(Label* call_runtime) { + Label next, start; + mov(ecx, eax); + + // Check if the enum length field is properly initialized, indicating that + // there is an enum cache. + mov(ebx, FieldOperand(ecx, HeapObject::kMapOffset)); + + EnumLength(edx, ebx); + cmp(edx, Immediate(Smi::FromInt(kInvalidEnumCacheSentinel))); + j(equal, call_runtime); + + jmp(&start); + + bind(&next); + mov(ebx, FieldOperand(ecx, HeapObject::kMapOffset)); + + // For all objects but the receiver, check that the cache is empty. + EnumLength(edx, ebx); + cmp(edx, Immediate(Smi::kZero)); + j(not_equal, call_runtime); + + bind(&start); + + // Check that there are no elements. Register rcx contains the current JS + // object we've reached through the prototype chain. + Label no_elements; + mov(ecx, FieldOperand(ecx, JSObject::kElementsOffset)); + cmp(ecx, isolate()->factory()->empty_fixed_array()); + j(equal, &no_elements); + + // Second chance, the object may be using the empty slow element dictionary. + cmp(ecx, isolate()->factory()->empty_slow_element_dictionary()); + j(not_equal, call_runtime); + + bind(&no_elements); + mov(ecx, FieldOperand(ebx, Map::kPrototypeOffset)); + cmp(ecx, isolate()->factory()->null_value()); + j(not_equal, &next); +} + + +void MacroAssembler::TestJSArrayForAllocationMemento( + Register receiver_reg, + Register scratch_reg, + Label* no_memento_found) { + Label map_check; + Label top_check; + ExternalReference new_space_allocation_top = + ExternalReference::new_space_allocation_top_address(isolate()); + const int kMementoMapOffset = JSArray::kSize - kHeapObjectTag; + const int kMementoLastWordOffset = + kMementoMapOffset + AllocationMemento::kSize - kPointerSize; + + // Bail out if the object is not in new space. + JumpIfNotInNewSpace(receiver_reg, scratch_reg, no_memento_found); + // If the object is in new space, we need to check whether it is on the same + // page as the current top. + lea(scratch_reg, Operand(receiver_reg, kMementoLastWordOffset)); + xor_(scratch_reg, Operand::StaticVariable(new_space_allocation_top)); + test(scratch_reg, Immediate(~Page::kPageAlignmentMask)); + j(zero, &top_check); + // The object is on a different page than allocation top. Bail out if the + // object sits on the page boundary as no memento can follow and we cannot + // touch the memory following it. + lea(scratch_reg, Operand(receiver_reg, kMementoLastWordOffset)); + xor_(scratch_reg, receiver_reg); + test(scratch_reg, Immediate(~Page::kPageAlignmentMask)); + j(not_zero, no_memento_found); + // Continue with the actual map check. + jmp(&map_check); + // If top is on the same page as the current object, we need to check whether + // we are below top. + bind(&top_check); + lea(scratch_reg, Operand(receiver_reg, kMementoLastWordOffset)); + cmp(scratch_reg, Operand::StaticVariable(new_space_allocation_top)); + j(greater_equal, no_memento_found); + // Memento map check. + bind(&map_check); + mov(scratch_reg, Operand(receiver_reg, kMementoMapOffset)); + cmp(scratch_reg, Immediate(isolate()->factory()->allocation_memento_map())); +} + +void MacroAssembler::TruncatingDiv(Register dividend, int32_t divisor) { + DCHECK(!dividend.is(eax)); + DCHECK(!dividend.is(edx)); + base::MagicNumbersForDivision mag = + base::SignedDivisionByConstant(static_cast(divisor)); + mov(eax, Immediate(mag.multiplier)); + imul(dividend); + bool neg = (mag.multiplier & (static_cast(1) << 31)) != 0; + if (divisor > 0 && neg) add(edx, dividend); + if (divisor < 0 && !neg && mag.multiplier > 0) sub(edx, dividend); + if (mag.shift > 0) sar(edx, mag.shift); + mov(eax, dividend); + shr(eax, 31); + add(edx, eax); +} + + +} // namespace internal +} // namespace v8 + +#endif // V8_TARGET_ARCH_X87 diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/x87/macro-assembler-x87.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/x87/macro-assembler-x87.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/x87/macro-assembler-x87.h 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/x87/macro-assembler-x87.h 2017-12-29 01:59:56.457468424 +0100 @@ -0,0 +1,923 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_X87_MACRO_ASSEMBLER_X87_H_ +#define V8_X87_MACRO_ASSEMBLER_X87_H_ + +#include "src/assembler.h" +#include "src/bailout-reason.h" +#include "src/frames.h" +#include "src/globals.h" + +namespace v8 { +namespace internal { + +// Give alias names to registers for calling conventions. +const Register kReturnRegister0 = {Register::kCode_eax}; +const Register kReturnRegister1 = {Register::kCode_edx}; +const Register kReturnRegister2 = {Register::kCode_edi}; +const Register kJSFunctionRegister = {Register::kCode_edi}; +const Register kContextRegister = {Register::kCode_esi}; +const Register kAllocateSizeRegister = {Register::kCode_edx}; +const Register kInterpreterAccumulatorRegister = {Register::kCode_eax}; +const Register kInterpreterBytecodeOffsetRegister = {Register::kCode_ecx}; +const Register kInterpreterBytecodeArrayRegister = {Register::kCode_edi}; +const Register kInterpreterDispatchTableRegister = {Register::kCode_esi}; +const Register kJavaScriptCallArgCountRegister = {Register::kCode_eax}; +const Register kJavaScriptCallNewTargetRegister = {Register::kCode_edx}; +const Register kRuntimeCallFunctionRegister = {Register::kCode_ebx}; +const Register kRuntimeCallArgCountRegister = {Register::kCode_eax}; + +// Spill slots used by interpreter dispatch calling convention. +const int kInterpreterDispatchTableSpillSlot = -1; + +// Convenience for platform-independent signatures. We do not normally +// distinguish memory operands from other operands on ia32. +typedef Operand MemOperand; + +enum RememberedSetAction { EMIT_REMEMBERED_SET, OMIT_REMEMBERED_SET }; +enum SmiCheck { INLINE_SMI_CHECK, OMIT_SMI_CHECK }; +enum PointersToHereCheck { + kPointersToHereMaybeInteresting, + kPointersToHereAreAlwaysInteresting +}; + +enum RegisterValueType { REGISTER_VALUE_IS_SMI, REGISTER_VALUE_IS_INT32 }; + +enum class ReturnAddressState { kOnStack, kNotOnStack }; + +#ifdef DEBUG +bool AreAliased(Register reg1, Register reg2, Register reg3 = no_reg, + Register reg4 = no_reg, Register reg5 = no_reg, + Register reg6 = no_reg, Register reg7 = no_reg, + Register reg8 = no_reg); +#endif + +class TurboAssembler: public Assembler { + public: + TurboAssembler(Isolate* isolate, void* buffer, int buffer_size, + CodeObjectRequired create_code_object) + : Assembler(isolate, buffer, buffer_size), isolate_(isolate) { + if (create_code_object == CodeObjectRequired::kYes) { + code_object_ = + Handle::New(isolate->heap()->undefined_value(), isolate); + } + } + + void set_has_frame(bool value) { has_frame_ = value; } + bool has_frame() { return has_frame_; } + + Isolate* isolate() const { return isolate_; } + + Handle CodeObject() { + DCHECK(!code_object_.is_null()); + return code_object_; + } + + void CheckPageFlag(Register object, Register scratch, int mask, Condition cc, + Label* condition_met, + Label::Distance condition_met_distance = Label::kFar); + + // Activation support. + void EnterFrame(StackFrame::Type type); + void EnterFrame(StackFrame::Type type, bool load_constant_pool_pointer_reg) { + // Out-of-line constant pool not implemented on x87. + UNREACHABLE(); + } + void LeaveFrame(StackFrame::Type type); + + // Print a message to stdout and abort execution. + void Abort(BailoutReason reason); + + // Calls Abort(msg) if the condition cc is not satisfied. + // Use --debug_code to enable. + void Assert(Condition cc, BailoutReason reason); + + // Like Assert(), but without condition. + // Use --debug_code to enable. + void AssertUnreachable(BailoutReason reason); + + // Like Assert(), but always enabled. + void Check(Condition cc, BailoutReason reason); + + // Check that the stack is aligned. + void CheckStackAlignment(); + + // Nop, because x87 does not have a root register. + void InitializeRootRegister() {} + + // Move a constant into a destination using the most efficient encoding. + void Move(Register dst, const Immediate& x); + + void Move(Register dst, Smi* source) { Move(dst, Immediate(source)); } + + // Move if the registers are not identical. + void Move(Register target, Register source); + + void Move(const Operand& dst, const Immediate& x); + + void Move(Register dst, Handle handle); + + void Call(Handle target, RelocInfo::Mode rmode) { call(target, rmode); } + void Call(Label* target) { call(target); } + + inline bool AllowThisStubCall(CodeStub* stub); + void CallStubDelayed(CodeStub* stub); + + void CallRuntimeDelayed(Zone* zone, Runtime::FunctionId fid, + SaveFPRegsMode save_doubles = kDontSaveFPRegs); + + // Jump the register contains a smi. + inline void JumpIfSmi(Register value, Label* smi_label, + Label::Distance distance = Label::kFar) { + test(value, Immediate(kSmiTagMask)); + j(zero, smi_label, distance); + } + // Jump if the operand is a smi. + inline void JumpIfSmi(Operand value, Label* smi_label, + Label::Distance distance = Label::kFar) { + test(value, Immediate(kSmiTagMask)); + j(zero, smi_label, distance); + } + + void SmiUntag(Register reg) { sar(reg, kSmiTagSize); } + + // Removes current frame and its arguments from the stack preserving + // the arguments and a return address pushed to the stack for the next call. + // |ra_state| defines whether return address is already pushed to stack or + // not. Both |callee_args_count| and |caller_args_count_reg| do not include + // receiver. |callee_args_count| is not modified, |caller_args_count_reg| + // is trashed. |number_of_temp_values_after_return_address| specifies + // the number of words pushed to the stack after the return address. This is + // to allow "allocation" of scratch registers that this function requires + // by saving their values on the stack. + void PrepareForTailCall(const ParameterCount& callee_args_count, + Register caller_args_count_reg, Register scratch0, + Register scratch1, ReturnAddressState ra_state, + int number_of_temp_values_after_return_address); + + // Before calling a C-function from generated code, align arguments on stack. + // After aligning the frame, arguments must be stored in esp[0], esp[4], + // etc., not pushed. The argument count assumes all arguments are word sized. + // Some compilers/platforms require the stack to be aligned when calling + // C++ code. + // Needs a scratch register to do some arithmetic. This register will be + // trashed. + void PrepareCallCFunction(int num_arguments, Register scratch); + + // Calls a C function and cleans up the space for arguments allocated + // by PrepareCallCFunction. The called function is not allowed to trigger a + // garbage collection, since that might move the code and invalidate the + // return address (unless this is somehow accounted for by the called + // function). + void CallCFunction(ExternalReference function, int num_arguments); + void CallCFunction(Register function, int num_arguments); + + void ShlPair(Register high, Register low, uint8_t imm8); + void ShlPair_cl(Register high, Register low); + void ShrPair(Register high, Register low, uint8_t imm8); + void ShrPair_cl(Register high, Register src); + void SarPair(Register high, Register low, uint8_t imm8); + void SarPair_cl(Register high, Register low); + + // Generates function and stub prologue code. + void StubPrologue(StackFrame::Type type); + void Prologue(bool code_pre_aging); + + void Lzcnt(Register dst, Register src) { Lzcnt(dst, Operand(src)); } + void Lzcnt(Register dst, const Operand& src); + + void Tzcnt(Register dst, Register src) { Tzcnt(dst, Operand(src)); } + void Tzcnt(Register dst, const Operand& src); + + void Popcnt(Register dst, Register src) { Popcnt(dst, Operand(src)); } + void Popcnt(Register dst, const Operand& src); + + void Ret(); + + // Return and drop arguments from stack, where the number of arguments + // may be bigger than 2^16 - 1. Requires a scratch register. + void Ret(int bytes_dropped, Register scratch); + + // Insert code to verify that the x87 stack has the specified depth (0-7) + void VerifyX87StackDepth(uint32_t depth); + + void LoadUint32NoSSE2(Register src) { + LoadUint32NoSSE2(Operand(src)); + } + void LoadUint32NoSSE2(const Operand& src); + + // FCmp is similar to integer cmp, but requires unsigned + // jcc instructions (je, ja, jae, jb, jbe, je, and jz). + void FCmp(); + void X87SetRC(int rc); + void X87SetFPUCW(int cw); + + void SlowTruncateToIDelayed(Zone* zone, Register result_reg, + Register input_reg, + int offset = HeapNumber::kValueOffset - + kHeapObjectTag); + void TruncateX87TOSToI(Zone* zone, Register result_reg); + + void Push(Register src) { push(src); } + void Push(const Operand& src) { push(src); } + void Push(Immediate value) { push(value); } + void Push(Handle handle) { push(Immediate(handle)); } + void Push(Smi* smi) { Push(Immediate(smi)); } + + private: + bool has_frame_; + Isolate* isolate_; + // This handle will be patched with the code object on installation. + Handle code_object_; +}; + +// MacroAssembler implements a collection of frequently used macros. +class MacroAssembler: public TurboAssembler { + public: + MacroAssembler(Isolate* isolate, void* buffer, int size, + CodeObjectRequired create_code_object); + + int jit_cookie() const { return jit_cookie_; } + + void Load(Register dst, const Operand& src, Representation r); + void Store(Register src, const Operand& dst, Representation r); + + // Load a register with a long value as efficiently as possible. + void Set(Register dst, int32_t x) { + if (x == 0) { + xor_(dst, dst); + } else { + mov(dst, Immediate(x)); + } + } + void Set(const Operand& dst, int32_t x) { mov(dst, Immediate(x)); } + + // Operations on roots in the root-array. + void LoadRoot(Register destination, Heap::RootListIndex index); + void StoreRoot(Register source, Register scratch, Heap::RootListIndex index); + void CompareRoot(Register with, Register scratch, Heap::RootListIndex index); + // These methods can only be used with constant roots (i.e. non-writable + // and not in new space). + void CompareRoot(Register with, Heap::RootListIndex index); + void CompareRoot(const Operand& with, Heap::RootListIndex index); + void PushRoot(Heap::RootListIndex index); + + // Compare the object in a register to a value and jump if they are equal. + void JumpIfRoot(Register with, Heap::RootListIndex index, Label* if_equal, + Label::Distance if_equal_distance = Label::kFar) { + CompareRoot(with, index); + j(equal, if_equal, if_equal_distance); + } + void JumpIfRoot(const Operand& with, Heap::RootListIndex index, + Label* if_equal, + Label::Distance if_equal_distance = Label::kFar) { + CompareRoot(with, index); + j(equal, if_equal, if_equal_distance); + } + + // Compare the object in a register to a value and jump if they are not equal. + void JumpIfNotRoot(Register with, Heap::RootListIndex index, + Label* if_not_equal, + Label::Distance if_not_equal_distance = Label::kFar) { + CompareRoot(with, index); + j(not_equal, if_not_equal, if_not_equal_distance); + } + void JumpIfNotRoot(const Operand& with, Heap::RootListIndex index, + Label* if_not_equal, + Label::Distance if_not_equal_distance = Label::kFar) { + CompareRoot(with, index); + j(not_equal, if_not_equal, if_not_equal_distance); + } + + // These functions do not arrange the registers in any particular order so + // they are not useful for calls that can cause a GC. The caller can + // exclude up to 3 registers that do not need to be saved and restored. + void PushCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1 = no_reg, + Register exclusion2 = no_reg, + Register exclusion3 = no_reg); + void PopCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1 = no_reg, + Register exclusion2 = no_reg, + Register exclusion3 = no_reg); + + // --------------------------------------------------------------------------- + // GC Support + enum RememberedSetFinalAction { kReturnAtEnd, kFallThroughAtEnd }; + + // Record in the remembered set the fact that we have a pointer to new space + // at the address pointed to by the addr register. Only works if addr is not + // in new space. + void RememberedSetHelper(Register object, // Used for debug code. + Register addr, Register scratch, + SaveFPRegsMode save_fp, + RememberedSetFinalAction and_then); + + void CheckPageFlagForMap( + Handle map, int mask, Condition cc, Label* condition_met, + Label::Distance condition_met_distance = Label::kFar); + + // Check if object is in new space. Jumps if the object is not in new space. + // The register scratch can be object itself, but scratch will be clobbered. + void JumpIfNotInNewSpace(Register object, Register scratch, Label* branch, + Label::Distance distance = Label::kFar) { + InNewSpace(object, scratch, zero, branch, distance); + } + + // Check if object is in new space. Jumps if the object is in new space. + // The register scratch can be object itself, but it will be clobbered. + void JumpIfInNewSpace(Register object, Register scratch, Label* branch, + Label::Distance distance = Label::kFar) { + InNewSpace(object, scratch, not_zero, branch, distance); + } + + // Check if an object has a given incremental marking color. Also uses ecx! + void HasColor(Register object, Register scratch0, Register scratch1, + Label* has_color, Label::Distance has_color_distance, + int first_bit, int second_bit); + + void JumpIfBlack(Register object, Register scratch0, Register scratch1, + Label* on_black, + Label::Distance on_black_distance = Label::kFar); + + // Checks the color of an object. If the object is white we jump to the + // incremental marker. + void JumpIfWhite(Register value, Register scratch1, Register scratch2, + Label* value_is_white, Label::Distance distance); + + // Notify the garbage collector that we wrote a pointer into an object. + // |object| is the object being stored into, |value| is the object being + // stored. value and scratch registers are clobbered by the operation. + // The offset is the offset from the start of the object, not the offset from + // the tagged HeapObject pointer. For use with FieldOperand(reg, off). + void RecordWriteField( + Register object, int offset, Register value, Register scratch, + SaveFPRegsMode save_fp, + RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET, + SmiCheck smi_check = INLINE_SMI_CHECK, + PointersToHereCheck pointers_to_here_check_for_value = + kPointersToHereMaybeInteresting); + + // As above, but the offset has the tag presubtracted. For use with + // Operand(reg, off). + void RecordWriteContextSlot( + Register context, int offset, Register value, Register scratch, + SaveFPRegsMode save_fp, + RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET, + SmiCheck smi_check = INLINE_SMI_CHECK, + PointersToHereCheck pointers_to_here_check_for_value = + kPointersToHereMaybeInteresting) { + RecordWriteField(context, offset + kHeapObjectTag, value, scratch, save_fp, + remembered_set_action, smi_check, + pointers_to_here_check_for_value); + } + + // For page containing |object| mark region covering |address| + // dirty. |object| is the object being stored into, |value| is the + // object being stored. The address and value registers are clobbered by the + // operation. RecordWrite filters out smis so it does not update the + // write barrier if the value is a smi. + void RecordWrite( + Register object, Register address, Register value, SaveFPRegsMode save_fp, + RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET, + SmiCheck smi_check = INLINE_SMI_CHECK, + PointersToHereCheck pointers_to_here_check_for_value = + kPointersToHereMaybeInteresting); + + // Notify the garbage collector that we wrote a code entry into a + // JSFunction. Only scratch is clobbered by the operation. + void RecordWriteCodeEntryField(Register js_function, Register code_entry, + Register scratch); + + // For page containing |object| mark the region covering the object's map + // dirty. |object| is the object being stored into, |map| is the Map object + // that was stored. + void RecordWriteForMap(Register object, Handle map, Register scratch1, + Register scratch2, SaveFPRegsMode save_fp); + + // Frame restart support + void MaybeDropFrames(); + + // Enter specific kind of exit frame. Expects the number of + // arguments in register eax and sets up the number of arguments in + // register edi and the pointer to the first argument in register + // esi. + void EnterExitFrame(int argc, bool save_doubles, StackFrame::Type frame_type); + + void EnterApiExitFrame(int argc); + + // Leave the current exit frame. Expects the return value in + // register eax:edx (untouched) and the pointer to the first + // argument in register esi (if pop_arguments == true). + void LeaveExitFrame(bool save_doubles, bool pop_arguments = true); + + // Leave the current exit frame. Expects the return value in + // register eax (untouched). + void LeaveApiExitFrame(bool restore_context); + + // Find the function context up the context chain. + void LoadContext(Register dst, int context_chain_length); + + // Load the global proxy from the current context. + void LoadGlobalProxy(Register dst); + + // Load the global function with the given index. + void LoadGlobalFunction(int index, Register function); + + // Load the initial map from the global function. The registers + // function and map can be the same. + void LoadGlobalFunctionInitialMap(Register function, Register map); + + // Push and pop the registers that can hold pointers. + void PushSafepointRegisters() { pushad(); } + void PopSafepointRegisters() { popad(); } + // Store the value in register/immediate src in the safepoint + // register stack slot for register dst. + void StoreToSafepointRegisterSlot(Register dst, Register src); + void StoreToSafepointRegisterSlot(Register dst, Immediate src); + void LoadFromSafepointRegisterSlot(Register dst, Register src); + + void CmpHeapObject(Register reg, Handle object); + void PushObject(Handle object); + + void CmpObject(Register reg, Handle object) { + AllowDeferredHandleDereference heap_object_check; + if (object->IsHeapObject()) { + CmpHeapObject(reg, Handle::cast(object)); + } else { + cmp(reg, Immediate(Smi::cast(*object))); + } + } + + void GetWeakValue(Register value, Handle cell); + void LoadWeakValue(Register value, Handle cell, Label* miss); + + // --------------------------------------------------------------------------- + // JavaScript invokes + + // Invoke the JavaScript function code by either calling or jumping. + + void InvokeFunctionCode(Register function, Register new_target, + const ParameterCount& expected, + const ParameterCount& actual, InvokeFlag flag, + const CallWrapper& call_wrapper); + + // On function call, call into the debugger if necessary. + void CheckDebugHook(Register fun, Register new_target, + const ParameterCount& expected, + const ParameterCount& actual); + + // Invoke the JavaScript function in the given register. Changes the + // current context to the context in the function before invoking. + void InvokeFunction(Register function, Register new_target, + const ParameterCount& actual, InvokeFlag flag, + const CallWrapper& call_wrapper); + + void InvokeFunction(Register function, const ParameterCount& expected, + const ParameterCount& actual, InvokeFlag flag, + const CallWrapper& call_wrapper); + + void InvokeFunction(Handle function, + const ParameterCount& expected, + const ParameterCount& actual, InvokeFlag flag, + const CallWrapper& call_wrapper); + + // Support for constant splitting. + bool IsUnsafeImmediate(const Immediate& x); + void SafeMove(Register dst, const Immediate& x); + void SafePush(const Immediate& x); + + // Compare object type for heap object. + // Incoming register is heap_object and outgoing register is map. + void CmpObjectType(Register heap_object, InstanceType type, Register map); + + // Compare instance type for map. + void CmpInstanceType(Register map, InstanceType type); + + // Compare an object's map with the specified map. + void CompareMap(Register obj, Handle map); + + // Check if the map of an object is equal to a specified map and branch to + // label if not. Skip the smi check if not required (object is known to be a + // heap object). If mode is ALLOW_ELEMENT_TRANSITION_MAPS, then also match + // against maps that are ElementsKind transition maps of the specified map. + void CheckMap(Register obj, Handle map, Label* fail, + SmiCheckType smi_check_type); + + // Check if the object in register heap_object is a string. Afterwards the + // register map contains the object map and the register instance_type + // contains the instance_type. The registers map and instance_type can be the + // same in which case it contains the instance type afterwards. Either of the + // registers map and instance_type can be the same as heap_object. + Condition IsObjectStringType(Register heap_object, Register map, + Register instance_type); + + void FXamMinusZero(); + void FXamSign(); + void X87CheckIA(); + + void ClampUint8(Register reg); + void ClampTOSToUint8(Register result_reg); + + void SlowTruncateToI(Register result_reg, Register input_reg, + int offset = HeapNumber::kValueOffset - kHeapObjectTag); + + void TruncateHeapNumberToI(Register result_reg, Register input_reg); + + void X87TOSToI(Register result_reg, MinusZeroMode minus_zero_mode, + Label* lost_precision, Label* is_nan, Label* minus_zero, + Label::Distance dst = Label::kFar); + + // Smi tagging support. + void SmiTag(Register reg) { + STATIC_ASSERT(kSmiTag == 0); + STATIC_ASSERT(kSmiTagSize == 1); + add(reg, reg); + } + + // Modifies the register even if it does not contain a Smi! + void UntagSmi(Register reg, Label* is_smi) { + STATIC_ASSERT(kSmiTagSize == 1); + sar(reg, kSmiTagSize); + STATIC_ASSERT(kSmiTag == 0); + j(not_carry, is_smi); + } + + // Jump if register contain a non-smi. + inline void JumpIfNotSmi(Register value, Label* not_smi_label, + Label::Distance distance = Label::kFar) { + test(value, Immediate(kSmiTagMask)); + j(not_zero, not_smi_label, distance); + } + // Jump if the operand is not a smi. + inline void JumpIfNotSmi(Operand value, Label* smi_label, + Label::Distance distance = Label::kFar) { + test(value, Immediate(kSmiTagMask)); + j(not_zero, smi_label, distance); + } + // Jump if the value cannot be represented by a smi. + inline void JumpIfNotValidSmiValue(Register value, Register scratch, + Label* on_invalid, + Label::Distance distance = Label::kFar) { + mov(scratch, value); + add(scratch, Immediate(0x40000000U)); + j(sign, on_invalid, distance); + } + + // Jump if the unsigned integer value cannot be represented by a smi. + inline void JumpIfUIntNotValidSmiValue( + Register value, Label* on_invalid, + Label::Distance distance = Label::kFar) { + cmp(value, Immediate(0x40000000U)); + j(above_equal, on_invalid, distance); + } + + void LoadInstanceDescriptors(Register map, Register descriptors); + void EnumLength(Register dst, Register map); + void NumberOfOwnDescriptors(Register dst, Register map); + void LoadAccessor(Register dst, Register holder, int accessor_index, + AccessorComponent accessor); + + template + void DecodeField(Register reg) { + static const int shift = Field::kShift; + static const int mask = Field::kMask >> Field::kShift; + if (shift != 0) { + sar(reg, shift); + } + and_(reg, Immediate(mask)); + } + + template + void DecodeFieldToSmi(Register reg) { + static const int shift = Field::kShift; + static const int mask = (Field::kMask >> Field::kShift) << kSmiTagSize; + STATIC_ASSERT((mask & (0x80000000u >> (kSmiTagSize - 1))) == 0); + STATIC_ASSERT(kSmiTag == 0); + if (shift < kSmiTagSize) { + shl(reg, kSmiTagSize - shift); + } else if (shift > kSmiTagSize) { + sar(reg, shift - kSmiTagSize); + } + and_(reg, Immediate(mask)); + } + + // Abort execution if argument is not a smi, enabled via --debug-code. + void AssertSmi(Register object); + + // Abort execution if argument is a smi, enabled via --debug-code. + void AssertNotSmi(Register object); + + // Abort execution if argument is not a FixedArray, enabled via --debug-code. + void AssertFixedArray(Register object); + + // Abort execution if argument is not a JSFunction, enabled via --debug-code. + void AssertFunction(Register object); + + // Abort execution if argument is not a JSBoundFunction, + // enabled via --debug-code. + void AssertBoundFunction(Register object); + + // Abort execution if argument is not a JSGeneratorObject (or subclass), + // enabled via --debug-code. + void AssertGeneratorObject(Register object); + + // Abort execution if argument is not undefined or an AllocationSite, enabled + // via --debug-code. + void AssertUndefinedOrAllocationSite(Register object); + + // --------------------------------------------------------------------------- + // Exception handling + + // Push a new stack handler and link it into stack handler chain. + void PushStackHandler(); + + // Unlink the stack handler on top of the stack from the stack handler chain. + void PopStackHandler(); + + // --------------------------------------------------------------------------- + // Inline caching support + + void GetNumberHash(Register r0, Register scratch); + + // --------------------------------------------------------------------------- + // Allocation support + + // Allocate an object in new space or old space. If the given space + // is exhausted control continues at the gc_required label. The allocated + // object is returned in result and end of the new object is returned in + // result_end. The register scratch can be passed as no_reg in which case + // an additional object reference will be added to the reloc info. The + // returned pointers in result and result_end have not yet been tagged as + // heap objects. If result_contains_top_on_entry is true the content of + // result is known to be the allocation top on entry (could be result_end + // from a previous call). If result_contains_top_on_entry is true scratch + // should be no_reg as it is never used. + void Allocate(int object_size, Register result, Register result_end, + Register scratch, Label* gc_required, AllocationFlags flags); + + void Allocate(int header_size, ScaleFactor element_size, + Register element_count, RegisterValueType element_count_type, + Register result, Register result_end, Register scratch, + Label* gc_required, AllocationFlags flags); + + void Allocate(Register object_size, Register result, Register result_end, + Register scratch, Label* gc_required, AllocationFlags flags); + + // Allocate a heap number in new space with undefined value. The + // register scratch2 can be passed as no_reg; the others must be + // valid registers. Returns tagged pointer in result register, or + // jumps to gc_required if new space is full. + void AllocateHeapNumber(Register result, Register scratch1, Register scratch2, + Label* gc_required, MutableMode mode = IMMUTABLE); + + // Allocate and initialize a JSValue wrapper with the specified {constructor} + // and {value}. + void AllocateJSValue(Register result, Register constructor, Register value, + Register scratch, Label* gc_required); + + // Initialize fields with filler values. Fields starting at |current_address| + // not including |end_address| are overwritten with the value in |filler|. At + // the end the loop, |current_address| takes the value of |end_address|. + void InitializeFieldsWithFiller(Register current_address, + Register end_address, Register filler); + + // --------------------------------------------------------------------------- + // Support functions. + + // Check a boolean-bit of a Smi field. + void BooleanBitTest(Register object, int field_offset, int bit_index); + + // Machine code version of Map::GetConstructor(). + // |temp| holds |result|'s map when done. + void GetMapConstructor(Register result, Register map, Register temp); + + // --------------------------------------------------------------------------- + // Runtime calls + + // Call a code stub. Generate the code if necessary. + void CallStub(CodeStub* stub); + + // Tail call a code stub (jump). Generate the code if necessary. + void TailCallStub(CodeStub* stub); + + // Call a runtime routine. + void CallRuntime(const Runtime::Function* f, int num_arguments, + SaveFPRegsMode save_doubles = kDontSaveFPRegs); + void CallRuntimeSaveDoubles(Runtime::FunctionId fid) { + const Runtime::Function* function = Runtime::FunctionForId(fid); + CallRuntime(function, function->nargs, kSaveFPRegs); + } + + // Convenience function: Same as above, but takes the fid instead. + void CallRuntime(Runtime::FunctionId fid, + SaveFPRegsMode save_doubles = kDontSaveFPRegs) { + const Runtime::Function* function = Runtime::FunctionForId(fid); + CallRuntime(function, function->nargs, save_doubles); + } + + // Convenience function: Same as above, but takes the fid instead. + void CallRuntime(Runtime::FunctionId fid, int num_arguments, + SaveFPRegsMode save_doubles = kDontSaveFPRegs) { + CallRuntime(Runtime::FunctionForId(fid), num_arguments, save_doubles); + } + + // Convenience function: call an external reference. + void CallExternalReference(ExternalReference ref, int num_arguments); + + // Convenience function: tail call a runtime routine (jump). + void TailCallRuntime(Runtime::FunctionId fid); + + // Jump to a runtime routine. + void JumpToExternalReference(const ExternalReference& ext, + bool builtin_exit_frame = false); + + // --------------------------------------------------------------------------- + // Utilities + + // Emit code that loads |parameter_index|'th parameter from the stack to + // the register according to the CallInterfaceDescriptor definition. + // |sp_to_caller_sp_offset_in_words| specifies the number of words pushed + // below the caller's sp (on x87 it's at least return address). + template + void LoadParameterFromStack( + Register reg, typename Descriptor::ParameterIndices parameter_index, + int sp_to_ra_offset_in_words = 1) { + DCHECK(Descriptor::kPassLastArgsOnStack); + DCHECK_LT(parameter_index, Descriptor::kParameterCount); + DCHECK_LE(Descriptor::kParameterCount - Descriptor::kStackArgumentsCount, + parameter_index); + int offset = (Descriptor::kParameterCount - parameter_index - 1 + + sp_to_ra_offset_in_words) * + kPointerSize; + mov(reg, Operand(esp, offset)); + } + + // Emit code to discard a non-negative number of pointer-sized elements + // from the stack, clobbering only the esp register. + void Drop(int element_count); + + void Jump(Handle target, RelocInfo::Mode rmode) { jmp(target, rmode); } + void Pop(Register dst) { pop(dst); } + void Pop(const Operand& dst) { pop(dst); } + void PushReturnAddressFrom(Register src) { push(src); } + void PopReturnAddressTo(Register dst) { pop(dst); } + + // Emit code for a truncating division by a constant. The dividend register is + // unchanged, the result is in edx, and eax gets clobbered. + void TruncatingDiv(Register dividend, int32_t divisor); + + // --------------------------------------------------------------------------- + // StatsCounter support + + void SetCounter(StatsCounter* counter, int value); + void IncrementCounter(StatsCounter* counter, int value); + void DecrementCounter(StatsCounter* counter, int value); + void IncrementCounter(Condition cc, StatsCounter* counter, int value); + void DecrementCounter(Condition cc, StatsCounter* counter, int value); + + // --------------------------------------------------------------------------- + // String utilities. + + // Checks if both objects are sequential one-byte strings, and jumps to label + // if either is not. + void JumpIfNotBothSequentialOneByteStrings( + Register object1, Register object2, Register scratch1, Register scratch2, + Label* on_not_flat_one_byte_strings); + + // Checks if the given register or operand is a unique name + void JumpIfNotUniqueNameInstanceType(Register reg, Label* not_unique_name, + Label::Distance distance = Label::kFar) { + JumpIfNotUniqueNameInstanceType(Operand(reg), not_unique_name, distance); + } + + void JumpIfNotUniqueNameInstanceType(Operand operand, Label* not_unique_name, + Label::Distance distance = Label::kFar); + + void EmitSeqStringSetCharCheck(Register string, Register index, + Register value, uint32_t encoding_mask); + + static int SafepointRegisterStackIndex(Register reg) { + return SafepointRegisterStackIndex(reg.code()); + } + + // Load the type feedback vector from a JavaScript frame. + void EmitLoadFeedbackVector(Register vector); + + void EnterBuiltinFrame(Register context, Register target, Register argc); + void LeaveBuiltinFrame(Register context, Register target, Register argc); + + // Expects object in eax and returns map with validated enum cache + // in eax. Assumes that any other register can be used as a scratch. + void CheckEnumCache(Label* call_runtime); + + // AllocationMemento support. Arrays may have an associated + // AllocationMemento object that can be checked for in order to pretransition + // to another type. + // On entry, receiver_reg should point to the array object. + // scratch_reg gets clobbered. + // If allocation info is present, conditional code is set to equal. + void TestJSArrayForAllocationMemento(Register receiver_reg, + Register scratch_reg, + Label* no_memento_found); + + private: + int jit_cookie_; + + // Helper functions for generating invokes. + void InvokePrologue(const ParameterCount& expected, + const ParameterCount& actual, Label* done, + bool* definitely_mismatches, InvokeFlag flag, + Label::Distance done_distance, + const CallWrapper& call_wrapper); + + void EnterExitFramePrologue(StackFrame::Type frame_type); + void EnterExitFrameEpilogue(int argc, bool save_doubles); + + void LeaveExitFrameEpilogue(bool restore_context); + + // Allocation support helpers. + void LoadAllocationTopHelper(Register result, Register scratch, + AllocationFlags flags); + + void UpdateAllocationTopHelper(Register result_end, Register scratch, + AllocationFlags flags); + + // Helper for implementing JumpIfNotInNewSpace and JumpIfInNewSpace. + void InNewSpace(Register object, Register scratch, Condition cc, + Label* condition_met, + Label::Distance condition_met_distance = Label::kFar); + + // Helper for finding the mark bits for an address. Afterwards, the + // bitmap register points at the word with the mark bits and the mask + // the position of the first bit. Uses ecx as scratch and leaves addr_reg + // unchanged. + inline void GetMarkBits(Register addr_reg, Register bitmap_reg, + Register mask_reg); + + // Compute memory operands for safepoint stack slots. + Operand SafepointRegisterSlot(Register reg); + static int SafepointRegisterStackIndex(int reg_code); + + // Needs access to SafepointRegisterStackIndex for compiled frame + // traversal. + friend class StandardFrame; +}; + +// The code patcher is used to patch (typically) small parts of code e.g. for +// debugging and other types of instrumentation. When using the code patcher +// the exact number of bytes specified must be emitted. Is not legal to emit +// relocation information. If any of these constraints are violated it causes +// an assertion. +class CodePatcher { + public: + CodePatcher(Isolate* isolate, byte* address, int size); + ~CodePatcher(); + + // Macro assembler to emit code. + MacroAssembler* masm() { return &masm_; } + + private: + byte* address_; // The address of the code being patched. + int size_; // Number of bytes of the expected patch size. + MacroAssembler masm_; // Macro assembler used to generate the code. +}; + +// ----------------------------------------------------------------------------- +// Static helper functions. + +// Generate an Operand for loading a field from an object. +inline Operand FieldOperand(Register object, int offset) { + return Operand(object, offset - kHeapObjectTag); +} + +// Generate an Operand for loading an indexed field from an object. +inline Operand FieldOperand(Register object, Register index, ScaleFactor scale, + int offset) { + return Operand(object, index, scale, offset - kHeapObjectTag); +} + +inline Operand FixedArrayElementOperand(Register array, Register index_as_smi, + int additional_offset = 0) { + int offset = FixedArray::kHeaderSize + additional_offset * kPointerSize; + return FieldOperand(array, index_as_smi, times_half_pointer_size, offset); +} + +inline Operand ContextOperand(Register context, int index) { + return Operand(context, Context::SlotOffset(index)); +} + +inline Operand ContextOperand(Register context, Register index) { + return Operand(context, index, times_pointer_size, Context::SlotOffset(0)); +} + +inline Operand NativeContextOperand() { + return ContextOperand(esi, Context::NATIVE_CONTEXT_INDEX); +} + +#define ACCESS_MASM(masm) masm-> + +} // namespace internal +} // namespace v8 + +#endif // V8_X87_MACRO_ASSEMBLER_X87_H_ diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/x87/OWNERS qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/x87/OWNERS --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/x87/OWNERS 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/x87/OWNERS 2017-12-25 17:42:57.218465603 +0100 @@ -0,0 +1,2 @@ +weiliang.lin@intel.com +chunyang.dai@intel.com diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/x87/simulator-x87.cc qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/x87/simulator-x87.cc --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/x87/simulator-x87.cc 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/x87/simulator-x87.cc 2017-12-25 17:42:57.224465515 +0100 @@ -0,0 +1,7 @@ +// Copyright 2008 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/x87/simulator-x87.h" + +// Since there is no simulator for the ia32 architecture this file is empty. diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/x87/simulator-x87.h qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/x87/simulator-x87.h --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/src/x87/simulator-x87.h 1970-01-01 01:00:00.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/src/x87/simulator-x87.h 2017-12-25 17:42:57.224465515 +0100 @@ -0,0 +1,52 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_X87_SIMULATOR_X87_H_ +#define V8_X87_SIMULATOR_X87_H_ + +#include "src/allocation.h" + +namespace v8 { +namespace internal { + +// Since there is no simulator for the ia32 architecture the only thing we can +// do is to call the entry directly. +#define CALL_GENERATED_CODE(isolate, entry, p0, p1, p2, p3, p4) \ + (entry(p0, p1, p2, p3, p4)) + + +typedef int (*regexp_matcher)(String*, int, const byte*, + const byte*, int*, int, Address, int, Isolate*); + +// Call the generated regexp code directly. The code at the entry address should +// expect eight int/pointer sized arguments and return an int. +#define CALL_GENERATED_REGEXP_CODE(isolate, entry, p0, p1, p2, p3, p4, p5, p6, \ + p7, p8) \ + (FUNCTION_CAST(entry)(p0, p1, p2, p3, p4, p5, p6, p7, p8)) + + +// The stack limit beyond which we will throw stack overflow errors in +// generated code. Because generated code on ia32 uses the C stack, we +// just use the C stack limit. +class SimulatorStack : public v8::internal::AllStatic { + public: + static inline uintptr_t JsLimitFromCLimit(Isolate* isolate, + uintptr_t c_limit) { + USE(isolate); + return c_limit; + } + + static inline uintptr_t RegisterCTryCatch(Isolate* isolate, + uintptr_t try_catch_address) { + USE(isolate); + return try_catch_address; + } + + static inline void UnregisterCTryCatch(Isolate* isolate) { USE(isolate); } +}; + +} // namespace internal +} // namespace v8 + +#endif // V8_X87_SIMULATOR_X87_H_ diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/test/cctest/BUILD.gn qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/test/cctest/BUILD.gn --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/test/cctest/BUILD.gn 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/test/cctest/BUILD.gn 2017-12-25 17:42:57.224465515 +0100 @@ -287,6 +287,17 @@ "test-macro-assembler-x64.cc", "test-run-wasm-relocation-x64.cc", ] + } else if (v8_current_cpu == "x87") { + sources += [ ### gcmole(arch:x87) ### + "test-assembler-x87.cc", + "test-code-stubs-x87.cc", + "test-code-stubs.cc", + "test-code-stubs.h", + "test-disasm-x87.cc", + "test-log-stack-tracer.cc", + "test-macro-assembler-x87.cc", + "test-run-wasm-relocation-x87.cc", + ] } else if (v8_current_cpu == "ppc" || v8_current_cpu == "ppc64") { sources += [ ### gcmole(arch:ppc) ### "test-assembler-ppc.cc", @@ -332,7 +343,7 @@ defines = [] - if (is_component_build) { + if (is_component_build || v8_build_shared) { # cctest can't be built against a shared library, so we # need to depend on the underlying static target in that case. deps += [ "../..:v8_maybe_snapshot" ] diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/test/cctest/cctest.gyp qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/test/cctest/cctest.gyp --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/test/cctest/cctest.gyp 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/test/cctest/cctest.gyp 2017-12-25 17:43:10.318273560 +0100 @@ -308,6 +308,16 @@ 'test-disasm-mips64.cc', 'test-macro-assembler-mips64.cc', ], + 'cctest_sources_x87': [ ### gcmole(arch:x87) ### + 'test-assembler-x87.cc', + 'test-code-stubs.cc', + 'test-code-stubs.h', + 'test-code-stubs-x87.cc', + 'test-disasm-x87.cc', + 'test-macro-assembler-x87.cc', + 'test-log-stack-tracer.cc', + 'test-run-wasm-relocation-x87.cc', + ], }, 'includes': ['../../gypfiles/toolchain.gypi', '../../gypfiles/features.gypi'], 'targets': [ @@ -392,6 +402,11 @@ '<@(cctest_sources_mips64el)', ], }], + ['v8_target_arch=="x87"', { + 'sources': [ + '<@(cctest_sources_x87)', + ], + }], [ 'OS=="linux" or OS=="qnx"', { 'sources': [ 'test-platform-linux.cc', diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/tools/dev/gen-tags.py qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/tools/dev/gen-tags.py --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/tools/dev/gen-tags.py 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/tools/dev/gen-tags.py 2017-12-25 17:43:15.534197094 +0100 @@ -20,7 +20,7 @@ import sys # All arches that this script understands. -ARCHES = ["ia32", "x64", "arm", "arm64", "mips", "mips64", "ppc", "s390"] +ARCHES = ["ia32", "x64", "arm", "arm64", "mips", "mips64", "ppc", "s390", "x87"] def PrintHelpAndExit(): print(__doc__) diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/tools/dev/gm.py qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/tools/dev/gm.py --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/tools/dev/gm.py 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/tools/dev/gm.py 2017-12-25 17:43:15.534197094 +0100 @@ -33,7 +33,7 @@ # All arches that this script understands. ARCHES = ["ia32", "x64", "arm", "arm64", "mipsel", "mips64el", "ppc", "ppc64", - "s390", "s390x"] + "s390", "s390x", "x87"] # Arches that get built/run when you don't specify any. DEFAULT_ARCHES = ["ia32", "x64", "arm", "arm64"] # Modes that this script understands. diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/tools/run-tests.py qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/tools/run-tests.py --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/tools/run-tests.py 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/tools/run-tests.py 2017-12-25 17:43:15.534197094 +0100 @@ -187,6 +187,7 @@ "android_x64", "arm", "ia32", + "x87", "mips", "mipsel", "mips64", @@ -210,6 +211,7 @@ "mips64el", "s390", "s390x", + "x87", "arm64"] diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/tools/testrunner/local/statusfile.py qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/tools/testrunner/local/statusfile.py --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/tools/testrunner/local/statusfile.py 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/tools/testrunner/local/statusfile.py 2017-12-25 17:43:15.534197094 +0100 @@ -59,10 +59,10 @@ # Support arches, modes to be written as keywords instead of strings. VARIABLES = {ALWAYS: True} for var in ["debug", "release", "big", "little", - "android_arm", "android_arm64", "android_ia32", "android_x64", - "arm", "arm64", "ia32", "mips", "mipsel", "mips64", "mips64el", - "x64", "ppc", "ppc64", "s390", "s390x", "macos", "windows", - "linux", "aix"]: + "android_arm", "android_arm64", "android_ia32", "android_x87", + "android_x64", "arm", "arm64", "ia32", "mips", "mipsel", "mips64", + "mips64el", "x64", "x87", "ppc", "ppc64", "s390", "s390x", "macos", + "windows", "linux", "aix"]: VARIABLES[var] = var # Allow using variants as keywords. diff -Nur qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/tools/verify_source_deps.py qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/tools/verify_source_deps.py --- qtwebengine-everywhere-src-5.10.0/src/3rdparty/chromium/v8/tools/verify_source_deps.py 2017-11-28 14:06:53.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/3rdparty/chromium/v8/tools/verify_source_deps.py 2017-12-25 17:43:15.535197080 +0100 @@ -82,6 +82,7 @@ 'solaris', 'vtune', 'v8-version.h', + 'x87', ] ALL_GN_PREFIXES = [ diff -Nur qtwebengine-everywhere-src-5.10.0/src/core/core_module.pro qtwebengine-everywhere-src-5.10.0-no-sse2/src/core/core_module.pro --- qtwebengine-everywhere-src-5.10.0/src/core/core_module.pro 2017-11-29 09:42:29.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/core/core_module.pro 2017-12-25 13:05:24.093938639 +0100 @@ -44,6 +44,31 @@ else: QMAKE_LFLAGS += $$NINJA_LFLAGS POST_TARGETDEPS += $$NINJA_TARGETDEPS +# go through the shared libraries that GN wants to link to +# ignore the dummy convert_dict shared library used only to get a .pri file +# add the ones NOT in lib/sse2 to LIBS_PRIVATE +# don't add those in lib/sse2 that are only replacements for the normal ones +# collect all shared libraries, non-SSE2 and SSE2, so they can be installed +for(shlib, NINJA_SOLIBS) { + !contains(shlib, .*convert_dict.*) { + contains(shlib, .*/lib/sse2/.*) { + shlibs_sse2 += $$shlib + } else { + LIBS_PRIVATE += $$shlib + shlibs += $$shlib + } + } +} + +# set the shared libraries to be installed +# add an rpath to their installation location +shlib_install_path = $$[QT_INSTALL_LIBS]/qtwebengine +!isEmpty(shlibs) { + shlibs.files += $$shlibs + shlibs_sse2.files += $$shlibs_sse2 + LIBS_PRIVATE += -Wl,--rpath,$$shlib_install_path +} + LIBS_PRIVATE += -L$$api_library_path CONFIG *= no_smart_library_merge @@ -113,7 +138,12 @@ locales.path = $$[QT_INSTALL_TRANSLATIONS]/qtwebengine_locales resources.CONFIG += no_check_exist resources.path = $$[QT_INSTALL_DATA]/resources - INSTALLS += locales resources + # install the shared libraries + shlibs.CONFIG += no_check_exist + shlibs.path = $$shlib_install_path + shlibs_sse2.CONFIG += no_check_exist + shlibs_sse2.path = $$shlib_install_path/sse2 + INSTALLS += locales resources shlibs shlibs_sse2 !qtConfig(webengine-system-icu) { icu.CONFIG += no_check_exist diff -Nur qtwebengine-everywhere-src-5.10.0/src/process/process.pro qtwebengine-everywhere-src-5.10.0-no-sse2/src/process/process.pro --- qtwebengine-everywhere-src-5.10.0/src/process/process.pro 2017-11-29 09:42:29.000000000 +0100 +++ qtwebengine-everywhere-src-5.10.0-no-sse2/src/process/process.pro 2017-12-30 00:42:27.968630675 +0100 @@ -9,6 +9,8 @@ SOURCES = main.cpp +QMAKE_LFLAGS += -Wl,-rpath-link,$$OUT_PWD/../core/release + win32 { SOURCES += \ support_win.cpp