02167f0744
The src/core/Release directory was renamed to src/core/release.
30293 lines
1.0 MiB
30293 lines
1.0 MiB
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 <algorithm>
|
|
#include <cmath>
|
|
#include <limits>
|
|
-#if defined(ARCH_CPU_X86_FAMILY)
|
|
+#ifdef __SSE__
|
|
#include <xmmintrin.h>
|
|
#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 <vector>
|
|
|
|
#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 <cmath>
|
|
#include <limits>
|
|
|
|
+#include "base/cpu.h"
|
|
#include "base/logging.h"
|
|
#include "build/build_config.h"
|
|
|
|
-#if defined(ARCH_CPU_X86_FAMILY)
|
|
-#include <xmmintrin.h>
|
|
-#define CONVOLVE_FUNC Convolve_SSE
|
|
-#elif defined(ARCH_CPU_ARM_FAMILY) && defined(USE_NEON)
|
|
+#if defined(ARCH_CPU_ARM_FAMILY) && defined(USE_NEON)
|
|
#include <arm_neon.h>
|
|
-#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<uintptr_t>(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<float>(1.0 - kernel_interpolation_factor)));
|
|
- m_sums2 = _mm_mul_ps(m_sums2, _mm_set_ps1(
|
|
- static_cast<float>(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 <xmmintrin.h>
|
|
+
|
|
+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<uintptr_t>(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<float>(1.0 - kernel_interpolation_factor)));
|
|
+ m_sums2 = _mm_mul_ps(m_sums2, _mm_set_ps1(
|
|
+ static_cast<float>(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 <algorithm>
|
|
|
|
+#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 <xmmintrin.h>
|
|
+#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<float, float> (*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 <arm_neon.h>
|
|
#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<uintptr_t>(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<float, float> 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<float, float> 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 <memory>
|
|
|
|
#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 <algorithm>
|
|
+
|
|
+#include <xmmintrin.h> // 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<float, float> 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<float, float> 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<float, float> 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 <memory>
|
|
|
|
#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<float, float>& 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 <xmmintrin.h>
|
|
#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 <vector>
|
|
|
|
#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 <intrin.h>
|
|
#define ANGLE_USE_SSE
|
|
#elif defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__))
|
|
+#if defined(__x86_64__) || defined(__SSE2__)
|
|
#include <x86intrin.h>
|
|
+#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 <emmintrin.h>
|
|
#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 <Accelerate/Accelerate.h>
|
|
#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 <emmintrin.h>
|
|
#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 <emmintrin.h>
|
|
#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<uintptr_t>(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 <stdint.h>
|
|
@@ -35,10 +38,14 @@
|
|
#include <Accelerate/Accelerate.h>
|
|
#endif
|
|
|
|
-#if defined(ARCH_CPU_X86_FAMILY)
|
|
+#if (defined(ARCH_CPU_X86) && defined(__SSE2__)) || defined(ARCH_CPU_X86_64)
|
|
#include <emmintrin.h>
|
|
#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 <arm_neon.h>
|
|
#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<uintptr_t>(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 <cstddef>
|
|
+#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 <emmintrin.h>
|
|
|
|
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<const uint32_t*>(source);
|
|
uint32_t* destination32 = reinterpret_cast_ptr<uint32_t*>(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> 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<RealFourier>(new RealFourierOpenmax(fft_order));
|
|
+ else
|
|
+ return std::unique_ptr<RealFourier>(new RealFourierOoura(fft_order));
|
|
+#else
|
|
return std::unique_ptr<RealFourier>(new RealFourierOpenmax(fft_order));
|
|
+#endif
|
|
#else
|
|
return std::unique_ptr<RealFourier>(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 <arm_neon.h>
|
|
#endif
|
|
#include "webrtc/typedefs.h"
|
|
-#if defined(WEBRTC_ARCH_X86_FAMILY)
|
|
+#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__))
|
|
#include <emmintrin.h>
|
|
#endif
|
|
#include <algorithm>
|
|
@@ -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<const FftData> 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<const FftData> 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<const FftData> H,
|
|
std::vector<std::array<float, kFftLengthBy2Plus1>>* 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<const FftData> H,
|
|
std::vector<std::array<float, kFftLengthBy2Plus1>>* H2);
|
|
@@ -50,7 +50,7 @@
|
|
const std::vector<std::array<float, kFftLengthBy2Plus1>>& H2,
|
|
std::array<float, kFftLengthBy2Plus1>* 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<std::array<float, kFftLengthBy2Plus1>>& H2,
|
|
std::array<float, kFftLengthBy2Plus1>* erl);
|
|
@@ -65,7 +65,7 @@
|
|
const FftData& G,
|
|
rtc::ArrayView<FftData> 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<FftData> H);
|
|
@@ -80,7 +80,7 @@
|
|
rtc::ArrayView<const FftData> 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<const FftData> 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 <numeric>
|
|
#include <string>
|
|
#include "webrtc/typedefs.h"
|
|
-#if defined(WEBRTC_ARCH_X86_FAMILY)
|
|
+#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__))
|
|
#include <emmintrin.h>
|
|
#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 <emmintrin.h>
|
|
#endif
|
|
#include <math.h>
|
|
@@ -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<float, kFftLengthBy2Plus1>& 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<float, kFftLengthBy2Plus1>& 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 <emmintrin.h>
|
|
#endif
|
|
#include <algorithm>
|
|
@@ -43,7 +43,7 @@
|
|
std::array<float, kFftLengthBy2Plus1>* 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 <arm_neon.h>
|
|
#endif
|
|
#include "webrtc/typedefs.h"
|
|
-#if defined(WEBRTC_ARCH_X86_FAMILY)
|
|
+#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__))
|
|
#include <emmintrin.h>
|
|
#endif
|
|
#include <algorithm>
|
|
@@ -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 <emmintrin.h>
|
|
#endif
|
|
#include <algorithm>
|
|
@@ -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 <emmintrin.h>
|
|
#endif
|
|
#include <math.h>
|
|
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 <arm_neon.h>
|
|
#endif
|
|
-#if defined(WEBRTC_ARCH_X86_FAMILY)
|
|
+#if defined(WEBRTC_ARCH_X86_64) || (defined(WEBRTC_ARCH_X86) && defined(__SSE2__))
|
|
#include <emmintrin.h>
|
|
#endif
|
|
#include <math.h>
|
|
@@ -39,7 +39,7 @@
|
|
// Elementwise square root.
|
|
void Sqrt(rtc::ArrayView<float> 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<int>(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<int>(x.size());
|
|
const int vector_limit = x_size >> 2;
|
|
@@ -159,7 +159,7 @@
|
|
void Accumulate(rtc::ArrayView<const float> x, rtc::ArrayView<float> 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<int>(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<Code> 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<int>(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<int>(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<int>(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> 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> 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<Address>(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<int>(times_1));
|
|
+ STATIC_ASSERT(1 == static_cast<int>(times_2));
|
|
+ STATIC_ASSERT(2 == static_cast<int>(times_4));
|
|
+ STATIC_ASSERT(3 == static_cast<int>(times_8));
|
|
+ int scale = static_cast<int>(mode - one);
|
|
+ DCHECK(scale >= 0 && scale < 4);
|
|
+ return static_cast<ScaleFactor>(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<MoveOperands*> 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> 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> 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<const char*>(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<RecordWriteMode>(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<uint32_t>(src);
|
|
+ uint32_t upper = static_cast<uint32_t>(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<RoundingMode>(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<RoundingMode>(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<Builtins::Name>(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<Label*>(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<int>(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<int>(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<HeapObject> 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<uint32_t>(src);
|
|
+ uint32_t upper = static_cast<uint32_t>(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<HeapObject> value = OpParameter<Handle<HeapObject>>(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<int32_t>(displacement_node);
|
|
+ if (displacement_mode == kNegativeDisplacement) {
|
|
+ displacement = -displacement;
|
|
+ }
|
|
+ if (base != nullptr) {
|
|
+ if (base->opcode() == IrOpcode::kInt32Constant) {
|
|
+ displacement += OpParameter<int32_t>(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<int>(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<InstructionOperand*>(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<PushParameter>* 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<int>(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<int>(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<int32_t>(node)
|
|
+ : OpParameter<int64_t>(node);
|
|
+ if (hint == MachineType::Int8()) {
|
|
+ if (constant >= std::numeric_limits<int8_t>::min() &&
|
|
+ constant <= std::numeric_limits<int8_t>::max()) {
|
|
+ return hint;
|
|
+ }
|
|
+ } else if (hint == MachineType::Uint8()) {
|
|
+ if (constant >= std::numeric_limits<uint8_t>::min() &&
|
|
+ constant <= std::numeric_limits<uint8_t>::max()) {
|
|
+ return hint;
|
|
+ }
|
|
+ } else if (hint == MachineType::Int16()) {
|
|
+ if (constant >= std::numeric_limits<int16_t>::min() &&
|
|
+ constant <= std::numeric_limits<int16_t>::max()) {
|
|
+ return hint;
|
|
+ }
|
|
+ } else if (hint == MachineType::Uint16()) {
|
|
+ if (constant >= std::numeric_limits<uint16_t>::min() &&
|
|
+ constant <= std::numeric_limits<uint16_t>::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<ExternalReferenceMatcher> 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
|
|
+ // <Operation>WithOverflow node.
|
|
+ if (ProjectionIndexOf(value->op()) == 1u) {
|
|
+ // We cannot combine the <Operation>WithOverflow with this branch
|
|
+ // unless the 0th projection (the use of the actual value of the
|
|
+ // <Operation> 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<int32_t>::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> 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>(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 <undefined>;");
|
|
+ __ 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<Object> lit) const {
|
|
+}
|
|
+
|
|
+
|
|
+void FullCodeGenerator::AccumulatorValueContext::Plug(
|
|
+ Handle<Object> lit) const {
|
|
+ if (lit->IsSmi()) {
|
|
+ __ SafeMove(result_register(), Immediate(Smi::cast(*lit)));
|
|
+ } else {
|
|
+ __ Move(result_register(), Immediate(Handle<HeapObject>::cast(lit)));
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+void FullCodeGenerator::StackValueContext::Plug(Handle<Object> lit) const {
|
|
+ codegen()->OperandStackDepthIncrement(1);
|
|
+ if (lit->IsSmi()) {
|
|
+ __ SafePush(Immediate(Smi::cast(*lit)));
|
|
+ } else {
|
|
+ __ push(Immediate(Handle<HeapObject>::cast(lit)));
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+void FullCodeGenerator::TestContext::Plug(Handle<Object> 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<HeapObject>::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<HeapObject> 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<HeapObject> 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<SharedFunctionInfo> 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<FixedArray> 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<CaseClause*>* 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<Code> 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<BoilerplateDescription> 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<ConstantElementsPair> 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<Expression*>* 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> 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<Expression*>* args = expr->arguments();
|
|
+ int arg_count = args->length();
|
|
+ for (int i = 0; i < arg_count; i++) {
|
|
+ VisitForStackValue(args->at(i));
|
|
+ }
|
|
+
|
|
+ SetCallPosition(expr);
|
|
+ Handle<Code> 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<Expression*>* 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<Expression*>* 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<Expression*>* 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<Expression*>* 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<Expression*>* 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<Expression*>* 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<Expression*>* 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<Expression*>* 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<Expression*>* 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<Expression*>* 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<Expression*>* 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> 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<String> 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<Code> 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<HeapObject> 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 <profiling_counter>, <delta> ;; Not changed
|
|
+ // jns ok
|
|
+ // call <interrupt stub>
|
|
+ // ok:
|
|
+ *jns_instr_address = kJnsInstruction;
|
|
+ *jns_offset_address = kJnsOffset;
|
|
+ break;
|
|
+ case ON_STACK_REPLACEMENT:
|
|
+ // sub <profiling_counter>, <delta> ;; Not changed
|
|
+ // nop
|
|
+ // nop
|
|
+ // call <on-stack replacment>
|
|
+ // 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<MachOHeader> WriteHeader(Writer* w) {
|
|
DCHECK(w->position() == 0);
|
|
Writer::Slot<MachOHeader> header = w->CreateSlotHere<MachOHeader>();
|
|
-#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<MachOSegmentCommand> cmd =
|
|
w->CreateSlotHere<MachOSegmentCommand>();
|
|
-#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<ELFHeader> header = w->CreateSlotHere<ELFHeader>();
|
|
-#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<Binding>(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<intptr_t>(desc_->CodeStart() + desc_->CodeSize());
|
|
Writer::Slot<uint32_t> fb_block_size = w->CreateSlotHere<uint32_t>();
|
|
uintptr_t fb_block_start = w->position();
|
|
-#if V8_TARGET_ARCH_IA32
|
|
+#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X87
|
|
w->Write<uint8_t>(DW_OP_reg5); // The frame pointer's here on ia32
|
|
#elif V8_TARGET_ARCH_X64
|
|
w->Write<uint8_t>(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> 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> 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<Map> 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<CallHandlerInfo> 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<Address>(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<JSGlobalObject> global, Handle<Name> name,
|
|
+ Register scratch, Label* miss) {
|
|
+ Handle<PropertyCell> cell = JSGlobalObject::EnsureEmptyPropertyCell(
|
|
+ global, name, PropertyCellType::kInvalidated);
|
|
+ Isolate* isolate = masm->isolate();
|
|
+ DCHECK(cell->value()->IsTheHole(isolate));
|
|
+ Handle<WeakCell> 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> 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<Descriptor>(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> name) {
|
|
+ if (!label->is_unused()) {
|
|
+ __ bind(label);
|
|
+ __ mov(this->name(), Immediate(name));
|
|
+ }
|
|
+}
|
|
+
|
|
+void PropertyHandlerCompiler::GenerateAccessCheck(
|
|
+ Handle<WeakCell> 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> name, Label* miss) {
|
|
+ Handle<Map> 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<Cell> 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<JSObject> current = Handle<JSObject>::null();
|
|
+ if (receiver_map->IsJSGlobalObjectMap()) {
|
|
+ current = isolate()->global_object();
|
|
+ }
|
|
+
|
|
+ Handle<Map> current_map(receiver_map->GetPrototypeChainRootMap(isolate()),
|
|
+ isolate());
|
|
+ Handle<Map> 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<JSGlobalObject>::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<WeakCell> 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<WeakCell> weak_cell =
|
|
+ Map::GetOrCreatePrototypeWeakCell(current, isolate());
|
|
+ __ LoadWeakValue(reg, weak_cell, miss);
|
|
+ }
|
|
+
|
|
+ // Return the register containing the holder.
|
|
+ return reg;
|
|
+}
|
|
+
|
|
+
|
|
+void NamedLoadHandlerCompiler::FrontendFooter(Handle<Name> 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> 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<Code> NamedStoreHandlerCompiler::CompileStoreCallback(
|
|
+ Handle<JSObject> object, Handle<Name> name, Handle<AccessorInfo> callback,
|
|
+ LanguageMode language_mode) {
|
|
+ Register holder_reg = Frontend(name);
|
|
+ __ LoadParameterFromStack<Descriptor>(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<WeakCell> 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<uint8_t*>(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<byte>(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<int32_t>('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<ByteArray> 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<HeapObject> RegExpMacroAssemblerX87::GetCode(Handle<String> 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> code =
|
|
+ isolate()->factory()->NewCode(code_desc,
|
|
+ Code::ComputeFlags(Code::REGEXP),
|
|
+ masm_->CodeObject());
|
|
+ PROFILE(masm_->isolate(),
|
|
+ RegExpCodeCreateEvent(AbstractCode::cast(*code), *source));
|
|
+ return Handle<HeapObject>::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 <typename T>
|
|
+static T& frame_entry(Address re_frame, int frame_offset) {
|
|
+ return reinterpret_cast<T&>(Memory::int32_at(re_frame + frame_offset));
|
|
+}
|
|
+
|
|
+
|
|
+template <typename T>
|
|
+static T* frame_entry_address(Address re_frame, int frame_offset) {
|
|
+ return reinterpret_cast<T*>(re_frame + frame_offset);
|
|
+}
|
|
+
|
|
+
|
|
+int RegExpMacroAssemblerX87::CheckStackGuardState(Address* return_address,
|
|
+ Code* re_code,
|
|
+ Address re_frame) {
|
|
+ return NativeRegExpMacroAssembler::CheckStackGuardState(
|
|
+ frame_entry<Isolate*>(re_frame, kIsolate),
|
|
+ frame_entry<int>(re_frame, kStartIndex),
|
|
+ frame_entry<int>(re_frame, kDirectCall) == 1, return_address, re_code,
|
|
+ frame_entry_address<String*>(re_frame, kInputString),
|
|
+ frame_entry_address<const byte*>(re_frame, kInputStart),
|
|
+ frame_entry_address<const byte*>(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<ByteArray> 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<HeapObject> GetCode(Handle<String> 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<int>(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<const char> 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<HeapObject> 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<int8_t>(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<int8_t>(imm16 & 0xff));
|
|
+ EMIT(static_cast<int8_t>(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<int8_t>(src.immediate() & 0xff));
|
|
+ EMIT(static_cast<int8_t>(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<HeapObject> 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<HeapObject> 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<HeapObject> 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<HeapObject> 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<uint8_t>(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<int>(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<int>(*reinterpret_cast<int8_t*>(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<uint32_t>(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> code, RelocInfo::Mode rmode) {
|
|
+ return 1 /* EMIT */ + sizeof(uint32_t) /* emit */;
|
|
+}
|
|
+
|
|
+
|
|
+void Assembler::call(Handle<Code> 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<uint32_t>(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> 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<int>(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<int>(cc) < 16));
|
|
+ // 0000 1111 1000 tttn #32-bit disp.
|
|
+ EMIT(0x0F);
|
|
+ EMIT(0x80 | cc);
|
|
+ if (RelocInfo::IsRuntimeEntry(rmode)) {
|
|
+ emit(reinterpret_cast<uint32_t>(entry), rmode);
|
|
+ } else {
|
|
+ emit(entry - (pc_ + sizeof(int32_t)), rmode);
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+void Assembler::j(Condition cc, Handle<Code> 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<byte>(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<int32_t*>(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<Label**>(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<uint32_t>(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 <deque>
|
|
+
|
|
+#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<Condition>(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<HeapObject> 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<int32_t>(table),
|
|
+ RelocInfo::INTERNAL_REFERENCE);
|
|
+ }
|
|
+
|
|
+ static Operand StaticVariable(const ExternalReference& ext) {
|
|
+ return Operand(reinterpret_cast<int32_t>(ext.address()),
|
|
+ RelocInfo::EXTERNAL_REFERENCE);
|
|
+ }
|
|
+
|
|
+ static Operand StaticArray(Register index,
|
|
+ ScaleFactor scale,
|
|
+ const ExternalReference& arr) {
|
|
+ return Operand(index, scale, reinterpret_cast<int32_t>(arr.address()),
|
|
+ RelocInfo::EXTERNAL_REFERENCE);
|
|
+ }
|
|
+
|
|
+ static Operand ForCell(Handle<Cell> cell) {
|
|
+ return Operand(reinterpret_cast<int32_t>(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<Type, 0, 2> {};
|
|
+ class NextField: public BitField<int, 2, 32-2> {};
|
|
+
|
|
+ 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<HeapObject> 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<HeapObject> 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<HeapObject> 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<HeapObject> 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> code, RelocInfo::Mode mode);
|
|
+ void call(Handle<Code> 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> 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> 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<uint32_t*>(addr_at(pos));
|
|
+ }
|
|
+ void long_at_put(int pos, uint32_t x) {
|
|
+ *reinterpret_cast<uint32_t*>(addr_at(pos)) = x;
|
|
+ }
|
|
+
|
|
+ // code emission
|
|
+ void GrowBuffer();
|
|
+ inline void emit(uint32_t x);
|
|
+ inline void emit(Handle<HeapObject> handle);
|
|
+ inline void emit(uint32_t x, RelocInfo::Mode rmode);
|
|
+ inline void emit(Handle<Code> 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<int> 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<HeapObjectRequest> 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<int32_t*>(pc_);
|
|
+ *p -= delta; // Relocate entry.
|
|
+ } else if (IsCodeAgeSequence(rmode_)) {
|
|
+ if (*pc_ == kCallOpcode) {
|
|
+ int32_t* p = reinterpret_cast<int32_t*>(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<int32_t*>(
|
|
+ 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<int32_t*>(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<Address>(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<HeapObject> RelocInfo::target_object_handle(Assembler* origin) {
|
|
+ DCHECK(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
|
|
+ return Handle<HeapObject>::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<Address>(pc_);
|
|
+}
|
|
+
|
|
+
|
|
+Address RelocInfo::target_runtime_entry(Assembler* origin) {
|
|
+ DCHECK(IsRuntimeEntry(rmode_));
|
|
+ return reinterpret_cast<Address>(*reinterpret_cast<int32_t*>(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<Cell> RelocInfo::target_cell_handle() {
|
|
+ DCHECK(rmode_ == RelocInfo::CELL);
|
|
+ Address address = Memory::Address_at(pc_);
|
|
+ return Handle<Cell>(reinterpret_cast<Cell**>(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<Code> RelocInfo::code_age_stub_handle(Assembler* origin) {
|
|
+ DCHECK(rmode_ == RelocInfo::CODE_AGE_SEQUENCE);
|
|
+ DCHECK(*pc_ == kCallOpcode);
|
|
+ return Handle<Code>::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 <typename ObjectVisitor>
|
|
+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<typename StaticVisitor>
|
|
+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<int32_t>(x);
|
|
+ rmode_ = rmode;
|
|
+}
|
|
+
|
|
+Immediate::Immediate(const ExternalReference& ext) {
|
|
+ value_.immediate = reinterpret_cast<int32_t>(ext.address());
|
|
+ rmode_ = RelocInfo::EXTERNAL_REFERENCE;
|
|
+}
|
|
+
|
|
+
|
|
+Immediate::Immediate(Label* internal_offset) {
|
|
+ value_.immediate = reinterpret_cast<int32_t>(internal_offset);
|
|
+ rmode_ = RelocInfo::INTERNAL_REFERENCE;
|
|
+}
|
|
+
|
|
+
|
|
+Immediate::Immediate(Handle<HeapObject> handle) {
|
|
+ value_.immediate = reinterpret_cast<intptr_t>(handle.address());
|
|
+ rmode_ = RelocInfo::EMBEDDED_OBJECT;
|
|
+}
|
|
+
|
|
+
|
|
+Immediate::Immediate(Smi* value) {
|
|
+ value_.immediate = reinterpret_cast<intptr_t>(value);
|
|
+ rmode_ = RelocInfo::NONE32;
|
|
+}
|
|
+
|
|
+
|
|
+Immediate::Immediate(Address addr) {
|
|
+ value_.immediate = reinterpret_cast<int32_t>(addr);
|
|
+ rmode_ = RelocInfo::NONE32;
|
|
+}
|
|
+
|
|
+
|
|
+void Assembler::emit(uint32_t x) {
|
|
+ *reinterpret_cast<uint32_t*>(pc_) = x;
|
|
+ pc_ += sizeof(uint32_t);
|
|
+}
|
|
+
|
|
+
|
|
+void Assembler::emit_q(uint64_t x) {
|
|
+ *reinterpret_cast<uint64_t*>(pc_) = x;
|
|
+ pc_ += sizeof(uint64_t);
|
|
+}
|
|
+
|
|
+
|
|
+void Assembler::emit(Handle<HeapObject> handle) {
|
|
+ emit(reinterpret_cast<intptr_t>(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> code, RelocInfo::Mode rmode) {
|
|
+ emit(reinterpret_cast<intptr_t>(code.address()), rmode);
|
|
+}
|
|
+
|
|
+
|
|
+void Assembler::emit(const Immediate& x) {
|
|
+ if (x.rmode_ == RelocInfo::INTERNAL_REFERENCE) {
|
|
+ Label* label = reinterpret_cast<Label*>(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<uint8_t>(x.immediate());
|
|
+ *pc_++ = value;
|
|
+}
|
|
+
|
|
+void Assembler::emit_w(const Immediate& x) {
|
|
+ DCHECK(RelocInfo::IsNone(x.rmode_));
|
|
+ uint16_t value = static_cast<uint16_t>(x.immediate());
|
|
+ reinterpret_cast<uint16_t*>(pc_)[0] = value;
|
|
+ pc_ += sizeof(uint16_t);
|
|
+}
|
|
+
|
|
+
|
|
+Address Assembler::target_address_at(Address pc, Address constant_pool) {
|
|
+ return pc + sizeof(int32_t) + *reinterpret_cast<int32_t*>(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<int32_t*>(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<int>(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<byte>(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<int8_t*>(&buf_[len_++]) = disp;
|
|
+}
|
|
+
|
|
+
|
|
+void Operand::set_dispr(int32_t disp, RelocInfo::Mode rmode) {
|
|
+ DCHECK(len_ == 1 || len_ == 2);
|
|
+ int32_t* p = reinterpret_cast<int32_t*>(&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<byte*>(base::OS::Allocate(1 * KB, &actual_size, true));
|
|
+ if (buffer == nullptr) return nullptr;
|
|
+
|
|
+ MacroAssembler masm(isolate, buffer, static_cast<int>(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<UnaryMathFunctionWithIsolate>(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<int32_t>(buffer_) + l->pos();
|
|
+ }
|
|
+ private:
|
|
+ byte* buffer_;
|
|
+};
|
|
+
|
|
+
|
|
+MemMoveFunction CreateMemMoveFunction(Isolate* isolate) {
|
|
+ size_t actual_size;
|
|
+ // Allocate buffer in executable space.
|
|
+ byte* buffer =
|
|
+ static_cast<byte*>(base::OS::Allocate(1 * KB, &actual_size, true));
|
|
+ if (buffer == nullptr) return nullptr;
|
|
+ MacroAssembler masm(isolate, buffer, static_cast<int>(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<MemMoveFunction>(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<int*>(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<uint32_t>(Double::kSignificandMask >> 32)));
|
|
+ __ add(result_reg,
|
|
+ Immediate(static_cast<uint32_t>(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<Smi*>(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<Map> 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<WeakCell> 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> 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>(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 <class T>
|
|
+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<Map> 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 <class T>
|
|
+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<ArrayNoArgumentConstructorStub>(
|
|
+ isolate);
|
|
+ ArrayConstructorStubAheadOfTimeHelper<ArraySingleArgumentConstructorStub>(
|
|
+ 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<ArrayNoArgumentConstructorStub>(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<Map::ElementsKindBits>(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<int32_t>(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<int>(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> 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<int, 0, 3> {};
|
|
+ class ResultBits: public BitField<int, 3, 3> {};
|
|
+ class IndexBits: public BitField<int, 6, 3> {};
|
|
+ class LookupModeBits: public BitField<LookupMode, 9, 1> {};
|
|
+
|
|
+ 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<int, 0, 3> {};
|
|
+ class ValueBits: public BitField<int, 3, 3> {};
|
|
+ class AddressBits: public BitField<int, 6, 3> {};
|
|
+ class RememberedSetActionBits: public BitField<RememberedSetAction, 9, 1> {};
|
|
+ class SaveFPRegsModeBits : public BitField<SaveFPRegsMode, 10, 1> {};
|
|
+
|
|
+ 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> 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<ByteArray> 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<intptr_t>(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<intptr_t>(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 <assert.h>
|
|
+#include <stdarg.h>
|
|
+#include <stdio.h>
|
|
+
|
|
+#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<char> buffer, byte* instruction);
|
|
+
|
|
+ private:
|
|
+ const NameConverter& converter_;
|
|
+ InstructionTable* instruction_table_;
|
|
+ v8::internal::EmbeddedVector<char, 128> 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<char> 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<int32_t*>(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<int32_t*>(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<int32_t*>(modrmp + 2)
|
|
+ : *reinterpret_cast<int8_t*>(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<int32_t*>(modrmp + 1)
|
|
+ : *reinterpret_cast<int8_t*>(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<int32_t*>(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<int32_t*>(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<int8_t>(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<int32_t*>(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<int8_t>(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<char> 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<byte*>(*reinterpret_cast<int32_t*>(data+1));
|
|
+ AppendToBuffer("mov %s,%s",
|
|
+ NameOfCPURegister(*data & 0x07),
|
|
+ NameOfAddress(addr));
|
|
+ data += 5;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ case CALL_JUMP_INSTR: {
|
|
+ byte* addr = data + *reinterpret_cast<int32_t*>(data+1) + 5;
|
|
+ AppendToBuffer("%s %s", idesc.mnem, NameOfAddress(addr));
|
|
+ data += 5;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ case SHORT_IMMEDIATE_INSTR: {
|
|
+ byte* addr = reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(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<uint16_t*>(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<int32_t*>(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<int8_t>(data[1]);
|
|
+ AppendToBuffer("shufps %s,%s,%d",
|
|
+ NameOfXMMRegister(rm),
|
|
+ NameOfXMMRegister(regop),
|
|
+ static_cast<int>(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<int8_t>(data[1]);
|
|
+ data += 2;
|
|
+ AppendToBuffer("%s,%s,%d", NameOfCPURegister(rm),
|
|
+ NameOfCPURegister(regop), static_cast<int>(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<int32_t*>(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<int16_t*>(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<int16_t*>(data);
|
|
+ AppendToBuffer(",0x%x", imm);
|
|
+ data += 2;
|
|
+ } else if (*data == 0xF7) {
|
|
+ data++;
|
|
+ AppendToBuffer("%s ", "test_w");
|
|
+ data += PrintRightOperand(data);
|
|
+ int imm = *reinterpret_cast<int16_t*>(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<int8_t>(data[1]);
|
|
+ AppendToBuffer("roundsd %s,%s,%d",
|
|
+ NameOfXMMRegister(regop),
|
|
+ NameOfXMMRegister(rm),
|
|
+ static_cast<int>(imm8));
|
|
+ data += 2;
|
|
+ } else if (*data == 0x16) {
|
|
+ data++;
|
|
+ int mod, regop, rm;
|
|
+ get_modrm(*data, &mod, &rm, ®op);
|
|
+ int8_t imm8 = static_cast<int8_t>(data[1]);
|
|
+ AppendToBuffer("pextrd %s,%s,%d",
|
|
+ NameOfCPURegister(regop),
|
|
+ NameOfXMMRegister(rm),
|
|
+ static_cast<int>(imm8));
|
|
+ data += 2;
|
|
+ } else if (*data == 0x17) {
|
|
+ data++;
|
|
+ int mod, regop, rm;
|
|
+ get_modrm(*data, &mod, ®op, &rm);
|
|
+ int8_t imm8 = static_cast<int8_t>(data[1]);
|
|
+ AppendToBuffer("extractps %s,%s,%d",
|
|
+ NameOfCPURegister(rm),
|
|
+ NameOfXMMRegister(regop),
|
|
+ static_cast<int>(imm8));
|
|
+ data += 2;
|
|
+ } else if (*data == 0x22) {
|
|
+ data++;
|
|
+ int mod, regop, rm;
|
|
+ get_modrm(*data, &mod, ®op, &rm);
|
|
+ int8_t imm8 = static_cast<int8_t>(data[1]);
|
|
+ AppendToBuffer("pinsrd %s,%s,%d",
|
|
+ NameOfXMMRegister(regop),
|
|
+ NameOfCPURegister(rm),
|
|
+ static_cast<int>(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<int8_t>(data[1]);
|
|
+ AppendToBuffer("pshufd %s,%s,%d",
|
|
+ NameOfXMMRegister(regop),
|
|
+ NameOfXMMRegister(rm),
|
|
+ static_cast<int>(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<int8_t>(data[1]);
|
|
+ DCHECK(regop == esi || regop == edx);
|
|
+ AppendToBuffer("%s %s,%d",
|
|
+ (regop == esi) ? "psllq" : "psrlq",
|
|
+ NameOfXMMRegister(rm),
|
|
+ static_cast<int>(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<int32_t*>(data+1));
|
|
+ data += 5;
|
|
+ break;
|
|
+
|
|
+ case 0x6A:
|
|
+ AppendToBuffer("push 0x%x", *reinterpret_cast<int8_t*>(data + 1));
|
|
+ data += 2;
|
|
+ break;
|
|
+
|
|
+ case 0xA8:
|
|
+ AppendToBuffer("test al,0x%x", *reinterpret_cast<uint8_t*>(data+1));
|
|
+ data += 2;
|
|
+ break;
|
|
+
|
|
+ case 0xA9:
|
|
+ AppendToBuffer("test eax,0x%x", *reinterpret_cast<int32_t*>(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<void*>(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<char> 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<char, 128> buffer;
|
|
+ buffer[0] = '\0';
|
|
+ byte* prev_pc = pc;
|
|
+ pc += d.InstructionDecode(buffer, pc);
|
|
+ fprintf(f, "%p", static_cast<void*>(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<size_t>(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> object = isolate()->heap()->root_handle(index);
|
|
+ if (object->IsHeapObject()) {
|
|
+ mov(destination, Handle<HeapObject>::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> object = isolate()->heap()->root_handle(index);
|
|
+ if (object->IsHeapObject()) {
|
|
+ cmp(with, Handle<HeapObject>::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> object = isolate()->heap()->root_handle(index);
|
|
+ if (object->IsHeapObject()) {
|
|
+ cmp(with, Handle<HeapObject>::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<int32_t>(kZapValue)));
|
|
+ mov(dst, Immediate(bit_cast<int32_t>(kZapValue)));
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+void MacroAssembler::RecordWriteForMap(Register object, Handle<Map> 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<int32_t>(kZapValue)));
|
|
+ mov(scratch1, Immediate(bit_cast<int32_t>(kZapValue)));
|
|
+ mov(scratch2, Immediate(bit_cast<int32_t>(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<int32_t>(kZapValue)));
|
|
+ mov(value, Immediate(bit_cast<int32_t>(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> map) {
|
|
+ cmp(FieldOperand(obj, HeapObject::kMapOffset), map);
|
|
+}
|
|
+
|
|
+
|
|
+void MacroAssembler::CheckMap(Register obj,
|
|
+ Handle<Map> 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<ScaleFactor>(times_2 - 1) == times_1);
|
|
+ STATIC_ASSERT(static_cast<ScaleFactor>(times_4 - 1) == times_2);
|
|
+ STATIC_ASSERT(static_cast<ScaleFactor>(times_8 - 1) == times_4);
|
|
+ DCHECK(element_size >= times_2);
|
|
+ DCHECK(kSmiTagSize == 1);
|
|
+ element_size = static_cast<ScaleFactor>(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> 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<Code> 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<JSFunction> 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<HeapObject> object) {
|
|
+ cmp(reg, object);
|
|
+}
|
|
+
|
|
+void MacroAssembler::PushObject(Handle<Object> object) {
|
|
+ if (object->IsHeapObject()) {
|
|
+ Push(Handle<HeapObject>::cast(object));
|
|
+ } else {
|
|
+ Push(Smi::cast(*object));
|
|
+ }
|
|
+}
|
|
+
|
|
+void MacroAssembler::GetWeakValue(Register value, Handle<WeakCell> cell) {
|
|
+ mov(value, cell);
|
|
+ mov(value, FieldOperand(value, WeakCell::kValueOffset));
|
|
+}
|
|
+
|
|
+
|
|
+void MacroAssembler::LoadWeakValue(Register value, Handle<WeakCell> 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<HeapObject> 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<int>(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<Map::NumberOfOwnDescriptorsBits>(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> 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<uint32_t> mag =
|
|
+ base::SignedDivisionByConstant(static_cast<uint32_t>(divisor));
|
|
+ mov(eax, Immediate(mag.multiplier));
|
|
+ imul(dividend);
|
|
+ bool neg = (mag.multiplier & (static_cast<uint32_t>(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<HeapObject>::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<HeapObject> 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<HeapObject> handle);
|
|
+
|
|
+ void Call(Handle<Code> 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<HeapObject> 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<HeapObject> 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> 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> 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<HeapObject> object);
|
|
+ void PushObject(Handle<Object> object);
|
|
+
|
|
+ void CmpObject(Register reg, Handle<Object> object) {
|
|
+ AllowDeferredHandleDereference heap_object_check;
|
|
+ if (object->IsHeapObject()) {
|
|
+ CmpHeapObject(reg, Handle<HeapObject>::cast(object));
|
|
+ } else {
|
|
+ cmp(reg, Immediate(Smi::cast(*object)));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ void GetWeakValue(Register value, Handle<WeakCell> cell);
|
|
+ void LoadWeakValue(Register value, Handle<WeakCell> 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<JSFunction> 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> 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> 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<typename Field>
|
|
+ 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<typename Field>
|
|
+ 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 <class Descriptor>
|
|
+ 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<Code> 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<regexp_matcher>(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
|