diff --git a/browser/branding/branding-common.mozbuild b/browser/branding/branding-common.mozbuild new file mode 100644 index 0000000000..524ff3046a --- /dev/null +++ b/browser/branding/branding-common.mozbuild @@ -0,0 +1,43 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +JS_PREFERENCE_FILES += [ + 'pref/firefox-branding.js', +] + +if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows': + BRANDING_FILES += [ + 'appname.bmp', + 'bgintro.bmp', + 'branding.nsi', + 'clock.bmp', + 'document.ico', + 'firefox.ico', + 'newtab.ico', + 'newwindow.ico', + 'particles.bmp', + 'pbmode.ico', + 'pencil-rtl.bmp', + 'pencil.bmp', + 'wizHeader.bmp', + 'wizHeaderRTL.bmp', + 'wizWatermark.bmp', + ] +elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa': + BRANDING_FILES += [ + 'background.png', + 'disk.icns', + 'document.icns', + 'dsstore', + 'firefox.icns', + ] +elif CONFIG['MOZ_WIDGET_GTK']: + BRANDING_FILES += [ + 'default16.png', + 'default32.png', + 'default48.png', + 'mozicon128.png', + ] diff --git a/build/autoconf/arch.m4 b/build/autoconf/arch.m4 index 3728283931..b62ea71f06 100644 --- a/build/autoconf/arch.m4 +++ b/build/autoconf/arch.m4 @@ -37,7 +37,6 @@ if test -z "$MOZ_ARCH"; then ;; arm-Darwin) MOZ_ARCH=toolchain-default - MOZ_THUMB=yes ;; esac fi diff --git a/build/autoconf/compiler-opts.m4 b/build/autoconf/compiler-opts.m4 index 8c4eb764db..71e82e9a7f 100644 --- a/build/autoconf/compiler-opts.m4 +++ b/build/autoconf/compiler-opts.m4 @@ -355,6 +355,20 @@ fi AC_SUBST(MOZ_PROGRAM_LDFLAGS) +dnl ASan assumes no symbols are being interposed, and when that happens, +dnl it's not happy with it. Unconveniently, since Firefox is exporting +dnl libffi symbols and Gtk+3 pulls system libffi via libwayland-client, +dnl system libffi interposes libffi symbols that ASan assumes are in +dnl libxul, so it barfs about buffer overflows. +dnl Using -Wl,-Bsymbolic ensures no exported symbol can be interposed. +if test -n "$GCC_USE_GNU_LD"; then + case "$LDFLAGS" in + *-fsanitize=address*) + LDFLAGS="$LDFLAGS -Wl,-Bsymbolic" + ;; + esac +fi + ]) dnl GCC and clang will fail if given an unknown warning option like -Wfoobar. diff --git a/build/autoconf/config.status.m4 b/build/autoconf/config.status.m4 index a3e9d4fedd..05f402aa2f 100644 --- a/build/autoconf/config.status.m4 +++ b/build/autoconf/config.status.m4 @@ -28,7 +28,7 @@ define([AC_SUBST_SET], [ifdef([AC_SUBST_SET_$1], , [define([AC_SUBST_SET_$1], )dnl AC_DIVERT_PUSH(MOZ_DIVERSION_SUBST)dnl - (''' $1 ''', set(r''' [$]$1 '''.split())) + (''' $1 ''', unique_list(r''' [$]$1 '''.split())) AC_DIVERT_POP()dnl ])])])]) @@ -129,6 +129,13 @@ topsrcdir = os.path.normpath(topsrcdir) topobjdir = os.path.abspath(os.path.dirname(<<<__file__>>>)) +def unique_list(l): + result = [] + for i in l: + if l not in result: + result.append(i) + return result + dnl All defines and substs are stored with an additional space at the beginning dnl and at the end of the string, to avoid any problem with values starting or dnl ending with quotes. @@ -228,9 +235,9 @@ define([MOZ_BUILD_BACKEND], BUILD_BACKENDS="RecursiveMake" MOZ_ARG_ENABLE_STRING(build-backend, -[ --enable-build-backend={AndroidEclipse,CppEclipse,VisualStudio,FasterMake} +[ --enable-build-backend={AndroidEclipse,CppEclipse,VisualStudio,FasterMake,CompileDB} Enable additional build backends], [ BUILD_BACKENDS="RecursiveMake `echo $enableval | sed 's/,/ /g'`"]) -AC_SUBST_LIST([BUILD_BACKENDS]) +AC_SUBST_SET([BUILD_BACKENDS]) ]) diff --git a/build/autoconf/ios.m4 b/build/autoconf/ios.m4 new file mode 100644 index 0000000000..b341cda895 --- /dev/null +++ b/build/autoconf/ios.m4 @@ -0,0 +1,108 @@ +dnl This Source Code Form is subject to the terms of the Mozilla Public +dnl License, v. 2.0. If a copy of the MPL was not distributed with this +dnl file, You can obtain one at http://mozilla.org/MPL/2.0/. + +AC_DEFUN([MOZ_IOS_PATH_PROG], +[ +changequote({,}) +_prog_name=ifelse($2, {}, `echo $1 | tr "[:upper:]" "[:lower:]"`, $2) +changequote([,]) +AC_CACHE_CHECK([for $_prog_name in iOS SDK], +ac_cv_ios_path_$1, +[ +_path=`xcrun --sdk $ios_sdk --find $_prog_name 2>/dev/null` +_res=$? +if test $_res -ne 0; then + AC_MSG_ERROR([Could not find '$_prog_name' in the iOS SDK]) +fi +ac_cv_ios_path_$1=$_path +]) +$1="${ac_cv_ios_path_$1}$3" +]) + +AC_DEFUN([MOZ_IOS_SDK], +[ + +MOZ_ARG_WITH_STRING(ios-sdk, +[ --with-ios-sdk=TYPE + Type of iOS SDK to use (iphonesimulator, iphoneos) + and optionally version (like iphoneos8.2)], + ios_sdk=$withval) + +MOZ_ARG_ENABLE_STRING(ios-target, + [ --enable-ios-target=VER (default=8.0) + Set the minimum iOS version needed at runtime], + [_IOS_TARGET=$enableval]) +_IOS_TARGET_DEFAULT=8.0 + +case "$target" in +arm*-apple-darwin*) + if test -z "$ios_sdk" -o "$ios_sdk" = "yes"; then + ios_sdk=iphoneos + fi + case "$ios_sdk" in + iphoneos*) + ios_target_arg="-miphoneos-version-min" + ;; + *) + AC_MSG_ERROR([Only 'iphoneos' SDKs are valid when targeting iOS device, don't know what to do with '$ios_sdk'.]) + ;; + esac + ;; +*-apple-darwin*) + ios_target_arg="-mios-simulator-version-min" + case "$ios_sdk" in + # Empty SDK is okay, this might be an OS X desktop build. + ""|iphonesimulator*) + ;; + # Default to iphonesimulator + yes) + ios_sdk=iphonesimulator + ;; + *) + AC_MSG_ERROR([Only 'iphonesimulator' SDKs are valid when targeting iOS simulator.]) + ;; + esac + ;; +esac + + +if test -n "$ios_sdk"; then + if test -z "$_IOS_TARGET"; then + _IOS_TARGET=$_IOS_TARGET_DEFAULT + ios_target_arg="${ios_target_arg}=${_IOS_TARGET}" + fi + # Ensure that xcrun knows where this SDK is. + ios_sdk_path=`xcrun --sdk $ios_sdk --show-sdk-path 2>/dev/null` + _ret=$? + if test $_ret -ne 0; then + AC_MSG_ERROR([iOS SDK '$ios_sdk' could not be found.]) + fi + MOZ_IOS=1 + export HOST_CC=clang + export HOST_CXX=clang++ + # Add isysroot, arch, and ios target arguments + case "$target_cpu" in + arm*) + ARGS="-arch armv7" + ;; + *) + # Unfortunately simulator builds need this. + export CROSS_COMPILE=1 + ;; + esac + ARGS=" $ARGS -isysroot $ios_sdk_path $ios_target_arg" + # Now find our tools + MOZ_IOS_PATH_PROG(CC, clang, $ARGS) + MOZ_IOS_PATH_PROG(CXX, clang++, $ARGS) + export CPP="$CC -E" + export LD="$CXX" + MOZ_IOS_PATH_PROG(AR) + MOZ_IOS_PATH_PROG(AS, as, $ARGS) + MOZ_IOS_PATH_PROG(OTOOL) + MOZ_IOS_PATH_PROG(STRIP) + export PKG_CONFIG_PATH=${ios_sdk_path}/usr/lib/pkgconfig/ +fi + +AC_SUBST(MOZ_IOS) +]) diff --git a/build/docs/files-metadata.rst b/build/docs/files-metadata.rst index d0157eff7f..5af4d96fdc 100644 --- a/build/docs/files-metadata.rst +++ b/build/docs/files-metadata.rst @@ -19,9 +19,9 @@ files. e.g.:: with Files('**/Makefile.in'): BUG_COMPONENT = ('Core', 'Build Config') -This working example says, *for all Makefile.in files in all directories -in this one and underneath it, set the Bugzilla component to -Core :: Build Config*. +This working example says, *for all Makefile.in files in every directory +underneath this one - including this directory - set the Bugzilla +component to Core :: Build Config*. For more info, read the :ref:`docs on Files `. diff --git a/build/mach_bootstrap.py b/build/mach_bootstrap.py index 2fa024563f..7ebac27330 100644 --- a/build/mach_bootstrap.py +++ b/build/mach_bootstrap.py @@ -85,6 +85,7 @@ MACH_MODULES = [ 'python/mozbuild/mozbuild/mach_commands.py', 'python/mozbuild/mozbuild/backend/mach_commands.py', 'python/mozbuild/mozbuild/compilation/codecomplete.py', + 'python/mozbuild/mozbuild/compilation/database.py', 'python/mozbuild/mozbuild/frontend/mach_commands.py', 'services/common/tests/mach_commands.py', 'testing/luciddream/mach_commands.py', diff --git a/build/mobile/robocop/AndroidManifest.xml.in b/build/mobile/robocop/AndroidManifest.xml.in index a76e11dbae..914f4dd31f 100644 --- a/build/mobile/robocop/AndroidManifest.xml.in +++ b/build/mobile/robocop/AndroidManifest.xml.in @@ -2,6 +2,9 @@ diff --git a/build/mobile/robocop/Makefile.in b/build/mobile/robocop/Makefile.in index 522f4d9f50..5298a18acf 100644 --- a/build/mobile/robocop/Makefile.in +++ b/build/mobile/robocop/Makefile.in @@ -2,18 +2,13 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -mobile-tests := mobile/android/base/tests +mobile-tests := mobile/android/tests/browser/robocop TESTPATH := $(topsrcdir)/$(mobile-tests) -dir-tests := $(DEPTH)/$(mobile-tests) - -ANDROID_APK_NAME := robocop-debug ANDROID_EXTRA_JARS += \ $(srcdir)/robotium-solo-4.3.1.jar \ $(NULL) -ANDROID_ASSETS_DIR := $(TESTPATH)/assets - _JAVA_HARNESS := \ Actions.java \ Assert.java \ @@ -40,57 +35,29 @@ java-tests := \ $(wildcard $(TESTPATH)/components/*.java) \ $(wildcard $(TESTPATH)/helpers/*.java) -PP_TARGETS += manifest -manifest := $(srcdir)/AndroidManifest.xml.in -manifest_TARGET := AndroidManifest.xml +PP_TARGETS += manifest +manifest := $(srcdir)/AndroidManifest.xml.in +manifest_TARGET := export +manifest_FLAGS += \ + -DMOZ_ANDROID_SHARED_ID='$(ANDROID_PACKAGE_NAME).sharedID' \ + -DMOZ_ANDROID_SHARED_ACCOUNT_TYPE='$(ANDROID_PACKAGE_NAME)_sync' \ + $(NULL) + ANDROID_MANIFEST_FILE := $(CURDIR)/AndroidManifest.xml -# Install robocop configs and helper -INSTALL_TARGETS += robocop -robocop_TARGET := libs -robocop_DEST := $(CURDIR) -robocop_FILES := \ - $(TESTPATH)/robocop.ini \ - $(TESTPATH)/robocop_autophone.ini \ - $(NULL) -robocop-deps := $(notdir $(robocop_FILES)) - -ROBOCOP_FILES := \ - $(wildcard $(TESTPATH)/*.html) \ - $(wildcard $(TESTPATH)/*.jpg) \ - $(wildcard $(TESTPATH)/*.sjs) \ - $(wildcard $(TESTPATH)/test*.js) \ - $(wildcard $(TESTPATH)/robocop*.js) \ - $(wildcard $(TESTPATH)/*.xml) \ - $(wildcard $(TESTPATH)/*.ogg) \ - $(wildcard $(TESTPATH)/*.mp4) \ - $(wildcard $(TESTPATH)/*.webm) \ - $(wildcard $(TESTPATH)/*.swf) \ - $(wildcard $(TESTPATH)/reader_mode_pages) \ - $(NULL) - -ROBOCOP_DEST = $(DEPTH)/_tests/testing/mochitest/tests/robocop/ -INSTALL_TARGETS += ROBOCOP - GARBAGE += \ AndroidManifest.xml \ - $(robocop-deps) \ - $(testconstants-dep) \ $(NULL) JAVAFILES += \ $(java-harness) \ $(java-tests) \ - $(robocop-deps) \ - $(testconstants-dep) \ $(NULL) include $(topsrcdir)/config/rules.mk tools:: $(ANDROID_APK_NAME).apk -GENERATED_DIRS += $(dir-tests) - # The test APK needs to know the contents of the target APK while not # being linked against them. This is a best effort to avoid getting # out of sync with base's build config. diff --git a/build/mobile/robocop/README b/build/mobile/robocop/README index ba567c68b8..9da93e6496 100644 --- a/build/mobile/robocop/README +++ b/build/mobile/robocop/README @@ -9,4 +9,4 @@ from the original download found at: http://code.google.com/p/robotium/ Firefox for Android developers should read the documentation in -mobile/android/base/tests/README.rst. +mobile/android/tests/browser/robocop/README.rst. diff --git a/build/mobile/robocop/moz.build b/build/mobile/robocop/moz.build index 12d61eb171..b2c5dfa47f 100644 --- a/build/mobile/robocop/moz.build +++ b/build/mobile/robocop/moz.build @@ -6,15 +6,27 @@ DEFINES['ANDROID_PACKAGE_NAME'] = CONFIG['ANDROID_PACKAGE_NAME'] -main = add_android_eclipse_project('Robocop', OBJDIR + '/AndroidManifest.xml') -main.package_name = 'org.mozilla.roboexample.test' -main.res = SRCDIR + '/res' -main.recursive_make_targets += [OBJDIR + '/AndroidManifest.xml'] -main.extra_jars += [SRCDIR + '/robotium-solo-4.3.1.jar'] -main.assets = TOPSRCDIR + '/mobile/android/base/tests/assets' -main.referenced_projects += ['Fennec'] +base = '/mobile/android/tests/browser/robocop/' -main.add_classpathentry('harness', SRCDIR, - dstdir='harness/org/mozilla/gecko') -main.add_classpathentry('src', TOPSRCDIR + '/mobile/android/base/tests', - dstdir='src/org/mozilla/gecko/tests') +ANDROID_APK_NAME = 'robocop-debug' +ANDROID_APK_PACKAGE = 'org.mozilla.roboexample.test' +ANDROID_ASSETS_DIRS += [base + 'assets'] + +TEST_HARNESS_FILES.testing.mochitest += [ + base + 'robocop.ini', + base + 'robocop_autophone.ini', +] +TEST_HARNESS_FILES.testing.mochitest.tests.robocop += [base + x for x in [ + '*.html', + '*.jpg', + '*.mp4', + '*.ogg', + '*.sjs', + '*.swf', + '*.webm', + '*.xml', + 'robocop*.js', + 'test*.js', +]] +# The ** below preserves directory structure. +TEST_HARNESS_FILES.testing.mochitest.tests.robocop.reader_mode_pages += [base + 'reader_mode_pages/**'] diff --git a/build/mobile/sutagent/android/Makefile.in b/build/mobile/sutagent/android/Makefile.in index 5dc76b8776..51e93a0abf 100644 --- a/build/mobile/sutagent/android/Makefile.in +++ b/build/mobile/sutagent/android/Makefile.in @@ -2,7 +2,7 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -ANDROID_APK_NAME := sutAgentAndroid +include $(topsrcdir)/config/config.mk JAVAFILES = \ AlertLooperThread.java \ @@ -25,6 +25,4 @@ ANDROID_EXTRA_JARS = \ $(srcdir)/network-libs/jmdns.jar \ $(NULL) -include $(topsrcdir)/config/rules.mk - tools:: $(ANDROID_APK_NAME).apk diff --git a/build/mobile/sutagent/android/fencp/Makefile.in b/build/mobile/sutagent/android/fencp/Makefile.in index e833b263dc..adcfb8f68e 100644 --- a/build/mobile/sutagent/android/fencp/Makefile.in +++ b/build/mobile/sutagent/android/fencp/Makefile.in @@ -2,7 +2,7 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -ANDROID_APK_NAME := FenCP +include $(topsrcdir)/config/config.mk JAVAFILES = \ DirCursor.java \ @@ -11,6 +11,4 @@ JAVAFILES = \ FileCursor.java \ $(NULL) -include $(topsrcdir)/config/rules.mk - tools:: $(ANDROID_APK_NAME).apk diff --git a/build/mobile/sutagent/android/fencp/moz.build b/build/mobile/sutagent/android/fencp/moz.build index c271ec3908..19fbe97974 100644 --- a/build/mobile/sutagent/android/fencp/moz.build +++ b/build/mobile/sutagent/android/fencp/moz.build @@ -3,3 +3,6 @@ # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. + +ANDROID_APK_NAME = 'FenCP' +ANDROID_APK_PACKAGE = 'org.mozilla.fencp' diff --git a/build/mobile/sutagent/android/ffxcp/Makefile.in b/build/mobile/sutagent/android/ffxcp/Makefile.in index ba51e14bcb..001fc8105c 100644 --- a/build/mobile/sutagent/android/ffxcp/Makefile.in +++ b/build/mobile/sutagent/android/ffxcp/Makefile.in @@ -2,7 +2,7 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -ANDROID_APK_NAME := FfxCP +include $(topsrcdir)/config/config.mk JAVAFILES = \ DirCursor.java \ @@ -11,6 +11,4 @@ JAVAFILES = \ FileCursor.java \ $(NULL) -include $(topsrcdir)/config/rules.mk - tools:: $(ANDROID_APK_NAME).apk diff --git a/build/mobile/sutagent/android/ffxcp/moz.build b/build/mobile/sutagent/android/ffxcp/moz.build index c271ec3908..5abafd29c8 100644 --- a/build/mobile/sutagent/android/ffxcp/moz.build +++ b/build/mobile/sutagent/android/ffxcp/moz.build @@ -3,3 +3,6 @@ # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. + +ANDROID_APK_NAME = 'FfxCP' +ANDROID_APK_PACKAGE = 'org.mozilla.ffxcp' diff --git a/build/mobile/sutagent/android/moz.build b/build/mobile/sutagent/android/moz.build index c271ec3908..c9505ec6b2 100644 --- a/build/mobile/sutagent/android/moz.build +++ b/build/mobile/sutagent/android/moz.build @@ -3,3 +3,6 @@ # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. + +ANDROID_APK_NAME = 'sutAgentAndroid' +ANDROID_APK_PACKAGE = 'com.mozilla.SUTAgentAndroid' diff --git a/build/mobile/sutagent/android/watcher/Makefile.in b/build/mobile/sutagent/android/watcher/Makefile.in index fc8f9e5c35..2c23532117 100644 --- a/build/mobile/sutagent/android/watcher/Makefile.in +++ b/build/mobile/sutagent/android/watcher/Makefile.in @@ -2,7 +2,7 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -ANDROID_APK_NAME := Watcher +include $(topsrcdir)/config/config.mk JAVAFILES = \ IWatcherService.java \ @@ -12,6 +12,4 @@ JAVAFILES = \ WatcherService.java \ $(NULL) -include $(topsrcdir)/config/rules.mk - tools:: $(ANDROID_APK_NAME).apk diff --git a/build/mobile/sutagent/android/watcher/moz.build b/build/mobile/sutagent/android/watcher/moz.build index c271ec3908..938ff53563 100644 --- a/build/mobile/sutagent/android/watcher/moz.build +++ b/build/mobile/sutagent/android/watcher/moz.build @@ -3,3 +3,6 @@ # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. + +ANDROID_APK_NAME = 'Watcher' +ANDROID_APK_PACKAGE = 'com.mozilla.watcher' diff --git a/build/moz.build b/build/moz.build index e314db4eca..90e349dc7a 100644 --- a/build/moz.build +++ b/build/moz.build @@ -25,8 +25,11 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android': 'mobile/sutagent/android/watcher', 'mobile/sutagent/android/ffxcp', 'mobile/sutagent/android/fencp', - 'mobile/robocop', ] + if not CONFIG['MOZ_B2GDROID']: + TEST_DIRS += [ + 'mobile/robocop', + ] for var in ('GRE_MILESTONE', 'MOZ_APP_VERSION', 'MOZ_APP_BASENAME', 'MOZ_APP_VENDOR', 'MOZ_APP_ID', 'MAR_CHANNEL_ID', diff --git a/config/check_vanilla_allocations.py b/config/check_vanilla_allocations.py index ad3ce4f444..2a3be879ec 100644 --- a/config/check_vanilla_allocations.py +++ b/config/check_vanilla_allocations.py @@ -75,8 +75,8 @@ def main(): # Run |nm|. Options: # -u: show only undefined symbols # -C: demangle symbol names - # -l: show a filename and line number for each undefined symbol - cmd = ['nm', '-u', '-C', '-l', args.file] + # -A: show an object filename for each undefined symbol + cmd = ['nm', '-u', '-C', '-A', args.file] lines = subprocess.check_output(cmd, universal_newlines=True, stderr=subprocess.PIPE).split('\n') @@ -112,26 +112,30 @@ def main(): # This regexp matches the relevant lines in the output of |nm|, which look # like the following. # - # U malloc /path/to/objdir/dist/include/js/Utility.h:142 + # js/src/libjs_static.a:jsutil.o: U malloc # - alloc_fns_re = r'U (' + r'|'.join(alloc_fns) + r').*\/([\w\.]+):(\d+)$' + alloc_fns_re = r'([^:/ ]+):\s+U (' + r'|'.join(alloc_fns) + r')' # This tracks which allocation/free functions have been seen in jsutil.cpp. jsutil_cpp = set([]) + # Would it be helpful to emit detailed line number information after a failure? + emit_line_info = False + for line in lines: m = re.search(alloc_fns_re, line) if m is None: continue - fn = m.group(1) - filename = m.group(2) - linenum = m.group(3) - if filename == 'jsutil.cpp': + filename = m.group(1) + fn = m.group(2) + if filename == 'jsutil.o': jsutil_cpp.add(fn) else: # An allocation is present in a non-special file. Fail! - fail("'" + fn + "' present at " + filename + ':' + linenum) + fail("'" + fn + "' present in " + filename) + # Try to give more precise information about the offending code. + emit_line_info = True # Check that all functions we expect are used in jsutil.cpp. (This will @@ -147,6 +151,34 @@ def main(): fail('unexpected allocation fns used in jsutil.cpp: ' + ', '.join(jsutil_cpp)) + # If we found any improper references to allocation functions, try to use + # DWARF debug info to get more accurate line number information about the + # bad calls. This is a lot slower than 'nm -A', and it is not always + # precise when building with --enable-optimized. + if emit_line_info: + print('check_vanilla_allocations.py: Source lines with allocation calls:') + print('check_vanilla_allocations.py: Accurate in unoptimized builds; jsutil.cpp expected.') + + # Run |nm|. Options: + # -u: show only undefined symbols + # -C: demangle symbol names + # -l: show line number information for each undefined symbol + cmd = ['nm', '-u', '-C', '-l', args.file] + lines = subprocess.check_output(cmd, universal_newlines=True, + stderr=subprocess.PIPE).split('\n') + + # This regexp matches the relevant lines in the output of |nm -l|, + # which look like the following. + # + # U malloc jsutil.cpp:117 + # + alloc_lines_re = r'U ((' + r'|'.join(alloc_fns) + r').*)\s+(\S+:\d+)$' + + for line in lines: + m = re.search(alloc_lines_re, line) + if m: + print('check_vanilla_allocations.py:', m.group(1), 'called at', m.group(3)) + if has_failed: sys.exit(1) diff --git a/config/faster/rules.mk b/config/faster/rules.mk new file mode 100644 index 0000000000..654d79ac83 --- /dev/null +++ b/config/faster/rules.mk @@ -0,0 +1,176 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# /!\ Please make sure to update the following comment when you touch this +# file. Thank you /!\ + +# The traditional Mozilla build system relied on going through the entire +# build tree a number of times with different targets, and many of the +# things happening at each step required other things happening in previous +# steps without any documentation of those dependencies. +# +# This new build system tries to start afresh by establishing what files or +# operations are needed for the build, and applying the necessary rules to +# have those in place, relying on make dependencies to get them going. +# +# As of writing, only building non-compiled parts of Firefox is supported +# here (a few other things are also left out). This is a starting point, with +# the intent to grow this build system to make it more complete. +# +# This file contains rules and dependencies to get things working. The intent +# is for a Makefile to define some dependencies and variables, and include +# this file. What needs to be defined there, and ends up being generated by +# python/mozbuild/mozbuild/backend/fastermake.py is the following: +# - TOPSRCDIR/TOPOBJDIR, respectively the top source directory and the top +# object directory +# - PYTHON, the path to the python executable +# - ACDEFINES, which contains a set of -Dvar=name to be used during +# preprocessing +# - MOZ_CHROME_FILE_FORMAT, which defines whether to use file copies or +# symbolic links +# - JAR_MN_TARGETS, which defines the targets to use for jar manifest +# processing, see further below +# - INSTALL_MANIFESTS, which defines the list of base directories handled +# by install manifests, see further below +# - MANIFEST_TARGETS, which defines the file paths of chrome manifests, see +# further below +# +# A convention used between this file and the Makefile including it is that +# global Make variables names are uppercase, while "local" Make variables +# applied to specific targets are lowercase. + +# Targets to be triggered for a default build +default: $(addprefix install-,$(INSTALL_MANIFESTS)) +default: $(addprefix jar-,$(JAR_MN_TARGETS)) + +# Explicit files to be built for a default build +default: $(addprefix $(TOPOBJDIR)/,$(MANIFEST_TARGETS)) +default: $(TOPOBJDIR)/dist/bin/greprefs.js +default: $(TOPOBJDIR)/dist/bin/platform.ini +default: $(TOPOBJDIR)/dist/bin/webapprt/webapprt.ini + +# Targets from the recursive make backend to be built for a default build +default: $(TOPOBJDIR)/config/makefiles/xpidl/xpidl + +.PHONY: FORCE + +# Extra define to trigger some workarounds. We should strive to limit the +# use of those. As of writing the only ones are in +# toolkit/content/buildconfig.html and browser/locales/jar.mn. +ACDEFINES += -DBUILD_FASTER + +# Generic rule to fall back to the recursive make backend +$(TOPOBJDIR)/%: FORCE + $(MAKE) -C $(dir $@) $(notdir $@) + +# Files under the faster/ sub-directory, however, are not meant to use the +# fallback +$(TOPOBJDIR)/faster/%: ; + +# And files under dist/ are meant to be copied from their first dependency +# if there is no other rule. +$(TOPOBJDIR)/dist/%: + rm -f $@ + cp $< $@ + +# Install files using install manifests +# +# The list of base directories is given in INSTALL_MANIFESTS. The +# corresponding install manifests are named correspondingly, with forward +# slashes replaced with underscores, and prefixed with `install_`. That is, +# the install manifest for `dist/bin` would be `install_dist_bin`. +$(addprefix install-,$(INSTALL_MANIFESTS)): install-%: $(TOPOBJDIR)/config/buildid + @# For now, force preprocessed files to be reprocessed every time. + @# The overhead is not that big, and this avoids waiting for proper + @# support for defines tracking in process_install_manifest. + @touch install_$(subst /,_,$*) + $(PYTHON) -m mozbuild.action.process_install_manifest \ + --no-remove \ + --no-remove-empty-directories \ + $(TOPOBJDIR)/$* \ + -DAB_CD=en-US \ + -DMOZ_APP_BUILDID=$(shell cat $(TOPOBJDIR)/config/buildid) \ + $(ACDEFINES) \ + $(MOZ_DEBUG_DEFINES) \ + install_$(subst /,_,$*) + +# Install files from jar manifests. Ideally, they would be using install +# manifests, but the code to read jar manifests and emit appropriate +# install manifests is not there yet. +# Things missing: +# - DEFINES from config/config.mk +# - L10N +# - -e when USE_EXTENSION_MANIFEST is set in moz.build +# +# The list given in JAR_MN_TARGETS corresponds to the list of `jar-%` targets +# to be processed, with the `jar-` prefix stripped. +# The Makefile is expected to specify the source jar manifest as a dependency +# to each target. There is no expectation that the `jar-%` target name matches +# the source file name in any way. For example: +# JAR_MN_TARGETS = foo +# jar-foo: /path/to/some/jar.mn +# Additionally, extra defines can be specified for the processing of the jar +# manifest by settig the `defines` variable specifically for the given target. +# For example: +# jar-foo: defines = -Dqux=foo +# The default base path where files are going to be installed is `dist/bin`. +# It is possible to use a different path by setting the `install_target` +# variable. For example: +# jar-foo: install_target = dist/bin/foo +# When processing jar manifests, relative paths given inside a jar manifest +# can be resolved from an object directory. The default path for that object +# directory is the translation of the jar manifest directory path from the +# source directory to the object directory. That is, for +# $(TOPSRCDIR)/path/to/jar.mn, the default would be $(TOPOBJDIR)/path/to. +# In case a different path must be used for the object directory, the `objdir` +# variable can be set. For example: +# jar-foo: objdir=/some/other/path +jar-%: objdir ?= $(dir $(patsubst $(TOPSRCDIR)%,$(TOPOBJDIR)%,$<)) +jar-%: install_target ?= dist/bin +jar-%: + cd $(objdir) && \ + $(PYTHON) -m mozbuild.action.jar_maker \ + -j $(TOPOBJDIR)/$(install_target)/chrome \ + -t $(TOPSRCDIR) \ + -f $(MOZ_CHROME_FILE_FORMAT) \ + -c $(dir $<)/en-US \ + -DAB_CD=en-US \ + $(defines) \ + $(ACDEFINES) \ + $(MOZ_DEBUG_DEFINES) \ + $< + +# Create some chrome manifests +# This rule is forced to run every time because it may be updating files that +# already exit. +# +# The list of chrome manifests is given in MANIFEST_TARGETS, relative to the +# top object directory. The content for those manifests is given in the +# `content` variable associated with the target. For example: +# MANIFEST_TARGETS = foo +# $(TOPOBJDIR)/foo: content = "manifest foo.manifest" "manifest bar.manifest" +$(addprefix $(TOPOBJDIR)/,$(MANIFEST_TARGETS)): FORCE + $(PYTHON) -m mozbuild.action.buildlist \ + $@ \ + $(content) + +# ============================================================================ +# Below is a set of additional dependencies and variables used to build things +# that are not supported by data in moz.build. + +# GENERATED_FILES are not supported yet, and even if they were, the +# dependencies are missing information. +$(foreach p,linux osx windows,jar-browser-themes-$(p)-jar.mn): \ +jar-browser-themes-%-jar.mn: \ + $(TOPOBJDIR)/browser/themes/%/tab-selected-end.svg \ + $(TOPOBJDIR)/browser/themes/%/tab-selected-start.svg + +# Files to build with the recursive backend and simply copy +$(TOPOBJDIR)/dist/bin/greprefs.js: $(TOPOBJDIR)/modules/libpref/greprefs.js +$(TOPOBJDIR)/dist/bin/platform.ini: $(TOPOBJDIR)/toolkit/xre/platform.ini +$(TOPOBJDIR)/dist/bin/webapprt/webapprt.ini: $(TOPOBJDIR)/webapprt/webapprt.ini + +# The xpidl target in config/makefiles/xpidl requires the install manifest for +# dist/idl to have been processed. +$(TOPOBJDIR)/config/makefiles/xpidl/xpidl: $(TOPOBJDIR)/install-dist_idl diff --git a/config/makefiles/java-build.mk b/config/makefiles/java-build.mk index 0960886108..e6fb3015d7 100644 --- a/config/makefiles/java-build.mk +++ b/config/makefiles/java-build.mk @@ -16,15 +16,19 @@ endif #} JAVAFILES ifdef ANDROID_APK_NAME #{ -android_res_dirs := $(addprefix $(srcdir)/,$(or $(ANDROID_RES_DIRS),res)) +$(if $(ANDROID_APK_PACKAGE),,$(error Missing ANDROID_APK_PACKAGE with ANDROID_APK_NAME)) + +android_res_dirs := $(or $(ANDROID_RES_DIRS),$(srcdir)/res) _ANDROID_RES_FLAG := $(addprefix -S ,$(android_res_dirs)) -_ANDROID_ASSETS_FLAG := $(addprefix -A ,$(ANDROID_ASSETS_DIR)) +_ANDROID_ASSETS_FLAG := $(if $(ANDROID_ASSETS_DIRS),$(addprefix -A ,$(ANDROID_ASSETS_DIRS))) android_manifest := $(or $(ANDROID_MANIFEST_FILE),AndroidManifest.xml) -GENERATED_DIRS += classes +GENERATED_DIRS += classes generated + +generated_r_java := generated/$(subst .,/,$(ANDROID_APK_PACKAGE))/R.java classes.dex: $(call mkdir_deps,classes) -classes.dex: R.java +classes.dex: $(generated_r_java) classes.dex: $(ANDROID_APK_NAME).ap_ classes.dex: $(ANDROID_EXTRA_JARS) classes.dex: $(JAVAFILES) @@ -38,17 +42,35 @@ classes.dex: $(JAVAFILES) # all causes Make to treat the target differently, in a way that # defeats our dependencies. -R.java: .aapt.deps ; +$(generated_r_java): .aapt.deps ; $(ANDROID_APK_NAME).ap_: .aapt.deps ; # This uses the fact that Android resource directories list all # resource files one subdirectory below the parent resource directory. android_res_files := $(wildcard $(addsuffix /*,$(wildcard $(addsuffix /*,$(android_res_dirs))))) -.aapt.deps: $(android_manifest) $(android_res_files) $(wildcard $(ANDROID_ASSETS_DIR)) +# An extra package like org.example.app generates dependencies like: +# generated/org/example/app/R.java: .aapt.deps ; +# classes.dex: generated/org/example/app/R.java +# GARBAGE: generated/org/example/app/R.java +$(foreach extra_package,$(ANDROID_EXTRA_PACKAGES), \ + $(eval generated/$(subst .,/,$(extra_package))/R.java: .aapt.deps ;) \ + $(eval classes.dex: generated/$(subst .,/,$(extra_package))/R.java) \ + $(eval GARBAGE: generated/$(subst .,/,$(extra_package))/R.java) \ +) + +# aapt flag -m: 'make package directories under location specified by -J'. +# The --extra-package list is colon separated. +.aapt.deps: $(android_manifest) $(android_res_files) $(wildcard $(ANDROID_ASSETS_DIRS)) @$(TOUCH) $@ $(AAPT) package -f -M $< -I $(ANDROID_SDK)/android.jar $(_ANDROID_RES_FLAG) $(_ANDROID_ASSETS_FLAG) \ - -J ${@D} \ + --custom-package $(ANDROID_APK_PACKAGE) \ + --non-constant-id \ + --auto-add-overlay \ + $(if $(ANDROID_EXTRA_PACKAGES),--extra-packages $(subst $(NULL) ,:,$(strip $(ANDROID_EXTRA_PACKAGES)))) \ + $(if $(ANDROID_EXTRA_RES_DIRS),$(addprefix -S ,$(ANDROID_EXTRA_RES_DIRS))) \ + -m \ + -J ${@D}/generated \ -F $(ANDROID_APK_NAME).ap_ $(ANDROID_APK_NAME)-unsigned-unaligned.apk: $(ANDROID_APK_NAME).ap_ classes.dex @@ -60,10 +82,10 @@ $(ANDROID_APK_NAME)-unaligned.apk: $(ANDROID_APK_NAME)-unsigned-unaligned.apk $(DEBUG_JARSIGNER) $@ $(ANDROID_APK_NAME).apk: $(ANDROID_APK_NAME)-unaligned.apk - $(ZIPALIGN) -f -v 4 $< $@ + $(ZIPALIGN) -f 4 $< $@ GARBAGE += \ - R.java \ + $(generated_r_java) \ classes.dex \ $(ANDROID_APK_NAME).ap_ \ $(ANDROID_APK_NAME)-unsigned-unaligned.apk \ diff --git a/config/makefiles/xpidl/Makefile.in b/config/makefiles/xpidl/Makefile.in index d0b2a981c6..3d39326d2a 100644 --- a/config/makefiles/xpidl/Makefile.in +++ b/config/makefiles/xpidl/Makefile.in @@ -53,8 +53,14 @@ endif chrome_manifests := @chrome_manifests@ +%/interfaces.manifest: Makefile + $(call py_action,buildlist,$@ $(foreach xpt,$(filter $*/%,$(registered_xpt_files)),'interfaces $(notdir $(xpt))')) + +interfaces_manifests := @interfaces_manifests@ + xpidl_modules := @xpidl_modules@ -xpt_files := @xpt_files@ +registered_xpt_files := @registered_xpt_files@ +xpt_files := $(registered_xpt_files) @xpt_files@ @xpidl_rules@ @@ -62,7 +68,7 @@ depends_files := $(foreach root,$(xpidl_modules),$(idl_deps_dir)/$(root).pp) GARBAGE += $(xpt_files) $(depends_files) -xpidl:: $(xpt_files) $(chrome_manifests) +xpidl:: $(xpt_files) $(chrome_manifests) $(interfaces_manifests) $(xpt_files): $(process_py) $(call mkdir_deps,$(idl_deps_dir) $(dist_include_dir)) diff --git a/config/rules.mk b/config/rules.mk index 65df7fb013..900293a966 100644 --- a/config/rules.mk +++ b/config/rules.mk @@ -1159,14 +1159,6 @@ endif ################################################################################ # Copy each element of EXTRA_COMPONENTS to $(FINAL_TARGET)/components -ifneq (,$(filter %.js,$(EXTRA_COMPONENTS) $(EXTRA_PP_COMPONENTS))) -ifeq (,$(filter %.manifest,$(EXTRA_COMPONENTS) $(EXTRA_PP_COMPONENTS))) -ifndef NO_JS_MANIFEST -$(error .js component without matching .manifest. See https://developer.mozilla.org/en/XPCOM/XPCOM_changes_in_Gecko_2.0) -endif -endif -endif - ifdef EXTRA_COMPONENTS misc:: $(EXTRA_COMPONENTS) ifndef NO_DIST_INSTALL diff --git a/configure.in b/configure.in index a56257cc9b..e4e2244761 100644 --- a/configure.in +++ b/configure.in @@ -3937,6 +3937,8 @@ esac case "$MOZ_BUILD_APP" in browser) AC_DEFINE(MOZ_PHOENIX) + + BUILD_BACKENDS="$BUILD_BACKENDS FasterMake" ;; xulrunner) diff --git a/js/src/asmjs/AsmJSCompile.cpp b/js/src/asmjs/AsmJSCompile.cpp index 1e68637b20..ec96c383a4 100644 --- a/js/src/asmjs/AsmJSCompile.cpp +++ b/js/src/asmjs/AsmJSCompile.cpp @@ -123,6 +123,11 @@ class ModuleCompiler bool finishGeneratingFunction(AsmFunction& func, CodeGenerator& codegen, const AsmJSFunctionLabels& labels) { + // If we have hit OOM then invariants which we assert below may not + // hold, so abort now. + if (masm().oom()) + return false; + // Code range unsigned line = func.lineno(); unsigned column = func.column(); diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index a7fb91c10f..5b4cc08612 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -2211,14 +2211,13 @@ NeedNegativeZeroCheck(MDefinition* def) // Figure out the order in which the addition's operands will // execute. EdgeCaseAnalysis::analyzeLate has renumbered the MIR // definitions for us so that this just requires comparing ids. - MDefinition* first = use_def->toAdd()->getOperand(0); - MDefinition* second = use_def->toAdd()->getOperand(1); + MDefinition* first = use_def->toAdd()->lhs(); + MDefinition* second = use_def->toAdd()->rhs(); if (first->id() > second->id()) { MDefinition* temp = first; first = second; second = temp; } - // Negative zero checks can be removed on the first executed // operand only if it is guaranteed the second executed operand // will produce a value other than -0. While the second is diff --git a/js/src/jit/arm/Assembler-arm.cpp b/js/src/jit/arm/Assembler-arm.cpp index ac3d064c5a..b1dbdfe148 100644 --- a/js/src/jit/arm/Assembler-arm.cpp +++ b/js/src/jit/arm/Assembler-arm.cpp @@ -2169,7 +2169,7 @@ Assembler::as_BranchPool(uint32_t value, RepatchLabel* label, ARMBuffer::PoolEnt if (label->bound()) { BufferOffset dest(label); as_b(dest.diffB(ret), c, ret); - } else { + } else if (!oom()) { label->use(ret.getOffset()); } #ifdef JS_DISASM_ARM @@ -2369,14 +2369,12 @@ Assembler::as_b(BOffImm off, Condition c, Label* documentation) BufferOffset Assembler::as_b(Label* l, Condition c) { - if (m_buffer.oom()) { - BufferOffset ret; - return ret; - } - if (l->bound()) { // Note only one instruction is emitted here, the NOP is overwritten. BufferOffset ret = allocBranchInst(); + if (oom()) + return BufferOffset(); + as_b(BufferOffset(l).diffB(ret), c, ret); #ifdef JS_DISASM_ARM spewBranch(m_buffer.getInstOrNull(ret), l); @@ -2384,6 +2382,9 @@ Assembler::as_b(Label* l, Condition c) return ret; } + if (oom()) + return BufferOffset(); + int32_t old; BufferOffset ret; if (l->used()) { @@ -2400,6 +2401,10 @@ Assembler::as_b(Label* l, Condition c) BOffImm inv; ret = as_b(inv, c, l); } + + if (oom()) + return BufferOffset(); + DebugOnly check = l->use(ret.getOffset()); MOZ_ASSERT(check == old); return ret; @@ -2436,14 +2441,12 @@ Assembler::as_bl(BOffImm off, Condition c, Label* documentation) BufferOffset Assembler::as_bl(Label* l, Condition c) { - if (m_buffer.oom()) { - BufferOffset ret; - return ret; - } - if (l->bound()) { // Note only one instruction is emitted here, the NOP is overwritten. BufferOffset ret = allocBranchInst(); + if (oom()) + return BufferOffset(); + as_bl(BufferOffset(l).diffB(ret), c, ret); #ifdef JS_DISASM_ARM spewBranch(m_buffer.getInstOrNull(ret), l); @@ -2451,6 +2454,9 @@ Assembler::as_bl(Label* l, Condition c) return ret; } + if (oom()) + return BufferOffset(); + int32_t old; BufferOffset ret; // See if the list was empty :( @@ -2468,6 +2474,10 @@ Assembler::as_bl(Label* l, Condition c) BOffImm inv; ret = as_bl(inv, c, l); } + + if (oom()) + return BufferOffset(); + DebugOnly check = l->use(ret.getOffset()); MOZ_ASSERT(check == old); return ret; diff --git a/js/src/jit/mips-shared/Architecture-mips-shared.cpp b/js/src/jit/mips-shared/Architecture-mips-shared.cpp index 37c3a4c7fb..11815903a3 100644 --- a/js/src/jit/mips-shared/Architecture-mips-shared.cpp +++ b/js/src/jit/mips-shared/Architecture-mips-shared.cpp @@ -23,7 +23,7 @@ uint32_t GetMIPSFlags() static uint32_t flags = 0; if (isSet) return flags; -#ifdef JS_SIMULATOR_MIPS32 +#if defined(JS_SIMULATOR_MIPS32) || defined(JS_SIMULATOR_MIPS64) isSet = true; flags |= HWCAP_FPU; return flags; @@ -46,7 +46,7 @@ uint32_t GetMIPSFlags() #endif return flags; -#endif // JS_SIMULATOR_MIPS32 +#endif // JS_SIMULATOR_MIPS32 || JS_SIMULATOR_MIPS64 } bool hasFPU() diff --git a/js/src/jit/mips-shared/Architecture-mips-shared.h b/js/src/jit/mips-shared/Architecture-mips-shared.h index c0048e4d20..4b613dd213 100644 --- a/js/src/jit/mips-shared/Architecture-mips-shared.h +++ b/js/src/jit/mips-shared/Architecture-mips-shared.h @@ -17,11 +17,13 @@ // gcc appears to use _mips_hard_float to denote // that the target is a hard-float target. #ifdef _mips_hard_float -#define JS_CODEGEN_MIPS32_HARDFP +#define JS_CODEGEN_MIPS_HARDFP #endif -#if _MIPS_SIM == _ABIO32 +#if (defined(_MIPS_SIM) && (_MIPS_SIM == _ABIO32)) || defined(JS_SIMULATOR_MIPS32) #define USES_O32_ABI +#elif (defined(_MIPS_SIM) && (_MIPS_SIM == _ABI64)) || defined(JS_SIMULATOR_MIPS64) +#define USES_N64_ABI #else #error "Unsupported ABI" #endif @@ -73,6 +75,7 @@ class Registers a1 = r5, a2 = r6, a3 = r7, +#if defined(USES_O32_ABI) t0 = r8, t1 = r9, t2 = r10, @@ -81,6 +84,24 @@ class Registers t5 = r13, t6 = r14, t7 = r15, + ta0 = t4, + ta1 = t5, + ta2 = t6, + ta3 = t7, +#elif defined(USES_N64_ABI) + a4 = r8, + a5 = r9, + a6 = r10, + a7 = r11, + t0 = r12, + t1 = r13, + t2 = r14, + t3 = r15, + ta0 = a4, + ta1 = a5, + ta2 = a6, + ta3 = a7, +#endif s0 = r16, s1 = r17, s2 = r18, @@ -107,10 +128,10 @@ class Registers uintptr_t r; }; + static const char * const RegNames[]; static const char* GetName(Code code) { MOZ_ASSERT(code < Total); - static const char * const Names[] = REGISTERS_NAMES; - return Names[code]; + return RegNames[code]; } static const char* GetName(Encoding i) { return GetName(Code(i)); @@ -122,12 +143,12 @@ class Registers static const Encoding Invalid = invalid_reg; static const uint32_t Total = 32; - static const uint32_t Allocatable = REGISTERS_ALLOCATABLE; + static const uint32_t Allocatable; typedef uint32_t SetType; static const SetType AllMask = 0xffffffff; static const SetType SharedArgRegMask = (1 << a0) | (1 << a1) | (1 << a2) | (1 << a3); - static const SetType ArgRegMask = REGISTERS_ARGREGMASK; + static const SetType ArgRegMask; static const SetType VolatileMask = (1 << Registers::v0) | @@ -140,10 +161,10 @@ class Registers (1 << Registers::t1) | (1 << Registers::t2) | (1 << Registers::t3) | - (1 << Registers::t4) | - (1 << Registers::t5) | - (1 << Registers::t6) | - (1 << Registers::t7); + (1 << Registers::ta0) | + (1 << Registers::ta1) | + (1 << Registers::ta2) | + (1 << Registers::ta3); // We use this constant to save registers when entering functions. This // is why $ra is added here even though it is not "Non Volatile". @@ -179,11 +200,11 @@ class Registers static const SetType TempMask = VolatileMask & ~NonAllocatableMask; // Registers returned from a JS -> JS call. - static const SetType JSCallMask = REGISTERS_JSCALLMASK; + static const SetType JSCallMask; // Registers returned from a JS -> C call. static const SetType SharedCallMask = (1 << Registers::v0); - static const SetType CallMask = REGISTERS_CALLMASK; + static const SetType CallMask; static const SetType AllocatableMask = AllMask & ~NonAllocatableMask; @@ -202,7 +223,7 @@ class Registers // Smallest integer type that can hold a register bitmask. typedef uint32_t PackedRegisterMask; -class BaseFloatRegisters +class FloatRegistersMIPSShared { public: enum FPRegisterID { @@ -265,13 +286,13 @@ class BaseFloatRegisters template class TypedRegisterSet; -class BaseFloatRegister +class FloatRegisterMIPSShared { public: bool isInt32x4() const { return false; } bool isFloat32x4() const { return false; } - typedef BaseFloatRegisters::SetType SetType; + typedef FloatRegistersMIPSShared::SetType SetType; static uint32_t SetSize(SetType x) { static_assert(sizeof(SetType) == 8, "SetType must be 64 bits"); diff --git a/js/src/jit/mips-shared/Assembler-mips-shared.cpp b/js/src/jit/mips-shared/Assembler-mips-shared.cpp index b602ca606c..0ff773f9f9 100644 --- a/js/src/jit/mips-shared/Assembler-mips-shared.cpp +++ b/js/src/jit/mips-shared/Assembler-mips-shared.cpp @@ -480,12 +480,31 @@ AssemblerMIPSShared::as_addiu(Register rd, Register rs, int32_t j) return writeInst(InstImm(op_addiu, rs, rd, Imm16(j)).encode()); } +BufferOffset +AssemblerMIPSShared::as_daddu(Register rd, Register rs, Register rt) +{ + return writeInst(InstReg(op_special, rs, rt, rd, ff_daddu).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_daddiu(Register rd, Register rs, int32_t j) +{ + MOZ_ASSERT(Imm16::IsInSignedRange(j)); + return writeInst(InstImm(op_daddiu, rs, rd, Imm16(j)).encode()); +} + BufferOffset AssemblerMIPSShared::as_subu(Register rd, Register rs, Register rt) { return writeInst(InstReg(op_special, rs, rt, rd, ff_subu).encode()); } +BufferOffset +AssemblerMIPSShared::as_dsubu(Register rd, Register rs, Register rt) +{ + return writeInst(InstReg(op_special, rs, rt, rd, ff_dsubu).encode()); +} + BufferOffset AssemblerMIPSShared::as_mult(Register rs, Register rt) { @@ -498,6 +517,18 @@ AssemblerMIPSShared::as_multu(Register rs, Register rt) return writeInst(InstReg(op_special, rs, rt, ff_multu).encode()); } +BufferOffset +AssemblerMIPSShared::as_dmult(Register rs, Register rt) +{ + return writeInst(InstReg(op_special, rs, rt, ff_dmult).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_dmultu(Register rs, Register rt) +{ + return writeInst(InstReg(op_special, rs, rt, ff_dmultu).encode()); +} + BufferOffset AssemblerMIPSShared::as_div(Register rs, Register rt) { @@ -510,6 +541,18 @@ AssemblerMIPSShared::as_divu(Register rs, Register rt) return writeInst(InstReg(op_special, rs, rt, ff_divu).encode()); } +BufferOffset +AssemblerMIPSShared::as_ddiv(Register rs, Register rt) +{ + return writeInst(InstReg(op_special, rs, rt, ff_ddiv).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_ddivu(Register rs, Register rt) +{ + return writeInst(InstReg(op_special, rs, rt, ff_ddivu).encode()); +} + BufferOffset AssemblerMIPSShared::as_mul(Register rd, Register rs, Register rt) { @@ -531,12 +574,32 @@ AssemblerMIPSShared::as_sll(Register rd, Register rt, uint16_t sa) return writeInst(InstReg(op_special, rs_zero, rt, rd, sa, ff_sll).encode()); } +BufferOffset +AssemblerMIPSShared::as_dsll(Register rd, Register rt, uint16_t sa) +{ + MOZ_ASSERT(sa < 32); + return writeInst(InstReg(op_special, rs_zero, rt, rd, sa, ff_dsll).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_dsll32(Register rd, Register rt, uint16_t sa) +{ + MOZ_ASSERT(31 < sa && sa < 64); + return writeInst(InstReg(op_special, rs_zero, rt, rd, sa - 32, ff_dsll32).encode()); +} + BufferOffset AssemblerMIPSShared::as_sllv(Register rd, Register rt, Register rs) { return writeInst(InstReg(op_special, rs, rt, rd, ff_sllv).encode()); } +BufferOffset +AssemblerMIPSShared::as_dsllv(Register rd, Register rt, Register rs) +{ + return writeInst(InstReg(op_special, rs, rt, rd, ff_dsllv).encode()); +} + BufferOffset AssemblerMIPSShared::as_srl(Register rd, Register rt, uint16_t sa) { @@ -544,12 +607,32 @@ AssemblerMIPSShared::as_srl(Register rd, Register rt, uint16_t sa) return writeInst(InstReg(op_special, rs_zero, rt, rd, sa, ff_srl).encode()); } +BufferOffset +AssemblerMIPSShared::as_dsrl(Register rd, Register rt, uint16_t sa) +{ + MOZ_ASSERT(sa < 32); + return writeInst(InstReg(op_special, rs_zero, rt, rd, sa, ff_dsrl).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_dsrl32(Register rd, Register rt, uint16_t sa) +{ + MOZ_ASSERT(31 < sa && sa < 64); + return writeInst(InstReg(op_special, rs_zero, rt, rd, sa - 32, ff_dsrl32).encode()); +} + BufferOffset AssemblerMIPSShared::as_srlv(Register rd, Register rt, Register rs) { return writeInst(InstReg(op_special, rs, rt, rd, ff_srlv).encode()); } +BufferOffset +AssemblerMIPSShared::as_dsrlv(Register rd, Register rt, Register rs) +{ + return writeInst(InstReg(op_special, rs, rt, rd, ff_dsrlv).encode()); +} + BufferOffset AssemblerMIPSShared::as_sra(Register rd, Register rt, uint16_t sa) { @@ -557,12 +640,32 @@ AssemblerMIPSShared::as_sra(Register rd, Register rt, uint16_t sa) return writeInst(InstReg(op_special, rs_zero, rt, rd, sa, ff_sra).encode()); } +BufferOffset +AssemblerMIPSShared::as_dsra(Register rd, Register rt, uint16_t sa) +{ + MOZ_ASSERT(sa < 32); + return writeInst(InstReg(op_special, rs_zero, rt, rd, sa, ff_dsra).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_dsra32(Register rd, Register rt, uint16_t sa) +{ + MOZ_ASSERT(31 < sa && sa < 64); + return writeInst(InstReg(op_special, rs_zero, rt, rd, sa - 32, ff_dsra32).encode()); +} + BufferOffset AssemblerMIPSShared::as_srav(Register rd, Register rt, Register rs) { return writeInst(InstReg(op_special, rs, rt, rd, ff_srav).encode()); } +BufferOffset +AssemblerMIPSShared::as_dsrav(Register rd, Register rt, Register rs) +{ + return writeInst(InstReg(op_special, rs, rt, rd, ff_dsrav).encode()); +} + BufferOffset AssemblerMIPSShared::as_rotr(Register rd, Register rt, uint16_t sa) { @@ -570,12 +673,32 @@ AssemblerMIPSShared::as_rotr(Register rd, Register rt, uint16_t sa) return writeInst(InstReg(op_special, rs_one, rt, rd, sa, ff_srl).encode()); } +BufferOffset +AssemblerMIPSShared::as_drotr(Register rd, Register rt, uint16_t sa) +{ + MOZ_ASSERT(sa < 32); + return writeInst(InstReg(op_special, rs_one, rt, rd, sa, ff_dsrl).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_drotr32(Register rd, Register rt, uint16_t sa) +{ + MOZ_ASSERT(31 < sa && sa < 64); + return writeInst(InstReg(op_special, rs_one, rt, rd, sa - 32, ff_dsrl32).encode()); +} + BufferOffset AssemblerMIPSShared::as_rotrv(Register rd, Register rt, Register rs) { return writeInst(InstReg(op_special, rs, rt, rd, 1, ff_srlv).encode()); } +BufferOffset +AssemblerMIPSShared::as_drotrv(Register rd, Register rt, Register rs) +{ + return writeInst(InstReg(op_special, rs, rt, rd, 1, ff_dsrlv).encode()); +} + // Load and store instructions BufferOffset AssemblerMIPSShared::as_lb(Register rd, Register rs, int16_t off) @@ -607,6 +730,12 @@ AssemblerMIPSShared::as_lw(Register rd, Register rs, int16_t off) return writeInst(InstImm(op_lw, rs, rd, Imm16(off)).encode()); } +BufferOffset +AssemblerMIPSShared::as_lwu(Register rd, Register rs, int16_t off) +{ + return writeInst(InstImm(op_lwu, rs, rd, Imm16(off)).encode()); +} + BufferOffset AssemblerMIPSShared::as_lwl(Register rd, Register rs, int16_t off) { @@ -619,6 +748,30 @@ AssemblerMIPSShared::as_lwr(Register rd, Register rs, int16_t off) return writeInst(InstImm(op_lwr, rs, rd, Imm16(off)).encode()); } +BufferOffset +AssemblerMIPSShared::as_ll(Register rd, Register rs, int16_t off) +{ + return writeInst(InstImm(op_ll, rs, rd, Imm16(off)).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_ld(Register rd, Register rs, int16_t off) +{ + return writeInst(InstImm(op_ld, rs, rd, Imm16(off)).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_ldl(Register rd, Register rs, int16_t off) +{ + return writeInst(InstImm(op_ldl, rs, rd, Imm16(off)).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_ldr(Register rd, Register rs, int16_t off) +{ + return writeInst(InstImm(op_ldr, rs, rd, Imm16(off)).encode()); +} + BufferOffset AssemblerMIPSShared::as_sb(Register rd, Register rs, int16_t off) { @@ -649,6 +802,30 @@ AssemblerMIPSShared::as_swr(Register rd, Register rs, int16_t off) return writeInst(InstImm(op_swr, rs, rd, Imm16(off)).encode()); } +BufferOffset +AssemblerMIPSShared::as_sc(Register rd, Register rs, int16_t off) +{ + return writeInst(InstImm(op_sc, rs, rd, Imm16(off)).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_sd(Register rd, Register rs, int16_t off) +{ + return writeInst(InstImm(op_sd, rs, rd, Imm16(off)).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_sdl(Register rd, Register rs, int16_t off) +{ + return writeInst(InstImm(op_sdl, rs, rd, Imm16(off)).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_sdr(Register rd, Register rs, int16_t off) +{ + return writeInst(InstImm(op_sdr, rs, rd, Imm16(off)).encode()); +} + // Move from HI/LO register. BufferOffset AssemblerMIPSShared::as_mfhi(Register rd) @@ -725,6 +902,12 @@ AssemblerMIPSShared::as_clz(Register rd, Register rs) return writeInst(InstReg(op_special2, rs, rd, rd, ff_clz).encode()); } +BufferOffset +AssemblerMIPSShared::as_dclz(Register rd, Register rs) +{ + return writeInst(InstReg(op_special2, rs, rd, rd, ff_dclz).encode()); +} + BufferOffset AssemblerMIPSShared::as_ins(Register rt, Register rs, uint16_t pos, uint16_t size) { @@ -734,6 +917,33 @@ AssemblerMIPSShared::as_ins(Register rt, Register rs, uint16_t pos, uint16_t siz return writeInst(InstReg(op_special3, rs, rt, rd, pos, ff_ins).encode()); } +BufferOffset +AssemblerMIPSShared::as_dins(Register rt, Register rs, uint16_t pos, uint16_t size) +{ + MOZ_ASSERT(pos < 32 && size != 0 && size <= 32 && pos + size != 0 && pos + size <= 32); + Register rd; + rd = Register::FromCode(pos + size - 1); + return writeInst(InstReg(op_special3, rs, rt, rd, pos, ff_dins).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_dinsm(Register rt, Register rs, uint16_t pos, uint16_t size) +{ + MOZ_ASSERT(pos < 32 && size >= 2 && size <= 64 && pos + size > 32 && pos + size <= 64); + Register rd; + rd = Register::FromCode(pos + size - 1 - 32); + return writeInst(InstReg(op_special3, rs, rt, rd, pos, ff_dinsm).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_dinsu(Register rt, Register rs, uint16_t pos, uint16_t size) +{ + MOZ_ASSERT(pos >= 32 && pos < 64 && size >= 1 && size <= 32 && pos + size > 32 && pos + size <= 64); + Register rd; + rd = Register::FromCode(pos + size - 1 - 32); + return writeInst(InstReg(op_special3, rs, rt, rd, pos - 32, ff_dinsu).encode()); +} + BufferOffset AssemblerMIPSShared::as_ext(Register rt, Register rs, uint16_t pos, uint16_t size) { @@ -743,6 +953,46 @@ AssemblerMIPSShared::as_ext(Register rt, Register rs, uint16_t pos, uint16_t siz return writeInst(InstReg(op_special3, rs, rt, rd, pos, ff_ext).encode()); } +// Sign extend +BufferOffset +AssemblerMIPSShared::as_seb(Register rd, Register rt) +{ + return writeInst(InstReg(op_special3, zero, rt, rd, 16, ff_bshfl).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_seh(Register rd, Register rt) +{ + return writeInst(InstReg(op_special3, zero, rt, rd, 24, ff_bshfl).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_dext(Register rt, Register rs, uint16_t pos, uint16_t size) +{ + MOZ_ASSERT(pos < 32 && size != 0 && size <= 32 && pos + size != 0 && pos + size <= 63); + Register rd; + rd = Register::FromCode(size - 1); + return writeInst(InstReg(op_special3, rs, rt, rd, pos, ff_dext).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_dextm(Register rt, Register rs, uint16_t pos, uint16_t size) +{ + MOZ_ASSERT(pos < 32 && size > 32 && size <= 64 && pos + size > 32 && pos + size <= 64); + Register rd; + rd = Register::FromCode(size - 1 - 32); + return writeInst(InstReg(op_special3, rs, rt, rd, pos, ff_dextm).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_dextu(Register rt, Register rs, uint16_t pos, uint16_t size) +{ + MOZ_ASSERT(pos >= 32 && pos < 64 && size != 0 && size <= 32 && pos + size > 32 && pos + size <= 64); + Register rd; + rd = Register::FromCode(size - 1); + return writeInst(InstReg(op_special3, rs, rt, rd, pos - 32, ff_dextu).encode()); +} + // FP instructions BufferOffset AssemblerMIPSShared::as_ld(FloatRegister fd, Register base, int32_t off) @@ -796,6 +1046,30 @@ AssemblerMIPSShared::as_mfc1(Register rt, FloatRegister fs) return writeInst(InstReg(op_cop1, rs_mfc1, rt, fs).encode()); } +BufferOffset +AssemblerMIPSShared::as_mthc1(Register rt, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_mthc1, rt, fs).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_mfhc1(Register rt, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_mfhc1, rt, fs).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_dmtc1(Register rt, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_dmtc1, rt, fs).encode()); +} + +BufferOffset +AssemblerMIPSShared::as_dmfc1(Register rt, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_dmfc1, rt, fs).encode()); +} + // FP convert instructions BufferOffset AssemblerMIPSShared::as_ceilws(FloatRegister fd, FloatRegister fs) @@ -845,6 +1119,12 @@ AssemblerMIPSShared::as_truncwd(FloatRegister fd, FloatRegister fs) return writeInst(InstReg(op_cop1, rs_d, zero, fs, fd, ff_trunc_w_fmt).encode()); } +BufferOffset +AssemblerMIPSShared::as_cvtdl(FloatRegister fd, FloatRegister fs) +{ + return writeInst(InstReg(op_cop1, rs_l, zero, fs, fd, ff_cvt_d_fmt).encode()); +} + BufferOffset AssemblerMIPSShared::as_cvtds(FloatRegister fd, FloatRegister fs) { diff --git a/js/src/jit/mips-shared/Assembler-mips-shared.h b/js/src/jit/mips-shared/Assembler-mips-shared.h index 17b7c8de0c..2c41ce981a 100644 --- a/js/src/jit/mips-shared/Assembler-mips-shared.h +++ b/js/src/jit/mips-shared/Assembler-mips-shared.h @@ -30,14 +30,18 @@ static MOZ_CONSTEXPR_VAR Register a0 = { Registers::a0 }; static MOZ_CONSTEXPR_VAR Register a1 = { Registers::a1 }; static MOZ_CONSTEXPR_VAR Register a2 = { Registers::a2 }; static MOZ_CONSTEXPR_VAR Register a3 = { Registers::a3 }; +static MOZ_CONSTEXPR_VAR Register a4 = { Registers::ta0 }; +static MOZ_CONSTEXPR_VAR Register a5 = { Registers::ta1 }; +static MOZ_CONSTEXPR_VAR Register a6 = { Registers::ta2 }; +static MOZ_CONSTEXPR_VAR Register a7 = { Registers::ta3 }; static MOZ_CONSTEXPR_VAR Register t0 = { Registers::t0 }; static MOZ_CONSTEXPR_VAR Register t1 = { Registers::t1 }; static MOZ_CONSTEXPR_VAR Register t2 = { Registers::t2 }; static MOZ_CONSTEXPR_VAR Register t3 = { Registers::t3 }; -static MOZ_CONSTEXPR_VAR Register t4 = { Registers::t4 }; -static MOZ_CONSTEXPR_VAR Register t5 = { Registers::t5 }; -static MOZ_CONSTEXPR_VAR Register t6 = { Registers::t6 }; -static MOZ_CONSTEXPR_VAR Register t7 = { Registers::t7 }; +static MOZ_CONSTEXPR_VAR Register t4 = { Registers::ta0 }; +static MOZ_CONSTEXPR_VAR Register t5 = { Registers::ta1 }; +static MOZ_CONSTEXPR_VAR Register t6 = { Registers::ta2 }; +static MOZ_CONSTEXPR_VAR Register t7 = { Registers::ta3 }; static MOZ_CONSTEXPR_VAR Register s0 = { Registers::s0 }; static MOZ_CONSTEXPR_VAR Register s1 = { Registers::s1 }; static MOZ_CONSTEXPR_VAR Register s2 = { Registers::s2 }; @@ -85,10 +89,12 @@ static MOZ_CONSTEXPR_VAR Register IntArgReg0 = a0; static MOZ_CONSTEXPR_VAR Register IntArgReg1 = a1; static MOZ_CONSTEXPR_VAR Register IntArgReg2 = a2; static MOZ_CONSTEXPR_VAR Register IntArgReg3 = a3; +static MOZ_CONSTEXPR_VAR Register IntArgReg4 = a4; +static MOZ_CONSTEXPR_VAR Register IntArgReg5 = a5; +static MOZ_CONSTEXPR_VAR Register IntArgReg6 = a6; +static MOZ_CONSTEXPR_VAR Register IntArgReg7 = a7; static MOZ_CONSTEXPR_VAR Register GlobalReg = s6; // used by Odin static MOZ_CONSTEXPR_VAR Register HeapReg = s7; // used by Odin -static MOZ_CONSTEXPR_VAR Register CallTempNonArgRegs[] = CALL_TEMP_NON_ARG_REGS; -static const uint32_t NumCallTempNonArgRegs = mozilla::ArrayLength(CallTempNonArgRegs); static MOZ_CONSTEXPR_VAR Register PreBarrierReg = a1; @@ -246,6 +252,12 @@ enum Opcode { op_blezl = 22 << OpcodeShift, op_bgtzl = 23 << OpcodeShift, + op_daddi = 24 << OpcodeShift, + op_daddiu = 25 << OpcodeShift, + + op_ldl = 26 << OpcodeShift, + op_ldr = 27 << OpcodeShift, + op_special2 = 28 << OpcodeShift, op_special3 = 31 << OpcodeShift, @@ -256,17 +268,24 @@ enum Opcode { op_lbu = 36 << OpcodeShift, op_lhu = 37 << OpcodeShift, op_lwr = 38 << OpcodeShift, + op_lwu = 39 << OpcodeShift, op_sb = 40 << OpcodeShift, op_sh = 41 << OpcodeShift, op_swl = 42 << OpcodeShift, op_sw = 43 << OpcodeShift, + op_sdl = 44 << OpcodeShift, + op_sdr = 45 << OpcodeShift, op_swr = 46 << OpcodeShift, + op_ll = 48 << OpcodeShift, op_lwc1 = 49 << OpcodeShift, op_ldc1 = 53 << OpcodeShift, + op_ld = 55 << OpcodeShift, + op_sc = 56 << OpcodeShift, op_swc1 = 57 << OpcodeShift, - op_sdc1 = 61 << OpcodeShift + op_sdc1 = 61 << OpcodeShift, + op_sd = 63 << OpcodeShift, }; enum RSField { @@ -274,9 +293,11 @@ enum RSField { // cop1 encoding of RS field. rs_mfc1 = 0 << RSShift, rs_one = 1 << RSShift, + rs_dmfc1 = 1 << RSShift, rs_cfc1 = 2 << RSShift, rs_mfhc1 = 3 << RSShift, rs_mtc1 = 4 << RSShift, + rs_dmtc1 = 5 << RSShift, rs_ctc1 = 6 << RSShift, rs_mthc1 = 7 << RSShift, rs_bc1 = 8 << RSShift, @@ -315,10 +336,18 @@ enum FunctionField { ff_mfhi = 16, ff_mflo = 18, + ff_dsllv = 20, + ff_dsrlv = 22, + ff_dsrav = 23, + ff_mult = 24, ff_multu = 25, ff_div = 26, ff_divu = 27, + ff_dmult = 28, + ff_dmultu = 29, + ff_ddiv = 30, + ff_ddivu = 31, ff_add = 32, ff_addu = 33, @@ -331,6 +360,10 @@ enum FunctionField { ff_slt = 42, ff_sltu = 43, + ff_dadd = 44, + ff_daddu = 45, + ff_dsub = 46, + ff_dsubu = 47, ff_tge = 48, ff_tgeu = 49, @@ -338,15 +371,29 @@ enum FunctionField { ff_tltu = 51, ff_teq = 52, ff_tne = 54, + ff_dsll = 56, + ff_dsrl = 58, + ff_dsra = 59, + ff_dsll32 = 60, + ff_dsrl32 = 62, + ff_dsra32 = 63, // special2 encoding of function field. ff_mul = 2, ff_clz = 32, ff_clo = 33, + ff_dclz = 36, // special3 encoding of function field. ff_ext = 0, + ff_dextm = 1, + ff_dextu = 2, + ff_dext = 3, ff_ins = 4, + ff_dinsm = 5, + ff_dinsu = 6, + ff_dins = 7, + ff_bshfl = 32, // cop1 encoding of function field. ff_add_fmt = 0, @@ -811,12 +858,19 @@ class AssemblerMIPSShared : public AssemblerShared // Arithmetic instructions BufferOffset as_addu(Register rd, Register rs, Register rt); BufferOffset as_addiu(Register rd, Register rs, int32_t j); + BufferOffset as_daddu(Register rd, Register rs, Register rt); + BufferOffset as_daddiu(Register rd, Register rs, int32_t j); BufferOffset as_subu(Register rd, Register rs, Register rt); + BufferOffset as_dsubu(Register rd, Register rs, Register rt); BufferOffset as_mult(Register rs, Register rt); BufferOffset as_multu(Register rs, Register rt); + BufferOffset as_dmult(Register rs, Register rt); + BufferOffset as_dmultu(Register rs, Register rt); BufferOffset as_div(Register rs, Register rt); BufferOffset as_divu(Register rs, Register rt); BufferOffset as_mul(Register rd, Register rs, Register rt); + BufferOffset as_ddiv(Register rs, Register rt); + BufferOffset as_ddivu(Register rs, Register rt); // Logical instructions BufferOffset as_and(Register rd, Register rs, Register rt); @@ -832,13 +886,25 @@ class AssemblerMIPSShared : public AssemblerShared // Shift instructions // as_sll(zero, zero, x) instructions are reserved as nop BufferOffset as_sll(Register rd, Register rt, uint16_t sa); + BufferOffset as_dsll(Register rd, Register rt, uint16_t sa); + BufferOffset as_dsll32(Register rd, Register rt, uint16_t sa); BufferOffset as_sllv(Register rd, Register rt, Register rs); + BufferOffset as_dsllv(Register rd, Register rt, Register rs); BufferOffset as_srl(Register rd, Register rt, uint16_t sa); + BufferOffset as_dsrl(Register rd, Register rt, uint16_t sa); + BufferOffset as_dsrl32(Register rd, Register rt, uint16_t sa); BufferOffset as_srlv(Register rd, Register rt, Register rs); + BufferOffset as_dsrlv(Register rd, Register rt, Register rs); BufferOffset as_sra(Register rd, Register rt, uint16_t sa); + BufferOffset as_dsra(Register rd, Register rt, uint16_t sa); + BufferOffset as_dsra32(Register rd, Register rt, uint16_t sa); BufferOffset as_srav(Register rd, Register rt, Register rs); BufferOffset as_rotr(Register rd, Register rt, uint16_t sa); BufferOffset as_rotrv(Register rd, Register rt, Register rs); + BufferOffset as_dsrav(Register rd, Register rt, Register rs); + BufferOffset as_drotr(Register rd, Register rt, uint16_t sa); + BufferOffset as_drotr32(Register rd, Register rt, uint16_t sa); + BufferOffset as_drotrv(Register rd, Register rt, Register rs); // Load and store instructions BufferOffset as_lb(Register rd, Register rs, int16_t off); @@ -846,13 +912,22 @@ class AssemblerMIPSShared : public AssemblerShared BufferOffset as_lh(Register rd, Register rs, int16_t off); BufferOffset as_lhu(Register rd, Register rs, int16_t off); BufferOffset as_lw(Register rd, Register rs, int16_t off); + BufferOffset as_lwu(Register rd, Register rs, int16_t off); BufferOffset as_lwl(Register rd, Register rs, int16_t off); BufferOffset as_lwr(Register rd, Register rs, int16_t off); + BufferOffset as_ll(Register rd, Register rs, int16_t off); + BufferOffset as_ld(Register rd, Register rs, int16_t off); + BufferOffset as_ldl(Register rd, Register rs, int16_t off); + BufferOffset as_ldr(Register rd, Register rs, int16_t off); BufferOffset as_sb(Register rd, Register rs, int16_t off); BufferOffset as_sh(Register rd, Register rs, int16_t off); BufferOffset as_sw(Register rd, Register rs, int16_t off); BufferOffset as_swl(Register rd, Register rs, int16_t off); BufferOffset as_swr(Register rd, Register rs, int16_t off); + BufferOffset as_sc(Register rd, Register rs, int16_t off); + BufferOffset as_sd(Register rd, Register rs, int16_t off); + BufferOffset as_sdl(Register rd, Register rs, int16_t off); + BufferOffset as_sdr(Register rd, Register rs, int16_t off); // Move from HI/LO register. BufferOffset as_mfhi(Register rd); @@ -872,8 +947,19 @@ class AssemblerMIPSShared : public AssemblerShared // Bit twiddling. BufferOffset as_clz(Register rd, Register rs); + BufferOffset as_dclz(Register rd, Register rs); BufferOffset as_ins(Register rt, Register rs, uint16_t pos, uint16_t size); + BufferOffset as_dins(Register rt, Register rs, uint16_t pos, uint16_t size); + BufferOffset as_dinsm(Register rt, Register rs, uint16_t pos, uint16_t size); + BufferOffset as_dinsu(Register rt, Register rs, uint16_t pos, uint16_t size); BufferOffset as_ext(Register rt, Register rs, uint16_t pos, uint16_t size); + BufferOffset as_dext(Register rt, Register rs, uint16_t pos, uint16_t size); + BufferOffset as_dextm(Register rt, Register rs, uint16_t pos, uint16_t size); + BufferOffset as_dextu(Register rt, Register rs, uint16_t pos, uint16_t size); + + // Sign extend + BufferOffset as_seb(Register rd, Register rt); + BufferOffset as_seh(Register rd, Register rt); // FP instructions @@ -891,6 +977,10 @@ class AssemblerMIPSShared : public AssemblerShared BufferOffset as_mtc1(Register rt, FloatRegister fs); BufferOffset as_mfc1(Register rt, FloatRegister fs); + BufferOffset as_mthc1(Register rt, FloatRegister fs); + BufferOffset as_mfhc1(Register rt, FloatRegister fs); + BufferOffset as_dmtc1(Register rt, FloatRegister fs); + BufferOffset as_dmfc1(Register rt, FloatRegister fs); public: // FP convert instructions @@ -970,7 +1060,8 @@ class AssemblerMIPSShared : public AssemblerShared public: static bool SupportsFloatingPoint() { -#if (defined(__mips_hard_float) && !defined(__mips_single_float)) || defined(JS_SIMULATOR_MIPS32) +#if (defined(__mips_hard_float) && !defined(__mips_single_float)) || \ + defined(JS_SIMULATOR_MIPS32) || defined(JS_SIMULATOR_MIPS64) return true; #else return false; @@ -1234,42 +1325,6 @@ class InstJump : public Instruction } }; -static const uint32_t NumIntArgRegs = NUM_INT_ARG_REGS; - -static inline bool -GetIntArgReg(uint32_t usedArgSlots, Register* out) -{ - if (usedArgSlots < NumIntArgRegs) { - *out = Register::FromCode(a0.code() + usedArgSlots); - return true; - } - return false; -} - -// Get a register in which we plan to put a quantity that will be used as an -// integer argument. This differs from GetIntArgReg in that if we have no more -// actual argument registers to use we will fall back on using whatever -// CallTempReg* don't overlap the argument registers, and only fail once those -// run out too. -static inline bool -GetTempRegForIntArg(uint32_t usedIntArgs, uint32_t usedFloatArgs, Register* out) -{ - // NOTE: We can't properly determine which regs are used if there are - // float arguments. If this is needed, we will have to guess. - MOZ_ASSERT(usedFloatArgs == 0); - - if (GetIntArgReg(usedIntArgs, out)) - return true; - // Unfortunately, we have to assume things about the point at which - // GetIntArgReg returns false, because we need to know how many registers it - // can allocate. - usedIntArgs -= NumIntArgRegs; - if (usedIntArgs >= NumCallTempNonArgRegs) - return false; - *out = CallTempNonArgRegs[usedIntArgs]; - return true; -} - } // namespace jit } // namespace js diff --git a/js/src/jit/mips-shared/Bailouts-mips-shared.cpp b/js/src/jit/mips-shared/Bailouts-mips-shared.cpp new file mode 100644 index 0000000000..16042181b2 --- /dev/null +++ b/js/src/jit/mips-shared/Bailouts-mips-shared.cpp @@ -0,0 +1,27 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jit/mips-shared/Bailouts-mips-shared.h" + +#include "jscntxt.h" +#include "jscompartment.h" + +using namespace js; +using namespace js::jit; + +BailoutFrameInfo::BailoutFrameInfo(const JitActivationIterator& activations, + InvalidationBailoutStack* bailout) + : machine_(bailout->machine()) +{ + framePointer_ = (uint8_t*) bailout->fp(); + topFrameSize_ = framePointer_ - bailout->sp(); + topIonScript_ = bailout->ionScript(); + attachOnJitActivation(activations); + + uint8_t* returnAddressToFp_ = bailout->osiPointReturnAddress(); + const OsiIndex* osiIndex = topIonScript_->getOsiIndex(returnAddressToFp_); + snapshotOffset_ = osiIndex->snapshotOffset(); +} diff --git a/js/src/jit/mips32/Bailouts-mips32.h b/js/src/jit/mips-shared/Bailouts-mips-shared.h similarity index 93% rename from js/src/jit/mips32/Bailouts-mips32.h rename to js/src/jit/mips-shared/Bailouts-mips-shared.h index 0c4d7f3132..575d13def2 100644 --- a/js/src/jit/mips32/Bailouts-mips32.h +++ b/js/src/jit/mips-shared/Bailouts-mips-shared.h @@ -4,8 +4,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef jit_mips32_Bailouts_mips32_h -#define jit_mips32_Bailouts_mips32_h +#ifndef jit_mips_shared_Bailouts_mips_shared_h +#define jit_mips_shared_Bailouts_mips_shared_h #include "jit/Bailouts.h" #include "jit/JitCompartment.h" @@ -74,4 +74,4 @@ class BailoutStack } // namespace jit } // namespace js -#endif /* jit_mips32_Bailouts_mips32_h */ +#endif /* jit_mips_shared_Bailouts_mips_shared_h */ diff --git a/js/src/jit/mips-shared/BaselineCompiler-mips-shared.cpp b/js/src/jit/mips-shared/BaselineCompiler-mips-shared.cpp new file mode 100644 index 0000000000..b8d8017a2f --- /dev/null +++ b/js/src/jit/mips-shared/BaselineCompiler-mips-shared.cpp @@ -0,0 +1,16 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jit/mips-shared/BaselineCompiler-mips-shared.h" + +using namespace js; +using namespace js::jit; + +BaselineCompilerMIPSShared::BaselineCompilerMIPSShared(JSContext* cx, TempAllocator& alloc, + JSScript* script) + : BaselineCompilerShared(cx, alloc, script) +{ +} diff --git a/js/src/jit/mips-shared/BaselineCompiler-mips-shared.h b/js/src/jit/mips-shared/BaselineCompiler-mips-shared.h new file mode 100644 index 0000000000..43f32f9978 --- /dev/null +++ b/js/src/jit/mips-shared/BaselineCompiler-mips-shared.h @@ -0,0 +1,24 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef jit_mips_shared_BaselineCompiler_mips_shared_h +#define jit_mips_shared_BaselineCompiler_mips_shared_h + +#include "jit/shared/BaselineCompiler-shared.h" + +namespace js { +namespace jit { + +class BaselineCompilerMIPSShared : public BaselineCompilerShared +{ + protected: + BaselineCompilerMIPSShared(JSContext* cx, TempAllocator& alloc, JSScript* script); +}; + +} // namespace jit +} // namespace js + +#endif /* jit_mips_shared_BaselineCompiler_mips_shared_h */ diff --git a/js/src/jit/mips-shared/BaselineIC-mips-shared.cpp b/js/src/jit/mips-shared/BaselineIC-mips-shared.cpp new file mode 100644 index 0000000000..dc4fcab1ab --- /dev/null +++ b/js/src/jit/mips-shared/BaselineIC-mips-shared.cpp @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jit/BaselineIC.h" +#include "jit/SharedICHelpers.h" + +using namespace js; +using namespace js::jit; + +namespace js { +namespace jit { + +bool +ICCompare_Double::Compiler::generateStubCode(MacroAssembler& masm) +{ + Label failure, isNaN; + masm.ensureDouble(R0, FloatReg0, &failure); + masm.ensureDouble(R1, FloatReg1, &failure); + + Register dest = R0.scratchReg(); + + Assembler::DoubleCondition doubleCond = JSOpToDoubleCondition(op); + + masm.ma_cmp_set_double(dest, FloatReg0, FloatReg1, doubleCond); + + masm.tagValue(JSVAL_TYPE_BOOLEAN, dest, R0); + EmitReturnFromIC(masm); + + // Failure case - jump to next stub + masm.bind(&failure); + EmitStubGuardFailure(masm); + return true; +} + +} // namespace jit +} // namespace js diff --git a/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp b/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp new file mode 100644 index 0000000000..89e37fb2b3 --- /dev/null +++ b/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp @@ -0,0 +1,1890 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jit/mips-shared/CodeGenerator-mips-shared.h" + +#include "mozilla/MathAlgorithms.h" + +#include "jscntxt.h" +#include "jscompartment.h" +#include "jsnum.h" + +#include "jit/CodeGenerator.h" +#include "jit/JitCompartment.h" +#include "jit/JitFrames.h" +#include "jit/MIR.h" +#include "jit/MIRGraph.h" +#include "js/Conversions.h" +#include "vm/Shape.h" +#include "vm/TraceLogging.h" + +#include "jsscriptinlines.h" + +#include "jit/MacroAssembler-inl.h" +#include "jit/shared/CodeGenerator-shared-inl.h" + +using namespace js; +using namespace js::jit; + +using mozilla::FloorLog2; +using mozilla::NegativeInfinity; +using JS::GenericNaN; +using JS::ToInt32; + +// inline +Address +CodeGeneratorMIPSShared::ToAddress(const LAllocation& a) +{ + MOZ_ASSERT(a.isMemory()); + int32_t offset = ToStackOffset(&a); + + return Address(StackPointer, offset); +} + +// inline +Address +CodeGeneratorMIPSShared::ToAddress(const LAllocation* a) +{ + return ToAddress(*a); +} + + +// shared +CodeGeneratorMIPSShared::CodeGeneratorMIPSShared(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm) + : CodeGeneratorShared(gen, graph, masm) +{ +} + +void +CodeGeneratorMIPSShared::branchToBlock(Assembler::FloatFormat fmt, FloatRegister lhs, FloatRegister rhs, + MBasicBlock* mir, Assembler::DoubleCondition cond) +{ + // Skip past trivial blocks. + mir = skipTrivialBlocks(mir); + + Label* label = mir->lir()->label(); + if (Label* oolEntry = labelForBackedgeWithImplicitCheck(mir)) { + // Note: the backedge is initially a jump to the next instruction. + // It will be patched to the target block's label during link(). + RepatchLabel rejoin; + + CodeOffsetJump backedge; + Label skip; + if (fmt == Assembler::DoubleFloat) + masm.ma_bc1d(lhs, rhs, &skip, Assembler::InvertCondition(cond), ShortJump); + else + masm.ma_bc1s(lhs, rhs, &skip, Assembler::InvertCondition(cond), ShortJump); + + backedge = masm.backedgeJump(&rejoin); + masm.bind(&rejoin); + masm.bind(&skip); + + if (!patchableBackedges_.append(PatchableBackedgeInfo(backedge, label, oolEntry))) + MOZ_CRASH(); + } else { + if (fmt == Assembler::DoubleFloat) + masm.branchDouble(cond, lhs, rhs, mir->lir()->label()); + else + masm.branchFloat(cond, lhs, rhs, mir->lir()->label()); + } +} + +void +OutOfLineBailout::accept(CodeGeneratorMIPSShared* codegen) +{ + codegen->visitOutOfLineBailout(this); +} + +void +CodeGeneratorMIPSShared::visitTestIAndBranch(LTestIAndBranch* test) +{ + const LAllocation* opd = test->getOperand(0); + MBasicBlock* ifTrue = test->ifTrue(); + MBasicBlock* ifFalse = test->ifFalse(); + + emitBranch(ToRegister(opd), Imm32(0), Assembler::NonZero, ifTrue, ifFalse); +} + +void +CodeGeneratorMIPSShared::visitCompare(LCompare* comp) +{ + Assembler::Condition cond = JSOpToCondition(comp->mir()->compareType(), comp->jsop()); + const LAllocation* left = comp->getOperand(0); + const LAllocation* right = comp->getOperand(1); + const LDefinition* def = comp->getDef(0); + + if (right->isConstant()) + masm.cmp32Set(cond, ToRegister(left), Imm32(ToInt32(right)), ToRegister(def)); + else if (right->isGeneralReg()) + masm.cmp32Set(cond, ToRegister(left), ToRegister(right), ToRegister(def)); + else + masm.cmp32Set(cond, ToRegister(left), ToAddress(right), ToRegister(def)); +} + +void +CodeGeneratorMIPSShared::visitCompareAndBranch(LCompareAndBranch* comp) +{ + Assembler::Condition cond = JSOpToCondition(comp->cmpMir()->compareType(), comp->jsop()); + if (comp->right()->isConstant()) { + emitBranch(ToRegister(comp->left()), Imm32(ToInt32(comp->right())), cond, + comp->ifTrue(), comp->ifFalse()); + } else if (comp->right()->isGeneralReg()) { + emitBranch(ToRegister(comp->left()), ToRegister(comp->right()), cond, + comp->ifTrue(), comp->ifFalse()); + } else { + masm.load32(ToAddress(comp->right()), ScratchRegister); + emitBranch(ToRegister(comp->left()), ScratchRegister, cond, + comp->ifTrue(), comp->ifFalse()); + } +} + +bool +CodeGeneratorMIPSShared::generateOutOfLineCode() +{ + if (!CodeGeneratorShared::generateOutOfLineCode()) + return false; + + if (deoptLabel_.used()) { + // All non-table-based bailouts will go here. + masm.bind(&deoptLabel_); + + // Push the frame size, so the handler can recover the IonScript. + // Frame size is stored in 'ra' and pushed by GenerateBailoutThunk + // We have to use 'ra' because generateBailoutTable will implicitly do + // the same. + masm.move32(Imm32(frameSize()), ra); + + JitCode* handler = gen->jitRuntime()->getGenericBailoutHandler(); + + masm.branch(handler); + } + + return true; +} + +void +CodeGeneratorMIPSShared::bailoutFrom(Label* label, LSnapshot* snapshot) +{ + if (masm.bailed()) + return; + + MOZ_ASSERT(label->used()); + MOZ_ASSERT(!label->bound()); + + encode(snapshot); + + // Though the assembler doesn't track all frame pushes, at least make sure + // the known value makes sense. We can't use bailout tables if the stack + // isn't properly aligned to the static frame size. + MOZ_ASSERT_IF(frameClass_ != FrameSizeClass::None(), + frameClass_.frameSize() == masm.framePushed()); + + // We don't use table bailouts because retargeting is easier this way. + InlineScriptTree* tree = snapshot->mir()->block()->trackedTree(); + OutOfLineBailout* ool = new(alloc()) OutOfLineBailout(snapshot, masm.framePushed()); + addOutOfLineCode(ool, new(alloc()) BytecodeSite(tree, tree->script()->code())); + + masm.retarget(label, ool->entry()); +} + +void +CodeGeneratorMIPSShared::bailout(LSnapshot* snapshot) +{ + Label label; + masm.jump(&label); + bailoutFrom(&label, snapshot); +} + +void +CodeGeneratorMIPSShared::visitOutOfLineBailout(OutOfLineBailout* ool) +{ + // Push snapshotOffset and make sure stack is aligned. + masm.subPtr(Imm32(2 * sizeof(void*)), StackPointer); + masm.storePtr(ImmWord(ool->snapshot()->snapshotOffset()), Address(StackPointer, 0)); + + masm.jump(&deoptLabel_); +} + +void +CodeGeneratorMIPSShared::visitMinMaxD(LMinMaxD* ins) +{ + FloatRegister first = ToFloatRegister(ins->first()); + FloatRegister second = ToFloatRegister(ins->second()); + FloatRegister output = ToFloatRegister(ins->output()); + + MOZ_ASSERT(first == output); + + Assembler::DoubleCondition cond = ins->mir()->isMax() + ? Assembler::DoubleLessThanOrEqual + : Assembler::DoubleGreaterThanOrEqual; + Label nan, equal, returnSecond, done; + + // First or second is NaN, result is NaN. + masm.ma_bc1d(first, second, &nan, Assembler::DoubleUnordered, ShortJump); + // Make sure we handle -0 and 0 right. + masm.ma_bc1d(first, second, &equal, Assembler::DoubleEqual, ShortJump); + masm.ma_bc1d(first, second, &returnSecond, cond, ShortJump); + masm.ma_b(&done, ShortJump); + + // Check for zero. + masm.bind(&equal); + masm.loadConstantDouble(0.0, ScratchDoubleReg); + // First wasn't 0 or -0, so just return it. + masm.ma_bc1d(first, ScratchDoubleReg, &done, Assembler::DoubleNotEqualOrUnordered, ShortJump); + + // So now both operands are either -0 or 0. + if (ins->mir()->isMax()) { + // -0 + -0 = -0 and -0 + 0 = 0. + masm.addDouble(second, first); + } else { + masm.negateDouble(first); + masm.subDouble(second, first); + masm.negateDouble(first); + } + masm.ma_b(&done, ShortJump); + + masm.bind(&nan); + masm.loadConstantDouble(GenericNaN(), output); + masm.ma_b(&done, ShortJump); + + masm.bind(&returnSecond); + masm.moveDouble(second, output); + + masm.bind(&done); +} + +void +CodeGeneratorMIPSShared::visitMinMaxF(LMinMaxF* ins) +{ + FloatRegister first = ToFloatRegister(ins->first()); + FloatRegister second = ToFloatRegister(ins->second()); + FloatRegister output = ToFloatRegister(ins->output()); + + MOZ_ASSERT(first == output); + + Assembler::DoubleCondition cond = ins->mir()->isMax() + ? Assembler::DoubleLessThanOrEqual + : Assembler::DoubleGreaterThanOrEqual; + Label nan, equal, returnSecond, done; + + // First or second is NaN, result is NaN. + masm.ma_bc1s(first, second, &nan, Assembler::DoubleUnordered, ShortJump); + // Make sure we handle -0 and 0 right. + masm.ma_bc1s(first, second, &equal, Assembler::DoubleEqual, ShortJump); + masm.ma_bc1s(first, second, &returnSecond, cond, ShortJump); + masm.ma_b(&done, ShortJump); + + // Check for zero. + masm.bind(&equal); + masm.loadConstantFloat32(0.0f, ScratchFloat32Reg); + // First wasn't 0 or -0, so just return it. + masm.ma_bc1s(first, ScratchFloat32Reg, &done, Assembler::DoubleNotEqualOrUnordered, ShortJump); + + // So now both operands are either -0 or 0. + if (ins->mir()->isMax()) { + // -0 + -0 = -0 and -0 + 0 = 0. + masm.as_adds(first, first, second); + } else { + masm.as_negs(first, first); + masm.as_subs(first, first, second); + masm.as_negs(first, first); + } + masm.ma_b(&done, ShortJump); + + masm.bind(&nan); + masm.loadConstantFloat32(GenericNaN(), output); + masm.ma_b(&done, ShortJump); + masm.bind(&returnSecond); + masm.as_movs(output, second); + + masm.bind(&done); +} + +void +CodeGeneratorMIPSShared::visitAbsD(LAbsD* ins) +{ + FloatRegister input = ToFloatRegister(ins->input()); + MOZ_ASSERT(input == ToFloatRegister(ins->output())); + masm.as_absd(input, input); +} + +void +CodeGeneratorMIPSShared::visitAbsF(LAbsF* ins) +{ + FloatRegister input = ToFloatRegister(ins->input()); + MOZ_ASSERT(input == ToFloatRegister(ins->output())); + masm.as_abss(input, input); +} + +void +CodeGeneratorMIPSShared::visitSqrtD(LSqrtD* ins) +{ + FloatRegister input = ToFloatRegister(ins->input()); + FloatRegister output = ToFloatRegister(ins->output()); + masm.as_sqrtd(output, input); +} + +void +CodeGeneratorMIPSShared::visitSqrtF(LSqrtF* ins) +{ + FloatRegister input = ToFloatRegister(ins->input()); + FloatRegister output = ToFloatRegister(ins->output()); + masm.as_sqrts(output, input); +} + +void +CodeGeneratorMIPSShared::visitAddI(LAddI* ins) +{ + const LAllocation* lhs = ins->getOperand(0); + const LAllocation* rhs = ins->getOperand(1); + const LDefinition* dest = ins->getDef(0); + + MOZ_ASSERT(rhs->isConstant() || rhs->isGeneralReg()); + + // If there is no snapshot, we don't need to check for overflow + if (!ins->snapshot()) { + if (rhs->isConstant()) + masm.ma_addu(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs))); + else + masm.as_addu(ToRegister(dest), ToRegister(lhs), ToRegister(rhs)); + return; + } + + Label overflow; + if (rhs->isConstant()) + masm.ma_addTestOverflow(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs)), &overflow); + else + masm.ma_addTestOverflow(ToRegister(dest), ToRegister(lhs), ToRegister(rhs), &overflow); + + bailoutFrom(&overflow, ins->snapshot()); +} + +void +CodeGeneratorMIPSShared::visitSubI(LSubI* ins) +{ + const LAllocation* lhs = ins->getOperand(0); + const LAllocation* rhs = ins->getOperand(1); + const LDefinition* dest = ins->getDef(0); + + MOZ_ASSERT(rhs->isConstant() || rhs->isGeneralReg()); + + // If there is no snapshot, we don't need to check for overflow + if (!ins->snapshot()) { + if (rhs->isConstant()) + masm.ma_subu(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs))); + else + masm.as_subu(ToRegister(dest), ToRegister(lhs), ToRegister(rhs)); + return; + } + + Label overflow; + if (rhs->isConstant()) + masm.ma_subTestOverflow(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs)), &overflow); + else + masm.ma_subTestOverflow(ToRegister(dest), ToRegister(lhs), ToRegister(rhs), &overflow); + + bailoutFrom(&overflow, ins->snapshot()); +} + +void +CodeGeneratorMIPSShared::visitMulI(LMulI* ins) +{ + const LAllocation* lhs = ins->lhs(); + const LAllocation* rhs = ins->rhs(); + Register dest = ToRegister(ins->output()); + MMul* mul = ins->mir(); + + MOZ_ASSERT_IF(mul->mode() == MMul::Integer, !mul->canBeNegativeZero() && !mul->canOverflow()); + + if (rhs->isConstant()) { + int32_t constant = ToInt32(rhs); + Register src = ToRegister(lhs); + + // Bailout on -0.0 + if (mul->canBeNegativeZero() && constant <= 0) { + Assembler::Condition cond = (constant == 0) ? Assembler::LessThan : Assembler::Equal; + bailoutCmp32(cond, src, Imm32(0), ins->snapshot()); + } + + switch (constant) { + case -1: + if (mul->canOverflow()) + bailoutCmp32(Assembler::Equal, src, Imm32(INT32_MIN), ins->snapshot()); + + masm.ma_negu(dest, src); + break; + case 0: + masm.move32(Imm32(0), dest); + break; + case 1: + masm.move32(src, dest); + break; + case 2: + if (mul->canOverflow()) { + Label mulTwoOverflow; + masm.ma_addTestOverflow(dest, src, src, &mulTwoOverflow); + + bailoutFrom(&mulTwoOverflow, ins->snapshot()); + } else { + masm.as_addu(dest, src, src); + } + break; + default: + uint32_t shift = FloorLog2(constant); + + if (!mul->canOverflow() && (constant > 0)) { + // If it cannot overflow, we can do lots of optimizations. + uint32_t rest = constant - (1 << shift); + + // See if the constant has one bit set, meaning it can be + // encoded as a bitshift. + if ((1 << shift) == constant) { + masm.ma_sll(dest, src, Imm32(shift)); + return; + } + + // If the constant cannot be encoded as (1<canOverflow() && (constant > 0) && (src != dest)) { + // To stay on the safe side, only optimize things that are a + // power of 2. + + if ((1 << shift) == constant) { + // dest = lhs * pow(2, shift) + masm.ma_sll(dest, src, Imm32(shift)); + // At runtime, check (lhs == dest >> shift), if this does + // not hold, some bits were lost due to overflow, and the + // computation should be resumed as a double. + masm.ma_sra(ScratchRegister, dest, Imm32(shift)); + bailoutCmp32(Assembler::NotEqual, src, ScratchRegister, ins->snapshot()); + return; + } + } + + if (mul->canOverflow()) { + Label mulConstOverflow; + masm.ma_mul_branch_overflow(dest, ToRegister(lhs), Imm32(ToInt32(rhs)), + &mulConstOverflow); + + bailoutFrom(&mulConstOverflow, ins->snapshot()); + } else { + masm.ma_mult(src, Imm32(ToInt32(rhs))); + masm.as_mflo(dest); + } + break; + } + } else { + Label multRegOverflow; + + if (mul->canOverflow()) { + masm.ma_mul_branch_overflow(dest, ToRegister(lhs), ToRegister(rhs), &multRegOverflow); + bailoutFrom(&multRegOverflow, ins->snapshot()); + } else { + masm.as_mult(ToRegister(lhs), ToRegister(rhs)); + masm.as_mflo(dest); + } + + if (mul->canBeNegativeZero()) { + Label done; + masm.ma_b(dest, dest, &done, Assembler::NonZero, ShortJump); + + // Result is -0 if lhs or rhs is negative. + // In that case result must be double value so bailout + Register scratch = SecondScratchReg; + masm.as_or(scratch, ToRegister(lhs), ToRegister(rhs)); + bailoutCmp32(Assembler::Signed, scratch, scratch, ins->snapshot()); + + masm.bind(&done); + } + } +} + +void +CodeGeneratorMIPSShared::visitDivI(LDivI* ins) +{ + // Extract the registers from this instruction + Register lhs = ToRegister(ins->lhs()); + Register rhs = ToRegister(ins->rhs()); + Register dest = ToRegister(ins->output()); + Register temp = ToRegister(ins->getTemp(0)); + MDiv* mir = ins->mir(); + + Label done; + + // Handle divide by zero. + if (mir->canBeDivideByZero()) { + if (mir->canTruncateInfinities()) { + // Truncated division by zero is zero (Infinity|0 == 0) + Label notzero; + masm.ma_b(rhs, rhs, ¬zero, Assembler::NonZero, ShortJump); + masm.move32(Imm32(0), dest); + masm.ma_b(&done, ShortJump); + masm.bind(¬zero); + } else { + MOZ_ASSERT(mir->fallible()); + bailoutCmp32(Assembler::Zero, rhs, rhs, ins->snapshot()); + } + } + + // Handle an integer overflow exception from -2147483648 / -1. + if (mir->canBeNegativeOverflow()) { + Label notMinInt; + masm.move32(Imm32(INT32_MIN), temp); + masm.ma_b(lhs, temp, ¬MinInt, Assembler::NotEqual, ShortJump); + + masm.move32(Imm32(-1), temp); + if (mir->canTruncateOverflow()) { + // (-INT32_MIN)|0 == INT32_MIN + Label skip; + masm.ma_b(rhs, temp, &skip, Assembler::NotEqual, ShortJump); + masm.move32(Imm32(INT32_MIN), dest); + masm.ma_b(&done, ShortJump); + masm.bind(&skip); + } else { + MOZ_ASSERT(mir->fallible()); + bailoutCmp32(Assembler::Equal, rhs, temp, ins->snapshot()); + } + masm.bind(¬MinInt); + } + + // Handle negative 0. (0/-Y) + if (!mir->canTruncateNegativeZero() && mir->canBeNegativeZero()) { + Label nonzero; + masm.ma_b(lhs, lhs, &nonzero, Assembler::NonZero, ShortJump); + bailoutCmp32(Assembler::LessThan, rhs, Imm32(0), ins->snapshot()); + masm.bind(&nonzero); + } + // Note: above safety checks could not be verified as Ion seems to be + // smarter and requires double arithmetic in such cases. + + // All regular. Lets call div. + if (mir->canTruncateRemainder()) { + masm.as_div(lhs, rhs); + masm.as_mflo(dest); + } else { + MOZ_ASSERT(mir->fallible()); + + Label remainderNonZero; + masm.ma_div_branch_overflow(dest, lhs, rhs, &remainderNonZero); + bailoutFrom(&remainderNonZero, ins->snapshot()); + } + + masm.bind(&done); +} + +void +CodeGeneratorMIPSShared::visitDivPowTwoI(LDivPowTwoI* ins) +{ + Register lhs = ToRegister(ins->numerator()); + Register dest = ToRegister(ins->output()); + Register tmp = ToRegister(ins->getTemp(0)); + int32_t shift = ins->shift(); + + if (shift != 0) { + MDiv* mir = ins->mir(); + if (!mir->isTruncated()) { + // If the remainder is going to be != 0, bailout since this must + // be a double. + masm.ma_sll(tmp, lhs, Imm32(32 - shift)); + bailoutCmp32(Assembler::NonZero, tmp, tmp, ins->snapshot()); + } + + if (!mir->canBeNegativeDividend()) { + // Numerator is unsigned, so needs no adjusting. Do the shift. + masm.ma_sra(dest, lhs, Imm32(shift)); + return; + } + + // Adjust the value so that shifting produces a correctly rounded result + // when the numerator is negative. See 10-1 "Signed Division by a Known + // Power of 2" in Henry S. Warren, Jr.'s Hacker's Delight. + if (shift > 1) { + masm.ma_sra(tmp, lhs, Imm32(31)); + masm.ma_srl(tmp, tmp, Imm32(32 - shift)); + masm.add32(lhs, tmp); + } else { + masm.ma_srl(tmp, lhs, Imm32(32 - shift)); + masm.add32(lhs, tmp); + } + + // Do the shift. + masm.ma_sra(dest, tmp, Imm32(shift)); + } else { + masm.move32(lhs, dest); + } +} + +void +CodeGeneratorMIPSShared::visitModI(LModI* ins) +{ + // Extract the registers from this instruction + Register lhs = ToRegister(ins->lhs()); + Register rhs = ToRegister(ins->rhs()); + Register dest = ToRegister(ins->output()); + Register callTemp = ToRegister(ins->callTemp()); + MMod* mir = ins->mir(); + Label done, prevent; + + masm.move32(lhs, callTemp); + + // Prevent INT_MIN % -1; + // The integer division will give INT_MIN, but we want -(double)INT_MIN. + if (mir->canBeNegativeDividend()) { + masm.ma_b(lhs, Imm32(INT_MIN), &prevent, Assembler::NotEqual, ShortJump); + if (mir->isTruncated()) { + // (INT_MIN % -1)|0 == 0 + Label skip; + masm.ma_b(rhs, Imm32(-1), &skip, Assembler::NotEqual, ShortJump); + masm.move32(Imm32(0), dest); + masm.ma_b(&done, ShortJump); + masm.bind(&skip); + } else { + MOZ_ASSERT(mir->fallible()); + bailoutCmp32(Assembler::Equal, rhs, Imm32(-1), ins->snapshot()); + } + masm.bind(&prevent); + } + + // 0/X (with X < 0) is bad because both of these values *should* be + // doubles, and the result should be -0.0, which cannot be represented in + // integers. X/0 is bad because it will give garbage (or abort), when it + // should give either \infty, -\infty or NAN. + + // Prevent 0 / X (with X < 0) and X / 0 + // testing X / Y. Compare Y with 0. + // There are three cases: (Y < 0), (Y == 0) and (Y > 0) + // If (Y < 0), then we compare X with 0, and bail if X == 0 + // If (Y == 0), then we simply want to bail. + // if (Y > 0), we don't bail. + + if (mir->canBeDivideByZero()) { + if (mir->isTruncated()) { + Label skip; + masm.ma_b(rhs, Imm32(0), &skip, Assembler::NotEqual, ShortJump); + masm.move32(Imm32(0), dest); + masm.ma_b(&done, ShortJump); + masm.bind(&skip); + } else { + MOZ_ASSERT(mir->fallible()); + bailoutCmp32(Assembler::Equal, rhs, Imm32(0), ins->snapshot()); + } + } + + if (mir->canBeNegativeDividend()) { + Label notNegative; + masm.ma_b(rhs, Imm32(0), ¬Negative, Assembler::GreaterThan, ShortJump); + if (mir->isTruncated()) { + // NaN|0 == 0 and (0 % -X)|0 == 0 + Label skip; + masm.ma_b(lhs, Imm32(0), &skip, Assembler::NotEqual, ShortJump); + masm.move32(Imm32(0), dest); + masm.ma_b(&done, ShortJump); + masm.bind(&skip); + } else { + MOZ_ASSERT(mir->fallible()); + bailoutCmp32(Assembler::Equal, lhs, Imm32(0), ins->snapshot()); + } + masm.bind(¬Negative); + } + + masm.as_div(lhs, rhs); + masm.as_mfhi(dest); + + // If X%Y == 0 and X < 0, then we *actually* wanted to return -0.0 + if (mir->canBeNegativeDividend()) { + if (mir->isTruncated()) { + // -0.0|0 == 0 + } else { + MOZ_ASSERT(mir->fallible()); + // See if X < 0 + masm.ma_b(dest, Imm32(0), &done, Assembler::NotEqual, ShortJump); + bailoutCmp32(Assembler::Signed, callTemp, Imm32(0), ins->snapshot()); + } + } + masm.bind(&done); +} + +void +CodeGeneratorMIPSShared::visitModPowTwoI(LModPowTwoI* ins) +{ + Register in = ToRegister(ins->getOperand(0)); + Register out = ToRegister(ins->getDef(0)); + MMod* mir = ins->mir(); + Label negative, done; + + masm.move32(in, out); + masm.ma_b(in, in, &done, Assembler::Zero, ShortJump); + // Switch based on sign of the lhs. + // Positive numbers are just a bitmask + masm.ma_b(in, in, &negative, Assembler::Signed, ShortJump); + { + masm.and32(Imm32((1 << ins->shift()) - 1), out); + masm.ma_b(&done, ShortJump); + } + + // Negative numbers need a negate, bitmask, negate + { + masm.bind(&negative); + masm.neg32(out); + masm.and32(Imm32((1 << ins->shift()) - 1), out); + masm.neg32(out); + } + if (mir->canBeNegativeDividend()) { + if (!mir->isTruncated()) { + MOZ_ASSERT(mir->fallible()); + bailoutCmp32(Assembler::Equal, out, zero, ins->snapshot()); + } else { + // -0|0 == 0 + } + } + masm.bind(&done); +} + +void +CodeGeneratorMIPSShared::visitModMaskI(LModMaskI* ins) +{ + Register src = ToRegister(ins->getOperand(0)); + Register dest = ToRegister(ins->getDef(0)); + Register tmp0 = ToRegister(ins->getTemp(0)); + Register tmp1 = ToRegister(ins->getTemp(1)); + MMod* mir = ins->mir(); + + if (!mir->isTruncated() && mir->canBeNegativeDividend()) { + MOZ_ASSERT(mir->fallible()); + + Label bail; + masm.ma_mod_mask(src, dest, tmp0, tmp1, ins->shift(), &bail); + bailoutFrom(&bail, ins->snapshot()); + } else { + masm.ma_mod_mask(src, dest, tmp0, tmp1, ins->shift(), nullptr); + } +} + +void +CodeGeneratorMIPSShared::visitBitNotI(LBitNotI* ins) +{ + const LAllocation* input = ins->getOperand(0); + const LDefinition* dest = ins->getDef(0); + MOZ_ASSERT(!input->isConstant()); + + masm.ma_not(ToRegister(dest), ToRegister(input)); +} + +void +CodeGeneratorMIPSShared::visitBitOpI(LBitOpI* ins) +{ + const LAllocation* lhs = ins->getOperand(0); + const LAllocation* rhs = ins->getOperand(1); + const LDefinition* dest = ins->getDef(0); + // all of these bitops should be either imm32's, or integer registers. + switch (ins->bitop()) { + case JSOP_BITOR: + if (rhs->isConstant()) + masm.ma_or(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs))); + else + masm.as_or(ToRegister(dest), ToRegister(lhs), ToRegister(rhs)); + break; + case JSOP_BITXOR: + if (rhs->isConstant()) + masm.ma_xor(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs))); + else + masm.as_xor(ToRegister(dest), ToRegister(lhs), ToRegister(rhs)); + break; + case JSOP_BITAND: + if (rhs->isConstant()) + masm.ma_and(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs))); + else + masm.as_and(ToRegister(dest), ToRegister(lhs), ToRegister(rhs)); + break; + default: + MOZ_CRASH("unexpected binary opcode"); + } +} + +void +CodeGeneratorMIPSShared::visitShiftI(LShiftI* ins) +{ + Register lhs = ToRegister(ins->lhs()); + const LAllocation* rhs = ins->rhs(); + Register dest = ToRegister(ins->output()); + + if (rhs->isConstant()) { + int32_t shift = ToInt32(rhs) & 0x1F; + switch (ins->bitop()) { + case JSOP_LSH: + if (shift) + masm.ma_sll(dest, lhs, Imm32(shift)); + else + masm.move32(lhs, dest); + break; + case JSOP_RSH: + if (shift) + masm.ma_sra(dest, lhs, Imm32(shift)); + else + masm.move32(lhs, dest); + break; + case JSOP_URSH: + if (shift) { + masm.ma_srl(dest, lhs, Imm32(shift)); + } else { + // x >>> 0 can overflow. + masm.move32(lhs, dest); + if (ins->mir()->toUrsh()->fallible()) + bailoutCmp32(Assembler::LessThan, dest, Imm32(0), ins->snapshot()); + } + break; + default: + MOZ_CRASH("Unexpected shift op"); + } + } else { + // The shift amounts should be AND'ed into the 0-31 range + masm.ma_and(dest, ToRegister(rhs), Imm32(0x1F)); + + switch (ins->bitop()) { + case JSOP_LSH: + masm.ma_sll(dest, lhs, dest); + break; + case JSOP_RSH: + masm.ma_sra(dest, lhs, dest); + break; + case JSOP_URSH: + masm.ma_srl(dest, lhs, dest); + if (ins->mir()->toUrsh()->fallible()) { + // x >>> 0 can overflow. + bailoutCmp32(Assembler::LessThan, dest, Imm32(0), ins->snapshot()); + } + break; + default: + MOZ_CRASH("Unexpected shift op"); + } + } +} + +void +CodeGeneratorMIPSShared::visitUrshD(LUrshD* ins) +{ + Register lhs = ToRegister(ins->lhs()); + Register temp = ToRegister(ins->temp()); + + const LAllocation* rhs = ins->rhs(); + FloatRegister out = ToFloatRegister(ins->output()); + + if (rhs->isConstant()) { + masm.ma_srl(temp, lhs, Imm32(ToInt32(rhs))); + } else { + masm.ma_srl(temp, lhs, ToRegister(rhs)); + } + + masm.convertUInt32ToDouble(temp, out); +} + +void +CodeGeneratorMIPSShared::visitClzI(LClzI* ins) +{ + Register input = ToRegister(ins->input()); + Register output = ToRegister(ins->output()); + + masm.as_clz(output, input); +} + +void +CodeGeneratorMIPSShared::visitPowHalfD(LPowHalfD* ins) +{ + FloatRegister input = ToFloatRegister(ins->input()); + FloatRegister output = ToFloatRegister(ins->output()); + + Label done, skip; + + // Masm.pow(-Infinity, 0.5) == Infinity. + masm.loadConstantDouble(NegativeInfinity(), ScratchDoubleReg); + masm.ma_bc1d(input, ScratchDoubleReg, &skip, Assembler::DoubleNotEqualOrUnordered, ShortJump); + masm.as_negd(output, ScratchDoubleReg); + masm.ma_b(&done, ShortJump); + + masm.bind(&skip); + // Math.pow(-0, 0.5) == 0 == Math.pow(0, 0.5). + // Adding 0 converts any -0 to 0. + masm.loadConstantDouble(0.0, ScratchDoubleReg); + masm.as_addd(output, input, ScratchDoubleReg); + masm.as_sqrtd(output, output); + + masm.bind(&done); +} + +MoveOperand +CodeGeneratorMIPSShared::toMoveOperand(LAllocation a) const +{ + if (a.isGeneralReg()) + return MoveOperand(ToRegister(a)); + if (a.isFloatReg()) { + return MoveOperand(ToFloatRegister(a)); + } + int32_t offset = ToStackOffset(a); + MOZ_ASSERT((offset & 3) == 0); + + return MoveOperand(StackPointer, offset); +} + +void +CodeGeneratorMIPSShared::visitMathD(LMathD* math) +{ + const LAllocation* src1 = math->getOperand(0); + const LAllocation* src2 = math->getOperand(1); + const LDefinition* output = math->getDef(0); + + switch (math->jsop()) { + case JSOP_ADD: + masm.as_addd(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); + break; + case JSOP_SUB: + masm.as_subd(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); + break; + case JSOP_MUL: + masm.as_muld(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); + break; + case JSOP_DIV: + masm.as_divd(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); + break; + default: + MOZ_CRASH("unexpected opcode"); + } +} + +void +CodeGeneratorMIPSShared::visitMathF(LMathF* math) +{ + const LAllocation* src1 = math->getOperand(0); + const LAllocation* src2 = math->getOperand(1); + const LDefinition* output = math->getDef(0); + + switch (math->jsop()) { + case JSOP_ADD: + masm.as_adds(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); + break; + case JSOP_SUB: + masm.as_subs(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); + break; + case JSOP_MUL: + masm.as_muls(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); + break; + case JSOP_DIV: + masm.as_divs(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); + break; + default: + MOZ_CRASH("unexpected opcode"); + } +} + +void +CodeGeneratorMIPSShared::visitFloor(LFloor* lir) +{ + FloatRegister input = ToFloatRegister(lir->input()); + FloatRegister scratch = ScratchDoubleReg; + Register output = ToRegister(lir->output()); + + Label skipCheck, done; + + // If Nan, 0 or -0 check for bailout + masm.loadConstantDouble(0.0, scratch); + masm.ma_bc1d(input, scratch, &skipCheck, Assembler::DoubleNotEqual, ShortJump); + + // If high part is not zero, it is NaN or -0, so we bail. + masm.moveFromDoubleHi(input, SecondScratchReg); + bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(0), lir->snapshot()); + + // Input was zero, so return zero. + masm.move32(Imm32(0), output); + masm.ma_b(&done, ShortJump); + + masm.bind(&skipCheck); + masm.as_floorwd(scratch, input); + masm.moveFromDoubleLo(scratch, output); + + bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()); + bailoutCmp32(Assembler::Equal, output, Imm32(INT_MAX), lir->snapshot()); + + masm.bind(&done); +} + +void +CodeGeneratorMIPSShared::visitFloorF(LFloorF* lir) +{ + FloatRegister input = ToFloatRegister(lir->input()); + FloatRegister scratch = ScratchFloat32Reg; + Register output = ToRegister(lir->output()); + + Label skipCheck, done; + + // If Nan, 0 or -0 check for bailout + masm.loadConstantFloat32(0.0f, scratch); + masm.ma_bc1s(input, scratch, &skipCheck, Assembler::DoubleNotEqual, ShortJump); + + // If binary value is not zero, it is NaN or -0, so we bail. + masm.moveFromDoubleLo(input, SecondScratchReg); + bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(0), lir->snapshot()); + + // Input was zero, so return zero. + masm.move32(Imm32(0), output); + masm.ma_b(&done, ShortJump); + + masm.bind(&skipCheck); + masm.as_floorws(scratch, input); + masm.moveFromDoubleLo(scratch, output); + + bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()); + bailoutCmp32(Assembler::Equal, output, Imm32(INT_MAX), lir->snapshot()); + + masm.bind(&done); +} + +void +CodeGeneratorMIPSShared::visitCeil(LCeil* lir) +{ + FloatRegister input = ToFloatRegister(lir->input()); + FloatRegister scratch = ScratchDoubleReg; + Register output = ToRegister(lir->output()); + + Label performCeil, done; + + // If x < -1 or x > 0 then perform ceil. + masm.loadConstantDouble(0, scratch); + masm.branchDouble(Assembler::DoubleGreaterThan, input, scratch, &performCeil); + masm.loadConstantDouble(-1, scratch); + masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, scratch, &performCeil); + + // If high part is not zero, the input was not 0, so we bail. + masm.moveFromDoubleHi(input, SecondScratchReg); + bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(0), lir->snapshot()); + + // Input was zero, so return zero. + masm.move32(Imm32(0), output); + masm.ma_b(&done, ShortJump); + + masm.bind(&performCeil); + masm.as_ceilwd(scratch, input); + masm.moveFromDoubleLo(scratch, output); + + bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()); + bailoutCmp32(Assembler::Equal, output, Imm32(INT_MAX), lir->snapshot()); + + masm.bind(&done); +} + +void +CodeGeneratorMIPSShared::visitCeilF(LCeilF* lir) +{ + FloatRegister input = ToFloatRegister(lir->input()); + FloatRegister scratch = ScratchFloat32Reg; + Register output = ToRegister(lir->output()); + + Label performCeil, done; + + // If x < -1 or x > 0 then perform ceil. + masm.loadConstantFloat32(0, scratch); + masm.branchFloat(Assembler::DoubleGreaterThan, input, scratch, &performCeil); + masm.loadConstantFloat32(-1, scratch); + masm.branchFloat(Assembler::DoubleLessThanOrEqual, input, scratch, &performCeil); + + // If binary value is not zero, the input was not 0, so we bail. + masm.moveFromFloat32(input, SecondScratchReg); + bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(0), lir->snapshot()); + + // Input was zero, so return zero. + masm.move32(Imm32(0), output); + masm.ma_b(&done, ShortJump); + + masm.bind(&performCeil); + masm.as_ceilws(scratch, input); + masm.moveFromFloat32(scratch, output); + + bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()); + bailoutCmp32(Assembler::Equal, output, Imm32(INT_MAX), lir->snapshot()); + + masm.bind(&done); +} + +void +CodeGeneratorMIPSShared::visitRound(LRound* lir) +{ + FloatRegister input = ToFloatRegister(lir->input()); + FloatRegister temp = ToFloatRegister(lir->temp()); + FloatRegister scratch = ScratchDoubleReg; + Register output = ToRegister(lir->output()); + + Label bail, negative, end, skipCheck; + + // Load biggest number less than 0.5 in the temp register. + masm.loadConstantDouble(GetBiggestNumberLessThan(0.5), temp); + + // Branch to a slow path for negative inputs. Doesn't catch NaN or -0. + masm.loadConstantDouble(0.0, scratch); + masm.ma_bc1d(input, scratch, &negative, Assembler::DoubleLessThan, ShortJump); + + // If Nan, 0 or -0 check for bailout + masm.ma_bc1d(input, scratch, &skipCheck, Assembler::DoubleNotEqual, ShortJump); + + // If high part is not zero, it is NaN or -0, so we bail. + masm.moveFromDoubleHi(input, SecondScratchReg); + bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(0), lir->snapshot()); + + // Input was zero, so return zero. + masm.move32(Imm32(0), output); + masm.ma_b(&end, ShortJump); + + masm.bind(&skipCheck); + masm.as_addd(scratch, input, temp); + masm.as_floorwd(scratch, scratch); + + masm.moveFromDoubleLo(scratch, output); + + bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()); + bailoutCmp32(Assembler::Equal, output, Imm32(INT_MAX), lir->snapshot()); + + masm.jump(&end); + + // Input is negative, but isn't -0. + masm.bind(&negative); + + // Inputs in ]-0.5; 0] need to be added 0.5, other negative inputs need to + // be added the biggest double less than 0.5. + Label loadJoin; + masm.loadConstantDouble(-0.5, scratch); + masm.branchDouble(Assembler::DoubleLessThan, input, scratch, &loadJoin); + masm.loadConstantDouble(0.5, temp); + masm.bind(&loadJoin); + + masm.addDouble(input, temp); + + // If input + 0.5 >= 0, input is a negative number >= -0.5 and the + // result is -0. + masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, temp, scratch, &bail); + bailoutFrom(&bail, lir->snapshot()); + + // Truncate and round toward zero. + // This is off-by-one for everything but integer-valued inputs. + masm.as_floorwd(scratch, temp); + masm.moveFromDoubleLo(scratch, output); + + bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()); + + masm.bind(&end); +} + +void +CodeGeneratorMIPSShared::visitRoundF(LRoundF* lir) +{ + FloatRegister input = ToFloatRegister(lir->input()); + FloatRegister temp = ToFloatRegister(lir->temp()); + FloatRegister scratch = ScratchFloat32Reg; + Register output = ToRegister(lir->output()); + + Label bail, negative, end, skipCheck; + + // Load biggest number less than 0.5 in the temp register. + masm.loadConstantFloat32(GetBiggestNumberLessThan(0.5f), temp); + + // Branch to a slow path for negative inputs. Doesn't catch NaN or -0. + masm.loadConstantFloat32(0.0f, scratch); + masm.ma_bc1s(input, scratch, &negative, Assembler::DoubleLessThan, ShortJump); + + // If Nan, 0 or -0 check for bailout + masm.ma_bc1s(input, scratch, &skipCheck, Assembler::DoubleNotEqual, ShortJump); + + // If binary value is not zero, it is NaN or -0, so we bail. + masm.moveFromFloat32(input, SecondScratchReg); + bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(0), lir->snapshot()); + + // Input was zero, so return zero. + masm.move32(Imm32(0), output); + masm.ma_b(&end, ShortJump); + + masm.bind(&skipCheck); + masm.as_adds(scratch, input, temp); + masm.as_floorws(scratch, scratch); + + masm.moveFromFloat32(scratch, output); + + bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()); + bailoutCmp32(Assembler::Equal, output, Imm32(INT_MAX), lir->snapshot()); + + masm.jump(&end); + + // Input is negative, but isn't -0. + masm.bind(&negative); + + // Inputs in ]-0.5; 0] need to be added 0.5, other negative inputs need to + // be added the biggest double less than 0.5. + Label loadJoin; + masm.loadConstantFloat32(-0.5f, scratch); + masm.branchFloat(Assembler::DoubleLessThan, input, scratch, &loadJoin); + masm.loadConstantFloat32(0.5f, temp); + masm.bind(&loadJoin); + + masm.as_adds(temp, input, temp); + + // If input + 0.5 >= 0, input is a negative number >= -0.5 and the + // result is -0. + masm.branchFloat(Assembler::DoubleGreaterThanOrEqual, temp, scratch, &bail); + bailoutFrom(&bail, lir->snapshot()); + + // Truncate and round toward zero. + // This is off-by-one for everything but integer-valued inputs. + masm.as_floorws(scratch, temp); + masm.moveFromFloat32(scratch, output); + + bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()); + + masm.bind(&end); +} + +void +CodeGeneratorMIPSShared::visitTruncateDToInt32(LTruncateDToInt32* ins) +{ + emitTruncateDouble(ToFloatRegister(ins->input()), ToRegister(ins->output()), + ins->mir()); +} + +void +CodeGeneratorMIPSShared::visitTruncateFToInt32(LTruncateFToInt32* ins) +{ + emitTruncateFloat32(ToFloatRegister(ins->input()), ToRegister(ins->output()), + ins->mir()); +} + +void +CodeGeneratorMIPSShared::visitValue(LValue* value) +{ + const ValueOperand out = ToOutValue(value); + + masm.moveValue(value->value(), out); +} + +void +CodeGeneratorMIPSShared::visitDouble(LDouble* ins) +{ + const LDefinition* out = ins->getDef(0); + + masm.loadConstantDouble(ins->getDouble(), ToFloatRegister(out)); +} + +void +CodeGeneratorMIPSShared::visitFloat32(LFloat32* ins) +{ + const LDefinition* out = ins->getDef(0); + masm.loadConstantFloat32(ins->getFloat(), ToFloatRegister(out)); +} + +void +CodeGeneratorMIPSShared::visitTestDAndBranch(LTestDAndBranch* test) +{ + FloatRegister input = ToFloatRegister(test->input()); + + MBasicBlock* ifTrue = test->ifTrue(); + MBasicBlock* ifFalse = test->ifFalse(); + + masm.loadConstantDouble(0.0, ScratchDoubleReg); + // If 0, or NaN, the result is false. + + if (isNextBlock(ifFalse->lir())) { + branchToBlock(Assembler::DoubleFloat, input, ScratchDoubleReg, ifTrue, + Assembler::DoubleNotEqual); + } else { + branchToBlock(Assembler::DoubleFloat, input, ScratchDoubleReg, ifFalse, + Assembler::DoubleEqualOrUnordered); + jumpToBlock(ifTrue); + } +} + +void +CodeGeneratorMIPSShared::visitTestFAndBranch(LTestFAndBranch* test) +{ + FloatRegister input = ToFloatRegister(test->input()); + + MBasicBlock* ifTrue = test->ifTrue(); + MBasicBlock* ifFalse = test->ifFalse(); + + masm.loadConstantFloat32(0.0f, ScratchFloat32Reg); + // If 0, or NaN, the result is false. + + if (isNextBlock(ifFalse->lir())) { + branchToBlock(Assembler::SingleFloat, input, ScratchFloat32Reg, ifTrue, + Assembler::DoubleNotEqual); + } else { + branchToBlock(Assembler::SingleFloat, input, ScratchFloat32Reg, ifFalse, + Assembler::DoubleEqualOrUnordered); + jumpToBlock(ifTrue); + } +} + +void +CodeGeneratorMIPSShared::visitCompareD(LCompareD* comp) +{ + FloatRegister lhs = ToFloatRegister(comp->left()); + FloatRegister rhs = ToFloatRegister(comp->right()); + Register dest = ToRegister(comp->output()); + + Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop()); + masm.ma_cmp_set_double(dest, lhs, rhs, cond); +} + +void +CodeGeneratorMIPSShared::visitCompareF(LCompareF* comp) +{ + FloatRegister lhs = ToFloatRegister(comp->left()); + FloatRegister rhs = ToFloatRegister(comp->right()); + Register dest = ToRegister(comp->output()); + + Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop()); + masm.ma_cmp_set_float32(dest, lhs, rhs, cond); +} + +void +CodeGeneratorMIPSShared::visitCompareDAndBranch(LCompareDAndBranch* comp) +{ + FloatRegister lhs = ToFloatRegister(comp->left()); + FloatRegister rhs = ToFloatRegister(comp->right()); + + Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->cmpMir()->jsop()); + MBasicBlock* ifTrue = comp->ifTrue(); + MBasicBlock* ifFalse = comp->ifFalse(); + + if (isNextBlock(ifFalse->lir())) { + branchToBlock(Assembler::DoubleFloat, lhs, rhs, ifTrue, cond); + } else { + branchToBlock(Assembler::DoubleFloat, lhs, rhs, ifFalse, + Assembler::InvertCondition(cond)); + jumpToBlock(ifTrue); + } +} + +void +CodeGeneratorMIPSShared::visitCompareFAndBranch(LCompareFAndBranch* comp) +{ + FloatRegister lhs = ToFloatRegister(comp->left()); + FloatRegister rhs = ToFloatRegister(comp->right()); + + Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->cmpMir()->jsop()); + MBasicBlock* ifTrue = comp->ifTrue(); + MBasicBlock* ifFalse = comp->ifFalse(); + + if (isNextBlock(ifFalse->lir())) { + branchToBlock(Assembler::SingleFloat, lhs, rhs, ifTrue, cond); + } else { + branchToBlock(Assembler::SingleFloat, lhs, rhs, ifFalse, + Assembler::InvertCondition(cond)); + jumpToBlock(ifTrue); + } +} + +void +CodeGeneratorMIPSShared::visitBitAndAndBranch(LBitAndAndBranch* lir) +{ + if (lir->right()->isConstant()) + masm.ma_and(ScratchRegister, ToRegister(lir->left()), Imm32(ToInt32(lir->right()))); + else + masm.as_and(ScratchRegister, ToRegister(lir->left()), ToRegister(lir->right())); + emitBranch(ScratchRegister, ScratchRegister, Assembler::NonZero, lir->ifTrue(), + lir->ifFalse()); +} + +void +CodeGeneratorMIPSShared::visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble* lir) +{ + masm.convertUInt32ToDouble(ToRegister(lir->input()), ToFloatRegister(lir->output())); +} + +void +CodeGeneratorMIPSShared::visitAsmJSUInt32ToFloat32(LAsmJSUInt32ToFloat32* lir) +{ + masm.convertUInt32ToFloat32(ToRegister(lir->input()), ToFloatRegister(lir->output())); +} + +void +CodeGeneratorMIPSShared::visitNotI(LNotI* ins) +{ + masm.cmp32Set(Assembler::Equal, ToRegister(ins->input()), Imm32(0), + ToRegister(ins->output())); +} + +void +CodeGeneratorMIPSShared::visitNotD(LNotD* ins) +{ + // Since this operation is not, we want to set a bit if + // the double is falsey, which means 0.0, -0.0 or NaN. + FloatRegister in = ToFloatRegister(ins->input()); + Register dest = ToRegister(ins->output()); + + Label falsey, done; + masm.loadConstantDouble(0.0, ScratchDoubleReg); + masm.ma_bc1d(in, ScratchDoubleReg, &falsey, Assembler::DoubleEqualOrUnordered, ShortJump); + + masm.move32(Imm32(0), dest); + masm.ma_b(&done, ShortJump); + + masm.bind(&falsey); + masm.move32(Imm32(1), dest); + + masm.bind(&done); +} + +void +CodeGeneratorMIPSShared::visitNotF(LNotF* ins) +{ + // Since this operation is not, we want to set a bit if + // the float32 is falsey, which means 0.0, -0.0 or NaN. + FloatRegister in = ToFloatRegister(ins->input()); + Register dest = ToRegister(ins->output()); + + Label falsey, done; + masm.loadConstantFloat32(0.0f, ScratchFloat32Reg); + masm.ma_bc1s(in, ScratchFloat32Reg, &falsey, Assembler::DoubleEqualOrUnordered, ShortJump); + + masm.move32(Imm32(0), dest); + masm.ma_b(&done, ShortJump); + + masm.bind(&falsey); + masm.move32(Imm32(1), dest); + + masm.bind(&done); +} + +void +CodeGeneratorMIPSShared::visitGuardShape(LGuardShape* guard) +{ + Register obj = ToRegister(guard->input()); + Register tmp = ToRegister(guard->tempInt()); + + masm.loadPtr(Address(obj, JSObject::offsetOfShape()), tmp); + bailoutCmpPtr(Assembler::NotEqual, tmp, ImmGCPtr(guard->mir()->shape()), + guard->snapshot()); +} + +void +CodeGeneratorMIPSShared::visitGuardObjectGroup(LGuardObjectGroup* guard) +{ + Register obj = ToRegister(guard->input()); + Register tmp = ToRegister(guard->tempInt()); + MOZ_ASSERT(obj != tmp); + + masm.loadPtr(Address(obj, JSObject::offsetOfGroup()), tmp); + Assembler::Condition cond = guard->mir()->bailOnEquality() + ? Assembler::Equal + : Assembler::NotEqual; + bailoutCmpPtr(cond, tmp, ImmGCPtr(guard->mir()->group()), guard->snapshot()); +} + +void +CodeGeneratorMIPSShared::visitGuardClass(LGuardClass* guard) +{ + Register obj = ToRegister(guard->input()); + Register tmp = ToRegister(guard->tempInt()); + + masm.loadObjClass(obj, tmp); + bailoutCmpPtr(Assembler::NotEqual, tmp, ImmPtr(guard->mir()->getClass()), + guard->snapshot()); +} + +void +CodeGeneratorMIPSShared::visitMemoryBarrier(LMemoryBarrier* ins) +{ + memoryBarrier(ins->type()); +} + +void +CodeGeneratorMIPSShared::memoryBarrier(MemoryBarrierBits barrier) +{ + if (barrier) + masm.as_sync(); +} + +void +CodeGeneratorMIPSShared::generateInvalidateEpilogue() +{ + // Ensure that there is enough space in the buffer for the OsiPoint + // patching to occur. Otherwise, we could overwrite the invalidation + // epilogue. + for (size_t i = 0; i < sizeof(void*); i += Assembler::NopSize()) + masm.nop(); + + masm.bind(&invalidate_); + + // Push the return address of the point that we bailed out at to the stack + masm.Push(ra); + + // Push the Ion script onto the stack (when we determine what that + // pointer is). + invalidateEpilogueData_ = masm.pushWithPatch(ImmWord(uintptr_t(-1))); + JitCode* thunk = gen->jitRuntime()->getInvalidationThunk(); + + masm.branch(thunk); + + // We should never reach this point in JIT code -- the invalidation thunk + // should pop the invalidated JS frame and return directly to its caller. + masm.assumeUnreachable("Should have returned directly to its caller instead of here."); +} + +void +CodeGeneratorMIPSShared::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins) +{ + MOZ_CRASH("NYI"); +} + +void +CodeGeneratorMIPSShared::visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins) +{ + MOZ_CRASH("NYI"); +} + +void +CodeGeneratorMIPSShared::visitAsmJSCall(LAsmJSCall* ins) +{ + emitAsmJSCall(ins); +} + +void +CodeGeneratorMIPSShared::visitAsmJSLoadHeap(LAsmJSLoadHeap* ins) +{ + const MAsmJSLoadHeap* mir = ins->mir(); + const LAllocation* ptr = ins->ptr(); + const LDefinition* out = ins->output(); + + bool isSigned; + int size; + bool isFloat = false; + switch (mir->accessType()) { + case Scalar::Int8: isSigned = true; size = 8; break; + case Scalar::Uint8: isSigned = false; size = 8; break; + case Scalar::Int16: isSigned = true; size = 16; break; + case Scalar::Uint16: isSigned = false; size = 16; break; + case Scalar::Int32: isSigned = true; size = 32; break; + case Scalar::Uint32: isSigned = false; size = 32; break; + case Scalar::Float64: isFloat = true; size = 64; break; + case Scalar::Float32: isFloat = true; size = 32; break; + default: MOZ_CRASH("unexpected array type"); + } + + memoryBarrier(mir->barrierBefore()); + if (ptr->isConstant()) { + MOZ_ASSERT(!mir->needsBoundsCheck()); + int32_t ptrImm = ptr->toConstant()->toInt32(); + MOZ_ASSERT(ptrImm >= 0); + if (isFloat) { + if (size == 32) { + masm.loadFloat32(Address(HeapReg, ptrImm), ToFloatRegister(out)); + } else { + masm.loadDouble(Address(HeapReg, ptrImm), ToFloatRegister(out)); + } + } else { + masm.ma_load(ToRegister(out), Address(HeapReg, ptrImm), + static_cast(size), isSigned ? SignExtend : ZeroExtend); + } + memoryBarrier(mir->barrierAfter()); + return; + } + + Register ptrReg = ToRegister(ptr); + + if (!mir->needsBoundsCheck()) { + if (isFloat) { + if (size == 32) { + masm.loadFloat32(BaseIndex(HeapReg, ptrReg, TimesOne), ToFloatRegister(out)); + } else { + masm.loadDouble(BaseIndex(HeapReg, ptrReg, TimesOne), ToFloatRegister(out)); + } + } else { + masm.ma_load(ToRegister(out), BaseIndex(HeapReg, ptrReg, TimesOne), + static_cast(size), isSigned ? SignExtend : ZeroExtend); + } + memoryBarrier(mir->barrierAfter()); + return; + } + + BufferOffset bo = masm.ma_BoundsCheck(ScratchRegister); + + Label done, outOfRange; + masm.ma_b(ptrReg, ScratchRegister, &outOfRange, Assembler::AboveOrEqual, ShortJump); + // Offset is ok, let's load value. + if (isFloat) { + if (size == 32) + masm.loadFloat32(BaseIndex(HeapReg, ptrReg, TimesOne), ToFloatRegister(out)); + else + masm.loadDouble(BaseIndex(HeapReg, ptrReg, TimesOne), ToFloatRegister(out)); + } else { + masm.ma_load(ToRegister(out), BaseIndex(HeapReg, ptrReg, TimesOne), + static_cast(size), isSigned ? SignExtend : ZeroExtend); + } + masm.ma_b(&done, ShortJump); + masm.bind(&outOfRange); + // Offset is out of range. Load default values. + if (isFloat) { + if (size == 32) + masm.loadFloat32(Address(GlobalReg, AsmJSNaN32GlobalDataOffset - AsmJSGlobalRegBias), + ToFloatRegister(out)); + else + masm.loadDouble(Address(GlobalReg, AsmJSNaN64GlobalDataOffset - AsmJSGlobalRegBias), + ToFloatRegister(out)); + } else { + if (mir->isAtomicAccess()) + masm.ma_b(gen->outOfBoundsLabel()); + else + masm.move32(Imm32(0), ToRegister(out)); + } + masm.bind(&done); + + memoryBarrier(mir->barrierAfter()); + masm.append(AsmJSHeapAccess(bo.getOffset())); +} + +void +CodeGeneratorMIPSShared::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins) +{ + const MAsmJSStoreHeap* mir = ins->mir(); + const LAllocation* value = ins->value(); + const LAllocation* ptr = ins->ptr(); + + bool isSigned; + int size; + bool isFloat = false; + switch (mir->accessType()) { + case Scalar::Int8: isSigned = true; size = 8; break; + case Scalar::Uint8: isSigned = false; size = 8; break; + case Scalar::Int16: isSigned = true; size = 16; break; + case Scalar::Uint16: isSigned = false; size = 16; break; + case Scalar::Int32: isSigned = true; size = 32; break; + case Scalar::Uint32: isSigned = false; size = 32; break; + case Scalar::Float64: isFloat = true; size = 64; break; + case Scalar::Float32: isFloat = true; size = 32; break; + default: MOZ_CRASH("unexpected array type"); + } + + memoryBarrier(mir->barrierBefore()); + if (ptr->isConstant()) { + MOZ_ASSERT(!mir->needsBoundsCheck()); + int32_t ptrImm = ptr->toConstant()->toInt32(); + MOZ_ASSERT(ptrImm >= 0); + + if (isFloat) { + if (size == 32) { + masm.storeFloat32(ToFloatRegister(value), Address(HeapReg, ptrImm)); + } else { + masm.storeDouble(ToFloatRegister(value), Address(HeapReg, ptrImm)); + } + } else { + masm.ma_store(ToRegister(value), Address(HeapReg, ptrImm), + static_cast(size), isSigned ? SignExtend : ZeroExtend); + } + memoryBarrier(mir->barrierAfter()); + return; + } + + Register ptrReg = ToRegister(ptr); + Address dstAddr(ptrReg, 0); + + if (!mir->needsBoundsCheck()) { + if (isFloat) { + if (size == 32) { + masm.storeFloat32(ToFloatRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne)); + } else + masm.storeDouble(ToFloatRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne)); + } else { + masm.ma_store(ToRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne), + static_cast(size), isSigned ? SignExtend : ZeroExtend); + } + memoryBarrier(mir->barrierAfter()); + return; + } + + BufferOffset bo = masm.ma_BoundsCheck(ScratchRegister); + + Label done, outOfRange; + masm.ma_b(ptrReg, ScratchRegister, &outOfRange, Assembler::AboveOrEqual, ShortJump); + + // Offset is ok, let's store value. + if (isFloat) { + if (size == 32) { + masm.storeFloat32(ToFloatRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne)); + } else + masm.storeDouble(ToFloatRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne)); + } else { + masm.ma_store(ToRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne), + static_cast(size), isSigned ? SignExtend : ZeroExtend); + } + masm.ma_b(&done, ShortJump); + masm.bind(&outOfRange); + // Offset is out of range. + if (mir->isAtomicAccess()) + masm.ma_b(gen->outOfBoundsLabel()); + masm.bind(&done); + + memoryBarrier(mir->barrierAfter()); + masm.append(AsmJSHeapAccess(bo.getOffset())); +} + +void +CodeGeneratorMIPSShared::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins) +{ + MOZ_CRASH("NYI"); +} + +void +CodeGeneratorMIPSShared::visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins) +{ + MOZ_CRASH("NYI"); +} + +void +CodeGeneratorMIPSShared::visitAsmJSPassStackArg(LAsmJSPassStackArg* ins) +{ + const MAsmJSPassStackArg* mir = ins->mir(); + if (ins->arg()->isConstant()) { + masm.storePtr(ImmWord(ToInt32(ins->arg())), Address(StackPointer, mir->spOffset())); + } else { + if (ins->arg()->isGeneralReg()) { + masm.storePtr(ToRegister(ins->arg()), Address(StackPointer, mir->spOffset())); + } else { + masm.storeDouble(ToFloatRegister(ins->arg()).doubleOverlay(), + Address(StackPointer, mir->spOffset())); + } + } +} + +void +CodeGeneratorMIPSShared::visitUDivOrMod(LUDivOrMod* ins) +{ + Register lhs = ToRegister(ins->lhs()); + Register rhs = ToRegister(ins->rhs()); + Register output = ToRegister(ins->output()); + Label done; + + // Prevent divide by zero. + if (ins->canBeDivideByZero()) { + if (ins->mir()->isTruncated()) { + // Infinity|0 == 0 + Label notzero; + masm.ma_b(rhs, rhs, ¬zero, Assembler::NonZero, ShortJump); + masm.move32(Imm32(0), output); + masm.ma_b(&done, ShortJump); + masm.bind(¬zero); + } else { + bailoutCmp32(Assembler::Equal, rhs, Imm32(0), ins->snapshot()); + } + } + + masm.as_divu(lhs, rhs); + masm.as_mfhi(output); + + // If the remainder is > 0, bailout since this must be a double. + if (ins->mir()->isDiv()) { + if (!ins->mir()->toDiv()->canTruncateRemainder()) + bailoutCmp32(Assembler::NonZero, output, output, ins->snapshot()); + // Get quotient + masm.as_mflo(output); + } + + if (!ins->mir()->isTruncated()) + bailoutCmp32(Assembler::LessThan, output, Imm32(0), ins->snapshot()); + + masm.bind(&done); +} + +void +CodeGeneratorMIPSShared::visitEffectiveAddress(LEffectiveAddress* ins) +{ + const MEffectiveAddress* mir = ins->mir(); + Register base = ToRegister(ins->base()); + Register index = ToRegister(ins->index()); + Register output = ToRegister(ins->output()); + + BaseIndex address(base, index, mir->scale(), mir->displacement()); + masm.computeEffectiveAddress(address, output); +} + +void +CodeGeneratorMIPSShared::visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar* ins) +{ + const MAsmJSLoadGlobalVar* mir = ins->mir(); + unsigned addr = mir->globalDataOffset() - AsmJSGlobalRegBias; + if (mir->type() == MIRType_Int32) + masm.load32(Address(GlobalReg, addr), ToRegister(ins->output())); + else if (mir->type() == MIRType_Float32) + masm.loadFloat32(Address(GlobalReg, addr), ToFloatRegister(ins->output())); + else + masm.loadDouble(Address(GlobalReg, addr), ToFloatRegister(ins->output())); +} + +void +CodeGeneratorMIPSShared::visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar* ins) +{ + const MAsmJSStoreGlobalVar* mir = ins->mir(); + + MOZ_ASSERT(IsNumberType(mir->value()->type())); + unsigned addr = mir->globalDataOffset() - AsmJSGlobalRegBias; + if (mir->value()->type() == MIRType_Int32) + masm.store32(ToRegister(ins->value()), Address(GlobalReg, addr)); + else if (mir->value()->type() == MIRType_Float32) + masm.storeFloat32(ToFloatRegister(ins->value()), Address(GlobalReg, addr)); + else + masm.storeDouble(ToFloatRegister(ins->value()), Address(GlobalReg, addr)); +} + +void +CodeGeneratorMIPSShared::visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr* ins) +{ + const MAsmJSLoadFuncPtr* mir = ins->mir(); + + Register index = ToRegister(ins->index()); + Register out = ToRegister(ins->output()); + unsigned addr = mir->globalDataOffset() - AsmJSGlobalRegBias; + + BaseIndex source(GlobalReg, index, ScalePointer, addr); + masm.loadPtr(source, out); +} + +void +CodeGeneratorMIPSShared::visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc* ins) +{ + const MAsmJSLoadFFIFunc* mir = ins->mir(); + masm.loadPtr(Address(GlobalReg, mir->globalDataOffset() - AsmJSGlobalRegBias), + ToRegister(ins->output())); +} + +void +CodeGeneratorMIPSShared::visitNegI(LNegI* ins) +{ + Register input = ToRegister(ins->input()); + Register output = ToRegister(ins->output()); + + masm.ma_negu(output, input); +} + +void +CodeGeneratorMIPSShared::visitNegD(LNegD* ins) +{ + FloatRegister input = ToFloatRegister(ins->input()); + FloatRegister output = ToFloatRegister(ins->output()); + + masm.as_negd(output, input); +} + +void +CodeGeneratorMIPSShared::visitNegF(LNegF* ins) +{ + FloatRegister input = ToFloatRegister(ins->input()); + FloatRegister output = ToFloatRegister(ins->output()); + + masm.as_negs(output, input); +} diff --git a/js/src/jit/mips-shared/CodeGenerator-mips-shared.h b/js/src/jit/mips-shared/CodeGenerator-mips-shared.h new file mode 100644 index 0000000000..b7e237f464 --- /dev/null +++ b/js/src/jit/mips-shared/CodeGenerator-mips-shared.h @@ -0,0 +1,259 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef jit_mips_shared_CodeGenerator_mips_shared_h +#define jit_mips_shared_CodeGenerator_mips_shared_h + +#include "jit/shared/CodeGenerator-shared.h" + +namespace js { +namespace jit { + +class OutOfLineBailout; +class OutOfLineTableSwitch; + +class CodeGeneratorMIPSShared : public CodeGeneratorShared +{ + friend class MoveResolverMIPS; + + CodeGeneratorMIPSShared* thisFromCtor() { + return this; + } + + protected: + NonAssertingLabel deoptLabel_; + + inline Address ToAddress(const LAllocation& a); + inline Address ToAddress(const LAllocation* a); + + MoveOperand toMoveOperand(LAllocation a) const; + + template + void bailoutCmp32(Assembler::Condition c, T1 lhs, T2 rhs, LSnapshot* snapshot) { + Label bail; + masm.branch32(c, lhs, rhs, &bail); + bailoutFrom(&bail, snapshot); + } + template + void bailoutCmp32(Assembler::Condition c, Operand lhs, T rhs, LSnapshot* snapshot) { + if (lhs.getTag() == Operand::REG) + bailoutCmp32(c, lhs.toReg(), rhs, snapshot); + else if (lhs.getTag() == Operand::MEM) + bailoutCmp32(c, lhs.toAddress(), rhs, snapshot); + else + MOZ_CRASH("Invalid operand tag."); + } + template + void bailoutTest32(Assembler::Condition c, Register lhs, T rhs, LSnapshot* snapshot) { + Label bail; + masm.branchTest32(c, lhs, rhs, &bail); + bailoutFrom(&bail, snapshot); + } + template + void bailoutCmpPtr(Assembler::Condition c, T1 lhs, T2 rhs, LSnapshot* snapshot) { + Label bail; + masm.branchPtr(c, lhs, rhs, &bail); + bailoutFrom(&bail, snapshot); + } + void bailoutTestPtr(Assembler::Condition c, Register lhs, Register rhs, LSnapshot* snapshot) { + Label bail; + masm.branchTestPtr(c, lhs, rhs, &bail); + bailoutFrom(&bail, snapshot); + } + void bailoutIfFalseBool(Register reg, LSnapshot* snapshot) { + Label bail; + masm.branchTest32(Assembler::Zero, reg, Imm32(0xFF), &bail); + bailoutFrom(&bail, snapshot); + } + + void bailoutFrom(Label* label, LSnapshot* snapshot); + void bailout(LSnapshot* snapshot); + + protected: + bool generateOutOfLineCode(); + + template + void branchToBlock(Register lhs, T rhs, MBasicBlock* mir, Assembler::Condition cond) + { + mir = skipTrivialBlocks(mir); + + Label* label = mir->lir()->label(); + if (Label* oolEntry = labelForBackedgeWithImplicitCheck(mir)) { + // Note: the backedge is initially a jump to the next instruction. + // It will be patched to the target block's label during link(). + RepatchLabel rejoin; + CodeOffsetJump backedge; + Label skip; + + masm.ma_b(lhs, rhs, &skip, Assembler::InvertCondition(cond), ShortJump); + backedge = masm.backedgeJump(&rejoin); + masm.bind(&rejoin); + masm.bind(&skip); + + if (!patchableBackedges_.append(PatchableBackedgeInfo(backedge, label, oolEntry))) + MOZ_CRASH(); + } else { + masm.ma_b(lhs, rhs, label, cond); + } + } + void branchToBlock(Assembler::FloatFormat fmt, FloatRegister lhs, FloatRegister rhs, + MBasicBlock* mir, Assembler::DoubleCondition cond); + + // Emits a branch that directs control flow to the true block if |cond| is + // true, and the false block if |cond| is false. + template + void emitBranch(Register lhs, T rhs, Assembler::Condition cond, + MBasicBlock* mirTrue, MBasicBlock* mirFalse) + { + if (isNextBlock(mirFalse->lir())) { + branchToBlock(lhs, rhs, mirTrue, cond); + } else { + branchToBlock(lhs, rhs, mirFalse, Assembler::InvertCondition(cond)); + jumpToBlock(mirTrue); + } + } + void testZeroEmitBranch(Assembler::Condition cond, Register reg, + MBasicBlock* ifTrue, MBasicBlock* ifFalse) + { + emitBranch(reg, Imm32(0), cond, ifTrue, ifFalse); + } + + public: + // Instruction visitors. + virtual void visitMinMaxD(LMinMaxD* ins); + virtual void visitMinMaxF(LMinMaxF* ins); + virtual void visitAbsD(LAbsD* ins); + virtual void visitAbsF(LAbsF* ins); + virtual void visitSqrtD(LSqrtD* ins); + virtual void visitSqrtF(LSqrtF* ins); + virtual void visitAddI(LAddI* ins); + virtual void visitSubI(LSubI* ins); + virtual void visitBitNotI(LBitNotI* ins); + virtual void visitBitOpI(LBitOpI* ins); + + virtual void visitMulI(LMulI* ins); + + virtual void visitDivI(LDivI* ins); + virtual void visitDivPowTwoI(LDivPowTwoI* ins); + virtual void visitModI(LModI* ins); + virtual void visitModPowTwoI(LModPowTwoI* ins); + virtual void visitModMaskI(LModMaskI* ins); + virtual void visitPowHalfD(LPowHalfD* ins); + virtual void visitShiftI(LShiftI* ins); + virtual void visitUrshD(LUrshD* ins); + + virtual void visitClzI(LClzI* ins); + + virtual void visitTestIAndBranch(LTestIAndBranch* test); + virtual void visitCompare(LCompare* comp); + virtual void visitCompareAndBranch(LCompareAndBranch* comp); + virtual void visitTestDAndBranch(LTestDAndBranch* test); + virtual void visitTestFAndBranch(LTestFAndBranch* test); + virtual void visitCompareD(LCompareD* comp); + virtual void visitCompareF(LCompareF* comp); + virtual void visitCompareDAndBranch(LCompareDAndBranch* comp); + virtual void visitCompareFAndBranch(LCompareFAndBranch* comp); + virtual void visitBitAndAndBranch(LBitAndAndBranch* lir); + virtual void visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble* lir); + virtual void visitAsmJSUInt32ToFloat32(LAsmJSUInt32ToFloat32* lir); + virtual void visitNotI(LNotI* ins); + virtual void visitNotD(LNotD* ins); + virtual void visitNotF(LNotF* ins); + + virtual void visitMathD(LMathD* math); + virtual void visitMathF(LMathF* math); + virtual void visitFloor(LFloor* lir); + virtual void visitFloorF(LFloorF* lir); + virtual void visitCeil(LCeil* lir); + virtual void visitCeilF(LCeilF* lir); + virtual void visitRound(LRound* lir); + virtual void visitRoundF(LRoundF* lir); + virtual void visitTruncateDToInt32(LTruncateDToInt32* ins); + virtual void visitTruncateFToInt32(LTruncateFToInt32* ins); + + // Out of line visitors. + void visitOutOfLineBailout(OutOfLineBailout* ool); + protected: + virtual ValueOperand ToOutValue(LInstruction* ins) = 0; + void memoryBarrier(MemoryBarrierBits barrier); + + public: + CodeGeneratorMIPSShared(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm); + + void visitValue(LValue* value); + void visitDouble(LDouble* ins); + void visitFloat32(LFloat32* ins); + + void visitGuardShape(LGuardShape* guard); + void visitGuardObjectGroup(LGuardObjectGroup* guard); + void visitGuardClass(LGuardClass* guard); + + void visitNegI(LNegI* lir); + void visitNegD(LNegD* lir); + void visitNegF(LNegF* lir); + void visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins); + void visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins); + void visitAsmJSCall(LAsmJSCall* ins); + void visitAsmJSLoadHeap(LAsmJSLoadHeap* ins); + void visitAsmJSStoreHeap(LAsmJSStoreHeap* ins); + void visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins); + void visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins); + void visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar* ins); + void visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar* ins); + void visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr* ins); + void visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc* ins); + + void visitAsmJSPassStackArg(LAsmJSPassStackArg* ins); + + void visitMemoryBarrier(LMemoryBarrier* ins); + + void generateInvalidateEpilogue(); + + protected: + void visitEffectiveAddress(LEffectiveAddress* ins); + void visitUDivOrMod(LUDivOrMod* ins); + + public: + // Unimplemented SIMD instructions + void visitSimdSplatX4(LSimdSplatX4* lir) { MOZ_CRASH("NYI"); } + void visitInt32x4(LInt32x4* ins) { MOZ_CRASH("NYI"); } + void visitFloat32x4(LFloat32x4* ins) { MOZ_CRASH("NYI"); } + void visitSimdReinterpretCast(LSimdReinterpretCast* ins) { MOZ_CRASH("NYI"); } + void visitSimdExtractElementI(LSimdExtractElementI* ins) { MOZ_CRASH("NYI"); } + void visitSimdExtractElementF(LSimdExtractElementF* ins) { MOZ_CRASH("NYI"); } + void visitSimdSignMaskX4(LSimdSignMaskX4* ins) { MOZ_CRASH("NYI"); } + void visitSimdBinaryCompIx4(LSimdBinaryCompIx4* lir) { MOZ_CRASH("NYI"); } + void visitSimdBinaryCompFx4(LSimdBinaryCompFx4* lir) { MOZ_CRASH("NYI"); } + void visitSimdBinaryArithIx4(LSimdBinaryArithIx4* lir) { MOZ_CRASH("NYI"); } + void visitSimdBinaryArithFx4(LSimdBinaryArithFx4* lir) { MOZ_CRASH("NYI"); } + void visitSimdBinaryBitwiseX4(LSimdBinaryBitwiseX4* lir) { MOZ_CRASH("NYI"); } + void visitSimdGeneralShuffleI(LSimdGeneralShuffleI* lir) { MOZ_CRASH("NYI"); } + void visitSimdGeneralShuffleF(LSimdGeneralShuffleF* lir) { MOZ_CRASH("NYI"); } +}; + +// An out-of-line bailout thunk. +class OutOfLineBailout : public OutOfLineCodeBase +{ + LSnapshot* snapshot_; + uint32_t frameSize_; + + public: + OutOfLineBailout(LSnapshot* snapshot, uint32_t frameSize) + : snapshot_(snapshot), + frameSize_(frameSize) + { } + + void accept(CodeGeneratorMIPSShared* codegen); + + LSnapshot* snapshot() const { + return snapshot_; + } +}; + +} // namespace jit +} // namespace js + +#endif /* jit_mips_shared_CodeGenerator_mips_shared_h */ diff --git a/js/src/jit/mips-shared/MoveEmitter-mips-shared.cpp b/js/src/jit/mips-shared/MoveEmitter-mips-shared.cpp new file mode 100644 index 0000000000..ae8c7e6293 --- /dev/null +++ b/js/src/jit/mips-shared/MoveEmitter-mips-shared.cpp @@ -0,0 +1,186 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jit/mips-shared/MoveEmitter-mips-shared.h" + +#include "jit/MacroAssembler-inl.h" + +using namespace js; +using namespace js::jit; + +void +MoveEmitterMIPSShared::emit(const MoveResolver& moves) +{ + if (moves.numCycles()) { + // Reserve stack for cycle resolution + masm.reserveStack(moves.numCycles() * sizeof(double)); + pushedAtCycle_ = masm.framePushed(); + } + + for (size_t i = 0; i < moves.numMoves(); i++) + emit(moves.getMove(i)); +} + +Address +MoveEmitterMIPSShared::cycleSlot(uint32_t slot, uint32_t subslot) const +{ + int32_t offset = masm.framePushed() - pushedAtCycle_; + MOZ_ASSERT(Imm16::IsInSignedRange(offset)); + return Address(StackPointer, offset + slot * sizeof(double) + subslot); +} + +int32_t +MoveEmitterMIPSShared::getAdjustedOffset(const MoveOperand& operand) +{ + MOZ_ASSERT(operand.isMemoryOrEffectiveAddress()); + if (operand.base() != StackPointer) + return operand.disp(); + + // Adjust offset if stack pointer has been moved. + return operand.disp() + masm.framePushed() - pushedAtStart_; +} + +Address +MoveEmitterMIPSShared::getAdjustedAddress(const MoveOperand& operand) +{ + return Address(operand.base(), getAdjustedOffset(operand)); +} + + +Register +MoveEmitterMIPSShared::tempReg() +{ + spilledReg_ = SecondScratchReg; + return SecondScratchReg; +} + +void +MoveEmitterMIPSShared::emitMove(const MoveOperand& from, const MoveOperand& to) +{ + if (from.isGeneralReg()) { + // Second scratch register should not be moved by MoveEmitter. + MOZ_ASSERT(from.reg() != spilledReg_); + + if (to.isGeneralReg()) + masm.movePtr(from.reg(), to.reg()); + else if (to.isMemory()) + masm.storePtr(from.reg(), getAdjustedAddress(to)); + else + MOZ_CRASH("Invalid emitMove arguments."); + } else if (from.isMemory()) { + if (to.isGeneralReg()) { + masm.loadPtr(getAdjustedAddress(from), to.reg()); + } else if (to.isMemory()) { + masm.loadPtr(getAdjustedAddress(from), tempReg()); + masm.storePtr(tempReg(), getAdjustedAddress(to)); + } else { + MOZ_CRASH("Invalid emitMove arguments."); + } + } else if (from.isEffectiveAddress()) { + if (to.isGeneralReg()) { + masm.computeEffectiveAddress(getAdjustedAddress(from), to.reg()); + } else if (to.isMemory()) { + masm.computeEffectiveAddress(getAdjustedAddress(from), tempReg()); + masm.storePtr(tempReg(), getAdjustedAddress(to)); + } else { + MOZ_CRASH("Invalid emitMove arguments."); + } + } else { + MOZ_CRASH("Invalid emitMove arguments."); + } +} + +void +MoveEmitterMIPSShared::emitFloat32Move(const MoveOperand& from, const MoveOperand& to) +{ + // Ensure that we can use ScratchFloat32Reg in memory move. + MOZ_ASSERT_IF(from.isFloatReg(), from.floatReg() != ScratchFloat32Reg); + MOZ_ASSERT_IF(to.isFloatReg(), to.floatReg() != ScratchFloat32Reg); + + if (from.isFloatReg()) { + if (to.isFloatReg()) { + masm.moveFloat32(from.floatReg(), to.floatReg()); + } else if (to.isGeneralReg()) { + // This should only be used when passing float parameter in a1,a2,a3 + MOZ_ASSERT(to.reg() == a1 || to.reg() == a2 || to.reg() == a3); + masm.moveFromFloat32(from.floatReg(), to.reg()); + } else { + MOZ_ASSERT(to.isMemory()); + masm.storeFloat32(from.floatReg(), getAdjustedAddress(to)); + } + } else if (to.isFloatReg()) { + MOZ_ASSERT(from.isMemory()); + masm.loadFloat32(getAdjustedAddress(from), to.floatReg()); + } else if (to.isGeneralReg()) { + MOZ_ASSERT(from.isMemory()); + // This should only be used when passing float parameter in a1,a2,a3 + MOZ_ASSERT(to.reg() == a1 || to.reg() == a2 || to.reg() == a3); + masm.loadPtr(getAdjustedAddress(from), to.reg()); + } else { + MOZ_ASSERT(from.isMemory()); + MOZ_ASSERT(to.isMemory()); + masm.loadFloat32(getAdjustedAddress(from), ScratchFloat32Reg); + masm.storeFloat32(ScratchFloat32Reg, getAdjustedAddress(to)); + } +} + +void +MoveEmitterMIPSShared::emit(const MoveOp& move) +{ + const MoveOperand& from = move.from(); + const MoveOperand& to = move.to(); + + if (move.isCycleEnd() && move.isCycleBegin()) { + // A fun consequence of aliased registers is you can have multiple + // cycles at once, and one can end exactly where another begins. + breakCycle(from, to, move.endCycleType(), move.cycleBeginSlot()); + completeCycle(from, to, move.type(), move.cycleEndSlot()); + return; + } + + if (move.isCycleEnd()) { + MOZ_ASSERT(inCycle_); + completeCycle(from, to, move.type(), move.cycleEndSlot()); + MOZ_ASSERT(inCycle_ > 0); + inCycle_--; + return; + } + + if (move.isCycleBegin()) { + breakCycle(from, to, move.endCycleType(), move.cycleBeginSlot()); + inCycle_++; + } + + switch (move.type()) { + case MoveOp::FLOAT32: + emitFloat32Move(from, to); + break; + case MoveOp::DOUBLE: + emitDoubleMove(from, to); + break; + case MoveOp::INT32: + MOZ_ASSERT(sizeof(uintptr_t) == sizeof(int32_t)); + case MoveOp::GENERAL: + emitMove(from, to); + break; + default: + MOZ_CRASH("Unexpected move type"); + } +} + +void +MoveEmitterMIPSShared::assertDone() +{ + MOZ_ASSERT(inCycle_ == 0); +} + +void +MoveEmitterMIPSShared::finish() +{ + assertDone(); + + masm.freeStack(masm.framePushed() - pushedAtStart_); +} diff --git a/js/src/jit/mips-shared/MoveEmitter-mips-shared.h b/js/src/jit/mips-shared/MoveEmitter-mips-shared.h new file mode 100644 index 0000000000..0b32da549e --- /dev/null +++ b/js/src/jit/mips-shared/MoveEmitter-mips-shared.h @@ -0,0 +1,75 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef jit_mips_shared_MoveEmitter_mips_shared_h +#define jit_mips_shared_MoveEmitter_mips_shared_h + +#include "jit/MacroAssembler.h" +#include "jit/MoveResolver.h" + +namespace js { +namespace jit { + +class MoveEmitterMIPSShared +{ + protected: + uint32_t inCycle_; + MacroAssembler& masm; + + // Original stack push value. + uint32_t pushedAtStart_; + + // These store stack offsets to spill locations, snapshotting + // codegen->framePushed_ at the time they were allocated. They are -1 if no + // stack space has been allocated for that particular spill. + int32_t pushedAtCycle_; + int32_t pushedAtSpill_; + + // These are registers that are available for temporary use. They may be + // assigned InvalidReg. If no corresponding spill space has been assigned, + // then these registers do not need to be spilled. + Register spilledReg_; + FloatRegister spilledFloatReg_; + + void assertDone(); + Register tempReg(); + FloatRegister tempFloatReg(); + Address cycleSlot(uint32_t slot, uint32_t subslot) const; + int32_t getAdjustedOffset(const MoveOperand& operand); + Address getAdjustedAddress(const MoveOperand& operand); + + void emitMove(const MoveOperand& from, const MoveOperand& to); + void emitFloat32Move(const MoveOperand& from, const MoveOperand& to); + virtual void emitDoubleMove(const MoveOperand& from, const MoveOperand& to) = 0; + virtual void breakCycle(const MoveOperand& from, const MoveOperand& to, + MoveOp::Type type, uint32_t slot) = 0; + virtual void completeCycle(const MoveOperand& from, const MoveOperand& to, + MoveOp::Type type, uint32_t slot) = 0; + void emit(const MoveOp& move); + + public: + MoveEmitterMIPSShared(MacroAssembler& masm) + : inCycle_(0), + masm(masm), + pushedAtCycle_(-1), + pushedAtSpill_(-1), + pushedAtStart_(masm.framePushed()), + spilledReg_(InvalidReg), + spilledFloatReg_(InvalidFloatReg) + { } + ~MoveEmitterMIPSShared() { + assertDone(); + } + void emit(const MoveResolver& moves); + void finish(); + + void setScratchRegister(Register reg) {} +}; + +} // namespace jit +} // namespace js + +#endif /* jit_mips_shared_MoveEmitter_mips_shared_h */ diff --git a/js/src/jit/mips32/Architecture-mips32.cpp b/js/src/jit/mips32/Architecture-mips32.cpp index 549aa939fe..7aabb97b7b 100644 --- a/js/src/jit/mips32/Architecture-mips32.cpp +++ b/js/src/jit/mips32/Architecture-mips32.cpp @@ -11,6 +11,23 @@ namespace js { namespace jit { +const char * const Registers::RegNames[] = { "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", + "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", + "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", + "t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra" }; + +const uint32_t Allocatable = 14; + +const Registers::SetType Registers::ArgRegMask = Registers::SharedArgRegMask; + +const Registers::SetType Registers::JSCallMask = + (1 << Registers::a2) | + (1 << Registers::a3); + +const Registers::SetType Registers::CallMask = + (1 << Registers::v0) | + (1 << Registers::v1); // used for double-size returns + FloatRegisters::Code FloatRegisters::FromName(const char* name) { diff --git a/js/src/jit/mips32/Architecture-mips32.h b/js/src/jit/mips32/Architecture-mips32.h index 0c97800790..a34bfb8960 100644 --- a/js/src/jit/mips32/Architecture-mips32.h +++ b/js/src/jit/mips32/Architecture-mips32.h @@ -12,23 +12,6 @@ #include #include -#define REGISTERS_NAMES \ - { "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", \ - "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", \ - "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", \ - "t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra"}; - -#define REGISTERS_ALLOCATABLE 14 -#define REGISTERS_ARGREGMASK SharedArgRegMask - -#define REGISTERS_JSCALLMASK \ - (1 << Registers::a2) | \ - (1 << Registers::a3); - -#define REGISTERS_CALLMASK \ - (1 << Registers::v0) | \ - (1 << Registers::v1); // used for double-size returns - #include "jit/mips-shared/Architecture-mips-shared.h" #include "js/Utility.h" @@ -59,7 +42,7 @@ static const uint32_t BAILOUT_TABLE_ENTRY_SIZE = 2 * sizeof(void*); // When using O32 ABI, floating-point coprocessor is 32 bit. // When using N32 ABI, floating-point coprocessor is 64 bit. -class FloatRegisters : public BaseFloatRegisters +class FloatRegisters : public FloatRegistersMIPSShared { public: static const char* GetName(uint32_t i) { @@ -128,7 +111,7 @@ class FloatRegisters : public BaseFloatRegisters static const SetType AllocatableMask = AllMask & ~NonAllocatableMask; }; -class FloatRegister : public BaseFloatRegister +class FloatRegister : public FloatRegisterMIPSShared { public: enum RegType { diff --git a/js/src/jit/mips32/Assembler-mips32.h b/js/src/jit/mips32/Assembler-mips32.h index e1d88937c4..b986390b26 100644 --- a/js/src/jit/mips32/Assembler-mips32.h +++ b/js/src/jit/mips32/Assembler-mips32.h @@ -7,13 +7,6 @@ #ifndef jit_mips32_Assembler_mips32_h #define jit_mips32_Assembler_mips32_h -// NOTE: Don't use these macros directly -// CallTempNonArgRegs -#define CALL_TEMP_NON_ARG_REGS \ - { t0, t1, t2, t3, t4 }; -// NumIntArgRegs -#define NUM_INT_ARG_REGS 4; - #include "jit/mips-shared/Assembler-mips-shared.h" #include "jit/mips32/Architecture-mips32.h" @@ -24,6 +17,9 @@ namespace jit { static MOZ_CONSTEXPR_VAR Register CallTempReg4 = t4; static MOZ_CONSTEXPR_VAR Register CallTempReg5 = t5; +static MOZ_CONSTEXPR_VAR Register CallTempNonArgRegs[] = { t0, t1, t2, t3, t4 }; +static const uint32_t NumCallTempNonArgRegs = mozilla::ArrayLength(CallTempNonArgRegs); + class ABIArgGenerator { unsigned usedArgSlots_; @@ -159,6 +155,42 @@ class Assembler : public AssemblerMIPSShared static int32_t ExtractCodeLabelOffset(uint8_t* code); }; // Assembler +static const uint32_t NumIntArgRegs = 4; + +static inline bool +GetIntArgReg(uint32_t usedArgSlots, Register* out) +{ + if (usedArgSlots < NumIntArgRegs) { + *out = Register::FromCode(a0.code() + usedArgSlots); + return true; + } + return false; +} + +// Get a register in which we plan to put a quantity that will be used as an +// integer argument. This differs from GetIntArgReg in that if we have no more +// actual argument registers to use we will fall back on using whatever +// CallTempReg* don't overlap the argument registers, and only fail once those +// run out too. +static inline bool +GetTempRegForIntArg(uint32_t usedIntArgs, uint32_t usedFloatArgs, Register* out) +{ + // NOTE: We can't properly determine which regs are used if there are + // float arguments. If this is needed, we will have to guess. + MOZ_ASSERT(usedFloatArgs == 0); + + if (GetIntArgReg(usedIntArgs, out)) + return true; + // Unfortunately, we have to assume things about the point at which + // GetIntArgReg returns false, because we need to know how many registers it + // can allocate. + usedIntArgs -= NumIntArgRegs; + if (usedIntArgs >= NumCallTempNonArgRegs) + return false; + *out = CallTempNonArgRegs[usedIntArgs]; + return true; +} + static inline uint32_t GetArgStackDisp(uint32_t usedArgSlots) { diff --git a/js/src/jit/mips32/Bailouts-mips32.cpp b/js/src/jit/mips32/Bailouts-mips32.cpp index 800cf95c58..c8856fdc6d 100644 --- a/js/src/jit/mips32/Bailouts-mips32.cpp +++ b/js/src/jit/mips32/Bailouts-mips32.cpp @@ -4,11 +4,11 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "jit/mips32/Bailouts-mips32.h" - #include "jscntxt.h" #include "jscompartment.h" +#include "jit/mips-shared/Bailouts-mips-shared.h" + using namespace js; using namespace js::jit; @@ -46,17 +46,3 @@ BailoutFrameInfo::BailoutFrameInfo(const JitActivationIterator& activations, snapshotOffset_ = topIonScript_->bailoutToSnapshot(bailoutId); } - -BailoutFrameInfo::BailoutFrameInfo(const JitActivationIterator& activations, - InvalidationBailoutStack* bailout) - : machine_(bailout->machine()) -{ - framePointer_ = (uint8_t*) bailout->fp(); - topFrameSize_ = framePointer_ - bailout->sp(); - topIonScript_ = bailout->ionScript(); - attachOnJitActivation(activations); - - uint8_t* returnAddressToFp_ = bailout->osiPointReturnAddress(); - const OsiIndex* osiIndex = topIonScript_->getOsiIndex(returnAddressToFp_); - snapshotOffset_ = osiIndex->snapshotOffset(); -} diff --git a/js/src/jit/mips32/BaselineCompiler-mips32.cpp b/js/src/jit/mips32/BaselineCompiler-mips32.cpp index 5686045426..acbc67ff01 100644 --- a/js/src/jit/mips32/BaselineCompiler-mips32.cpp +++ b/js/src/jit/mips32/BaselineCompiler-mips32.cpp @@ -11,6 +11,6 @@ using namespace js::jit; BaselineCompilerMIPS::BaselineCompilerMIPS(JSContext* cx, TempAllocator& alloc, JSScript* script) - : BaselineCompilerShared(cx, alloc, script) + : BaselineCompilerMIPSShared(cx, alloc, script) { } diff --git a/js/src/jit/mips32/BaselineCompiler-mips32.h b/js/src/jit/mips32/BaselineCompiler-mips32.h index 22ab3fd069..cd6fe41eea 100644 --- a/js/src/jit/mips32/BaselineCompiler-mips32.h +++ b/js/src/jit/mips32/BaselineCompiler-mips32.h @@ -7,12 +7,12 @@ #ifndef jit_mips32_BaselineCompiler_mips32_h #define jit_mips32_BaselineCompiler_mips32_h -#include "jit/shared/BaselineCompiler-shared.h" +#include "jit/mips-shared/BaselineCompiler-mips-shared.h" namespace js { namespace jit { -class BaselineCompilerMIPS : public BaselineCompilerShared +class BaselineCompilerMIPS : public BaselineCompilerMIPSShared { protected: BaselineCompilerMIPS(JSContext* cx, TempAllocator& alloc, JSScript* script); diff --git a/js/src/jit/mips32/BaselineIC-mips32.cpp b/js/src/jit/mips32/BaselineIC-mips32.cpp index 2a3c9fdc7f..e41ecf774e 100644 --- a/js/src/jit/mips32/BaselineIC-mips32.cpp +++ b/js/src/jit/mips32/BaselineIC-mips32.cpp @@ -4,16 +4,12 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "jsiter.h" - #include "jit/BaselineCompiler.h" #include "jit/BaselineIC.h" #include "jit/BaselineJIT.h" #include "jit/Linker.h" #include "jit/SharedICHelpers.h" -#include "jsboolinlines.h" - using namespace js; using namespace js::jit; @@ -45,27 +41,5 @@ ICCompare_Int32::Compiler::generateStubCode(MacroAssembler& masm) return true; } -bool -ICCompare_Double::Compiler::generateStubCode(MacroAssembler& masm) -{ - Label failure, isNaN; - masm.ensureDouble(R0, FloatReg0, &failure); - masm.ensureDouble(R1, FloatReg1, &failure); - - Register dest = R0.scratchReg(); - - Assembler::DoubleCondition doubleCond = JSOpToDoubleCondition(op); - - masm.ma_cmp_set_double(dest, FloatReg0, FloatReg1, doubleCond); - - masm.tagValue(JSVAL_TYPE_BOOLEAN, dest, R0); - EmitReturnFromIC(masm); - - // Failure case - jump to next stub - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - } // namespace jit } // namespace js diff --git a/js/src/jit/mips32/CodeGenerator-mips32.cpp b/js/src/jit/mips32/CodeGenerator-mips32.cpp index 1970e2efb5..f469463fda 100644 --- a/js/src/jit/mips32/CodeGenerator-mips32.cpp +++ b/js/src/jit/mips32/CodeGenerator-mips32.cpp @@ -8,10 +8,6 @@ #include "mozilla/MathAlgorithms.h" -#include "jscntxt.h" -#include "jscompartment.h" -#include "jsnum.h" - #include "jit/CodeGenerator.h" #include "jit/JitCompartment.h" #include "jit/JitFrames.h" @@ -21,923 +17,12 @@ #include "vm/Shape.h" #include "vm/TraceLogging.h" -#include "jsscriptinlines.h" - #include "jit/MacroAssembler-inl.h" #include "jit/shared/CodeGenerator-shared-inl.h" using namespace js; using namespace js::jit; -using mozilla::FloorLog2; -using mozilla::NegativeInfinity; -using JS::GenericNaN; -using JS::ToInt32; - -// inline -Address -CodeGeneratorMIPS::ToAddress(const LAllocation& a) -{ - MOZ_ASSERT(a.isMemory()); - int32_t offset = ToStackOffset(&a); - - return Address(StackPointer, offset); -} - -// inline -Address -CodeGeneratorMIPS::ToAddress(const LAllocation* a) -{ - return ToAddress(*a); -} - - -// shared -CodeGeneratorMIPS::CodeGeneratorMIPS(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm) - : CodeGeneratorShared(gen, graph, masm) -{ -} - -void -CodeGeneratorMIPS::branchToBlock(Assembler::FloatFormat fmt, FloatRegister lhs, FloatRegister rhs, - MBasicBlock* mir, Assembler::DoubleCondition cond) -{ - // Skip past trivial blocks. - mir = skipTrivialBlocks(mir); - - Label* label = mir->lir()->label(); - if (Label* oolEntry = labelForBackedgeWithImplicitCheck(mir)) { - // Note: the backedge is initially a jump to the next instruction. - // It will be patched to the target block's label during link(). - RepatchLabel rejoin; - - CodeOffsetJump backedge; - Label skip; - if (fmt == Assembler::DoubleFloat) - masm.ma_bc1d(lhs, rhs, &skip, Assembler::InvertCondition(cond), ShortJump); - else - masm.ma_bc1s(lhs, rhs, &skip, Assembler::InvertCondition(cond), ShortJump); - - backedge = masm.backedgeJump(&rejoin); - masm.bind(&rejoin); - masm.bind(&skip); - - if (!patchableBackedges_.append(PatchableBackedgeInfo(backedge, label, oolEntry))) - MOZ_CRASH(); - } else { - if (fmt == Assembler::DoubleFloat) - masm.branchDouble(cond, lhs, rhs, mir->lir()->label()); - else - masm.branchFloat(cond, lhs, rhs, mir->lir()->label()); - } -} - -void -OutOfLineBailout::accept(CodeGeneratorMIPS* codegen) -{ - codegen->visitOutOfLineBailout(this); -} - -void -CodeGeneratorMIPS::visitTestIAndBranch(LTestIAndBranch* test) -{ - const LAllocation* opd = test->getOperand(0); - MBasicBlock* ifTrue = test->ifTrue(); - MBasicBlock* ifFalse = test->ifFalse(); - - emitBranch(ToRegister(opd), Imm32(0), Assembler::NonZero, ifTrue, ifFalse); -} - -void -CodeGeneratorMIPS::visitCompare(LCompare* comp) -{ - Assembler::Condition cond = JSOpToCondition(comp->mir()->compareType(), comp->jsop()); - const LAllocation* left = comp->getOperand(0); - const LAllocation* right = comp->getOperand(1); - const LDefinition* def = comp->getDef(0); - - if (right->isConstant()) - masm.cmp32Set(cond, ToRegister(left), Imm32(ToInt32(right)), ToRegister(def)); - else if (right->isGeneralReg()) - masm.cmp32Set(cond, ToRegister(left), ToRegister(right), ToRegister(def)); - else - masm.cmp32Set(cond, ToRegister(left), ToAddress(right), ToRegister(def)); -} - -void -CodeGeneratorMIPS::visitCompareAndBranch(LCompareAndBranch* comp) -{ - Assembler::Condition cond = JSOpToCondition(comp->cmpMir()->compareType(), comp->jsop()); - if (comp->right()->isConstant()) { - emitBranch(ToRegister(comp->left()), Imm32(ToInt32(comp->right())), cond, - comp->ifTrue(), comp->ifFalse()); - } else if (comp->right()->isGeneralReg()) { - emitBranch(ToRegister(comp->left()), ToRegister(comp->right()), cond, - comp->ifTrue(), comp->ifFalse()); - } else { - emitBranch(ToRegister(comp->left()), ToAddress(comp->right()), cond, - comp->ifTrue(), comp->ifFalse()); - } -} - -bool -CodeGeneratorMIPS::generateOutOfLineCode() -{ - if (!CodeGeneratorShared::generateOutOfLineCode()) - return false; - - if (deoptLabel_.used()) { - // All non-table-based bailouts will go here. - masm.bind(&deoptLabel_); - - // Push the frame size, so the handler can recover the IonScript. - // Frame size is stored in 'ra' and pushed by GenerateBailoutThunk - // We have to use 'ra' because generateBailoutTable will implicitly do - // the same. - masm.move32(Imm32(frameSize()), ra); - - JitCode* handler = gen->jitRuntime()->getGenericBailoutHandler(); - - masm.branch(handler); - } - - return true; -} - -void -CodeGeneratorMIPS::bailoutFrom(Label* label, LSnapshot* snapshot) -{ - if (masm.bailed()) - return; - - MOZ_ASSERT(label->used()); - MOZ_ASSERT(!label->bound()); - - encode(snapshot); - - // Though the assembler doesn't track all frame pushes, at least make sure - // the known value makes sense. We can't use bailout tables if the stack - // isn't properly aligned to the static frame size. - MOZ_ASSERT_IF(frameClass_ != FrameSizeClass::None(), - frameClass_.frameSize() == masm.framePushed()); - - // We don't use table bailouts because retargeting is easier this way. - InlineScriptTree* tree = snapshot->mir()->block()->trackedTree(); - OutOfLineBailout* ool = new(alloc()) OutOfLineBailout(snapshot, masm.framePushed()); - addOutOfLineCode(ool, new(alloc()) BytecodeSite(tree, tree->script()->code())); - - masm.retarget(label, ool->entry()); -} - -void -CodeGeneratorMIPS::bailout(LSnapshot* snapshot) -{ - Label label; - masm.jump(&label); - bailoutFrom(&label, snapshot); -} - -void -CodeGeneratorMIPS::visitOutOfLineBailout(OutOfLineBailout* ool) -{ - // Push snapshotOffset and make sure stack is aligned. - masm.subPtr(Imm32(2 * sizeof(void*)), StackPointer); - masm.storePtr(ImmWord(ool->snapshot()->snapshotOffset()), Address(StackPointer, 0)); - - masm.jump(&deoptLabel_); -} - -void -CodeGeneratorMIPS::visitMinMaxD(LMinMaxD* ins) -{ - FloatRegister first = ToFloatRegister(ins->first()); - FloatRegister second = ToFloatRegister(ins->second()); - FloatRegister output = ToFloatRegister(ins->output()); - - MOZ_ASSERT(first == output); - - Assembler::DoubleCondition cond = ins->mir()->isMax() - ? Assembler::DoubleLessThanOrEqual - : Assembler::DoubleGreaterThanOrEqual; - Label nan, equal, returnSecond, done; - - // First or second is NaN, result is NaN. - masm.ma_bc1d(first, second, &nan, Assembler::DoubleUnordered, ShortJump); - // Make sure we handle -0 and 0 right. - masm.ma_bc1d(first, second, &equal, Assembler::DoubleEqual, ShortJump); - masm.ma_bc1d(first, second, &returnSecond, cond, ShortJump); - masm.ma_b(&done, ShortJump); - - // Check for zero. - masm.bind(&equal); - masm.loadConstantDouble(0.0, ScratchDoubleReg); - // First wasn't 0 or -0, so just return it. - masm.ma_bc1d(first, ScratchDoubleReg, &done, Assembler::DoubleNotEqualOrUnordered, ShortJump); - - // So now both operands are either -0 or 0. - if (ins->mir()->isMax()) { - // -0 + -0 = -0 and -0 + 0 = 0. - masm.addDouble(second, first); - } else { - masm.negateDouble(first); - masm.subDouble(second, first); - masm.negateDouble(first); - } - masm.ma_b(&done, ShortJump); - - masm.bind(&nan); - masm.loadConstantDouble(GenericNaN(), output); - masm.ma_b(&done, ShortJump); - - masm.bind(&returnSecond); - masm.moveDouble(second, output); - - masm.bind(&done); -} - -void -CodeGeneratorMIPS::visitMinMaxF(LMinMaxF* ins) -{ - FloatRegister first = ToFloatRegister(ins->first()); - FloatRegister second = ToFloatRegister(ins->second()); - FloatRegister output = ToFloatRegister(ins->output()); - - MOZ_ASSERT(first == output); - - Assembler::DoubleCondition cond = ins->mir()->isMax() - ? Assembler::DoubleLessThanOrEqual - : Assembler::DoubleGreaterThanOrEqual; - Label nan, equal, returnSecond, done; - - // First or second is NaN, result is NaN. - masm.ma_bc1s(first, second, &nan, Assembler::DoubleUnordered, ShortJump); - // Make sure we handle -0 and 0 right. - masm.ma_bc1s(first, second, &equal, Assembler::DoubleEqual, ShortJump); - masm.ma_bc1s(first, second, &returnSecond, cond, ShortJump); - masm.ma_b(&done, ShortJump); - - // Check for zero. - masm.bind(&equal); - masm.loadConstantFloat32(0.0, ScratchFloat32Reg); - // First wasn't 0 or -0, so just return it. - masm.ma_bc1s(first, ScratchFloat32Reg, &done, Assembler::DoubleNotEqualOrUnordered, ShortJump); - - // So now both operands are either -0 or 0. - if (ins->mir()->isMax()) { - // -0 + -0 = -0 and -0 + 0 = 0. - masm.as_adds(first, first, second); - } else { - masm.as_negs(first, first); - masm.as_subs(first, first, second); - masm.as_negs(first, first); - } - masm.ma_b(&done, ShortJump); - - masm.bind(&nan); - masm.loadConstantFloat32(GenericNaN(), output); - masm.ma_b(&done, ShortJump); - masm.bind(&returnSecond); - masm.as_movs(output, second); - - masm.bind(&done); -} - -void -CodeGeneratorMIPS::visitAbsD(LAbsD* ins) -{ - FloatRegister input = ToFloatRegister(ins->input()); - MOZ_ASSERT(input == ToFloatRegister(ins->output())); - masm.as_absd(input, input); -} - -void -CodeGeneratorMIPS::visitAbsF(LAbsF* ins) -{ - FloatRegister input = ToFloatRegister(ins->input()); - MOZ_ASSERT(input == ToFloatRegister(ins->output())); - masm.as_abss(input, input); -} - -void -CodeGeneratorMIPS::visitSqrtD(LSqrtD* ins) -{ - FloatRegister input = ToFloatRegister(ins->input()); - FloatRegister output = ToFloatRegister(ins->output()); - masm.as_sqrtd(output, input); -} - -void -CodeGeneratorMIPS::visitSqrtF(LSqrtF* ins) -{ - FloatRegister input = ToFloatRegister(ins->input()); - FloatRegister output = ToFloatRegister(ins->output()); - masm.as_sqrts(output, input); -} - -void -CodeGeneratorMIPS::visitAddI(LAddI* ins) -{ - const LAllocation* lhs = ins->getOperand(0); - const LAllocation* rhs = ins->getOperand(1); - const LDefinition* dest = ins->getDef(0); - - MOZ_ASSERT(rhs->isConstant() || rhs->isGeneralReg()); - - // If there is no snapshot, we don't need to check for overflow - if (!ins->snapshot()) { - if (rhs->isConstant()) - masm.ma_addu(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs))); - else - masm.as_addu(ToRegister(dest), ToRegister(lhs), ToRegister(rhs)); - return; - } - - Label overflow; - if (rhs->isConstant()) - masm.ma_addTestOverflow(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs)), &overflow); - else - masm.ma_addTestOverflow(ToRegister(dest), ToRegister(lhs), ToRegister(rhs), &overflow); - - bailoutFrom(&overflow, ins->snapshot()); -} - -void -CodeGeneratorMIPS::visitSubI(LSubI* ins) -{ - const LAllocation* lhs = ins->getOperand(0); - const LAllocation* rhs = ins->getOperand(1); - const LDefinition* dest = ins->getDef(0); - - MOZ_ASSERT(rhs->isConstant() || rhs->isGeneralReg()); - - // If there is no snapshot, we don't need to check for overflow - if (!ins->snapshot()) { - if (rhs->isConstant()) - masm.ma_subu(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs))); - else - masm.as_subu(ToRegister(dest), ToRegister(lhs), ToRegister(rhs)); - return; - } - - Label overflow; - if (rhs->isConstant()) - masm.ma_subTestOverflow(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs)), &overflow); - else - masm.ma_subTestOverflow(ToRegister(dest), ToRegister(lhs), ToRegister(rhs), &overflow); - - bailoutFrom(&overflow, ins->snapshot()); -} - -void -CodeGeneratorMIPS::visitMulI(LMulI* ins) -{ - const LAllocation* lhs = ins->lhs(); - const LAllocation* rhs = ins->rhs(); - Register dest = ToRegister(ins->output()); - MMul* mul = ins->mir(); - - MOZ_ASSERT_IF(mul->mode() == MMul::Integer, !mul->canBeNegativeZero() && !mul->canOverflow()); - - if (rhs->isConstant()) { - int32_t constant = ToInt32(rhs); - Register src = ToRegister(lhs); - - // Bailout on -0.0 - if (mul->canBeNegativeZero() && constant <= 0) { - Assembler::Condition cond = (constant == 0) ? Assembler::LessThan : Assembler::Equal; - bailoutCmp32(cond, src, Imm32(0), ins->snapshot()); - } - - switch (constant) { - case -1: - if (mul->canOverflow()) - bailoutCmp32(Assembler::Equal, src, Imm32(INT32_MIN), ins->snapshot()); - - masm.ma_negu(dest, src); - break; - case 0: - masm.move32(Imm32(0), dest); - break; - case 1: - masm.move32(src, dest); - break; - case 2: - if (mul->canOverflow()) { - Label mulTwoOverflow; - masm.ma_addTestOverflow(dest, src, src, &mulTwoOverflow); - - bailoutFrom(&mulTwoOverflow, ins->snapshot()); - } else { - masm.as_addu(dest, src, src); - } - break; - default: - uint32_t shift = FloorLog2(constant); - - if (!mul->canOverflow() && (constant > 0)) { - // If it cannot overflow, we can do lots of optimizations. - uint32_t rest = constant - (1 << shift); - - // See if the constant has one bit set, meaning it can be - // encoded as a bitshift. - if ((1 << shift) == constant) { - masm.ma_sll(dest, src, Imm32(shift)); - return; - } - - // If the constant cannot be encoded as (1<canOverflow() && (constant > 0) && (src != dest)) { - // To stay on the safe side, only optimize things that are a - // power of 2. - - if ((1 << shift) == constant) { - // dest = lhs * pow(2, shift) - masm.ma_sll(dest, src, Imm32(shift)); - // At runtime, check (lhs == dest >> shift), if this does - // not hold, some bits were lost due to overflow, and the - // computation should be resumed as a double. - masm.ma_sra(ScratchRegister, dest, Imm32(shift)); - bailoutCmp32(Assembler::NotEqual, src, ScratchRegister, ins->snapshot()); - return; - } - } - - if (mul->canOverflow()) { - Label mulConstOverflow; - masm.ma_mul_branch_overflow(dest, ToRegister(lhs), Imm32(ToInt32(rhs)), - &mulConstOverflow); - - bailoutFrom(&mulConstOverflow, ins->snapshot()); - } else { - masm.ma_mult(src, Imm32(ToInt32(rhs))); - masm.as_mflo(dest); - } - break; - } - } else { - Label multRegOverflow; - - if (mul->canOverflow()) { - masm.ma_mul_branch_overflow(dest, ToRegister(lhs), ToRegister(rhs), &multRegOverflow); - bailoutFrom(&multRegOverflow, ins->snapshot()); - } else { - masm.as_mult(ToRegister(lhs), ToRegister(rhs)); - masm.as_mflo(dest); - } - - if (mul->canBeNegativeZero()) { - Label done; - masm.ma_b(dest, dest, &done, Assembler::NonZero, ShortJump); - - // Result is -0 if lhs or rhs is negative. - // In that case result must be double value so bailout - Register scratch = SecondScratchReg; - masm.as_or(scratch, ToRegister(lhs), ToRegister(rhs)); - bailoutCmp32(Assembler::Signed, scratch, scratch, ins->snapshot()); - - masm.bind(&done); - } - } -} - -void -CodeGeneratorMIPS::visitDivI(LDivI* ins) -{ - // Extract the registers from this instruction - Register lhs = ToRegister(ins->lhs()); - Register rhs = ToRegister(ins->rhs()); - Register dest = ToRegister(ins->output()); - Register temp = ToRegister(ins->getTemp(0)); - MDiv* mir = ins->mir(); - - Label done; - - // Handle divide by zero. - if (mir->canBeDivideByZero()) { - if (mir->canTruncateInfinities()) { - // Truncated division by zero is zero (Infinity|0 == 0) - Label notzero; - masm.ma_b(rhs, rhs, ¬zero, Assembler::NonZero, ShortJump); - masm.move32(Imm32(0), dest); - masm.ma_b(&done, ShortJump); - masm.bind(¬zero); - } else { - MOZ_ASSERT(mir->fallible()); - bailoutCmp32(Assembler::Zero, rhs, rhs, ins->snapshot()); - } - } - - // Handle an integer overflow exception from -2147483648 / -1. - if (mir->canBeNegativeOverflow()) { - Label notMinInt; - masm.move32(Imm32(INT32_MIN), temp); - masm.ma_b(lhs, temp, ¬MinInt, Assembler::NotEqual, ShortJump); - - masm.move32(Imm32(-1), temp); - if (mir->canTruncateOverflow()) { - // (-INT32_MIN)|0 == INT32_MIN - Label skip; - masm.ma_b(rhs, temp, &skip, Assembler::NotEqual, ShortJump); - masm.move32(Imm32(INT32_MIN), dest); - masm.ma_b(&done, ShortJump); - masm.bind(&skip); - } else { - MOZ_ASSERT(mir->fallible()); - bailoutCmp32(Assembler::Equal, rhs, temp, ins->snapshot()); - } - masm.bind(¬MinInt); - } - - // Handle negative 0. (0/-Y) - if (!mir->canTruncateNegativeZero() && mir->canBeNegativeZero()) { - Label nonzero; - masm.ma_b(lhs, lhs, &nonzero, Assembler::NonZero, ShortJump); - bailoutCmp32(Assembler::LessThan, rhs, Imm32(0), ins->snapshot()); - masm.bind(&nonzero); - } - // Note: above safety checks could not be verified as Ion seems to be - // smarter and requires double arithmetic in such cases. - - // All regular. Lets call div. - if (mir->canTruncateRemainder()) { - masm.as_div(lhs, rhs); - masm.as_mflo(dest); - } else { - MOZ_ASSERT(mir->fallible()); - - Label remainderNonZero; - masm.ma_div_branch_overflow(dest, lhs, rhs, &remainderNonZero); - bailoutFrom(&remainderNonZero, ins->snapshot()); - } - - masm.bind(&done); -} - -void -CodeGeneratorMIPS::visitDivPowTwoI(LDivPowTwoI* ins) -{ - Register lhs = ToRegister(ins->numerator()); - Register dest = ToRegister(ins->output()); - Register tmp = ToRegister(ins->getTemp(0)); - int32_t shift = ins->shift(); - - if (shift != 0) { - MDiv* mir = ins->mir(); - if (!mir->isTruncated()) { - // If the remainder is going to be != 0, bailout since this must - // be a double. - masm.ma_sll(tmp, lhs, Imm32(32 - shift)); - bailoutCmp32(Assembler::NonZero, tmp, tmp, ins->snapshot()); - } - - if (!mir->canBeNegativeDividend()) { - // Numerator is unsigned, so needs no adjusting. Do the shift. - masm.ma_sra(dest, lhs, Imm32(shift)); - return; - } - - // Adjust the value so that shifting produces a correctly rounded result - // when the numerator is negative. See 10-1 "Signed Division by a Known - // Power of 2" in Henry S. Warren, Jr.'s Hacker's Delight. - if (shift > 1) { - masm.ma_sra(tmp, lhs, Imm32(31)); - masm.ma_srl(tmp, tmp, Imm32(32 - shift)); - masm.add32(lhs, tmp); - } else { - masm.ma_srl(tmp, lhs, Imm32(32 - shift)); - masm.add32(lhs, tmp); - } - - // Do the shift. - masm.ma_sra(dest, tmp, Imm32(shift)); - } else { - masm.move32(lhs, dest); - } -} - -void -CodeGeneratorMIPS::visitModI(LModI* ins) -{ - // Extract the registers from this instruction - Register lhs = ToRegister(ins->lhs()); - Register rhs = ToRegister(ins->rhs()); - Register dest = ToRegister(ins->output()); - Register callTemp = ToRegister(ins->callTemp()); - MMod* mir = ins->mir(); - Label done, prevent; - - masm.move32(lhs, callTemp); - - // Prevent INT_MIN % -1; - // The integer division will give INT_MIN, but we want -(double)INT_MIN. - if (mir->canBeNegativeDividend()) { - masm.ma_b(lhs, Imm32(INT_MIN), &prevent, Assembler::NotEqual, ShortJump); - if (mir->isTruncated()) { - // (INT_MIN % -1)|0 == 0 - Label skip; - masm.ma_b(rhs, Imm32(-1), &skip, Assembler::NotEqual, ShortJump); - masm.move32(Imm32(0), dest); - masm.ma_b(&done, ShortJump); - masm.bind(&skip); - } else { - MOZ_ASSERT(mir->fallible()); - bailoutCmp32(Assembler::Equal, rhs, Imm32(-1), ins->snapshot()); - } - masm.bind(&prevent); - } - - // 0/X (with X < 0) is bad because both of these values *should* be - // doubles, and the result should be -0.0, which cannot be represented in - // integers. X/0 is bad because it will give garbage (or abort), when it - // should give either \infty, -\infty or NAN. - - // Prevent 0 / X (with X < 0) and X / 0 - // testing X / Y. Compare Y with 0. - // There are three cases: (Y < 0), (Y == 0) and (Y > 0) - // If (Y < 0), then we compare X with 0, and bail if X == 0 - // If (Y == 0), then we simply want to bail. - // if (Y > 0), we don't bail. - - if (mir->canBeDivideByZero()) { - if (mir->isTruncated()) { - Label skip; - masm.ma_b(rhs, Imm32(0), &skip, Assembler::NotEqual, ShortJump); - masm.move32(Imm32(0), dest); - masm.ma_b(&done, ShortJump); - masm.bind(&skip); - } else { - MOZ_ASSERT(mir->fallible()); - bailoutCmp32(Assembler::Equal, rhs, Imm32(0), ins->snapshot()); - } - } - - if (mir->canBeNegativeDividend()) { - Label notNegative; - masm.ma_b(rhs, Imm32(0), ¬Negative, Assembler::GreaterThan, ShortJump); - if (mir->isTruncated()) { - // NaN|0 == 0 and (0 % -X)|0 == 0 - Label skip; - masm.ma_b(lhs, Imm32(0), &skip, Assembler::NotEqual, ShortJump); - masm.move32(Imm32(0), dest); - masm.ma_b(&done, ShortJump); - masm.bind(&skip); - } else { - MOZ_ASSERT(mir->fallible()); - bailoutCmp32(Assembler::Equal, lhs, Imm32(0), ins->snapshot()); - } - masm.bind(¬Negative); - } - - masm.as_div(lhs, rhs); - masm.as_mfhi(dest); - - // If X%Y == 0 and X < 0, then we *actually* wanted to return -0.0 - if (mir->canBeNegativeDividend()) { - if (mir->isTruncated()) { - // -0.0|0 == 0 - } else { - MOZ_ASSERT(mir->fallible()); - // See if X < 0 - masm.ma_b(dest, Imm32(0), &done, Assembler::NotEqual, ShortJump); - bailoutCmp32(Assembler::Signed, callTemp, Imm32(0), ins->snapshot()); - } - } - masm.bind(&done); -} - -void -CodeGeneratorMIPS::visitModPowTwoI(LModPowTwoI* ins) -{ - Register in = ToRegister(ins->getOperand(0)); - Register out = ToRegister(ins->getDef(0)); - MMod* mir = ins->mir(); - Label negative, done; - - masm.move32(in, out); - masm.ma_b(in, in, &done, Assembler::Zero, ShortJump); - // Switch based on sign of the lhs. - // Positive numbers are just a bitmask - masm.ma_b(in, in, &negative, Assembler::Signed, ShortJump); - { - masm.and32(Imm32((1 << ins->shift()) - 1), out); - masm.ma_b(&done, ShortJump); - } - - // Negative numbers need a negate, bitmask, negate - { - masm.bind(&negative); - masm.neg32(out); - masm.and32(Imm32((1 << ins->shift()) - 1), out); - masm.neg32(out); - } - if (mir->canBeNegativeDividend()) { - if (!mir->isTruncated()) { - MOZ_ASSERT(mir->fallible()); - bailoutCmp32(Assembler::Equal, out, zero, ins->snapshot()); - } else { - // -0|0 == 0 - } - } - masm.bind(&done); -} - -void -CodeGeneratorMIPS::visitModMaskI(LModMaskI* ins) -{ - Register src = ToRegister(ins->getOperand(0)); - Register dest = ToRegister(ins->getDef(0)); - Register tmp0 = ToRegister(ins->getTemp(0)); - Register tmp1 = ToRegister(ins->getTemp(1)); - MMod* mir = ins->mir(); - - if (!mir->isTruncated() && mir->canBeNegativeDividend()) { - MOZ_ASSERT(mir->fallible()); - - Label bail; - masm.ma_mod_mask(src, dest, tmp0, tmp1, ins->shift(), &bail); - bailoutFrom(&bail, ins->snapshot()); - } else { - masm.ma_mod_mask(src, dest, tmp0, tmp1, ins->shift(), nullptr); - } -} - -void -CodeGeneratorMIPS::visitBitNotI(LBitNotI* ins) -{ - const LAllocation* input = ins->getOperand(0); - const LDefinition* dest = ins->getDef(0); - MOZ_ASSERT(!input->isConstant()); - - masm.ma_not(ToRegister(dest), ToRegister(input)); -} - -void -CodeGeneratorMIPS::visitBitOpI(LBitOpI* ins) -{ - const LAllocation* lhs = ins->getOperand(0); - const LAllocation* rhs = ins->getOperand(1); - const LDefinition* dest = ins->getDef(0); - // all of these bitops should be either imm32's, or integer registers. - switch (ins->bitop()) { - case JSOP_BITOR: - if (rhs->isConstant()) - masm.ma_or(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs))); - else - masm.as_or(ToRegister(dest), ToRegister(lhs), ToRegister(rhs)); - break; - case JSOP_BITXOR: - if (rhs->isConstant()) - masm.ma_xor(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs))); - else - masm.as_xor(ToRegister(dest), ToRegister(lhs), ToRegister(rhs)); - break; - case JSOP_BITAND: - if (rhs->isConstant()) - masm.ma_and(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs))); - else - masm.as_and(ToRegister(dest), ToRegister(lhs), ToRegister(rhs)); - break; - default: - MOZ_CRASH("unexpected binary opcode"); - } -} - -void -CodeGeneratorMIPS::visitShiftI(LShiftI* ins) -{ - Register lhs = ToRegister(ins->lhs()); - const LAllocation* rhs = ins->rhs(); - Register dest = ToRegister(ins->output()); - - if (rhs->isConstant()) { - int32_t shift = ToInt32(rhs) & 0x1F; - switch (ins->bitop()) { - case JSOP_LSH: - if (shift) - masm.ma_sll(dest, lhs, Imm32(shift)); - else - masm.move32(lhs, dest); - break; - case JSOP_RSH: - if (shift) - masm.ma_sra(dest, lhs, Imm32(shift)); - else - masm.move32(lhs, dest); - break; - case JSOP_URSH: - if (shift) { - masm.ma_srl(dest, lhs, Imm32(shift)); - } else { - // x >>> 0 can overflow. - masm.move32(lhs, dest); - if (ins->mir()->toUrsh()->fallible()) - bailoutCmp32(Assembler::LessThan, dest, Imm32(0), ins->snapshot()); - } - break; - default: - MOZ_CRASH("Unexpected shift op"); - } - } else { - // The shift amounts should be AND'ed into the 0-31 range - masm.ma_and(dest, ToRegister(rhs), Imm32(0x1F)); - - switch (ins->bitop()) { - case JSOP_LSH: - masm.ma_sll(dest, lhs, dest); - break; - case JSOP_RSH: - masm.ma_sra(dest, lhs, dest); - break; - case JSOP_URSH: - masm.ma_srl(dest, lhs, dest); - if (ins->mir()->toUrsh()->fallible()) { - // x >>> 0 can overflow. - bailoutCmp32(Assembler::LessThan, dest, Imm32(0), ins->snapshot()); - } - break; - default: - MOZ_CRASH("Unexpected shift op"); - } - } -} - -void -CodeGeneratorMIPS::visitUrshD(LUrshD* ins) -{ - Register lhs = ToRegister(ins->lhs()); - Register temp = ToRegister(ins->temp()); - - const LAllocation* rhs = ins->rhs(); - FloatRegister out = ToFloatRegister(ins->output()); - - if (rhs->isConstant()) { - masm.ma_srl(temp, lhs, Imm32(ToInt32(rhs))); - } else { - masm.ma_srl(temp, lhs, ToRegister(rhs)); - } - - masm.convertUInt32ToDouble(temp, out); -} - -void -CodeGeneratorMIPS::visitClzI(LClzI* ins) -{ - Register input = ToRegister(ins->input()); - Register output = ToRegister(ins->output()); - - masm.as_clz(output, input); -} - -void -CodeGeneratorMIPS::visitPowHalfD(LPowHalfD* ins) -{ - FloatRegister input = ToFloatRegister(ins->input()); - FloatRegister output = ToFloatRegister(ins->output()); - - Label done, skip; - - // Masm.pow(-Infinity, 0.5) == Infinity. - masm.loadConstantDouble(NegativeInfinity(), ScratchDoubleReg); - masm.ma_bc1d(input, ScratchDoubleReg, &skip, Assembler::DoubleNotEqualOrUnordered, ShortJump); - masm.as_negd(output, ScratchDoubleReg); - masm.ma_b(&done, ShortJump); - - masm.bind(&skip); - // Math.pow(-0, 0.5) == 0 == Math.pow(0, 0.5). - // Adding 0 converts any -0 to 0. - masm.loadConstantDouble(0.0, ScratchDoubleReg); - masm.as_addd(output, input, ScratchDoubleReg); - masm.as_sqrtd(output, output); - - masm.bind(&done); -} - -MoveOperand -CodeGeneratorMIPS::toMoveOperand(LAllocation a) const -{ - if (a.isGeneralReg()) - return MoveOperand(ToRegister(a)); - if (a.isFloatReg()) { - return MoveOperand(ToFloatRegister(a)); - } - int32_t offset = ToStackOffset(a); - MOZ_ASSERT((offset & 3) == 0); - - return MoveOperand(StackPointer, offset); -} - class js::jit::OutOfLineTableSwitch : public OutOfLineCodeBase { MTableSwitch* mir_; @@ -1013,316 +98,6 @@ CodeGeneratorMIPS::emitTableSwitchDispatch(MTableSwitch* mir, Register index, masm.branch(address); } -void -CodeGeneratorMIPS::visitMathD(LMathD* math) -{ - const LAllocation* src1 = math->getOperand(0); - const LAllocation* src2 = math->getOperand(1); - const LDefinition* output = math->getDef(0); - - switch (math->jsop()) { - case JSOP_ADD: - masm.as_addd(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); - break; - case JSOP_SUB: - masm.as_subd(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); - break; - case JSOP_MUL: - masm.as_muld(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); - break; - case JSOP_DIV: - masm.as_divd(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); - break; - default: - MOZ_CRASH("unexpected opcode"); - } -} - -void -CodeGeneratorMIPS::visitMathF(LMathF* math) -{ - const LAllocation* src1 = math->getOperand(0); - const LAllocation* src2 = math->getOperand(1); - const LDefinition* output = math->getDef(0); - - switch (math->jsop()) { - case JSOP_ADD: - masm.as_adds(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); - break; - case JSOP_SUB: - masm.as_subs(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); - break; - case JSOP_MUL: - masm.as_muls(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); - break; - case JSOP_DIV: - masm.as_divs(ToFloatRegister(output), ToFloatRegister(src1), ToFloatRegister(src2)); - break; - default: - MOZ_CRASH("unexpected opcode"); - } -} - -void -CodeGeneratorMIPS::visitFloor(LFloor* lir) -{ - FloatRegister input = ToFloatRegister(lir->input()); - FloatRegister scratch = ScratchDoubleReg; - Register output = ToRegister(lir->output()); - - Label skipCheck, done; - - // If Nan, 0 or -0 check for bailout - masm.loadConstantDouble(0.0, scratch); - masm.ma_bc1d(input, scratch, &skipCheck, Assembler::DoubleNotEqual, ShortJump); - - // If high part is not zero, it is NaN or -0, so we bail. - masm.moveFromDoubleHi(input, SecondScratchReg); - bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(0), lir->snapshot()); - - // Input was zero, so return zero. - masm.move32(Imm32(0), output); - masm.ma_b(&done, ShortJump); - - masm.bind(&skipCheck); - masm.as_floorwd(scratch, input); - masm.moveFromDoubleLo(scratch, output); - - bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()); - bailoutCmp32(Assembler::Equal, output, Imm32(INT_MAX), lir->snapshot()); - - masm.bind(&done); -} - -void -CodeGeneratorMIPS::visitFloorF(LFloorF* lir) -{ - FloatRegister input = ToFloatRegister(lir->input()); - FloatRegister scratch = ScratchFloat32Reg; - Register output = ToRegister(lir->output()); - - Label skipCheck, done; - - // If Nan, 0 or -0 check for bailout - masm.loadConstantFloat32(0.0, scratch); - masm.ma_bc1s(input, scratch, &skipCheck, Assembler::DoubleNotEqual, ShortJump); - - // If binary value is not zero, it is NaN or -0, so we bail. - masm.moveFromDoubleLo(input, SecondScratchReg); - bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(0), lir->snapshot()); - - // Input was zero, so return zero. - masm.move32(Imm32(0), output); - masm.ma_b(&done, ShortJump); - - masm.bind(&skipCheck); - masm.as_floorws(scratch, input); - masm.moveFromDoubleLo(scratch, output); - - bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()); - bailoutCmp32(Assembler::Equal, output, Imm32(INT_MAX), lir->snapshot()); - - masm.bind(&done); -} - -void -CodeGeneratorMIPS::visitCeil(LCeil* lir) -{ - FloatRegister input = ToFloatRegister(lir->input()); - FloatRegister scratch = ScratchDoubleReg; - Register output = ToRegister(lir->output()); - - Label performCeil, done; - - // If x < -1 or x > 0 then perform ceil. - masm.loadConstantDouble(0, scratch); - masm.branchDouble(Assembler::DoubleGreaterThan, input, scratch, &performCeil); - masm.loadConstantDouble(-1, scratch); - masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, scratch, &performCeil); - - // If high part is not zero, the input was not 0, so we bail. - masm.moveFromDoubleHi(input, SecondScratchReg); - bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(0), lir->snapshot()); - - // Input was zero, so return zero. - masm.move32(Imm32(0), output); - masm.ma_b(&done, ShortJump); - - masm.bind(&performCeil); - masm.as_ceilwd(scratch, input); - masm.moveFromDoubleLo(scratch, output); - - bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()); - bailoutCmp32(Assembler::Equal, output, Imm32(INT_MAX), lir->snapshot()); - - masm.bind(&done); -} - -void -CodeGeneratorMIPS::visitCeilF(LCeilF* lir) -{ - FloatRegister input = ToFloatRegister(lir->input()); - FloatRegister scratch = ScratchFloat32Reg; - Register output = ToRegister(lir->output()); - - Label performCeil, done; - - // If x < -1 or x > 0 then perform ceil. - masm.loadConstantFloat32(0, scratch); - masm.branchFloat(Assembler::DoubleGreaterThan, input, scratch, &performCeil); - masm.loadConstantFloat32(-1, scratch); - masm.branchFloat(Assembler::DoubleLessThanOrEqual, input, scratch, &performCeil); - - // If binary value is not zero, the input was not 0, so we bail. - masm.moveFromFloat32(input, SecondScratchReg); - bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(0), lir->snapshot()); - - // Input was zero, so return zero. - masm.move32(Imm32(0), output); - masm.ma_b(&done, ShortJump); - - masm.bind(&performCeil); - masm.as_ceilws(scratch, input); - masm.moveFromFloat32(scratch, output); - - bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()); - bailoutCmp32(Assembler::Equal, output, Imm32(INT_MAX), lir->snapshot()); - - masm.bind(&done); -} - -void -CodeGeneratorMIPS::visitRound(LRound* lir) -{ - FloatRegister input = ToFloatRegister(lir->input()); - FloatRegister temp = ToFloatRegister(lir->temp()); - FloatRegister scratch = ScratchDoubleReg; - Register output = ToRegister(lir->output()); - - Label bail, negative, end, skipCheck; - - // Load 0.5 in the temp register. - masm.loadConstantDouble(0.5, temp); - - // Branch to a slow path for negative inputs. Doesn't catch NaN or -0. - masm.loadConstantDouble(0.0, scratch); - masm.ma_bc1d(input, scratch, &negative, Assembler::DoubleLessThan, ShortJump); - - // If Nan, 0 or -0 check for bailout - masm.ma_bc1d(input, scratch, &skipCheck, Assembler::DoubleNotEqual, ShortJump); - - // If high part is not zero, it is NaN or -0, so we bail. - masm.moveFromDoubleHi(input, SecondScratchReg); - bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(0), lir->snapshot()); - - // Input was zero, so return zero. - masm.move32(Imm32(0), output); - masm.ma_b(&end, ShortJump); - - masm.bind(&skipCheck); - masm.loadConstantDouble(0.5, scratch); - masm.addDouble(input, scratch); - masm.as_floorwd(scratch, scratch); - - masm.moveFromDoubleLo(scratch, output); - - bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()); - bailoutCmp32(Assembler::Equal, output, Imm32(INT_MAX), lir->snapshot()); - - masm.jump(&end); - - // Input is negative, but isn't -0. - masm.bind(&negative); - masm.addDouble(input, temp); - - // If input + 0.5 >= 0, input is a negative number >= -0.5 and the - // result is -0. - masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, temp, scratch, &bail); - bailoutFrom(&bail, lir->snapshot()); - - // Truncate and round toward zero. - // This is off-by-one for everything but integer-valued inputs. - masm.as_floorwd(scratch, temp); - masm.moveFromDoubleLo(scratch, output); - - bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()); - - masm.bind(&end); -} - -void -CodeGeneratorMIPS::visitRoundF(LRoundF* lir) -{ - FloatRegister input = ToFloatRegister(lir->input()); - FloatRegister temp = ToFloatRegister(lir->temp()); - FloatRegister scratch = ScratchFloat32Reg; - Register output = ToRegister(lir->output()); - - Label bail, negative, end, skipCheck; - - // Load 0.5 in the temp register. - masm.loadConstantFloat32(0.5, temp); - - // Branch to a slow path for negative inputs. Doesn't catch NaN or -0. - masm.loadConstantFloat32(0.0, scratch); - masm.ma_bc1s(input, scratch, &negative, Assembler::DoubleLessThan, ShortJump); - - // If Nan, 0 or -0 check for bailout - masm.ma_bc1s(input, scratch, &skipCheck, Assembler::DoubleNotEqual, ShortJump); - - // If binary value is not zero, it is NaN or -0, so we bail. - masm.moveFromFloat32(input, SecondScratchReg); - bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(0), lir->snapshot()); - - // Input was zero, so return zero. - masm.move32(Imm32(0), output); - masm.ma_b(&end, ShortJump); - - masm.bind(&skipCheck); - masm.loadConstantFloat32(0.5, scratch); - masm.as_adds(scratch, input, scratch); - masm.as_floorws(scratch, scratch); - - masm.moveFromFloat32(scratch, output); - - bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()); - bailoutCmp32(Assembler::Equal, output, Imm32(INT_MAX), lir->snapshot()); - - masm.jump(&end); - - // Input is negative, but isn't -0. - masm.bind(&negative); - masm.as_adds(temp, input, temp); - - // If input + 0.5 >= 0, input is a negative number >= -0.5 and the - // result is -0. - masm.branchFloat(Assembler::DoubleGreaterThanOrEqual, temp, scratch, &bail); - bailoutFrom(&bail, lir->snapshot()); - - // Truncate and round toward zero. - // This is off-by-one for everything but integer-valued inputs. - masm.as_floorws(scratch, temp); - masm.moveFromFloat32(scratch, output); - - bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot()); - - masm.bind(&end); -} - -void -CodeGeneratorMIPS::visitTruncateDToInt32(LTruncateDToInt32* ins) -{ - emitTruncateDouble(ToFloatRegister(ins->input()), ToRegister(ins->output()), - ins->mir()); -} - -void -CodeGeneratorMIPS::visitTruncateFToInt32(LTruncateFToInt32* ins) -{ - emitTruncateFloat32(ToFloatRegister(ins->input()), ToRegister(ins->output()), - ins->mir()); -} - static const uint32_t FrameSizes[] = { 128, 256, 512, 1024 }; FrameSizeClass @@ -1375,14 +150,6 @@ CodeGeneratorMIPS::ToTempValue(LInstruction* ins, size_t pos) return ValueOperand(typeReg, payloadReg); } -void -CodeGeneratorMIPS::visitValue(LValue* value) -{ - const ValueOperand out = ToOutValue(value); - - masm.moveValue(value->value(), out); -} - void CodeGeneratorMIPS::visitBox(LBox* box) { @@ -1425,129 +192,12 @@ CodeGeneratorMIPS::visitUnbox(LUnbox* unbox) } } -void -CodeGeneratorMIPS::visitDouble(LDouble* ins) -{ - const LDefinition* out = ins->getDef(0); - - masm.loadConstantDouble(ins->getDouble(), ToFloatRegister(out)); -} - -void -CodeGeneratorMIPS::visitFloat32(LFloat32* ins) -{ - const LDefinition* out = ins->getDef(0); - masm.loadConstantFloat32(ins->getFloat(), ToFloatRegister(out)); -} - Register CodeGeneratorMIPS::splitTagForTest(const ValueOperand& value) { return value.typeReg(); } -void -CodeGeneratorMIPS::visitTestDAndBranch(LTestDAndBranch* test) -{ - FloatRegister input = ToFloatRegister(test->input()); - - MBasicBlock* ifTrue = test->ifTrue(); - MBasicBlock* ifFalse = test->ifFalse(); - - masm.loadConstantDouble(0.0, ScratchDoubleReg); - // If 0, or NaN, the result is false. - - if (isNextBlock(ifFalse->lir())) { - branchToBlock(Assembler::DoubleFloat, input, ScratchDoubleReg, ifTrue, - Assembler::DoubleNotEqual); - } else { - branchToBlock(Assembler::DoubleFloat, input, ScratchDoubleReg, ifFalse, - Assembler::DoubleEqualOrUnordered); - jumpToBlock(ifTrue); - } -} - -void -CodeGeneratorMIPS::visitTestFAndBranch(LTestFAndBranch* test) -{ - FloatRegister input = ToFloatRegister(test->input()); - - MBasicBlock* ifTrue = test->ifTrue(); - MBasicBlock* ifFalse = test->ifFalse(); - - masm.loadConstantFloat32(0.0, ScratchFloat32Reg); - // If 0, or NaN, the result is false. - - if (isNextBlock(ifFalse->lir())) { - branchToBlock(Assembler::SingleFloat, input, ScratchFloat32Reg, ifTrue, - Assembler::DoubleNotEqual); - } else { - branchToBlock(Assembler::SingleFloat, input, ScratchFloat32Reg, ifFalse, - Assembler::DoubleEqualOrUnordered); - jumpToBlock(ifTrue); - } -} - -void -CodeGeneratorMIPS::visitCompareD(LCompareD* comp) -{ - FloatRegister lhs = ToFloatRegister(comp->left()); - FloatRegister rhs = ToFloatRegister(comp->right()); - Register dest = ToRegister(comp->output()); - - Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop()); - masm.ma_cmp_set_double(dest, lhs, rhs, cond); -} - -void -CodeGeneratorMIPS::visitCompareF(LCompareF* comp) -{ - FloatRegister lhs = ToFloatRegister(comp->left()); - FloatRegister rhs = ToFloatRegister(comp->right()); - Register dest = ToRegister(comp->output()); - - Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop()); - masm.ma_cmp_set_float32(dest, lhs, rhs, cond); -} - -void -CodeGeneratorMIPS::visitCompareDAndBranch(LCompareDAndBranch* comp) -{ - FloatRegister lhs = ToFloatRegister(comp->left()); - FloatRegister rhs = ToFloatRegister(comp->right()); - - Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->cmpMir()->jsop()); - MBasicBlock* ifTrue = comp->ifTrue(); - MBasicBlock* ifFalse = comp->ifFalse(); - - if (isNextBlock(ifFalse->lir())) { - branchToBlock(Assembler::DoubleFloat, lhs, rhs, ifTrue, cond); - } else { - branchToBlock(Assembler::DoubleFloat, lhs, rhs, ifFalse, - Assembler::InvertCondition(cond)); - jumpToBlock(ifTrue); - } -} - -void -CodeGeneratorMIPS::visitCompareFAndBranch(LCompareFAndBranch* comp) -{ - FloatRegister lhs = ToFloatRegister(comp->left()); - FloatRegister rhs = ToFloatRegister(comp->right()); - - Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->cmpMir()->jsop()); - MBasicBlock* ifTrue = comp->ifTrue(); - MBasicBlock* ifFalse = comp->ifFalse(); - - if (isNextBlock(ifFalse->lir())) { - branchToBlock(Assembler::SingleFloat, lhs, rhs, ifTrue, cond); - } else { - branchToBlock(Assembler::SingleFloat, lhs, rhs, ifFalse, - Assembler::InvertCondition(cond)); - jumpToBlock(ifTrue); - } -} - void CodeGeneratorMIPS::visitCompareB(LCompareB* lir) { @@ -1640,477 +290,6 @@ CodeGeneratorMIPS::visitCompareBitwiseAndBranch(LCompareBitwiseAndBranch* lir) emitBranch(lhs.payloadReg(), rhs.payloadReg(), cond, lir->ifTrue(), lir->ifFalse()); } -void -CodeGeneratorMIPS::visitBitAndAndBranch(LBitAndAndBranch* lir) -{ - if (lir->right()->isConstant()) - masm.ma_and(ScratchRegister, ToRegister(lir->left()), Imm32(ToInt32(lir->right()))); - else - masm.as_and(ScratchRegister, ToRegister(lir->left()), ToRegister(lir->right())); - emitBranch(ScratchRegister, ScratchRegister, Assembler::NonZero, lir->ifTrue(), - lir->ifFalse()); -} - -void -CodeGeneratorMIPS::visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble* lir) -{ - masm.convertUInt32ToDouble(ToRegister(lir->input()), ToFloatRegister(lir->output())); -} - -void -CodeGeneratorMIPS::visitAsmJSUInt32ToFloat32(LAsmJSUInt32ToFloat32* lir) -{ - masm.convertUInt32ToFloat32(ToRegister(lir->input()), ToFloatRegister(lir->output())); -} - -void -CodeGeneratorMIPS::visitNotI(LNotI* ins) -{ - masm.cmp32Set(Assembler::Equal, ToRegister(ins->input()), Imm32(0), - ToRegister(ins->output())); -} - -void -CodeGeneratorMIPS::visitNotD(LNotD* ins) -{ - // Since this operation is not, we want to set a bit if - // the double is falsey, which means 0.0, -0.0 or NaN. - FloatRegister in = ToFloatRegister(ins->input()); - Register dest = ToRegister(ins->output()); - - Label falsey, done; - masm.loadConstantDouble(0.0, ScratchDoubleReg); - masm.ma_bc1d(in, ScratchDoubleReg, &falsey, Assembler::DoubleEqualOrUnordered, ShortJump); - - masm.move32(Imm32(0), dest); - masm.ma_b(&done, ShortJump); - - masm.bind(&falsey); - masm.move32(Imm32(1), dest); - - masm.bind(&done); -} - -void -CodeGeneratorMIPS::visitNotF(LNotF* ins) -{ - // Since this operation is not, we want to set a bit if - // the float32 is falsey, which means 0.0, -0.0 or NaN. - FloatRegister in = ToFloatRegister(ins->input()); - Register dest = ToRegister(ins->output()); - - Label falsey, done; - masm.loadConstantFloat32(0.0, ScratchFloat32Reg); - masm.ma_bc1s(in, ScratchFloat32Reg, &falsey, Assembler::DoubleEqualOrUnordered, ShortJump); - - masm.move32(Imm32(0), dest); - masm.ma_b(&done, ShortJump); - - masm.bind(&falsey); - masm.move32(Imm32(1), dest); - - masm.bind(&done); -} - -void -CodeGeneratorMIPS::visitGuardShape(LGuardShape* guard) -{ - Register obj = ToRegister(guard->input()); - Register tmp = ToRegister(guard->tempInt()); - - masm.loadPtr(Address(obj, JSObject::offsetOfShape()), tmp); - bailoutCmpPtr(Assembler::NotEqual, tmp, ImmGCPtr(guard->mir()->shape()), - guard->snapshot()); -} - -void -CodeGeneratorMIPS::visitGuardObjectGroup(LGuardObjectGroup* guard) -{ - Register obj = ToRegister(guard->input()); - Register tmp = ToRegister(guard->tempInt()); - MOZ_ASSERT(obj != tmp); - - masm.loadPtr(Address(obj, JSObject::offsetOfGroup()), tmp); - Assembler::Condition cond = guard->mir()->bailOnEquality() - ? Assembler::Equal - : Assembler::NotEqual; - bailoutCmpPtr(cond, tmp, ImmGCPtr(guard->mir()->group()), guard->snapshot()); -} - -void -CodeGeneratorMIPS::visitGuardClass(LGuardClass* guard) -{ - Register obj = ToRegister(guard->input()); - Register tmp = ToRegister(guard->tempInt()); - - masm.loadObjClass(obj, tmp); - bailoutCmpPtr(Assembler::NotEqual, tmp, Imm32((uint32_t)guard->mir()->getClass()), - guard->snapshot()); -} - -void -CodeGeneratorMIPS::generateInvalidateEpilogue() -{ - // Ensure that there is enough space in the buffer for the OsiPoint - // patching to occur. Otherwise, we could overwrite the invalidation - // epilogue. - for (size_t i = 0; i < sizeof(void*); i += Assembler::NopSize()) - masm.nop(); - - masm.bind(&invalidate_); - - // Push the return address of the point that we bailed out at to the stack - masm.Push(ra); - - // Push the Ion script onto the stack (when we determine what that - // pointer is). - invalidateEpilogueData_ = masm.pushWithPatch(ImmWord(uintptr_t(-1))); - JitCode* thunk = gen->jitRuntime()->getInvalidationThunk(); - - masm.branch(thunk); - - // We should never reach this point in JIT code -- the invalidation thunk - // should pop the invalidated JS frame and return directly to its caller. - masm.assumeUnreachable("Should have returned directly to its caller instead of here."); -} - -void -CodeGeneratorMIPS::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins) -{ - MOZ_CRASH("NYI"); -} - -void -CodeGeneratorMIPS::visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins) -{ - MOZ_CRASH("NYI"); -} - -void -CodeGeneratorMIPS::visitAsmJSCall(LAsmJSCall* ins) -{ - emitAsmJSCall(ins); -} - -void -CodeGeneratorMIPS::visitAsmJSLoadHeap(LAsmJSLoadHeap* ins) -{ - const MAsmJSLoadHeap* mir = ins->mir(); - const LAllocation* ptr = ins->ptr(); - const LDefinition* out = ins->output(); - - bool isSigned; - int size; - bool isFloat = false; - switch (mir->accessType()) { - case Scalar::Int8: isSigned = true; size = 8; break; - case Scalar::Uint8: isSigned = false; size = 8; break; - case Scalar::Int16: isSigned = true; size = 16; break; - case Scalar::Uint16: isSigned = false; size = 16; break; - case Scalar::Int32: isSigned = true; size = 32; break; - case Scalar::Uint32: isSigned = false; size = 32; break; - case Scalar::Float64: isFloat = true; size = 64; break; - case Scalar::Float32: isFloat = true; size = 32; break; - default: MOZ_CRASH("unexpected array type"); - } - - if (ptr->isConstant()) { - MOZ_ASSERT(!mir->needsBoundsCheck()); - int32_t ptrImm = ptr->toConstant()->toInt32(); - MOZ_ASSERT(ptrImm >= 0); - if (isFloat) { - if (size == 32) { - masm.loadFloat32(Address(HeapReg, ptrImm), ToFloatRegister(out)); - } else { - masm.loadDouble(Address(HeapReg, ptrImm), ToFloatRegister(out)); - } - } else { - masm.ma_load(ToRegister(out), Address(HeapReg, ptrImm), - static_cast(size), isSigned ? SignExtend : ZeroExtend); - } - return; - } - - Register ptrReg = ToRegister(ptr); - - if (!mir->needsBoundsCheck()) { - if (isFloat) { - if (size == 32) { - masm.loadFloat32(BaseIndex(HeapReg, ptrReg, TimesOne), ToFloatRegister(out)); - } else { - masm.loadDouble(BaseIndex(HeapReg, ptrReg, TimesOne), ToFloatRegister(out)); - } - } else { - masm.ma_load(ToRegister(out), BaseIndex(HeapReg, ptrReg, TimesOne), - static_cast(size), isSigned ? SignExtend : ZeroExtend); - } - return; - } - - BufferOffset bo = masm.ma_BoundsCheck(ScratchRegister); - - Label outOfRange; - Label done; - masm.ma_b(ptrReg, ScratchRegister, &outOfRange, Assembler::AboveOrEqual, ShortJump); - // Offset is ok, let's load value. - if (isFloat) { - if (size == 32) - masm.loadFloat32(BaseIndex(HeapReg, ptrReg, TimesOne), ToFloatRegister(out)); - else - masm.loadDouble(BaseIndex(HeapReg, ptrReg, TimesOne), ToFloatRegister(out)); - } else { - masm.ma_load(ToRegister(out), BaseIndex(HeapReg, ptrReg, TimesOne), - static_cast(size), isSigned ? SignExtend : ZeroExtend); - } - masm.ma_b(&done, ShortJump); - masm.bind(&outOfRange); - // Offset is out of range. Load default values. - if (isFloat) { - if (size == 32) - masm.loadFloat32(Address(GlobalReg, AsmJSNaN32GlobalDataOffset - AsmJSGlobalRegBias), - ToFloatRegister(out)); - else - masm.loadDouble(Address(GlobalReg, AsmJSNaN64GlobalDataOffset - AsmJSGlobalRegBias), - ToFloatRegister(out)); - } else { - masm.move32(Imm32(0), ToRegister(out)); - } - masm.bind(&done); - - masm.append(AsmJSHeapAccess(bo.getOffset())); -} - -void -CodeGeneratorMIPS::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins) -{ - const MAsmJSStoreHeap* mir = ins->mir(); - const LAllocation* value = ins->value(); - const LAllocation* ptr = ins->ptr(); - - bool isSigned; - int size; - bool isFloat = false; - switch (mir->accessType()) { - case Scalar::Int8: isSigned = true; size = 8; break; - case Scalar::Uint8: isSigned = false; size = 8; break; - case Scalar::Int16: isSigned = true; size = 16; break; - case Scalar::Uint16: isSigned = false; size = 16; break; - case Scalar::Int32: isSigned = true; size = 32; break; - case Scalar::Uint32: isSigned = false; size = 32; break; - case Scalar::Float64: isFloat = true; size = 64; break; - case Scalar::Float32: isFloat = true; size = 32; break; - default: MOZ_CRASH("unexpected array type"); - } - - if (ptr->isConstant()) { - MOZ_ASSERT(!mir->needsBoundsCheck()); - int32_t ptrImm = ptr->toConstant()->toInt32(); - MOZ_ASSERT(ptrImm >= 0); - - if (isFloat) { - if (size == 32) { - masm.storeFloat32(ToFloatRegister(value), Address(HeapReg, ptrImm)); - } else { - masm.storeDouble(ToFloatRegister(value), Address(HeapReg, ptrImm)); - } - } else { - masm.ma_store(ToRegister(value), Address(HeapReg, ptrImm), - static_cast(size), isSigned ? SignExtend : ZeroExtend); - } - return; - } - - Register ptrReg = ToRegister(ptr); - Address dstAddr(ptrReg, 0); - - if (!mir->needsBoundsCheck()) { - if (isFloat) { - if (size == 32) { - masm.storeFloat32(ToFloatRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne)); - } else - masm.storeDouble(ToFloatRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne)); - } else { - masm.ma_store(ToRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne), - static_cast(size), isSigned ? SignExtend : ZeroExtend); - } - return; - } - - BufferOffset bo = masm.ma_BoundsCheck(ScratchRegister); - - Label rejoin; - masm.ma_b(ptrReg, ScratchRegister, &rejoin, Assembler::AboveOrEqual, ShortJump); - - // Offset is ok, let's store value. - if (isFloat) { - if (size == 32) { - masm.storeFloat32(ToFloatRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne)); - } else - masm.storeDouble(ToFloatRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne)); - } else { - masm.ma_store(ToRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne), - static_cast(size), isSigned ? SignExtend : ZeroExtend); - } - masm.bind(&rejoin); - - masm.append(AsmJSHeapAccess(bo.getOffset())); -} - -void -CodeGeneratorMIPS::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins) -{ - MOZ_CRASH("NYI"); -} - -void -CodeGeneratorMIPS::visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins) -{ - MOZ_CRASH("NYI"); -} - -void -CodeGeneratorMIPS::visitAsmJSPassStackArg(LAsmJSPassStackArg* ins) -{ - const MAsmJSPassStackArg* mir = ins->mir(); - if (ins->arg()->isConstant()) { - masm.storePtr(ImmWord(ToInt32(ins->arg())), Address(StackPointer, mir->spOffset())); - } else { - if (ins->arg()->isGeneralReg()) { - masm.storePtr(ToRegister(ins->arg()), Address(StackPointer, mir->spOffset())); - } else { - masm.storeDouble(ToFloatRegister(ins->arg()).doubleOverlay(0), - Address(StackPointer, mir->spOffset())); - } - } -} - -void -CodeGeneratorMIPS::visitUDivOrMod(LUDivOrMod* ins) -{ - Register lhs = ToRegister(ins->lhs()); - Register rhs = ToRegister(ins->rhs()); - Register output = ToRegister(ins->output()); - Label done; - - // Prevent divide by zero. - if (ins->canBeDivideByZero()) { - if (ins->mir()->isTruncated()) { - // Infinity|0 == 0 - Label notzero; - masm.ma_b(rhs, rhs, ¬zero, Assembler::NonZero, ShortJump); - masm.move32(Imm32(0), output); - masm.ma_b(&done, ShortJump); - masm.bind(¬zero); - } else { - bailoutCmp32(Assembler::Equal, rhs, Imm32(0), ins->snapshot()); - } - } - - masm.as_divu(lhs, rhs); - masm.as_mfhi(output); - - // If the remainder is > 0, bailout since this must be a double. - if (ins->mir()->isDiv()) { - if (!ins->mir()->toDiv()->canTruncateRemainder()) - bailoutCmp32(Assembler::NonZero, output, output, ins->snapshot()); - // Get quotient - masm.as_mflo(output); - } - - if (!ins->mir()->isTruncated()) - bailoutCmp32(Assembler::LessThan, output, Imm32(0), ins->snapshot()); - - masm.bind(&done); -} - -void -CodeGeneratorMIPS::visitEffectiveAddress(LEffectiveAddress* ins) -{ - const MEffectiveAddress* mir = ins->mir(); - Register base = ToRegister(ins->base()); - Register index = ToRegister(ins->index()); - Register output = ToRegister(ins->output()); - - BaseIndex address(base, index, mir->scale(), mir->displacement()); - masm.computeEffectiveAddress(address, output); -} - -void -CodeGeneratorMIPS::visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar* ins) -{ - const MAsmJSLoadGlobalVar* mir = ins->mir(); - unsigned addr = mir->globalDataOffset() - AsmJSGlobalRegBias; - if (mir->type() == MIRType_Int32) - masm.load32(Address(GlobalReg, addr), ToRegister(ins->output())); - else if (mir->type() == MIRType_Float32) - masm.loadFloat32(Address(GlobalReg, addr), ToFloatRegister(ins->output())); - else - masm.loadDouble(Address(GlobalReg, addr), ToFloatRegister(ins->output())); -} - -void -CodeGeneratorMIPS::visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar* ins) -{ - const MAsmJSStoreGlobalVar* mir = ins->mir(); - - MOZ_ASSERT(IsNumberType(mir->value()->type())); - unsigned addr = mir->globalDataOffset() - AsmJSGlobalRegBias; - if (mir->value()->type() == MIRType_Int32) - masm.store32(ToRegister(ins->value()), Address(GlobalReg, addr)); - else if (mir->value()->type() == MIRType_Float32) - masm.storeFloat32(ToFloatRegister(ins->value()), Address(GlobalReg, addr)); - else - masm.storeDouble(ToFloatRegister(ins->value()), Address(GlobalReg, addr)); -} - -void -CodeGeneratorMIPS::visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr* ins) -{ - const MAsmJSLoadFuncPtr* mir = ins->mir(); - - Register index = ToRegister(ins->index()); - Register out = ToRegister(ins->output()); - unsigned addr = mir->globalDataOffset() - AsmJSGlobalRegBias; - - BaseIndex source(GlobalReg, index, TimesFour, addr); - masm.load32(source, out); -} - -void -CodeGeneratorMIPS::visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc* ins) -{ - const MAsmJSLoadFFIFunc* mir = ins->mir(); - masm.loadPtr(Address(GlobalReg, mir->globalDataOffset() - AsmJSGlobalRegBias), - ToRegister(ins->output())); -} - -void -CodeGeneratorMIPS::visitNegI(LNegI* ins) -{ - Register input = ToRegister(ins->input()); - Register output = ToRegister(ins->output()); - - masm.ma_negu(output, input); -} - -void -CodeGeneratorMIPS::visitNegD(LNegD* ins) -{ - FloatRegister input = ToFloatRegister(ins->input()); - FloatRegister output = ToFloatRegister(ins->output()); - - masm.as_negd(output, input); -} - -void -CodeGeneratorMIPS::visitNegF(LNegF* ins) -{ - FloatRegister input = ToFloatRegister(ins->input()); - FloatRegister output = ToFloatRegister(ins->output()); - - masm.as_negs(output, input); -} - void CodeGeneratorMIPS::setReturnDoubleRegs(LiveRegisterSet* regs) { diff --git a/js/src/jit/mips32/CodeGenerator-mips32.h b/js/src/jit/mips32/CodeGenerator-mips32.h index 1e56d0ade0..329c03efbf 100644 --- a/js/src/jit/mips32/CodeGenerator-mips32.h +++ b/js/src/jit/mips32/CodeGenerator-mips32.h @@ -7,114 +7,14 @@ #ifndef jit_mips32_CodeGenerator_mips32_h #define jit_mips32_CodeGenerator_mips32_h -#include "jit/mips32/Assembler-mips32.h" -#include "jit/shared/CodeGenerator-shared.h" +#include "jit/mips-shared/CodeGenerator-mips-shared.h" namespace js { namespace jit { -class OutOfLineBailout; -class OutOfLineTableSwitch; - -class CodeGeneratorMIPS : public CodeGeneratorShared +class CodeGeneratorMIPS : public CodeGeneratorMIPSShared { - friend class MoveResolverMIPS; - - CodeGeneratorMIPS* thisFromCtor() { - return this; - } - protected: - NonAssertingLabel deoptLabel_; - - inline Address ToAddress(const LAllocation& a); - inline Address ToAddress(const LAllocation* a); - - MoveOperand toMoveOperand(LAllocation a) const; - - template - void bailoutCmp32(Assembler::Condition c, T1 lhs, T2 rhs, LSnapshot* snapshot) { - Label skip; - masm.ma_b(lhs, rhs, &skip, Assembler::InvertCondition(c), ShortJump); - bailout(snapshot); - masm.bind(&skip); - } - template - void bailoutCmp32(Assembler::Condition c, Operand lhs, T rhs, LSnapshot* snapshot) { - if (lhs.getTag() == Operand::REG) - bailoutCmp32(c, lhs.toReg(), rhs, snapshot); - else if (lhs.getTag() == Operand::MEM) - bailoutCmp32(c, lhs.toAddress(), rhs, snapshot); - else - MOZ_CRASH("Invalid operand tag."); - } - template - void bailoutTest32(Assembler::Condition c, Register lhs, T rhs, LSnapshot* snapshot) { - Label bail; - masm.branchTest32(c, lhs, rhs, &bail); - bailoutFrom(&bail, snapshot); - } - template - void bailoutCmpPtr(Assembler::Condition c, T1 lhs, T2 rhs, LSnapshot* snapshot) { - bailoutCmp32(c, lhs, rhs, snapshot); - } - void bailoutTestPtr(Assembler::Condition c, Register lhs, Register rhs, LSnapshot* snapshot) { - Label bail; - masm.branchTestPtr(c, lhs, rhs, &bail); - bailoutFrom(&bail, snapshot); - } - void bailoutIfFalseBool(Register reg, LSnapshot* snapshot) { - Label bail; - masm.branchTest32(Assembler::Zero, reg, Imm32(0xFF), &bail); - bailoutFrom(&bail, snapshot); - } - - void bailoutFrom(Label* label, LSnapshot* snapshot); - void bailout(LSnapshot* snapshot); - - protected: - bool generateOutOfLineCode(); - - template - void branchToBlock(Register lhs, T rhs, MBasicBlock* mir, Assembler::Condition cond) - { - mir = skipTrivialBlocks(mir); - - Label* label = mir->lir()->label(); - if (Label* oolEntry = labelForBackedgeWithImplicitCheck(mir)) { - // Note: the backedge is initially a jump to the next instruction. - // It will be patched to the target block's label during link(). - RepatchLabel rejoin; - CodeOffsetJump backedge; - Label skip; - - masm.ma_b(lhs, rhs, &skip, Assembler::InvertCondition(cond), ShortJump); - backedge = masm.backedgeJump(&rejoin); - masm.bind(&rejoin); - masm.bind(&skip); - - if (!patchableBackedges_.append(PatchableBackedgeInfo(backedge, label, oolEntry))) - MOZ_CRASH(); - } else { - masm.ma_b(lhs, rhs, label, cond); - } - } - void branchToBlock(Assembler::FloatFormat fmt, FloatRegister lhs, FloatRegister rhs, - MBasicBlock* mir, Assembler::DoubleCondition cond); - - // Emits a branch that directs control flow to the true block if |cond| is - // true, and the false block if |cond| is false. - template - void emitBranch(Register lhs, T rhs, Assembler::Condition cond, - MBasicBlock* mirTrue, MBasicBlock* mirFalse) - { - if (isNextBlock(mirFalse->lir())) { - branchToBlock(lhs, rhs, mirTrue, cond); - } else { - branchToBlock(lhs, rhs, mirFalse, Assembler::InvertCondition(cond)); - jumpToBlock(mirTrue); - } - } void testNullEmitBranch(Assembler::Condition cond, const ValueOperand& value, MBasicBlock* ifTrue, MBasicBlock* ifFalse) { @@ -130,75 +30,17 @@ class CodeGeneratorMIPS : public CodeGeneratorShared { emitBranch(value.typeReg(), (Imm32)ImmType(JSVAL_TYPE_OBJECT), cond, ifTrue, ifFalse); } - void testZeroEmitBranch(Assembler::Condition cond, Register reg, - MBasicBlock* ifTrue, MBasicBlock* ifFalse) - { - emitBranch(reg, Imm32(0), cond, ifTrue, ifFalse); - } void emitTableSwitchDispatch(MTableSwitch* mir, Register index, Register base); public: - // Instruction visitors. - virtual void visitMinMaxD(LMinMaxD* ins); - virtual void visitMinMaxF(LMinMaxF* ins); - virtual void visitAbsD(LAbsD* ins); - virtual void visitAbsF(LAbsF* ins); - virtual void visitSqrtD(LSqrtD* ins); - virtual void visitSqrtF(LSqrtF* ins); - virtual void visitAddI(LAddI* ins); - virtual void visitSubI(LSubI* ins); - virtual void visitBitNotI(LBitNotI* ins); - virtual void visitBitOpI(LBitOpI* ins); - - virtual void visitMulI(LMulI* ins); - - virtual void visitDivI(LDivI* ins); - virtual void visitDivPowTwoI(LDivPowTwoI* ins); - virtual void visitModI(LModI* ins); - virtual void visitModPowTwoI(LModPowTwoI* ins); - virtual void visitModMaskI(LModMaskI* ins); - virtual void visitPowHalfD(LPowHalfD* ins); - virtual void visitShiftI(LShiftI* ins); - virtual void visitUrshD(LUrshD* ins); - - virtual void visitClzI(LClzI* ins); - - virtual void visitTestIAndBranch(LTestIAndBranch* test); - virtual void visitCompare(LCompare* comp); - virtual void visitCompareAndBranch(LCompareAndBranch* comp); - virtual void visitTestDAndBranch(LTestDAndBranch* test); - virtual void visitTestFAndBranch(LTestFAndBranch* test); - virtual void visitCompareD(LCompareD* comp); - virtual void visitCompareF(LCompareF* comp); - virtual void visitCompareDAndBranch(LCompareDAndBranch* comp); - virtual void visitCompareFAndBranch(LCompareFAndBranch* comp); virtual void visitCompareB(LCompareB* lir); virtual void visitCompareBAndBranch(LCompareBAndBranch* lir); virtual void visitCompareBitwise(LCompareBitwise* lir); virtual void visitCompareBitwiseAndBranch(LCompareBitwiseAndBranch* lir); - virtual void visitBitAndAndBranch(LBitAndAndBranch* lir); - virtual void visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble* lir); - virtual void visitAsmJSUInt32ToFloat32(LAsmJSUInt32ToFloat32* lir); - virtual void visitNotI(LNotI* ins); - virtual void visitNotD(LNotD* ins); - virtual void visitNotF(LNotF* ins); - - virtual void visitMathD(LMathD* math); - virtual void visitMathF(LMathF* math); - virtual void visitFloor(LFloor* lir); - virtual void visitFloorF(LFloorF* lir); - virtual void visitCeil(LCeil* lir); - virtual void visitCeilF(LCeilF* lir); - virtual void visitRound(LRound* lir); - virtual void visitRoundF(LRoundF* lir); - virtual void visitTruncateDToInt32(LTruncateDToInt32* ins); - virtual void visitTruncateFToInt32(LTruncateFToInt32* ins); // Out of line visitors. - void visitOutOfLineBailout(OutOfLineBailout* ool); void visitOutOfLineTableSwitch(OutOfLineTableSwitch* ool); - protected: ValueOperand ToValue(LInstruction* ins, size_t pos); ValueOperand ToOutValue(LInstruction* ins); @@ -208,84 +50,19 @@ class CodeGeneratorMIPS : public CodeGeneratorShared Register splitTagForTest(const ValueOperand& value); public: - CodeGeneratorMIPS(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm); + CodeGeneratorMIPS(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm) + : CodeGeneratorMIPSShared(gen, graph, masm) + { } public: void visitBox(LBox* box); void visitBoxFloatingPoint(LBoxFloatingPoint* box); void visitUnbox(LUnbox* unbox); - void visitValue(LValue* value); - void visitDouble(LDouble* ins); - void visitFloat32(LFloat32* ins); - - void visitGuardShape(LGuardShape* guard); - void visitGuardObjectGroup(LGuardObjectGroup* guard); - void visitGuardClass(LGuardClass* guard); - - void visitNegI(LNegI* lir); - void visitNegD(LNegD* lir); - void visitNegF(LNegF* lir); - void visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins); - void visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins); - void visitAsmJSCall(LAsmJSCall* ins); - void visitAsmJSLoadHeap(LAsmJSLoadHeap* ins); - void visitAsmJSStoreHeap(LAsmJSStoreHeap* ins); - void visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins); - void visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins); - void visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar* ins); - void visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar* ins); - void visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr* ins); - void visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc* ins); - - void visitAsmJSPassStackArg(LAsmJSPassStackArg* ins); - - void generateInvalidateEpilogue(); - void setReturnDoubleRegs(LiveRegisterSet* regs); - - protected: - void visitEffectiveAddress(LEffectiveAddress* ins); - void visitUDivOrMod(LUDivOrMod* ins); - - public: - // Unimplemented SIMD instructions - void visitSimdSplatX4(LSimdSplatX4* lir) { MOZ_CRASH("NYI"); } - void visitInt32x4(LInt32x4* ins) { MOZ_CRASH("NYI"); } - void visitFloat32x4(LFloat32x4* ins) { MOZ_CRASH("NYI"); } - void visitSimdReinterpretCast(LSimdReinterpretCast* ins) { MOZ_CRASH("NYI"); } - void visitSimdExtractElementI(LSimdExtractElementI* ins) { MOZ_CRASH("NYI"); } - void visitSimdExtractElementF(LSimdExtractElementF* ins) { MOZ_CRASH("NYI"); } - void visitSimdSignMaskX4(LSimdSignMaskX4* ins) { MOZ_CRASH("NYI"); } - void visitSimdBinaryCompIx4(LSimdBinaryCompIx4* lir) { MOZ_CRASH("NYI"); } - void visitSimdBinaryCompFx4(LSimdBinaryCompFx4* lir) { MOZ_CRASH("NYI"); } - void visitSimdBinaryArithIx4(LSimdBinaryArithIx4* lir) { MOZ_CRASH("NYI"); } - void visitSimdBinaryArithFx4(LSimdBinaryArithFx4* lir) { MOZ_CRASH("NYI"); } - void visitSimdBinaryBitwiseX4(LSimdBinaryBitwiseX4* lir) { MOZ_CRASH("NYI"); } - void visitSimdGeneralShuffleI(LSimdGeneralShuffleI* lir) { MOZ_CRASH("NYI"); } - void visitSimdGeneralShuffleF(LSimdGeneralShuffleF* lir) { MOZ_CRASH("NYI"); } }; typedef CodeGeneratorMIPS CodeGeneratorSpecific; -// An out-of-line bailout thunk. -class OutOfLineBailout : public OutOfLineCodeBase -{ - LSnapshot* snapshot_; - uint32_t frameSize_; - - public: - OutOfLineBailout(LSnapshot* snapshot, uint32_t frameSize) - : snapshot_(snapshot), - frameSize_(frameSize) - { } - - void accept(CodeGeneratorMIPS* codegen); - - LSnapshot* snapshot() const { - return snapshot_; - } -}; - } // namespace jit } // namespace js diff --git a/js/src/jit/mips32/MacroAssembler-mips32.cpp b/js/src/jit/mips32/MacroAssembler-mips32.cpp index 98c65caba0..f83f8d3879 100644 --- a/js/src/jit/mips32/MacroAssembler-mips32.cpp +++ b/js/src/jit/mips32/MacroAssembler-mips32.cpp @@ -144,7 +144,7 @@ MacroAssemblerMIPSCompat::convertUInt32ToFloat32(Register src, FloatRegister des // have enough precision. convertUInt32ToDouble(src, dest); convertDoubleToFloat32(dest, dest); - ma_b(&done); + ma_b(&done, ShortJump); bind(&positive); convertInt32ToFloat32(src, dest); @@ -3372,7 +3372,7 @@ MacroAssembler::Pop(Register reg) } void -MacroAssembler::Pop(const ValueOperand &val) +MacroAssembler::Pop(const ValueOperand& val) { popValue(val); framePushed_ -= sizeof(Value); diff --git a/js/src/jit/mips32/MoveEmitter-mips32.cpp b/js/src/jit/mips32/MoveEmitter-mips32.cpp index 82453c070d..7b5a8996f0 100644 --- a/js/src/jit/mips32/MoveEmitter-mips32.cpp +++ b/js/src/jit/mips32/MoveEmitter-mips32.cpp @@ -11,68 +11,6 @@ using namespace js; using namespace js::jit; -MoveEmitterMIPS::MoveEmitterMIPS(MacroAssembler& masm) - : inCycle_(0), - masm(masm), - pushedAtCycle_(-1), - pushedAtSpill_(-1), - spilledReg_(InvalidReg), - spilledFloatReg_(InvalidFloatReg) -{ - pushedAtStart_ = masm.framePushed(); -} - -void -MoveEmitterMIPS::emit(const MoveResolver& moves) -{ - if (moves.numCycles()) { - // Reserve stack for cycle resolution - masm.reserveStack(moves.numCycles() * sizeof(double)); - pushedAtCycle_ = masm.framePushed(); - } - - for (size_t i = 0; i < moves.numMoves(); i++) - emit(moves.getMove(i)); -} - -MoveEmitterMIPS::~MoveEmitterMIPS() -{ - assertDone(); -} - -Address -MoveEmitterMIPS::cycleSlot(uint32_t slot, uint32_t subslot) const -{ - int32_t offset = masm.framePushed() - pushedAtCycle_; - MOZ_ASSERT(Imm16::IsInSignedRange(offset)); - return Address(StackPointer, offset + slot * sizeof(double) + subslot); -} - -int32_t -MoveEmitterMIPS::getAdjustedOffset(const MoveOperand& operand) -{ - MOZ_ASSERT(operand.isMemoryOrEffectiveAddress()); - if (operand.base() != StackPointer) - return operand.disp(); - - // Adjust offset if stack pointer has been moved. - return operand.disp() + masm.framePushed() - pushedAtStart_; -} - -Address -MoveEmitterMIPS::getAdjustedAddress(const MoveOperand& operand) -{ - return Address(operand.base(), getAdjustedOffset(operand)); -} - - -Register -MoveEmitterMIPS::tempReg() -{ - spilledReg_ = SecondScratchReg; - return SecondScratchReg; -} - void MoveEmitterMIPS::breakCycle(const MoveOperand& from, const MoveOperand& to, MoveOp::Type type, uint32_t slotId) @@ -175,76 +113,6 @@ MoveEmitterMIPS::completeCycle(const MoveOperand& from, const MoveOperand& to, } } -void -MoveEmitterMIPS::emitMove(const MoveOperand& from, const MoveOperand& to) -{ - if (from.isGeneralReg()) { - // Second scratch register should not be moved by MoveEmitter. - MOZ_ASSERT(from.reg() != spilledReg_); - - if (to.isGeneralReg()) - masm.movePtr(from.reg(), to.reg()); - else if (to.isMemory()) - masm.storePtr(from.reg(), getAdjustedAddress(to)); - else - MOZ_CRASH("Invalid emitMove arguments."); - } else if (from.isMemory()) { - if (to.isGeneralReg()) { - masm.loadPtr(getAdjustedAddress(from), to.reg()); - } else if (to.isMemory()) { - masm.loadPtr(getAdjustedAddress(from), tempReg()); - masm.storePtr(tempReg(), getAdjustedAddress(to)); - } else { - MOZ_CRASH("Invalid emitMove arguments."); - } - } else if (from.isEffectiveAddress()) { - if (to.isGeneralReg()) { - masm.computeEffectiveAddress(getAdjustedAddress(from), to.reg()); - } else if (to.isMemory()) { - masm.computeEffectiveAddress(getAdjustedAddress(from), tempReg()); - masm.storePtr(tempReg(), getAdjustedAddress(to)); - } else { - MOZ_CRASH("Invalid emitMove arguments."); - } - } else { - MOZ_CRASH("Invalid emitMove arguments."); - } -} - -void -MoveEmitterMIPS::emitFloat32Move(const MoveOperand& from, const MoveOperand& to) -{ - // Ensure that we can use ScratchFloat32Reg in memory move. - MOZ_ASSERT_IF(from.isFloatReg(), from.floatReg() != ScratchFloat32Reg); - MOZ_ASSERT_IF(to.isFloatReg(), to.floatReg() != ScratchFloat32Reg); - - if (from.isFloatReg()) { - if (to.isFloatReg()) { - masm.moveFloat32(from.floatReg(), to.floatReg()); - } else if (to.isGeneralReg()) { - // This should only be used when passing float parameter in a1,a2,a3 - MOZ_ASSERT(to.reg() == a1 || to.reg() == a2 || to.reg() == a3); - masm.moveFromFloat32(from.floatReg(), to.reg()); - } else { - MOZ_ASSERT(to.isMemory()); - masm.storeFloat32(from.floatReg(), getAdjustedAddress(to)); - } - } else if (to.isFloatReg()) { - MOZ_ASSERT(from.isMemory()); - masm.loadFloat32(getAdjustedAddress(from), to.floatReg()); - } else if (to.isGeneralReg()) { - MOZ_ASSERT(from.isMemory()); - // This should only be used when passing float parameter in a1,a2,a3 - MOZ_ASSERT(to.reg() == a1 || to.reg() == a2 || to.reg() == a3); - masm.loadPtr(getAdjustedAddress(from), to.reg()); - } else { - MOZ_ASSERT(from.isMemory()); - MOZ_ASSERT(to.isMemory()); - masm.loadFloat32(getAdjustedAddress(from), ScratchFloat32Reg); - masm.storeFloat32(ScratchFloat32Reg, getAdjustedAddress(to)); - } -} - void MoveEmitterMIPS::emitDoubleMove(const MoveOperand& from, const MoveOperand& to) { @@ -286,61 +154,3 @@ MoveEmitterMIPS::emitDoubleMove(const MoveOperand& from, const MoveOperand& to) masm.storeDouble(ScratchDoubleReg, getAdjustedAddress(to)); } } - -void -MoveEmitterMIPS::emit(const MoveOp& move) -{ - const MoveOperand& from = move.from(); - const MoveOperand& to = move.to(); - - if (move.isCycleEnd() && move.isCycleBegin()) { - // A fun consequence of aliased registers is you can have multiple - // cycles at once, and one can end exactly where another begins. - breakCycle(from, to, move.endCycleType(), move.cycleBeginSlot()); - completeCycle(from, to, move.type(), move.cycleEndSlot()); - return; - } - - if (move.isCycleEnd()) { - MOZ_ASSERT(inCycle_); - completeCycle(from, to, move.type(), move.cycleEndSlot()); - MOZ_ASSERT(inCycle_ > 0); - inCycle_--; - return; - } - - if (move.isCycleBegin()) { - breakCycle(from, to, move.endCycleType(), move.cycleBeginSlot()); - inCycle_++; - } - - switch (move.type()) { - case MoveOp::FLOAT32: - emitFloat32Move(from, to); - break; - case MoveOp::DOUBLE: - emitDoubleMove(from, to); - break; - case MoveOp::INT32: - MOZ_ASSERT(sizeof(uintptr_t) == sizeof(int32_t)); - case MoveOp::GENERAL: - emitMove(from, to); - break; - default: - MOZ_CRASH("Unexpected move type"); - } -} - -void -MoveEmitterMIPS::assertDone() -{ - MOZ_ASSERT(inCycle_ == 0); -} - -void -MoveEmitterMIPS::finish() -{ - assertDone(); - - masm.freeStack(masm.framePushed() - pushedAtStart_); -} diff --git a/js/src/jit/mips32/MoveEmitter-mips32.h b/js/src/jit/mips32/MoveEmitter-mips32.h index a2e3299d4d..8d8d1c0c10 100644 --- a/js/src/jit/mips32/MoveEmitter-mips32.h +++ b/js/src/jit/mips32/MoveEmitter-mips32.h @@ -7,55 +7,23 @@ #ifndef jit_mips32_MoveEmitter_mips32_h #define jit_mips32_MoveEmitter_mips32_h -#include "jit/MacroAssembler.h" -#include "jit/MoveResolver.h" +#include "jit/mips-shared/MoveEmitter-mips-shared.h" namespace js { namespace jit { -class MoveEmitterMIPS +class MoveEmitterMIPS : public MoveEmitterMIPSShared { - uint32_t inCycle_; - MacroAssembler& masm; - - // Original stack push value. - uint32_t pushedAtStart_; - - // These store stack offsets to spill locations, snapshotting - // codegen->framePushed_ at the time they were allocated. They are -1 if no - // stack space has been allocated for that particular spill. - int32_t pushedAtCycle_; - int32_t pushedAtSpill_; - - // These are registers that are available for temporary use. They may be - // assigned InvalidReg. If no corresponding spill space has been assigned, - // then these registers do not need to be spilled. - Register spilledReg_; - FloatRegister spilledFloatReg_; - - void assertDone(); - Register tempReg(); - FloatRegister tempFloatReg(); - Address cycleSlot(uint32_t slot, uint32_t subslot) const; - int32_t getAdjustedOffset(const MoveOperand& operand); - Address getAdjustedAddress(const MoveOperand& operand); - - void emitMove(const MoveOperand& from, const MoveOperand& to); - void emitFloat32Move(const MoveOperand& from, const MoveOperand& to); void emitDoubleMove(const MoveOperand& from, const MoveOperand& to); void breakCycle(const MoveOperand& from, const MoveOperand& to, MoveOp::Type type, uint32_t slot); void completeCycle(const MoveOperand& from, const MoveOperand& to, MoveOp::Type type, uint32_t slot); - void emit(const MoveOp &move); public: - MoveEmitterMIPS(MacroAssembler &masm); - ~MoveEmitterMIPS(); - void emit(const MoveResolver &moves); - void finish(); - - void setScratchRegister(Register reg) {} + MoveEmitterMIPS(MacroAssembler& masm) + : MoveEmitterMIPSShared(masm) + { } }; typedef MoveEmitterMIPS MoveEmitter; diff --git a/js/src/jit/mips32/Trampoline-mips32.cpp b/js/src/jit/mips32/Trampoline-mips32.cpp index 5b6e68ecea..b169741ea8 100644 --- a/js/src/jit/mips32/Trampoline-mips32.cpp +++ b/js/src/jit/mips32/Trampoline-mips32.cpp @@ -13,7 +13,7 @@ #include "jit/JitFrames.h" #include "jit/JitSpewer.h" #include "jit/Linker.h" -#include "jit/mips32/Bailouts-mips32.h" +#include "jit/mips-shared/Bailouts-mips-shared.h" #include "jit/mips32/SharedICHelpers-mips32.h" #ifdef JS_ION_PERF # include "jit/PerfSpewer.h" diff --git a/js/src/jit/mips64/Architecture-mips64.cpp b/js/src/jit/mips64/Architecture-mips64.cpp new file mode 100644 index 0000000000..28c60d10ba --- /dev/null +++ b/js/src/jit/mips64/Architecture-mips64.cpp @@ -0,0 +1,93 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jit/mips64/Architecture-mips64.h" + +#include "jit/RegisterSets.h" + +namespace js { +namespace jit { + +const char * const Registers::RegNames[] = { "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", + "a4", "a5", "a6", "a7", "t0", "t1", "t2", "t3", + "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", + "t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra" }; + +const uint32_t Allocatable = 22; + +const Registers::SetType Registers::ArgRegMask = + Registers::SharedArgRegMask | + (1 << a4) | (1 << a5) | (1 << a6) | (1 << a7); + +const Registers::SetType Registers::JSCallMask = + (1 << Registers::v1); + +const Registers::SetType Registers::CallMask = + (1 << Registers::v0); + +FloatRegisters::Encoding +FloatRegisters::FromName(const char* name) +{ + for (size_t i = 0; i < Total; i++) { + if (strcmp(GetName(Encoding(i)), name) == 0) + return Encoding(i); + } + + return Invalid; +} + +FloatRegister +FloatRegister::singleOverlay() const +{ + MOZ_ASSERT(!isInvalid()); + if (kind_ == Codes::Double) + return FloatRegister(reg_, Codes::Single); + return *this; +} + +FloatRegister +FloatRegister::doubleOverlay() const +{ + MOZ_ASSERT(!isInvalid()); + if (kind_ != Codes::Double) + return FloatRegister(reg_, Codes::Double); + return *this; +} + +FloatRegisterSet +FloatRegister::ReduceSetForPush(const FloatRegisterSet& s) +{ + LiveFloatRegisterSet mod; + for (FloatRegisterIterator iter(s); iter.more(); iter++) { + if ((*iter).isSingle()) { + // Even for single size registers save complete double register. + mod.addUnchecked((*iter).doubleOverlay()); + } else { + mod.addUnchecked(*iter); + } + } + return mod.set(); +} + +uint32_t +FloatRegister::GetPushSizeInBytes(const FloatRegisterSet& s) +{ + FloatRegisterSet ss = s.reduceSetForPush(); + uint64_t bits = ss.bits(); + // We are only pushing double registers. + MOZ_ASSERT((bits & 0xffffffff) == 0); + uint32_t ret = mozilla::CountPopulation32(bits >> 32) * sizeof(double); + return ret; +} +uint32_t +FloatRegister::getRegisterDumpOffsetInBytes() +{ + return id() * sizeof(double); +} + +} // namespace ion +} // namespace js + diff --git a/js/src/jit/mips64/Architecture-mips64.h b/js/src/jit/mips64/Architecture-mips64.h new file mode 100644 index 0000000000..4d82f9a44c --- /dev/null +++ b/js/src/jit/mips64/Architecture-mips64.h @@ -0,0 +1,209 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef jit_mips64_Architecture_mips64_h +#define jit_mips64_Architecture_mips64_h + +#include "mozilla/MathAlgorithms.h" + +#include +#include + +#include "jit/mips-shared/Architecture-mips-shared.h" + +#include "js/Utility.h" + +namespace js { +namespace jit { + +// Shadow stack space is not required on MIPS64. +static const uint32_t ShadowStackSpace = 0; + +// MIPS64 have 64 bit floating-point coprocessor. There are 32 double +// precision register which can also be used as single precision registers. +class FloatRegisters : public FloatRegistersMIPSShared +{ + public: + enum ContentType { + Single, + Double, + NumTypes + }; + + static const char* GetName(uint32_t i) { + MOZ_ASSERT(i < TotalPhys); + return FloatRegistersMIPSShared::GetName(Encoding(i)); + } + + static Encoding FromName(const char* name); + + static const uint32_t Total = 32 * NumTypes; + static const uint32_t Allocatable = 60; + // When saving all registers we only need to do is save double registers. + static const uint32_t TotalPhys = 32; + + static_assert(sizeof(SetType) * 8 >= Total, + "SetType should be large enough to enumerate all registers."); + + // Magic values which are used to duplicate a mask of physical register for + // a specific type of register. A multiplication is used to copy and shift + // the bits of the physical register mask. + static const SetType SpreadSingle = SetType(1) << (uint32_t(Single) * TotalPhys); + static const SetType SpreadDouble = SetType(1) << (uint32_t(Double) * TotalPhys); + static const SetType SpreadScalar = SpreadSingle | SpreadDouble; + static const SetType SpreadVector = 0; + static const SetType Spread = SpreadScalar | SpreadVector; + + static const SetType AllPhysMask = ((SetType(1) << TotalPhys) - 1); + static const SetType AllMask = AllPhysMask * Spread; + static const SetType AllDoubleMask = AllPhysMask * SpreadDouble; + + static const SetType NonVolatileMask = + ( (1U << FloatRegisters::f24) | + (1U << FloatRegisters::f25) | + (1U << FloatRegisters::f26) | + (1U << FloatRegisters::f27) | + (1U << FloatRegisters::f28) | + (1U << FloatRegisters::f29) | + (1U << FloatRegisters::f30) | + (1U << FloatRegisters::f31) + ) * SpreadScalar + | AllPhysMask * SpreadVector; + + static const SetType VolatileMask = AllMask & ~NonVolatileMask; + + static const SetType WrapperMask = VolatileMask; + + static const SetType NonAllocatableMask = + ( // f21 and f23 are MIPS scratch float registers. + (1U << FloatRegisters::f21) | + (1U << FloatRegisters::f23) + ) * Spread; + + // Registers that can be allocated without being saved, generally. + static const SetType TempMask = VolatileMask & ~NonAllocatableMask; + + static const SetType AllocatableMask = AllMask & ~NonAllocatableMask; +}; + +template +class TypedRegisterSet; + +class FloatRegister : public FloatRegisterMIPSShared +{ + public: + typedef FloatRegisters Codes; + typedef size_t Code; + typedef Codes::Encoding Encoding; + typedef Codes::ContentType ContentType; + + Encoding reg_: 6; + private: + ContentType kind_ : 3; + + public: + MOZ_CONSTEXPR FloatRegister(uint32_t r, ContentType kind = Codes::Double) + : reg_(Encoding(r)), kind_(kind) + { } + MOZ_CONSTEXPR FloatRegister() + : reg_(Encoding(FloatRegisters::invalid_freg)), kind_(Codes::Double) + { } + + bool operator==(const FloatRegister& other) const { + MOZ_ASSERT(!isInvalid()); + MOZ_ASSERT(!other.isInvalid()); + return kind_ == other.kind_ && reg_ == other.reg_; + } + bool equiv(const FloatRegister& other) const { return other.kind_ == kind_; } + size_t size() const { return (kind_ == Codes::Double) ? sizeof(double) : sizeof (float); } + bool isInvalid() const { + return reg_ == FloatRegisters::invalid_freg; + } + + bool isSingle() const { return kind_ == Codes::Single; } + bool isDouble() const { return kind_ == Codes::Double; } + + FloatRegister singleOverlay() const; + FloatRegister doubleOverlay() const; + + FloatRegister asSingle() const { return singleOverlay(); } + FloatRegister asDouble() const { return doubleOverlay(); } + FloatRegister asInt32x4() const { MOZ_CRASH("NYI"); } + FloatRegister asFloat32x4() const { MOZ_CRASH("NYI"); } + + Code code() const { + MOZ_ASSERT(!isInvalid()); + return Code(reg_ | (kind_ << 5)); + } + Encoding encoding() const { + MOZ_ASSERT(!isInvalid()); + MOZ_ASSERT(uint32_t(reg_) < Codes::TotalPhys); + return reg_; + } + uint32_t id() const { + return reg_; + } + static FloatRegister FromCode(uint32_t i) { + uint32_t code = i & 0x1f; + uint32_t kind = i >> 5; + return FloatRegister(Code(code), ContentType(kind)); + } + + bool volatile_() const { + return !!((1 << reg_) & FloatRegisters::VolatileMask); + } + const char* name() const { + return FloatRegisters::GetName(reg_); + } + bool operator != (const FloatRegister& other) const { + return kind_ != other.kind_ || reg_ != other.reg_; + } + bool aliases(const FloatRegister& other) { + return reg_ == other.reg_; + } + uint32_t numAliased() const { + return 2; + } + void aliased(uint32_t aliasIdx, FloatRegister* ret) { + if (aliasIdx == 0) { + *ret = *this; + return; + } + MOZ_ASSERT(aliasIdx == 1); + if (isDouble()) + *ret = singleOverlay(); + else + *ret = doubleOverlay(); + } + uint32_t numAlignedAliased() const { + return 2; + } + void alignedAliased(uint32_t aliasIdx, FloatRegister* ret) { + MOZ_ASSERT(isDouble()); + if (aliasIdx == 0) { + *ret = *this; + return; + } + MOZ_ASSERT(aliasIdx == 1); + *ret = singleOverlay(); + } + + SetType alignedOrDominatedAliasedSet() const { + return Codes::Spread << reg_; + } + + static Code FromName(const char* name) { + return FloatRegisters::FromName(name); + } + static TypedRegisterSet ReduceSetForPush(const TypedRegisterSet& s); + static uint32_t GetPushSizeInBytes(const TypedRegisterSet& s); + uint32_t getRegisterDumpOffsetInBytes(); +}; + +} // namespace jit +} // namespace js + +#endif /* jit_mips64_Architecture_mips64_h */ diff --git a/js/src/jit/mips64/Assembler-mips64.cpp b/js/src/jit/mips64/Assembler-mips64.cpp new file mode 100644 index 0000000000..1f7accae64 --- /dev/null +++ b/js/src/jit/mips64/Assembler-mips64.cpp @@ -0,0 +1,493 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jit/mips64/Assembler-mips64.h" + +#include "mozilla/DebugOnly.h" + +using mozilla::DebugOnly; + +using namespace js; +using namespace js::jit; + +ABIArgGenerator::ABIArgGenerator() + : usedArgSlots_(0), + firstArgFloat(false), + current_() +{} + +ABIArg +ABIArgGenerator::next(MIRType type) +{ + switch (type) { + case MIRType_Int32: + case MIRType_Pointer: { + Register destReg; + if (GetIntArgReg(usedArgSlots_, &destReg)) + current_ = ABIArg(destReg); + else + current_ = ABIArg(GetArgStackDisp(usedArgSlots_)); + usedArgSlots_++; + break; + } + case MIRType_Float32: + case MIRType_Double: { + FloatRegister destFReg; + FloatRegister::ContentType contentType; + if (!usedArgSlots_) + firstArgFloat = true; + contentType = (type == MIRType_Double) ? + FloatRegisters::Double : FloatRegisters::Single; + if (GetFloatArgReg(usedArgSlots_, &destFReg)) + current_ = ABIArg(FloatRegister(destFReg.id(), contentType)); + else + current_ = ABIArg(GetArgStackDisp(usedArgSlots_)); + usedArgSlots_++; + break; + } + default: + MOZ_CRASH("Unexpected argument type"); + } + return current_; +} + +const Register ABIArgGenerator::NonArgReturnReg0 = t0; +const Register ABIArgGenerator::NonArgReturnReg1 = t1; +const Register ABIArgGenerator::NonArg_VolatileReg = v0; +const Register ABIArgGenerator::NonReturn_VolatileReg0 = a0; +const Register ABIArgGenerator::NonReturn_VolatileReg1 = a1; + +uint32_t +js::jit::RT(FloatRegister r) +{ + MOZ_ASSERT(r.id() < FloatRegisters::TotalPhys); + return r.id() << RTShift; +} + +uint32_t +js::jit::RD(FloatRegister r) +{ + MOZ_ASSERT(r.id() < FloatRegisters::TotalPhys); + return r.id() << RDShift; +} + +uint32_t +js::jit::SA(FloatRegister r) +{ + MOZ_ASSERT(r.id() < FloatRegisters::TotalPhys); + return r.id() << SAShift; +} + +// Used to patch jumps created by MacroAssemblerMIPS64Compat::jumpWithPatch. +void +jit::PatchJump(CodeLocationJump& jump_, CodeLocationLabel label, ReprotectCode reprotect) +{ + Instruction* inst = (Instruction*)jump_.raw(); + + // Six instructions used in load 64-bit imm. + MaybeAutoWritableJitCode awjc(inst, 6 * sizeof(uint32_t), reprotect); + Assembler::UpdateLoad64Value(inst, (uint64_t)label.raw()); + + AutoFlushICache::flush(uintptr_t(inst), 6 * sizeof(uint32_t)); +} + +// For more infromation about backedges look at comment in +// MacroAssemblerMIPS64Compat::backedgeJump() +void +jit::PatchBackedge(CodeLocationJump& jump, CodeLocationLabel label, + JitRuntime::BackedgeTarget target) +{ + uintptr_t sourceAddr = (uintptr_t)jump.raw(); + uintptr_t targetAddr = (uintptr_t)label.raw(); + InstImm* branch = (InstImm*)jump.raw(); + + MOZ_ASSERT(branch->extractOpcode() == (uint32_t(op_beq) >> OpcodeShift)); + + if (BOffImm16::IsInRange(targetAddr - sourceAddr)) { + branch->setBOffImm16(BOffImm16(targetAddr - sourceAddr)); + } else { + if (target == JitRuntime::BackedgeLoopHeader) { + Instruction* inst = &branch[1]; + Assembler::UpdateLoad64Value(inst, targetAddr); + // Jump to first ori. The lui will be executed in delay slot. + branch->setBOffImm16(BOffImm16(2 * sizeof(uint32_t))); + } else { + Instruction* inst = &branch[6]; + Assembler::UpdateLoad64Value(inst, targetAddr); + // Jump to first ori of interrupt loop. + branch->setBOffImm16(BOffImm16(6 * sizeof(uint32_t))); + } + } +} + +void +Assembler::executableCopy(uint8_t* buffer) +{ + MOZ_ASSERT(isFinished); + m_buffer.executableCopy(buffer); + + // Patch all long jumps during code copy. + for (size_t i = 0; i < longJumps_.length(); i++) { + Instruction* inst = (Instruction*) ((uintptr_t)buffer + longJumps_[i]); + + uint64_t value = Assembler::ExtractLoad64Value(inst); + Assembler::UpdateLoad64Value(inst, (uint64_t)buffer + value); + } + + AutoFlushICache::setRange(uintptr_t(buffer), m_buffer.size()); +} + +uintptr_t +Assembler::GetPointer(uint8_t* instPtr) +{ + Instruction* inst = (Instruction*)instPtr; + return Assembler::ExtractLoad64Value(inst); +} + +static JitCode * +CodeFromJump(Instruction* jump) +{ + uint8_t* target = (uint8_t*)Assembler::ExtractLoad64Value(jump); + return JitCode::FromExecutable(target); +} + +void +Assembler::TraceJumpRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader) +{ + while (reader.more()) { + JitCode* child = CodeFromJump((Instruction*)(code->raw() + reader.readUnsigned())); + TraceManuallyBarrieredEdge(trc, &child, "rel32"); + } +} + +static void +TraceOneDataRelocation(JSTracer* trc, Instruction* inst) +{ + void* ptr = (void*)Assembler::ExtractLoad64Value(inst); + void* prior = ptr; + + // All pointers on MIPS64 will have the top bits cleared. If those bits + // are not cleared, this must be a Value. + uintptr_t word = reinterpret_cast(ptr); + if (word >> JSVAL_TAG_SHIFT) { + jsval_layout layout; + layout.asBits = word; + Value v = IMPL_TO_JSVAL(layout); + TraceManuallyBarrieredEdge(trc, &v, "ion-masm-value"); + ptr = (void*)JSVAL_TO_IMPL(v).asBits; + } else { + // No barrier needed since these are constants. + TraceManuallyBarrieredGenericPointerEdge(trc, reinterpret_cast(&ptr), + "ion-masm-ptr"); + } + + if (ptr != prior) { + Assembler::UpdateLoad64Value(inst, uint64_t(ptr)); + AutoFlushICache::flush(uintptr_t(inst), 6 * sizeof(uint32_t)); + } +} + +static void +TraceDataRelocations(JSTracer* trc, uint8_t* buffer, CompactBufferReader& reader) +{ + while (reader.more()) { + size_t offset = reader.readUnsigned(); + Instruction* inst = (Instruction*)(buffer + offset); + TraceOneDataRelocation(trc, inst); + } +} + +static void +TraceDataRelocations(JSTracer* trc, MIPSBuffer* buffer, CompactBufferReader& reader) +{ + while (reader.more()) { + BufferOffset bo (reader.readUnsigned()); + MIPSBuffer::AssemblerBufferInstIterator iter(bo, buffer); + TraceOneDataRelocation(trc, iter.cur()); + } +} + +void +Assembler::TraceDataRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader) +{ + ::TraceDataRelocations(trc, code->raw(), reader); +} + +void +Assembler::trace(JSTracer* trc) +{ + for (size_t i = 0; i < jumps_.length(); i++) { + RelativePatch& rp = jumps_[i]; + if (rp.kind == Relocation::JITCODE) { + JitCode* code = JitCode::FromExecutable((uint8_t*)rp.target); + TraceManuallyBarrieredEdge(trc, &code, "masmrel32"); + MOZ_ASSERT(code == JitCode::FromExecutable((uint8_t*)rp.target)); + } + } + if (dataRelocations_.length()) { + CompactBufferReader reader(dataRelocations_); + ::TraceDataRelocations(trc, &m_buffer, reader); + } +} + +int64_t +Assembler::ExtractCodeLabelOffset(uint8_t* code) +{ + Instruction* inst = (Instruction*)code; + return Assembler::ExtractLoad64Value(inst); +} + +void +Assembler::Bind(uint8_t* rawCode, AbsoluteLabel* label, const void* address) +{ + if (label->used()) { + int64_t src = label->offset(); + do { + Instruction* inst = (Instruction*) (rawCode + src); + uint64_t next = Assembler::ExtractLoad64Value(inst); + Assembler::UpdateLoad64Value(inst, (uint64_t)address); + src = next; + } while (src != AbsoluteLabel::INVALID_OFFSET); + } + label->bind(); +} + +void +Assembler::bind(InstImm* inst, uint64_t branch, uint64_t target) +{ + int64_t offset = target - branch; + InstImm inst_bgezal = InstImm(op_regimm, zero, rt_bgezal, BOffImm16(0)); + InstImm inst_beq = InstImm(op_beq, zero, zero, BOffImm16(0)); + + // If encoded offset is 4, then the jump must be short + if (BOffImm16(inst[0]).decode() == 4) { + MOZ_ASSERT(BOffImm16::IsInRange(offset)); + inst[0].setBOffImm16(BOffImm16(offset)); + inst[1].makeNop(); + return; + } + + // Generate the long jump for calls because return address has to be the + // address after the reserved block. + if (inst[0].encode() == inst_bgezal.encode()) { + addLongJump(BufferOffset(branch)); + Assembler::WriteLoad64Instructions(inst, ScratchRegister, target); + inst[4] = InstReg(op_special, ScratchRegister, zero, ra, ff_jalr).encode(); + // There is 1 nop after this. + return; + } + + if (BOffImm16::IsInRange(offset)) { + bool conditional = (inst[0].encode() != inst_bgezal.encode() && + inst[0].encode() != inst_beq.encode()); + + inst[0].setBOffImm16(BOffImm16(offset)); + inst[1].makeNop(); + + // Skip the trailing nops in conditional branches. + // FIXME: On Loongson3 platform, the branch degrade performance. + if (0 && conditional) { + inst[2] = InstImm(op_regimm, zero, rt_bgez, BOffImm16(5 * sizeof(uint32_t))).encode(); + // There are 4 nops after this + } + return; + } + + if (inst[0].encode() == inst_beq.encode()) { + // Handle long unconditional jump. + addLongJump(BufferOffset(branch)); + Assembler::WriteLoad64Instructions(inst, ScratchRegister, target); + inst[4] = InstReg(op_special, ScratchRegister, zero, zero, ff_jr).encode(); + // There is 1 nop after this. + } else { + // Handle long conditional jump. + inst[0] = invertBranch(inst[0], BOffImm16(7 * sizeof(uint32_t))); + // No need for a "nop" here because we can clobber scratch. + addLongJump(BufferOffset(branch + sizeof(uint32_t))); + Assembler::WriteLoad64Instructions(&inst[1], ScratchRegister, target); + inst[5] = InstReg(op_special, ScratchRegister, zero, zero, ff_jr).encode(); + // There is 1 nop after this. + } +} + +void +Assembler::bind(RepatchLabel* label) +{ + BufferOffset dest = nextOffset(); + if (label->used()) { + // If the label has a use, then change this use to refer to + // the bound label; + BufferOffset b(label->offset()); + InstImm* inst1 = (InstImm*)editSrc(b); + + // If first instruction is branch, then this is a loop backedge. + if (inst1->extractOpcode() == ((uint32_t)op_beq >> OpcodeShift)) { + // Backedges are short jumps when bound, but can become long + // when patched. + uint64_t offset = dest.getOffset() - label->offset(); + MOZ_ASSERT(BOffImm16::IsInRange(offset)); + inst1->setBOffImm16(BOffImm16(offset)); + } else { + Assembler::UpdateLoad64Value(inst1, dest.getOffset()); + } + + } + label->bind(dest.getOffset()); +} + +uint32_t +Assembler::PatchWrite_NearCallSize() +{ + // Load an address needs 4 instructions, and a jump with a delay slot. + return (4 + 2) * sizeof(uint32_t); +} + +void +Assembler::PatchWrite_NearCall(CodeLocationLabel start, CodeLocationLabel toCall) +{ + Instruction* inst = (Instruction*) start.raw(); + uint8_t* dest = toCall.raw(); + + // Overwrite whatever instruction used to be here with a call. + // Always use long jump for two reasons: + // - Jump has to be the same size because of PatchWrite_NearCallSize. + // - Return address has to be at the end of replaced block. + // Short jump wouldn't be more efficient. + Assembler::WriteLoad64Instructions(inst, ScratchRegister, (uint64_t)dest); + inst[4] = InstReg(op_special, ScratchRegister, zero, ra, ff_jalr); + inst[5] = InstNOP(); + + // Ensure everyone sees the code that was just written into memory. + AutoFlushICache::flush(uintptr_t(inst), PatchWrite_NearCallSize()); +} + +uint64_t +Assembler::ExtractLoad64Value(Instruction* inst0) +{ + InstImm* i0 = (InstImm*) inst0; + InstImm* i1 = (InstImm*) inst0->next(); + InstReg* i2 = (InstReg*) inst1->next(); + InstImm* i3 = (InstImm*) inst2->next(); + InstImm* i5 = (InstImm*) inst3->next()->next(); + + MOZ_ASSERT(i0->extractOpcode() == ((uint32_t)op_lui >> OpcodeShift)); + MOZ_ASSERT(i1->extractOpcode() == ((uint32_t)op_ori >> OpcodeShift)); + MOZ_ASSERT(i3->extractOpcode() == ((uint32_t)op_ori >> OpcodeShift)); + + if ((i2->extractOpcode() == ((uint32_t)op_special >> OpcodeShift)) && + (i2->extractFunctionField() == ff_dsrl32)) + { + uint64_t value = (uint64_t(i0->extractImm16Value()) << 32) | + (uint64_t(i1->extractImm16Value()) << 16) | + uint64_t(i3->extractImm16Value()); + return uint64_t((int64_t(value) <<16) >> 16); + } + + MOZ_ASSERT(i5->extractOpcode() == ((uint32_t)op_ori >> OpcodeShift)); + uint64_t value = (uint64_t(i0->extractImm16Value()) << 48) | + (uint64_t(i1->extractImm16Value()) << 32) | + (uint64_t(i3->extractImm16Value()) << 16) | + uint64_t(i5->extractImm16Value()); + return value; +} + +void +Assembler::UpdateLoad64Value(Instruction* inst0, uint64_t value) +{ + InstImm* i0 = (InstImm*) inst0; + InstImm* i1 = (InstImm*) inst0->next(); + InstReg* i2 = (InstReg*) inst1->next(); + InstImm* i3 = (InstImm*) inst2->next(); + InstImm* i5 = (InstImm*) inst3->next()->next(); + + MOZ_ASSERT(i0->extractOpcode() == ((uint32_t)op_lui >> OpcodeShift)); + MOZ_ASSERT(i1->extractOpcode() == ((uint32_t)op_ori >> OpcodeShift)); + MOZ_ASSERT(i3->extractOpcode() == ((uint32_t)op_ori >> OpcodeShift)); + + if ((i2->extractOpcode() == ((uint32_t)op_special >> OpcodeShift)) && + (i2->extractFunctionField() == ff_dsrl32)) + { + i0->setImm16(Imm16::Lower(Imm32(value >> 32))); + i1->setImm16(Imm16::Upper(Imm32(value))); + i3->setImm16(Imm16::Lower(Imm32(value))); + return; + } + + MOZ_ASSERT(i5->extractOpcode() == ((uint32_t)op_ori >> OpcodeShift)); + + i0->setImm16(Imm16::Upper(Imm32(value >> 32))); + i1->setImm16(Imm16::Lower(Imm32(value >> 32))); + i3->setImm16(Imm16::Upper(Imm32(value))); + i5->setImm16(Imm16::Lower(Imm32(value))); +} + +void +Assembler::WriteLoad64Instructions(Instruction* inst0, Register reg, uint64_t value) +{ + Instruction* inst1 = inst0->next(); + Instruction* inst2 = inst1->next(); + Instruction* inst3 = inst2->next(); + + *inst0 = InstImm(op_lui, zero, reg, Imm16::Lower(Imm32(value >> 32))); + *inst1 = InstImm(op_ori, reg, reg, Imm16::Upper(Imm32(value))); + *inst2 = InstReg(op_special, rs_one, reg, reg, 48 - 32, ff_dsrl32); + *inst3 = InstImm(op_ori, reg, reg, Imm16::Lower(Imm32(value))); +} + +void +Assembler::PatchDataWithValueCheck(CodeLocationLabel label, PatchedImmPtr newValue, + PatchedImmPtr expectedValue) +{ + Instruction* inst = (Instruction*) label.raw(); + + // Extract old Value + DebugOnly value = Assembler::ExtractLoad64Value(inst); + MOZ_ASSERT(value == uint64_t(expectedValue.value)); + + // Replace with new value + Assembler::UpdateLoad64Value(inst, uint64_t(newValue.value)); + + AutoFlushICache::flush(uintptr_t(inst), 6 * sizeof(uint32_t)); +} + +void +Assembler::PatchInstructionImmediate(uint8_t* code, PatchedImmPtr imm) +{ + InstImm* inst = (InstImm*)code; + Assembler::UpdateLoad64Value(inst, (uint64_t)imm.value); +} + +void +Assembler::ToggleCall(CodeLocationLabel inst_, bool enabled) +{ + Instruction* inst = (Instruction*)inst_.raw(); + InstImm* i0 = (InstImm*) inst; + InstImm* i1 = (InstImm*) i0->next(); + InstImm* i3 = (InstImm*) i1->next()->next(); + Instruction* i4 = (Instruction*) i3->next(); + + MOZ_ASSERT(i0->extractOpcode() == ((uint32_t)op_lui >> OpcodeShift)); + MOZ_ASSERT(i1->extractOpcode() == ((uint32_t)op_ori >> OpcodeShift)); + MOZ_ASSERT(i3->extractOpcode() == ((uint32_t)op_ori >> OpcodeShift)); + + if (enabled) { + MOZ_ASSERT(i4->extractOpcode() != ((uint32_t)op_lui >> OpcodeShift)); + InstReg jalr = InstReg(op_special, ScratchRegister, zero, ra, ff_jalr); + *i4 = jalr; + } else { + InstNOP nop; + *i4 = nop; + } + + AutoFlushICache::flush(uintptr_t(i4), sizeof(uint32_t)); +} + +void +Assembler::UpdateBoundsCheck(uint64_t heapSize, Instruction* inst) +{ + // Replace with new value + Assembler::UpdateLoad64Value(inst, heapSize); +} diff --git a/js/src/jit/mips64/Assembler-mips64.h b/js/src/jit/mips64/Assembler-mips64.h new file mode 100644 index 0000000000..fc55787b42 --- /dev/null +++ b/js/src/jit/mips64/Assembler-mips64.h @@ -0,0 +1,217 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef jit_mips64_Assembler_mips64_h +#define jit_mips64_Assembler_mips64_h + +#include "jit/mips-shared/Assembler-mips-shared.h" + +#include "jit/mips64/Architecture-mips64.h" + +namespace js { +namespace jit { + +static MOZ_CONSTEXPR_VAR Register CallTempReg4 = a4; +static MOZ_CONSTEXPR_VAR Register CallTempReg5 = a5; + +static MOZ_CONSTEXPR_VAR Register CallTempNonArgRegs[] = { t0, t1, t2, t3 }; +static const uint32_t NumCallTempNonArgRegs = mozilla::ArrayLength(CallTempNonArgRegs); + +class ABIArgGenerator +{ + unsigned usedArgSlots_; + bool firstArgFloat; + ABIArg current_; + + public: + ABIArgGenerator(); + ABIArg next(MIRType argType); + ABIArg& current() { return current_; } + + uint32_t stackBytesConsumedSoFar() const { + if (usedArgSlots_ <= 8) + return 0; + + return (usedArgSlots_ - 8) * sizeof(int64_t); + } + + static const Register NonArgReturnReg0; + static const Register NonArgReturnReg1; + static const Register NonArg_VolatileReg; + static const Register NonReturn_VolatileReg0; + static const Register NonReturn_VolatileReg1; +}; + +static MOZ_CONSTEXPR_VAR Register JSReturnReg = v1; +static MOZ_CONSTEXPR_VAR Register JSReturnReg_Type = JSReturnReg; +static MOZ_CONSTEXPR_VAR Register JSReturnReg_Data = JSReturnReg; +static MOZ_CONSTEXPR_VAR FloatRegister ReturnFloat32Reg = { FloatRegisters::f0, FloatRegisters::Single }; +static MOZ_CONSTEXPR_VAR FloatRegister ReturnDoubleReg = { FloatRegisters::f0, FloatRegisters::Double }; +static MOZ_CONSTEXPR_VAR FloatRegister ScratchFloat32Reg = { FloatRegisters::f23, FloatRegisters::Single }; +static MOZ_CONSTEXPR_VAR FloatRegister ScratchDoubleReg = { FloatRegisters::f23, FloatRegisters::Double }; +static MOZ_CONSTEXPR_VAR FloatRegister SecondScratchFloat32Reg = { FloatRegisters::f21, FloatRegisters::Single }; +static MOZ_CONSTEXPR_VAR FloatRegister SecondScratchDoubleReg = { FloatRegisters::f21, FloatRegisters::Double }; + +// Registers used in the GenerateFFIIonExit Disable Activation block. +// None of these may be the second scratch register (t8). +static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnData = JSReturnReg_Data; +static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnType = JSReturnReg_Type; + +static MOZ_CONSTEXPR_VAR FloatRegister f0 = { FloatRegisters::f0, FloatRegisters::Double }; +static MOZ_CONSTEXPR_VAR FloatRegister f1 = { FloatRegisters::f1, FloatRegisters::Double }; +static MOZ_CONSTEXPR_VAR FloatRegister f2 = { FloatRegisters::f2, FloatRegisters::Double }; +static MOZ_CONSTEXPR_VAR FloatRegister f3 = { FloatRegisters::f3, FloatRegisters::Double }; +static MOZ_CONSTEXPR_VAR FloatRegister f4 = { FloatRegisters::f4, FloatRegisters::Double }; +static MOZ_CONSTEXPR_VAR FloatRegister f5 = { FloatRegisters::f5, FloatRegisters::Double }; +static MOZ_CONSTEXPR_VAR FloatRegister f6 = { FloatRegisters::f6, FloatRegisters::Double }; +static MOZ_CONSTEXPR_VAR FloatRegister f7 = { FloatRegisters::f7, FloatRegisters::Double }; +static MOZ_CONSTEXPR_VAR FloatRegister f8 = { FloatRegisters::f8, FloatRegisters::Double }; +static MOZ_CONSTEXPR_VAR FloatRegister f9 = { FloatRegisters::f9, FloatRegisters::Double }; +static MOZ_CONSTEXPR_VAR FloatRegister f10 = { FloatRegisters::f10, FloatRegisters::Double }; +static MOZ_CONSTEXPR_VAR FloatRegister f11 = { FloatRegisters::f11, FloatRegisters::Double }; +static MOZ_CONSTEXPR_VAR FloatRegister f12 = { FloatRegisters::f12, FloatRegisters::Double }; +static MOZ_CONSTEXPR_VAR FloatRegister f13 = { FloatRegisters::f13, FloatRegisters::Double }; +static MOZ_CONSTEXPR_VAR FloatRegister f14 = { FloatRegisters::f14, FloatRegisters::Double }; +static MOZ_CONSTEXPR_VAR FloatRegister f15 = { FloatRegisters::f15, FloatRegisters::Double }; +static MOZ_CONSTEXPR_VAR FloatRegister f16 = { FloatRegisters::f16, FloatRegisters::Double }; +static MOZ_CONSTEXPR_VAR FloatRegister f17 = { FloatRegisters::f17, FloatRegisters::Double }; +static MOZ_CONSTEXPR_VAR FloatRegister f18 = { FloatRegisters::f18, FloatRegisters::Double }; +static MOZ_CONSTEXPR_VAR FloatRegister f19 = { FloatRegisters::f19, FloatRegisters::Double }; +static MOZ_CONSTEXPR_VAR FloatRegister f20 = { FloatRegisters::f20, FloatRegisters::Double }; +static MOZ_CONSTEXPR_VAR FloatRegister f21 = { FloatRegisters::f21, FloatRegisters::Double }; +static MOZ_CONSTEXPR_VAR FloatRegister f22 = { FloatRegisters::f22, FloatRegisters::Double }; +static MOZ_CONSTEXPR_VAR FloatRegister f23 = { FloatRegisters::f23, FloatRegisters::Double }; +static MOZ_CONSTEXPR_VAR FloatRegister f24 = { FloatRegisters::f24, FloatRegisters::Double }; +static MOZ_CONSTEXPR_VAR FloatRegister f25 = { FloatRegisters::f25, FloatRegisters::Double }; +static MOZ_CONSTEXPR_VAR FloatRegister f26 = { FloatRegisters::f26, FloatRegisters::Double }; +static MOZ_CONSTEXPR_VAR FloatRegister f27 = { FloatRegisters::f27, FloatRegisters::Double }; +static MOZ_CONSTEXPR_VAR FloatRegister f28 = { FloatRegisters::f28, FloatRegisters::Double }; +static MOZ_CONSTEXPR_VAR FloatRegister f29 = { FloatRegisters::f29, FloatRegisters::Double }; +static MOZ_CONSTEXPR_VAR FloatRegister f30 = { FloatRegisters::f30, FloatRegisters::Double }; +static MOZ_CONSTEXPR_VAR FloatRegister f31 = { FloatRegisters::f31, FloatRegisters::Double }; + +// MIPS64 CPUs can only load multibyte data that is "naturally" +// eight-byte-aligned, sp register should be sixteen-byte-aligned. +static MOZ_CONSTEXPR_VAR uint32_t ABIStackAlignment = 16; +static MOZ_CONSTEXPR_VAR uint32_t JitStackAlignment = 16; + +static MOZ_CONSTEXPR_VAR uint32_t JitStackValueAlignment = JitStackAlignment / sizeof(Value); +static_assert(JitStackAlignment % sizeof(Value) == 0 && JitStackValueAlignment >= 1, + "Stack alignment should be a non-zero multiple of sizeof(Value)"); + +// TODO this is just a filler to prevent a build failure. The MIPS SIMD +// alignment requirements still need to be explored. +// TODO Copy the static_asserts from x64/x86 assembler files. +static MOZ_CONSTEXPR_VAR uint32_t SimdMemoryAlignment = 16; + +static MOZ_CONSTEXPR_VAR uint32_t AsmJSStackAlignment = SimdMemoryAlignment; + +static MOZ_CONSTEXPR_VAR Scale ScalePointer = TimesEight; + +class Assembler : public AssemblerMIPSShared +{ + public: + Assembler() + : AssemblerMIPSShared() + { } + + // MacroAssemblers hold onto gcthings, so they are traced by the GC. + void trace(JSTracer* trc); + + static uintptr_t GetPointer(uint8_t*); + + using AssemblerMIPSShared::bind; + using AssemblerMIPSShared::PatchDataWithValueCheck; + + void bind(RepatchLabel* label); + void Bind(uint8_t* rawCode, AbsoluteLabel* label, const void* address); + + static void TraceJumpRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader); + static void TraceDataRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader); + + void bind(InstImm* inst, uint64_t branch, uint64_t target); + + // Copy the assembly code to the given buffer, and perform any pending + // relocations relying on the target address. + void executableCopy(uint8_t* buffer); + + static uint32_t PatchWrite_NearCallSize(); + + static uint64_t ExtractLoad64Value(Instruction* inst0); + static void UpdateLoad64Value(Instruction* inst0, uint64_t value); + static void WriteLoad64Instructions(Instruction* inst0, Register reg, uint64_t value); + + + static void PatchWrite_NearCall(CodeLocationLabel start, CodeLocationLabel toCall); + static void PatchDataWithValueCheck(CodeLocationLabel label, PatchedImmPtr newValue, + PatchedImmPtr expectedValue); + + static void PatchInstructionImmediate(uint8_t* code, PatchedImmPtr imm); + + static void ToggleCall(CodeLocationLabel inst_, bool enabled); + + static void UpdateBoundsCheck(uint64_t logHeapSize, Instruction* inst); + static int64_t ExtractCodeLabelOffset(uint8_t* code); +}; // Assembler + +static const uint32_t NumIntArgRegs = 8; +static const uint32_t NumFloatArgRegs = NumIntArgRegs; + +static inline bool +GetIntArgReg(uint32_t usedArgSlots, Register* out) +{ + if (usedArgSlots < NumIntArgRegs) { + *out = Register::FromCode(a0.code() + usedArgSlots); + return true; + } + return false; +} + +static inline bool +GetFloatArgReg(uint32_t usedArgSlots, FloatRegister* out) +{ + if (usedArgSlots < NumFloatArgRegs) { + *out = FloatRegister::FromCode(f12.code() + usedArgSlots); + return true; + } + return false; +} + +// Get a register in which we plan to put a quantity that will be used as an +// integer argument. This differs from GetIntArgReg in that if we have no more +// actual argument registers to use we will fall back on using whatever +// CallTempReg* don't overlap the argument registers, and only fail once those +// run out too. +static inline bool +GetTempRegForIntArg(uint32_t usedIntArgs, uint32_t usedFloatArgs, Register* out) +{ + // NOTE: We can't properly determine which regs are used if there are + // float arguments. If this is needed, we will have to guess. + MOZ_ASSERT(usedFloatArgs == 0); + + if (GetIntArgReg(usedIntArgs, out)) + return true; + // Unfortunately, we have to assume things about the point at which + // GetIntArgReg returns false, because we need to know how many registers it + // can allocate. + usedIntArgs -= NumIntArgRegs; + if (usedIntArgs >= NumCallTempNonArgRegs) + return false; + *out = CallTempNonArgRegs[usedIntArgs]; + return true; +} + +static inline uint32_t +GetArgStackDisp(uint32_t usedArgSlots) +{ + MOZ_ASSERT(usedArgSlots >= NumIntArgRegs); + return (usedArgSlots - NumIntArgRegs) * sizeof(int64_t); +} + +} // namespace jit +} // namespace js + +#endif /* jit_mips64_Assembler_mips64_h */ diff --git a/js/src/jit/shared/IonAssemblerBuffer.h b/js/src/jit/shared/IonAssemblerBuffer.h index be1251e45e..a69230115a 100644 --- a/js/src/jit/shared/IonAssemblerBuffer.h +++ b/js/src/jit/shared/IonAssemblerBuffer.h @@ -152,8 +152,13 @@ class AssemblerBuffer bool ensureSpace(int size) { // Space can exist in the most recent Slice. - if (tail && tail->length() + size <= tail->Capacity()) + if (tail && tail->length() + size <= tail->Capacity()) { + // Simulate allocation failure even when we don't need a new slice. + if (js::oom::ShouldFailWithOOM()) + return fail_oom(); + return true; + } // Otherwise, a new Slice must be added. Slice* slice = newSlice(lifoAlloc_); diff --git a/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h b/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h index 8252945927..be8696269b 100644 --- a/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h +++ b/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h @@ -704,6 +704,8 @@ struct AssemblerBufferWithConstantPools : public AssemblerBufferputBytes(guardSize_ * InstSize, nullptr); + if (this->oom()) + return; BufferOffset afterPool = this->nextOffset(); Asm::WritePoolGuard(branch, this->getInst(branch), afterPool); @@ -864,7 +866,7 @@ struct AssemblerBufferWithConstantPools : public AssemblerBufferoom()) putInt(alignFillInst_); inhibitNops_ = false; } diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h index 422dee5122..2200d9152a 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h @@ -78,7 +78,7 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared LInstruction* ins_; public: - OutOfLineSimdFloatToIntCheck(Register temp, FloatRegister input, LInstruction* ins) + OutOfLineSimdFloatToIntCheck(Register temp, FloatRegister input, LInstruction *ins) : temp_(temp), input_(input), ins_(ins) {} @@ -238,7 +238,7 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared virtual void visitGuardClass(LGuardClass* guard); virtual void visitEffectiveAddress(LEffectiveAddress* ins); virtual void visitUDivOrMod(LUDivOrMod* ins); - virtual void visitUDivOrModConstant(LUDivOrModConstant* ins); + virtual void visitUDivOrModConstant(LUDivOrModConstant *ins); virtual void visitAsmJSPassStackArg(LAsmJSPassStackArg* ins); virtual void visitMemoryBarrier(LMemoryBarrier* ins); virtual void visitAtomicTypedArrayElementBinop(LAtomicTypedArrayElementBinop* lir); diff --git a/js/src/moz.build b/js/src/moz.build index 50a60ecdd8..e0472015ec 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -11,6 +11,8 @@ component_intl = ('Core', 'JavaScript: Internationalization API') component_jit = ('Core', 'JavaScript Engine: JIT') component_stl = ('Core', 'JavaScript: Standard Library') +FILES_PER_UNIFIED_FILE = 6 + with Files('../public/**'): BUG_COMPONENT = component_engine with Files('*'): @@ -47,6 +49,10 @@ with Files('builtin/make_intl_data.py'): with Files('../public/TrackedOptimizationInfo.h'): BUG_COMPONENT = component_jit + +if CONFIG['DEHYDRA_PATH']: + DIRS += ['analysis-tests'] + if CONFIG['JS_BUNDLED_EDITLINE']: DIRS += ['editline'] @@ -476,11 +482,16 @@ elif CONFIG['JS_CODEGEN_ARM64']: 'jit/arm64/vixl/MozSimulator-vixl.cpp', 'jit/arm64/vixl/Simulator-vixl.cpp' ] -elif CONFIG['JS_CODEGEN_MIPS32']: +elif CONFIG['JS_CODEGEN_MIPS32'] or CONFIG['JS_CODEGEN_MIPS64']: UNIFIED_SOURCES += [ 'jit/mips-shared/Architecture-mips-shared.cpp', 'jit/mips-shared/Assembler-mips-shared.cpp', + 'jit/mips-shared/Bailouts-mips-shared.cpp', + 'jit/mips-shared/BaselineCompiler-mips-shared.cpp', + 'jit/mips-shared/BaselineIC-mips-shared.cpp', + 'jit/mips-shared/CodeGenerator-mips-shared.cpp', 'jit/mips-shared/Lowering-mips-shared.cpp', + 'jit/mips-shared/MoveEmitter-mips-shared.cpp', ] if CONFIG['JS_CODEGEN_MIPS32']: UNIFIED_SOURCES += [ @@ -500,6 +511,11 @@ elif CONFIG['JS_CODEGEN_MIPS32']: UNIFIED_SOURCES += [ 'jit/mips32/Simulator-mips32.cpp' ] + elif CONFIG['JS_CODEGEN_MIPS64']: + UNIFIED_SOURCES += [ + 'jit/mips64/Architecture-mips64.cpp', + 'jit/mips64/Assembler-mips64.cpp', + ] if CONFIG['OS_ARCH'] == 'WINNT': SOURCES += [ diff --git a/js/xpconnect/src/XPCMaps.cpp b/js/xpconnect/src/XPCMaps.cpp index 14ea4de273..9d1ba84a51 100644 --- a/js/xpconnect/src/XPCMaps.cpp +++ b/js/xpconnect/src/XPCMaps.cpp @@ -164,15 +164,7 @@ JSObject2WrappedJSMap::SizeOfWrappedJS(mozilla::MallocSizeOf mallocSizeOf) const Native2WrappedNativeMap* Native2WrappedNativeMap::newMap(int length) { - Native2WrappedNativeMap* map = new Native2WrappedNativeMap(length); - if (map && map->mTable) - return map; - // Allocation of the map or the creation of its hash table has - // failed. This will cause a nullptr deref later when we attempt - // to use the map, so we abort immediately to provide a more - // useful crash stack. - NS_RUNTIMEABORT("Ran out of memory."); - return nullptr; + return new Native2WrappedNativeMap(length); } Native2WrappedNativeMap::Native2WrappedNativeMap(int length) @@ -300,15 +292,7 @@ ClassInfo2NativeSetMap::ShallowSizeOfIncludingThis(mozilla::MallocSizeOf mallocS ClassInfo2WrappedNativeProtoMap* ClassInfo2WrappedNativeProtoMap::newMap(int length) { - ClassInfo2WrappedNativeProtoMap* map = new ClassInfo2WrappedNativeProtoMap(length); - if (map && map->mTable) - return map; - // Allocation of the map or the creation of its hash table has - // failed. This will cause a nullptr deref later when we attempt - // to use the map, so we abort immediately to provide a more - // useful crash stack. - NS_RUNTIMEABORT("Ran out of memory."); - return nullptr; + return new ClassInfo2WrappedNativeProtoMap(length); } ClassInfo2WrappedNativeProtoMap::ClassInfo2WrappedNativeProtoMap(int length) diff --git a/layout/tools/reftest/reftest/__init__.py b/layout/tools/reftest/reftest/__init__.py index 5044b16a17..b58490c155 100644 --- a/layout/tools/reftest/reftest/__init__.py +++ b/layout/tools/reftest/reftest/__init__.py @@ -45,15 +45,16 @@ class ReftestManifest(object): self.dirs = set() self.files = set() self.manifests = set() + self.tests = set() def load(self, path): """Parse a reftest manifest file.""" - normalized = os.path.normpath(os.path.abspath(path)) - self.manifests.add(normalized) + normalized_path = os.path.normpath(os.path.abspath(path)) + self.manifests.add(normalized_path) if not self.path: - self.path = normalized + self.path = normalized_path - mdir = os.path.dirname(normalized) + mdir = os.path.dirname(normalized_path) self.dirs.add(mdir) with open(path, 'r') as fh: @@ -123,3 +124,4 @@ class ReftestManifest(object): test = os.path.normpath(os.path.join(mdir, urlprefix + f)) self.files.add(test) self.dirs.add(os.path.dirname(test)) + self.tests.add((test, normalized_path)) diff --git a/mobile/android/app/assets/example_asset.txt b/mobile/android/app/assets/example_asset.txt new file mode 100644 index 0000000000..34338f983e --- /dev/null +++ b/mobile/android/app/assets/example_asset.txt @@ -0,0 +1 @@ +This is an example asset. diff --git a/netwerk/build/moz.build b/netwerk/build/moz.build index d2c55abdee..19a5d102c6 100644 --- a/netwerk/build/moz.build +++ b/netwerk/build/moz.build @@ -28,11 +28,9 @@ LOCAL_INCLUDES += [ '/netwerk/streamconv/converters', ] -protocols = CONFIG['NECKO_PROTOCOLS'].copy() -if 'about' in protocols: - protocols.remove('about') +protocols = CONFIG['NECKO_PROTOCOLS'] LOCAL_INCLUDES += sorted([ - '/netwerk/protocol/%s' % d for d in protocols + '/netwerk/protocol/%s' % d for d in protocols if d != 'about' ]) if CONFIG['OS_ARCH'] == 'WINNT': diff --git a/python/mach_commands.py b/python/mach_commands.py index b64788dc75..aacaf143c6 100644 --- a/python/mach_commands.py +++ b/python/mach_commands.py @@ -5,10 +5,13 @@ from __future__ import absolute_import, print_function, unicode_literals import argparse -import glob import logging import mozpack.path as mozpath import os +import platform +import subprocess +import sys +import which from mozbuild.base import ( MachCommandBase, @@ -20,6 +23,46 @@ from mach.decorators import ( Command, ) +ESLINT_NOT_FOUND_MESSAGE = ''' +Could not find eslint! We looked at the --binary option, at the ESLINT +environment variable, and then at your path. Install eslint and needed plugins +with + +mach eslint --setup + +and try again. +'''.strip() + +NODE_NOT_FOUND_MESSAGE = ''' +nodejs is either not installed or is installed to a non-standard path. +Please install nodejs from https://nodejs.org and try again. + +Valid installation paths: +'''.strip() + +NPM_NOT_FOUND_MESSAGE = ''' +Node Package Manager (npm) is either not installed or installed to a +non-standard path. Please install npm from https://nodejs.org (it comes as an +option in the node installation) and try again. + +Valid installation paths: +'''.strip() + +ESLINT_PROMPT = ''' +Would you like to use eslint +'''.strip() + +ESLINT_PLUGIN_MOZILLA_PROMPT = ''' +eslint-plugin-mozilla is an eslint plugin containing rules that help enforce +JavaScript coding standards in the Mozilla project. Would you like to use this +plugin +'''.strip() + +ESLINT_PLUGIN_REACT_PROMPT = ''' +eslint-plugin-react is an eslint plugin containing rules that help React +developers follow strict guidelines. Would you like to install it +'''.strip() + @CommandProvider class MachCommands(MachCommandBase): @@ -53,6 +96,7 @@ class MachCommands(MachCommandBase): help='Tests to run. Each test can be a single file or a directory.') def python_test(self, tests, verbose=False, stop=False): self._activate_virtualenv() + import glob # Python's unittest, and in particular discover, has problems with # clashing namespaces when importing multiple test modules. What follows @@ -119,3 +163,157 @@ class MachCommands(MachCommandBase): return 1 return 0 if return_code == 0 else 1 + + @Command('eslint', category='devenv', + description='Run eslint or help configure eslint for optimal development.') + @CommandArgument('-s', '--setup', default=False, action='store_true', + help='configure eslint for optimal development.') + @CommandArgument('path', nargs='?', default='.', + help='Path to files to lint, like "browser/components/loop" ' + 'or "mobile/android". ' + 'Defaults to the current directory if not given.') + @CommandArgument('-e', '--ext', default='[.js,.jsm,.jsx]', + help='Filename extensions to lint, default: "[.js,.jsm,.jsx]".') + @CommandArgument('-b', '--binary', default=None, + help='Path to eslint binary.') + @CommandArgument('args', nargs=argparse.REMAINDER) # Passed through to eslint. + def eslint(self, setup, path, ext=None, binary=None, args=[]): + '''Run eslint.''' + + if setup: + return self.eslint_setup() + + if not binary: + binary = os.environ.get('ESLINT', None) + if not binary: + try: + binary = which.which('eslint') + except which.WhichError: + pass + + if not binary: + print(ESLINT_NOT_FOUND_MESSAGE) + return 1 + + # The cwd below is unfortunate. eslint --config=PATH/TO/.eslintrc works, + # but --ignore-path=PATH/TO/.eslintignore treats paths as relative to + # the current directory, rather than as relative to the location of + # .eslintignore (see https://github.com/eslint/eslint/issues/1382). + # mach commands always execute in the topsrcdir, so we could make all + # paths in .eslint relative to the topsrcdir, but it's not clear if + # that's a good choice for future eslint and IDE integrations. + # Unfortunately, running after chdir does not print the full path to + # files (convenient for opening with copy-and-paste). In the meantime, + # we just print the active path. + + self.log(logging.INFO, 'eslint', {'binary': binary, 'path': path}, + 'Running {binary} in {path}') + + cmd_args = [binary, + '--ext', ext, # This keeps ext as a single argument. + ] + args + # Path must come after arguments. Path is '.' due to cwd below. + cmd_args += ['.'] + + return self.run_process(cmd_args, + cwd=path, + pass_thru=True, # Allow user to run eslint interactively. + ensure_exit_code=False, # Don't throw on non-zero exit code. + ) + def eslint_setup(self, update_only=False): + """Ensure eslint is optimally configured. + + This command will inspect your eslint configuration and + guide you through an interactive wizard helping you configure + eslint for optimal use on Mozilla projects. + """ + sys.path.append(os.path.dirname(__file__)) + + # At the very least we need node installed. + nodePath = self.getNodeOrNpmPath("node") + if not nodePath: + return 1 + + npmPath = self.getNodeOrNpmPath("npm") + if not npmPath: + return 1 + + # Install eslint. + print("Installing eslint...") + with open(os.devnull, "w") as fnull: + subprocess.call([npmPath, "install", "eslint", "-g"], + stdout=fnull, stderr=fnull) + + # Install eslint-plugin-mozilla. + print("") + print("Installing eslint-plugin-mozilla...") + with open(os.devnull, "w") as fnull: + subprocess.call([npmPath, "link"], + cwd="testing/eslint-plugin-mozilla", + stdout=fnull, stderr=fnull) + + # Install eslint-plugin-react. + print("") + print("Installing eslint-plugin-react...") + with open(os.devnull, "w") as fnull: + subprocess.call([npmPath, "install", "-g", "eslint-plugin-react"], + stdout=fnull, stderr=fnull) + + def getPossibleNodePathsWin(self): + """ + Return possible nodejs paths on Windows. + """ + if platform.system() != "Windows": + return [] + + return { + "%s\\nodejs" % os.environ.get("SystemDrive"), + os.path.join(os.environ.get("ProgramFiles"), "nodejs"), + os.path.join(os.environ.get("PROGRAMW6432"), "nodejs"), + os.path.join(os.environ.get("PROGRAMFILES"), "nodejs") + } + + def getNodeOrNpmPath(self, filename): + """ + Return the nodejs or npm path. + """ + try: + appPath = which.which(filename) + return appPath + except which.WhichError: + pass + + if platform.system() == "Windows": + try: + for ext in ["", ".cmd", ".exe"]: + nodeOrNpmPath = which.which(filename + ext, + path=self.getPossibleNodePathsWin()) + if self.is_valid(nodeOrNpmPath): + return nodeOrNpmPath + except which.WhichError: + pass + + if filename == "node": + print(NODE_NOT_FOUND_MESSAGE) + elif filename == "npm": + print(NPM_NOT_FOUND_MESSAGE) + + if platform.system() == "Windows": + appPaths = self.getPossibleNodePathsWin() + + for p in appPaths: + print(" - " + p) + elif platform.system() == "Darwin": + print(" - /usr/local/bin/node") + elif platform.system() == "Linux": + print(" - /usr/bin/nodejs") + + return None + + def is_valid(self, path): + try: + with open(os.devnull, "w") as fnull: + subprocess.check_call([path, "--version"], stdout=fnull) + return True + except subprocess.CalledProcessError: + return False diff --git a/python/mozbuild/mozbuild/action/buildlist.py b/python/mozbuild/mozbuild/action/buildlist.py index 6076b6999b..4c25e09449 100644 --- a/python/mozbuild/mozbuild/action/buildlist.py +++ b/python/mozbuild/mozbuild/action/buildlist.py @@ -12,12 +12,16 @@ from __future__ import absolute_import, print_function import sys import os -from mozbuild.util import lock_file +from mozbuild.util import ( + ensureParentDir, + lock_file, +) def addEntriesToListFile(listFile, entries): """Given a file |listFile| containing one entry per line, add each entry in |entries| to the file, unless it is already present.""" + ensureParentDir(listFile) lock = lock_file(listFile + ".lck") try: if os.path.exists(listFile): diff --git a/python/mozbuild/mozbuild/action/process_install_manifest.py b/python/mozbuild/mozbuild/action/process_install_manifest.py index cba4f37c73..97559653c0 100644 --- a/python/mozbuild/mozbuild/action/process_install_manifest.py +++ b/python/mozbuild/mozbuild/action/process_install_manifest.py @@ -8,6 +8,7 @@ import argparse import sys from mozpack.copier import FileCopier from mozpack.manifests import InstallManifest +from mozbuild.util import DefinesAction COMPLETE = 'From {dest}: Kept {existing} existing; Added/updated {updated}; ' \ @@ -17,19 +18,36 @@ COMPLETE = 'From {dest}: Kept {existing} existing; Added/updated {updated}; ' \ def process_manifest(destdir, paths, remove_unaccounted=True, remove_all_directory_symlinks=True, - remove_empty_directories=True): + remove_empty_directories=True, + defines={}): manifest = InstallManifest() for path in paths: manifest |= InstallManifest(path=path) copier = FileCopier() - manifest.populate_registry(copier) + manifest.populate_registry(copier, defines_override=defines) return copier.copy(destdir, remove_unaccounted=remove_unaccounted, remove_all_directory_symlinks=remove_all_directory_symlinks, remove_empty_directories=remove_empty_directories) +class DefinesAction(argparse.Action): + def __call__(self, parser, namespace, values, option_string): + defines = getattr(namespace, self.dest) + if defines is None: + defines = {} + values = values.split('=', 1) + if len(values) == 1: + name, value = values[0], 1 + else: + name, value = values + if value.isdigit(): + value = int(value) + defines[name] = value + setattr(namespace, self.dest, defines) + + def main(argv): parser = argparse.ArgumentParser( description='Process install manifest files.') @@ -42,13 +60,17 @@ def main(argv): help='Do not remove all directory symlinks from destination.') parser.add_argument('--no-remove-empty-directories', action='store_true', help='Do not remove empty directories from destination.') + parser.add_argument('-D', action=DefinesAction, + dest='defines', metavar="VAR[=VAL]", + help='Define a variable to override what is specified in the manifest') args = parser.parse_args(argv) result = process_manifest(args.destdir, args.manifests, remove_unaccounted=not args.no_remove, remove_all_directory_symlinks=not args.no_remove_all_directory_symlinks, - remove_empty_directories=not args.no_remove_empty_directories) + remove_empty_directories=not args.no_remove_empty_directories, + defines=args.defines) print(COMPLETE.format(dest=args.destdir, existing=result.existing_files_count, diff --git a/python/mozbuild/mozbuild/backend/android_eclipse.py b/python/mozbuild/mozbuild/backend/android_eclipse.py index a1bd995c1d..30bc0a676c 100644 --- a/python/mozbuild/mozbuild/backend/android_eclipse.py +++ b/python/mozbuild/mozbuild/backend/android_eclipse.py @@ -55,21 +55,19 @@ class AndroidEclipseBackend(CommonBackend): """Write out Android Eclipse project files.""" if not isinstance(obj, ContextDerived): - return + return False - CommonBackend.consume_object(self, obj) + if CommonBackend.consume_object(self, obj): + # If CommonBackend acknowledged the object, we're done with it. + return True - # If CommonBackend acknowledged the object, we're done with it. - if obj._ack: - return - - # We don't want to handle most things, so we just acknowledge all objects... - obj.ack() - - # ... and handle the one case we care about specially. + # Handle the one case we care about specially. if isinstance(obj, ContextWrapped) and isinstance(obj.wrapped, AndroidEclipseProjectData): self._process_android_eclipse_project_data(obj.wrapped, obj.srcdir, obj.objdir) + # We don't want to handle most things, so we just acknowledge all objects + return True + def consume_finished(self): """The common backend handles WebIDL and test files. We don't handle these, so we don't call our superclass. diff --git a/python/mozbuild/mozbuild/backend/base.py b/python/mozbuild/mozbuild/backend/base.py index 0096409392..3a875ed90e 100644 --- a/python/mozbuild/mozbuild/backend/base.py +++ b/python/mozbuild/mozbuild/backend/base.py @@ -117,7 +117,8 @@ class BuildBackend(LoggingMixin): """ for obj in objs: obj_start = time.time() - self.consume_object(obj) + if not self.consume_object(obj): + raise Exception('Unhandled object of type %s' % type(obj)) self._execution_time += time.time() - obj_start if isinstance(obj, ContextDerived): diff --git a/python/mozbuild/mozbuild/backend/common.py b/python/mozbuild/mozbuild/backend/common.py index 3599220bde..723a2ad106 100644 --- a/python/mozbuild/mozbuild/backend/common.py +++ b/python/mozbuild/mozbuild/backend/common.py @@ -206,7 +206,7 @@ class CommonBackend(BuildBackend): # Do not handle ConfigFileSubstitution for Makefiles. Leave that # to other if mozpath.basename(obj.output_path) == 'Makefile': - return + return False with self._get_preprocessor(obj) as pp: pp.do_include(obj.input_path) self.backend_input_files.add(obj.input_path) @@ -251,9 +251,9 @@ class CommonBackend(BuildBackend): if hasattr(self, '_process_unified_sources'): self._process_unified_sources(obj) else: - return + return False - obj.ack() + return True def consume_finished(self): if len(self._idl_manager.idls): diff --git a/python/mozbuild/mozbuild/backend/configenvironment.py b/python/mozbuild/mozbuild/backend/configenvironment.py index 3cc0b7be4a..7c3527f7a1 100644 --- a/python/mozbuild/mozbuild/backend/configenvironment.py +++ b/python/mozbuild/mozbuild/backend/configenvironment.py @@ -27,6 +27,8 @@ else: class BuildConfig(object): """Represents the output of configure.""" + _CODE_CACHE = {} + def __init__(self): self.topsrcdir = None self.topobjdir = None @@ -35,26 +37,35 @@ class BuildConfig(object): self.substs = {} self.files = [] - @staticmethod - def from_config_status(path): + @classmethod + def from_config_status(cls, path): """Create an instance from a config.status file.""" + code_cache = cls._CODE_CACHE + mtime = os.path.getmtime(path) - with open(path, 'rt') as fh: - source = fh.read() - code = compile(source, path, 'exec', dont_inherit=1) - g = { - '__builtins__': __builtins__, - '__file__': path, - } - l = {} - exec(code, g, l) + # cache the compiled code as it can be reused + # we cache it the first time, or if the file changed + if not path in code_cache or code_cache[path][0] != mtime: + with open(path, 'rt') as fh: + source = fh.read() + code_cache[path] = ( + mtime, + compile(source, path, 'exec', dont_inherit=1) + ) - config = BuildConfig() + g = { + '__builtins__': __builtins__, + '__file__': path, + } + l = {} + exec(code_cache[path][1], g, l) - for name in l['__all__']: - setattr(config, name, l[name]) + config = BuildConfig() - return config + for name in l['__all__']: + setattr(config, name, l[name]) + + return config class ConfigEnvironment(object): diff --git a/python/mozbuild/mozbuild/backend/cpp_eclipse.py b/python/mozbuild/mozbuild/backend/cpp_eclipse.py index 50897a0a39..51062ab8a0 100644 --- a/python/mozbuild/mozbuild/backend/cpp_eclipse.py +++ b/python/mozbuild/mozbuild/backend/cpp_eclipse.py @@ -65,8 +65,6 @@ class CppEclipseBackend(CommonBackend): return os.path.join(srcdir_parent, workspace_dirname) def consume_object(self, obj): - obj.ack() - reldir = getattr(obj, 'relativedir', None) # Note that unlike VS, Eclipse' indexer seem to crawl the headers and @@ -74,6 +72,8 @@ class CppEclipseBackend(CommonBackend): if isinstance(obj, Defines): self._paths_to_defines.setdefault(reldir, {}).update(obj.defines) + return True + def consume_finished(self): settings_dir = os.path.join(self._project_dir, '.settings') launch_dir = os.path.join(self._project_dir, 'RunConfigurations') diff --git a/python/mozbuild/mozbuild/backend/fastermake.py b/python/mozbuild/mozbuild/backend/fastermake.py new file mode 100644 index 0000000000..5ffc406cd1 --- /dev/null +++ b/python/mozbuild/mozbuild/backend/fastermake.py @@ -0,0 +1,244 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +from __future__ import absolute_import, unicode_literals + +from mozbuild.backend.common import CommonBackend +from mozbuild.frontend.data import ( + ContextDerived, + Defines, + DistFiles, + FinalTargetFiles, + JARManifest, + JavaScriptModules, + JsPreferenceFile, + Resources, + VariablePassthru, +) +from mozbuild.makeutil import Makefile +from mozbuild.util import OrderedDefaultDict +from mozpack.manifests import InstallManifest +import mozpack.path as mozpath +from collections import OrderedDict +from itertools import chain + + +class FasterMakeBackend(CommonBackend): + def _init(self): + super(FasterMakeBackend, self)._init() + + self._seen_directories = set() + self._defines = dict() + self._jar_manifests = OrderedDict() + + self._manifest_entries = OrderedDefaultDict(list) + + self._install_manifests = OrderedDefaultDict(InstallManifest) + + def _add_preprocess(self, obj, path, dest, **kwargs): + target = mozpath.basename(path) + # This matches what PP_TARGETS do in config/rules. + if target.endswith('.in'): + target = target[:-3] + depfile = mozpath.join( + self.environment.topobjdir, 'faster', '.deps', + mozpath.join(obj.install_target, dest, target).replace('/', '_')) + self._install_manifests[obj.install_target].add_preprocess( + mozpath.join(obj.srcdir, path), + mozpath.join(dest, target), + depfile, + **kwargs) + + def consume_object(self, obj): + if not isinstance(obj, Defines) and isinstance(obj, ContextDerived): + defines = self._defines.get(obj.objdir, {}) + if defines: + defines = defines.defines + + if isinstance(obj, Defines): + self._defines[obj.objdir] = obj + + # We're assuming below that Defines come first for a given objdir, + # which is kind of set in stone from the order things are treated + # in emitter.py. + assert obj.objdir not in self._seen_directories + + elif isinstance(obj, JARManifest) and \ + obj.install_target.startswith('dist/bin'): + defines = self._defines.get(obj.objdir, []) + if defines: + defines = list(defines.get_defines()) + self._jar_manifests[obj.path] = (obj.objdir, + obj.install_target, + defines) + + elif isinstance(obj, VariablePassthru) and \ + obj.install_target.startswith('dist/bin'): + for f in obj.variables.get('EXTRA_COMPONENTS', {}): + path = mozpath.join(obj.install_target, 'components', + mozpath.basename(f)) + self._install_manifests[obj.install_target].add_symlink( + mozpath.join(obj.srcdir, f), + mozpath.join('components', mozpath.basename(f)) + ) + if f.endswith('.manifest'): + manifest = mozpath.join(obj.install_target, + 'chrome.manifest') + self._manifest_entries[manifest].append( + 'manifest components/%s' % mozpath.basename(f)) + + for f in obj.variables.get('EXTRA_PP_COMPONENTS', {}): + self._add_preprocess(obj, f, 'components', defines=defines) + + if f.endswith('.manifest'): + manifest = mozpath.join(obj.install_target, + 'chrome.manifest') + self._manifest_entries[manifest].append( + 'manifest components/%s' % mozpath.basename(f)) + + elif isinstance(obj, JavaScriptModules) and \ + obj.install_target.startswith('dist/bin'): + for path, strings in obj.modules.walk(): + base = mozpath.join('modules', path) + for f in strings: + if obj.flavor == 'extra': + self._install_manifests[obj.install_target].add_symlink( + mozpath.join(obj.srcdir, f), + mozpath.join(base, mozpath.basename(f)) + ) + elif obj.flavor == 'extra_pp': + self._add_preprocess(obj, f, base, defines=defines) + + elif isinstance(obj, JsPreferenceFile) and \ + obj.install_target.startswith('dist/bin'): + # The condition for the directory value in config/rules.mk is: + # ifneq (,$(DIST_SUBDIR)$(XPI_NAME)$(LIBXUL_SDK)) + # - LIBXUL_SDK is not supported (it likely doesn't work in the + # recursive backend anyways + # - when XPI_NAME is set, obj.install_target will start with + # dist/xpi-stage + # - when DIST_SUBDIR is set, obj.install_target will start with + # dist/bin/$(DIST_SUBDIR) + # So an equivalent condition that is not cumbersome for us and that + # is enough at least for now is checking if obj.install_target is + # different from dist/bin. + if obj.install_target == 'dist/bin': + pref_dir = 'defaults/pref' + else: + pref_dir = 'defaults/preferences' + + dest = mozpath.join(obj.install_target, pref_dir, + mozpath.basename(obj.path)) + # We preprocess these, but they don't necessarily have preprocessor + # directives, so tell the preprocessor to not complain about that. + self._add_preprocess(obj, obj.path, pref_dir, defines=defines, + silence_missing_directive_warnings=True) + + elif isinstance(obj, Resources) and \ + obj.install_target.startswith('dist/bin'): + for path, strings in obj.resources.walk(): + base = mozpath.join('res', path) + for f in strings: + flags = strings.flags_for(f) + if flags and flags.preprocess: + self._add_preprocess(obj, f, base, marker='%', + defines=obj.defines) + else: + self._install_manifests[obj.install_target].add_symlink( + mozpath.join(obj.srcdir, f), + mozpath.join(base, mozpath.basename(f)) + ) + + elif isinstance(obj, FinalTargetFiles) and \ + obj.install_target.startswith('dist/bin'): + for path, strings in obj.files.walk(): + base = mozpath.join(obj.install_target, path) + for f in strings: + self._install_manifests[obj.install_target].add_symlink( + mozpath.join(obj.srcdir, f), + mozpath.join(path, mozpath.basename(f)) + ) + + elif isinstance(obj, DistFiles) and \ + obj.install_target.startswith('dist/bin'): + # We preprocess these, but they don't necessarily have preprocessor + # directives, so tell the preprocessor to not complain about that. + for f in obj.files: + self._add_preprocess(obj, f, '', defines=defines, + silence_missing_directive_warnings=True) + + else: + # We currently ignore a lot of object types, so just acknowledge + # everything. + return True + + self._seen_directories.add(obj.objdir) + return True + + def consume_finished(self): + mk = Makefile() + # Add the default rule at the very beginning. + mk.create_rule(['default']) + mk.add_statement('TOPSRCDIR = %s' % self.environment.topsrcdir) + mk.add_statement('TOPOBJDIR = %s' % self.environment.topobjdir) + + # Add a few necessary variables inherited from configure + for var in ( + 'PYTHON', + 'ACDEFINES', + 'MOZ_CHROME_FILE_FORMAT', + ): + mk.add_statement('%s = %s' % (var, self.environment.substs[var])) + + # Add all necessary information for jar manifest processing + jar_mn_targets = [] + + for path, (objdir, install_target, defines) in \ + self._jar_manifests.iteritems(): + rel_manifest = mozpath.relpath(path, self.environment.topsrcdir) + target = rel_manifest.replace('/', '-') + assert target not in jar_mn_targets + jar_mn_targets.append(target) + target = 'jar-%s' % target + mk.create_rule([target]).add_dependencies([path]) + if objdir != mozpath.join(self.environment.topobjdir, + mozpath.dirname(rel_manifest)): + mk.create_rule([target]).add_dependencies( + ['objdir = %s' % objdir]) + if install_target != 'dist/bin': + mk.create_rule([target]).add_dependencies( + ['install_target = %s' % install_target]) + if defines: + mk.create_rule([target]).add_dependencies( + ['defines = %s' % ' '.join(defines)]) + + mk.add_statement('JAR_MN_TARGETS = %s' % ' '.join(jar_mn_targets)) + + # Add information for chrome manifest generation + manifest_targets = [] + + for target, entries in self._manifest_entries.iteritems(): + manifest_targets.append(target) + target = '$(TOPOBJDIR)/%s' % target + mk.create_rule([target]).add_dependencies( + ['content = %s' % ' '.join('"%s"' % e for e in entries)]) + + mk.add_statement('MANIFEST_TARGETS = %s' % ' '.join(manifest_targets)) + + # Add information for install manifests. + mk.add_statement('INSTALL_MANIFESTS = %s' + % ' '.join(self._install_manifests.keys())) + + mk.add_statement('include $(TOPSRCDIR)/config/faster/rules.mk') + + for base, install_manifest in self._install_manifests.iteritems(): + with self._write_file( + mozpath.join(self.environment.topobjdir, 'faster', + 'install_%s' % base.replace('/', '_'))) as fh: + install_manifest.write(fileobj=fh) + + with self._write_file( + mozpath.join(self.environment.topobjdir, 'faster', + 'Makefile')) as fh: + mk.dump(fh, removal_guard=False) diff --git a/python/mozbuild/mozbuild/backend/recursivemake.py b/python/mozbuild/mozbuild/backend/recursivemake.py index 37a0913129..e44798e65d 100644 --- a/python/mozbuild/mozbuild/backend/recursivemake.py +++ b/python/mozbuild/mozbuild/backend/recursivemake.py @@ -26,6 +26,10 @@ import mozpack.path as mozpath from .common import CommonBackend from ..frontend.data import ( + AndroidAssetsDirs, + AndroidResDirs, + AndroidExtraResDirs, + AndroidExtraPackages, AndroidEclipseProjectData, BrandingFiles, ConfigFileSubstitution, @@ -71,6 +75,11 @@ from ..util import ( from ..makeutil import Makefile MOZBUILD_VARIABLES = [ + b'ANDROID_APK_NAME', + b'ANDROID_APK_PACKAGE', + b'ANDROID_ASSETS_DIRS', + b'ANDROID_EXTRA_PACKAGES', + b'ANDROID_EXTRA_RES_DIRS', b'ANDROID_GENERATED_RESFILES', b'ANDROID_RES_DIRS', b'ASFLAGS', @@ -419,11 +428,11 @@ class RecursiveMakeBackend(CommonBackend): """Write out build files necessary to build with recursive make.""" if not isinstance(obj, ContextDerived): - return + return False backend_file = self._get_backend_file_for(obj) - CommonBackend.consume_object(self, obj) + consumed = CommonBackend.consume_object(self, obj) # CommonBackend handles XPIDLFile and TestManifest, but we want to do # some extra things for them. @@ -436,8 +445,8 @@ class RecursiveMakeBackend(CommonBackend): self._process_test_manifest(obj, backend_file) # If CommonBackend acknowledged the object, we're done with it. - if obj._ack: - return + if consumed: + return True if isinstance(obj, DirectoryTraversal): self._process_directory_traversal(obj, backend_file) @@ -514,7 +523,7 @@ class RecursiveMakeBackend(CommonBackend): elif isinstance(obj, Resources): self._process_resources(obj, obj.resources, backend_file) - + elif isinstance(obj, BrandingFiles): self._process_branding_files(obj, obj.files, backend_file) @@ -569,7 +578,7 @@ class RecursiveMakeBackend(CommonBackend): elif isinstance(obj.wrapped, AndroidEclipseProjectData): self._process_android_eclipse_project_data(obj.wrapped, backend_file) else: - return + return False elif isinstance(obj, SharedLibrary): self._process_shared_library(obj, backend_file) @@ -594,9 +603,26 @@ class RecursiveMakeBackend(CommonBackend): for f in obj.files: backend_file.write('DIST_FILES += %s\n' % f) + elif isinstance(obj, AndroidResDirs): + for p in obj.paths: + backend_file.write('ANDROID_RES_DIRS += %s\n' % p.full_path) + + elif isinstance(obj, AndroidAssetsDirs): + for p in obj.paths: + backend_file.write('ANDROID_ASSETS_DIRS += %s\n' % p.full_path) + + elif isinstance(obj, AndroidExtraResDirs): + for p in obj.paths: + backend_file.write('ANDROID_EXTRA_RES_DIRS += %s\n' % p.full_path) + + elif isinstance(obj, AndroidExtraPackages): + for p in obj.packages: + backend_file.write('ANDROID_EXTRA_PACKAGES += %s\n' % p) + else: - return - obj.ack() + return False + + return True def _fill_root_mk(self): """ @@ -1066,6 +1092,7 @@ INSTALL_TARGETS += %(prefix)s modules = manager.modules xpt_modules = sorted(modules.keys()) xpt_files = set() + registered_xpt_files = set() mk = Makefile() @@ -1097,15 +1124,16 @@ INSTALL_TARGETS += %(prefix)s rules = StringIO() mk.dump(rules, removal_guard=False) - # Write out manifests defining interfaces + interfaces_manifests = [] dist_dir = mozpath.join(self.environment.topobjdir, 'dist') for manifest, entries in manager.interface_manifests.items(): - path = mozpath.join(self.environment.topobjdir, manifest) - with self._write_file(path) as fh: - for xpt in sorted(entries): - fh.write('interfaces %s\n' % xpt) + interfaces_manifests.append(mozpath.join('$(DEPTH)', manifest)) + for xpt in sorted(entries): + registered_xpt_files.add(mozpath.join( + '$(DEPTH)', mozpath.dirname(manifest), xpt)) if install_target.startswith('dist/'): + path = mozpath.join(self.environment.topobjdir, manifest) path = mozpath.relpath(path, dist_dir) prefix, subpath = path.split('/', 1) key = 'dist_%s' % prefix @@ -1128,9 +1156,11 @@ INSTALL_TARGETS += %(prefix)s obj.config = self.environment self._create_makefile(obj, extra=dict( chrome_manifests = ' '.join(chrome_manifests), + interfaces_manifests = ' '.join(interfaces_manifests), xpidl_rules=rules.getvalue(), xpidl_modules=' '.join(xpt_modules), - xpt_files=' '.join(sorted(xpt_files)), + xpt_files=' '.join(sorted(xpt_files - registered_xpt_files)), + registered_xpt_files=' '.join(sorted(registered_xpt_files)), )) def _process_program(self, program, backend_file): diff --git a/python/mozbuild/mozbuild/backend/visualstudio.py b/python/mozbuild/mozbuild/backend/visualstudio.py index a5412e65fa..03fc6fb4a4 100644 --- a/python/mozbuild/mozbuild/backend/visualstudio.py +++ b/python/mozbuild/mozbuild/backend/visualstudio.py @@ -93,9 +93,6 @@ class VisualStudioBackend(CommonBackend): path=os.path.join(self._out_dir, 'mozilla.sln')) def consume_object(self, obj): - # Just acknowledge everything. - obj.ack() - reldir = getattr(obj, 'relativedir', None) if hasattr(obj, 'config') and reldir not in self._paths_to_configs: @@ -130,6 +127,9 @@ class VisualStudioBackend(CommonBackend): else: includes.append(os.path.join('$(TopSrcDir)', reldir, p)) + # Just acknowledge everything. + return True + def _add_sources(self, reldir, obj): s = self._paths_to_sources.setdefault(reldir, set()) s.update(obj.files) diff --git a/python/mozbuild/mozbuild/compilation/codecomplete.py b/python/mozbuild/mozbuild/compilation/codecomplete.py index 9e2a7c7f64..be8d698715 100644 --- a/python/mozbuild/mozbuild/compilation/codecomplete.py +++ b/python/mozbuild/mozbuild/compilation/codecomplete.py @@ -26,12 +26,9 @@ class Introspection(MachCommandBase): help='Source file to display compilation flags for') def compileflags(self, what): from mozbuild.util import resolve_target_to_make - import shlex + from mozbuild.compilation import util - top_make = os.path.join(self.topobjdir, 'Makefile') - if not os.path.exists(top_make): - print('Your tree has not been built yet. Please run ' - '|mach build| with no arguments.') + if not util.check_top_objdir(self.topobjdir): return 1 path_arg = self._wrap_path_argument(what) @@ -42,23 +39,7 @@ class Introspection(MachCommandBase): if make_dir is None and make_target is None: return 1 - build_vars = {} - - def on_line(line): - elements = [s.strip() for s in line.split('=', 1)] - - if len(elements) != 2: - return - - build_vars[elements[0]] = elements[1] - - try: - old_logger = self.log_manager.replace_terminal_handler(None) - self._run_make(directory=make_dir, target='showbuild', log=False, - print_directory=False, allow_parallel=False, silent=True, - line_handler=on_line) - finally: - self.log_manager.replace_terminal_handler(old_logger) + build_vars = util.get_build_vars(make_dir, self) if what.endswith('.c'): name = 'COMPILE_CFLAGS' @@ -68,20 +49,5 @@ class Introspection(MachCommandBase): if name not in build_vars: return - flags = ['-isystem', '-I', '-include', '-MF'] - new_args = [] - path = os.path.join(self.topobjdir, make_dir) - for arg in shlex.split(build_vars[name]): - if new_args and new_args[-1] in flags: - arg = os.path.normpath(os.path.join(path, arg)) - else: - flag = [(f, arg[len(f):]) for f in flags + ['--sysroot='] - if arg.startswith(f)] - if flag: - flag, val = flag[0] - if val: - arg = flag + os.path.normpath(os.path.join(path, val)) - new_args.append(arg) - - print(' '.join(new_args)) + print(util.get_flags(self.topobjdir, make_dir, build_vars, name)) diff --git a/python/mozbuild/mozbuild/compilation/database.py b/python/mozbuild/mozbuild/compilation/database.py new file mode 100644 index 0000000000..c0bd3b867e --- /dev/null +++ b/python/mozbuild/mozbuild/compilation/database.py @@ -0,0 +1,119 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. + +# This modules provides functionality for dealing with code completion. + +import os + +from mozbuild.base import MozbuildObject +from mozbuild.compilation import util +from mozbuild.backend.common import CommonBackend +from mozbuild.frontend.data import ( + Sources, + HostSources, + UnifiedSources, + GeneratedSources, +) +from mach.config import ConfigSettings +from mach.logging import LoggingManager + + +class CompileDBBackend(CommonBackend): + def _init(self): + CommonBackend._init(self) + if not util.check_top_objdir(self.environment.topobjdir): + raise Exception() + + # The database we're going to dump out to. + self._db = [] + + # The cache for per-directory flags + self._flags = {} + + log_manager = LoggingManager() + self._cmd = MozbuildObject(self.environment.topsrcdir, ConfigSettings(), + log_manager, self.environment.topobjdir) + + + def consume_object(self, obj): + if isinstance(obj, UnifiedSources): + # For unified sources, only include the unified source file. + # Note that unified sources are never used for host sources. + for f in obj.unified_source_mapping: + flags = self._get_dir_flags(obj.objdir) + self._build_db_line(obj, self.environment, f[0], + obj.canonical_suffix, flags, False) + elif isinstance(obj, Sources) or isinstance(obj, HostSources) or \ + isinstance(obj, GeneratedSources): + # For other sources, include each source file. + for f in obj.files: + flags = self._get_dir_flags(obj.objdir) + self._build_db_line(obj, self.environment, f, + obj.canonical_suffix, flags, + isinstance(obj, HostSources)) + + return True + + def consume_finished(self): + import json + # Output the database (a JSON file) to objdir/compile_commands.json + outputfile = os.path.join(self.environment.topobjdir, 'compile_commands.json') + with self._write_file(outputfile) as jsonout: + json.dump(self._db, jsonout, indent=0) + + def _get_dir_flags(self, directory): + if directory in self._flags: + return self._flags[directory] + + from mozbuild.util import resolve_target_to_make + + make_dir, make_target = resolve_target_to_make(self.environment.topobjdir, directory) + if make_dir is None and make_target is None: + raise Exception('Cannot figure out the make dir and target for ' + directory) + + build_vars = util.get_build_vars(directory, self._cmd) + + # We only care about the following build variables. + for name in ('COMPILE_CFLAGS', 'COMPILE_CXXFLAGS', + 'COMPILE_CMFLAGS', 'COMPILE_CMMFLAGS'): + if name not in build_vars: + continue + + build_vars[name] = util.get_flags(self.environment.topobjdir, directory, + build_vars, name) + + self._flags[directory] = build_vars + return self._flags[directory] + + def _build_db_line(self, obj, cenv, filename, canonical_suffix, flags, ishost): + # Distinguish between host and target files. + prefix = 'HOST_' if ishost else '' + if canonical_suffix == '.c': + compiler = cenv.substs[prefix + 'CC'] + cflags = flags['COMPILE_CFLAGS'] + # Add the Objective-C flags if needed. + if filename.endswith('.m'): + cflags += ' ' + flags['COMPILE_CMFLAGS'] + elif canonical_suffix == '.cpp': + compiler = cenv.substs[prefix + 'CXX'] + cflags = flags['COMPILE_CXXFLAGS'] + # Add the Objective-C++ flags if needed. + if filename.endswith('.mm'): + cflags += ' ' + flags['COMPILE_CMMFLAGS'] + else: + return + + cmd = ' '.join([ + compiler, + '-o', '/dev/null', '-c', + cflags, + filename, + ]) + + self._db.append({ + 'directory': obj.objdir, + 'command': cmd, + 'file': filename + }) + diff --git a/python/mozbuild/mozbuild/compilation/util.py b/python/mozbuild/mozbuild/compilation/util.py new file mode 100644 index 0000000000..8610ece772 --- /dev/null +++ b/python/mozbuild/mozbuild/compilation/util.py @@ -0,0 +1,63 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. + +import os +import shlex + +def check_top_objdir(topobjdir): + top_make = os.path.join(topobjdir, 'Makefile') + if not os.path.exists(top_make): + print('Your tree has not been built yet. Please run ' + '|mach build| with no arguments.') + return False + return True + +def get_build_vars(directory, cmd): + build_vars = {} + + def on_line(line): + elements = [s.strip() for s in line.split('=', 1)] + + if len(elements) != 2: + return + + build_vars[elements[0]] = elements[1] + + try: + old_logger = cmd.log_manager.replace_terminal_handler(None) + cmd._run_make(directory=directory, target='showbuild', log=False, + print_directory=False, allow_parallel=False, silent=True, + line_handler=on_line) + finally: + cmd.log_manager.replace_terminal_handler(old_logger) + + return build_vars + +def get_flags(topobjdir, make_dir, build_vars, name): + flags = ['-isystem', '-I', '-include', '-MF'] + new_args = [] + path = os.path.join(topobjdir, make_dir) + + # Take case to handle things such as the following correctly: + # * -DMOZ_APP_VERSION='"40.0a1"' + # * -DR_PLATFORM_INT_TYPES='' + # * -DAPP_ID='{ec8030f7-c20a-464f-9b0e-13a3a9e97384} + # * -D__UNUSED__='__attribute__((unused))' + lex = shlex.shlex(build_vars[name]) + lex.quotes = '"' + lex.wordchars += '+/\'"-=.*{}()[]<>' + + for arg in list(lex): + if new_args and new_args[-1] in flags: + arg = os.path.normpath(os.path.join(path, arg)) + else: + flag = [(f, arg[len(f):]) for f in flags + ['--sysroot='] + if arg.startswith(f)] + if flag: + flag, val = flag[0] + if val: + arg = flag + os.path.normpath(os.path.join(path, val)) + new_args.append(arg) + + return ' '.join(new_args) diff --git a/python/mozbuild/mozbuild/compilation/warnings.py b/python/mozbuild/mozbuild/compilation/warnings.py index 1bd6ecb3bc..801afc2b22 100644 --- a/python/mozbuild/mozbuild/compilation/warnings.py +++ b/python/mozbuild/mozbuild/compilation/warnings.py @@ -12,6 +12,7 @@ import os import re from mozbuild.util import hash_file +import mozpack.path as mozpath # Regular expression to strip ANSI color sequences from a string. This is @@ -147,17 +148,19 @@ class WarningsDatabase(object): for w in value['warnings']: yield w - @property - def type_counts(self): + def type_counts(self, dirpath=None): """Returns a mapping of warning types to their counts.""" types = {} for value in self._files.values(): for warning in value['warnings']: - count = types.get(warning['flag'], 0) + if dirpath and not mozpath.normsep(warning['filename']).startswith(dirpath): + continue + flag = warning['flag'] + count = types.get(flag, 0) count += 1 - types[warning['flag']] = count + types[flag] = count return types diff --git a/python/mozbuild/mozbuild/config_status.py b/python/mozbuild/mozbuild/config_status.py index d5ae53449f..4ac64d87a2 100644 --- a/python/mozbuild/mozbuild/config_status.py +++ b/python/mozbuild/mozbuild/config_status.py @@ -108,7 +108,7 @@ def config_status(topobjdir='.', topsrcdir='.', help='print diffs of changed files.') parser.add_argument('-b', '--backend', nargs='+', choices=['RecursiveMake', 'AndroidEclipse', 'CppEclipse', - 'VisualStudio', 'FasterMake'], + 'VisualStudio', 'FasterMake', 'CompileDB'], default=default_backends, help='what backend to build (default: %s).' % ' '.join(default_backends)) @@ -145,6 +145,9 @@ def config_status(topobjdir='.', topsrcdir='.', elif backend == 'FasterMake': from mozbuild.backend.fastermake import FasterMakeBackend backends_cls.append(FasterMakeBackend) + elif backend == 'CompileDB': + from mozbuild.compilation.database import CompileDBBackend + backends_cls.append(CompileDBBackend) else: backends_cls.append(RecursiveMakeBackend) diff --git a/python/mozbuild/mozbuild/frontend/context.py b/python/mozbuild/mozbuild/frontend/context.py index c8d94665b6..6965350fed 100644 --- a/python/mozbuild/mozbuild/frontend/context.py +++ b/python/mozbuild/mozbuild/frontend/context.py @@ -18,7 +18,10 @@ from __future__ import absolute_import, unicode_literals import os -from collections import OrderedDict +from collections import ( + Counter, + OrderedDict, +) from mozbuild.util import ( HierarchicalStringList, HierarchicalStringListWithFlagsFactory, @@ -32,9 +35,10 @@ from mozbuild.util import ( TypedList, TypedNamedTuple, ) + +from ..testing import all_test_flavors import mozpack.path as mozpath from types import FunctionType -from UserString import UserString import itertools @@ -43,6 +47,7 @@ class ContextDerivedValue(object): """Classes deriving from this one receive a special treatment in a Context. See Context documentation. """ + __slots__ = () class Context(KeyedDefaultDict): @@ -326,8 +331,13 @@ class PathMeta(type): assert isinstance(context, Context) if isinstance(value, Path): context = value.context - if not issubclass(cls, (SourcePath, ObjDirPath)): - cls = ObjDirPath if value.startswith('!') else SourcePath + if not issubclass(cls, (SourcePath, ObjDirPath, AbsolutePath)): + if value.startswith('!'): + cls = ObjDirPath + elif value.startswith('%'): + cls = AbsolutePath + else: + cls = SourcePath return super(PathMeta, cls).__call__(context, value) class Path(ContextDerivedValue, unicode): @@ -339,6 +349,7 @@ class Path(ContextDerivedValue, unicode): - 'srcdir/relative/paths' - '!/topobjdir/relative/paths' - '!objdir/relative/paths' + - '%/filesystem/absolute/paths' """ __metaclass__ = PathMeta @@ -394,6 +405,8 @@ class SourcePath(Path): def __init__(self, context, value): if value.startswith('!'): raise ValueError('Object directory paths are not allowed') + if value.startswith('%'): + raise ValueError('Filesystem absolute paths are not allowed') super(SourcePath, self).__init__(context, value) if value.startswith('/'): @@ -425,7 +438,7 @@ class ObjDirPath(Path): """Like Path, but limited to paths in the object directory.""" def __init__(self, context, value=None): if not value.startswith('!'): - raise ValueError('Source paths are not allowed') + raise ValueError('Object directory paths must start with ! prefix') super(ObjDirPath, self).__init__(context, value) if value.startswith('!/'): @@ -435,6 +448,18 @@ class ObjDirPath(Path): self.full_path = mozpath.normpath(path) +class AbsolutePath(Path): + """Like Path, but allows arbitrary paths outside the source and object directories.""" + def __init__(self, context, value=None): + if not value.startswith('%'): + raise ValueError('Absolute paths must start with % prefix') + if not os.path.isabs(value[1:]): + raise ValueError('Path \'%s\' is not absolute' % value[1:]) + super(AbsolutePath, self).__init__(context, value) + + self.full_path = mozpath.normpath(value[1:]) + + @memoize def ContextDerivedTypedList(klass, base_class=List): """Specialized TypedList for use with ContextDerivedValue types. @@ -464,6 +489,38 @@ def ContextDerivedTypedListWithItems(type, base_class=List): return _TypedListWithItems +@memoize +def ContextDerivedTypedRecord(*fields): + """Factory for objects with certain properties and dynamic + type checks. + + This API is extremely similar to the TypedNamedTuple API, + except that properties may be mutated. This supports syntax like: + + VARIABLE_NAME.property += [ + 'item1', + 'item2', + ] + """ + + class _TypedRecord(ContextDerivedValue): + __slots__ = tuple([name for name, _ in fields]) + + def __init__(self, context): + for fname, ftype in self._fields.items(): + if issubclass(ftype, ContextDerivedValue): + setattr(self, fname, self._fields[fname](context)) + else: + setattr(self, fname, self._fields[fname]()) + + def __setattr__(self, name, value): + if name in self._fields and not isinstance(value, self._fields[name]): + value = self._fields[name](value) + object.__setattr__(self, name, value) + + _TypedRecord._fields = dict(fields) + return _TypedRecord + BugzillaComponent = TypedNamedTuple('BugzillaComponent', [('product', unicode), ('component', unicode)]) @@ -471,6 +528,14 @@ WebPlatformTestManifest = TypedNamedTuple("WebPlatformTestManifest", [("manifest_path", unicode), ("test_root", unicode)]) +OrderedSourceList = ContextDerivedTypedList(SourcePath, StrictOrderingOnAppendList) +OrderedTestFlavorList = TypedList(Enum(*all_test_flavors()), + StrictOrderingOnAppendList) +OrderedStringList = TypedList(unicode, StrictOrderingOnAppendList) +DependentTestsEntry = ContextDerivedTypedRecord(('files', OrderedSourceList), + ('tags', OrderedStringList), + ('flavors', OrderedTestFlavorList)) + class Files(SubContext): """Metadata attached to files. @@ -491,8 +556,8 @@ class Files(SubContext): most one entity. Patterns with ``*`` or ``**`` are wildcard matches. ``*`` matches files - within a single directory. ``**`` matches files across several directories. - Here are some examples: + at least within a single directory. ``**`` matches files across several + directories. ``foo.html`` Will match only the ``foo.html`` file in the current directory. @@ -503,11 +568,18 @@ class Files(SubContext): ``foo/*.css`` Will match all ``.css`` files in the ``foo/`` directory. ``bar/*`` - Will match all files in the ``bar/`` directory but not any files in - child directories of ``bar/``, such as ``bar/dir1/baz``. - ``baz/**`` - Will match all files in the ``baz/`` directory and all directories - underneath. + Will match all files in the ``bar/`` directory and all of its + children directories. + ``bar/**`` + This is equivalent to ``bar/*`` above. + ``bar/**/foo`` + Will match all ``foo`` files in the ``bar/`` directory and all of its + children directories. + + The difference in behavior between ``*`` and ``**`` is only evident if + a pattern follows the ``*`` or ``**``. A pattern ending with ``*`` is + greedy. ``**`` is needed when you need an additional pattern after the + wildcard. e.g. ``**/foo``. """ VARIABLES = { @@ -532,17 +604,79 @@ class Files(SubContext): See :ref:`mozbuild_files_metadata_finalizing` for more info. """, None), + 'IMPACTED_TESTS': (DependentTestsEntry, list, + """File patterns, tags, and flavors for tests relevant to these files. + + Maps source files to the tests potentially impacted by those files. + Tests can be specified by file pattern, tag, or flavor. + + For example: + + with Files('runtests.py'): + IMPACTED_TESTS.files += [ + '**', + ] + + in testing/mochitest/moz.build will suggest that any of the tests + under testing/mochitest may be impacted by a change to runtests.py. + + File patterns may be made relative to the topsrcdir with a leading + '/', so + + with Files('httpd.js'): + IMPACTED_TESTS.files += [ + '/testing/mochitest/tests/Harness_sanity/**', + ] + + in netwerk/test/httpserver/moz.build will suggest that any change to httpd.js + will be relevant to the mochitest sanity tests. + + Tags and flavors are sorted string lists (flavors are limited to valid + values). + + For example: + + with Files('toolkit/devtools/*'): + IMPACTED_TESTS.tags += [ + 'devtools', + ] + + in the root moz.build would suggest that any test tagged 'devtools' would + potentially be impacted by a change to a file under toolkit/devtools, and + + with Files('dom/base/nsGlobalWindow.cpp'): + IMPACTED_TESTS.flavors += [ + 'mochitest', + ] + + Would suggest that nsGlobalWindow.cpp is potentially relevant to + any plain mochitest. + """, None), } def __init__(self, parent, pattern=None): super(Files, self).__init__(parent) self.pattern = pattern self.finalized = set() + self.test_files = set() + self.test_tags = set() + self.test_flavors = set() def __iadd__(self, other): assert isinstance(other, Files) + self.test_files |= other.test_files + self.test_tags |= other.test_tags + self.test_flavors |= other.test_flavors + for k, v in other.items(): + if k == 'IMPACTED_TESTS': + self.test_files |= set(mozpath.relpath(e.full_path, e.context.config.topsrcdir) + for e in v.files) + self.test_tags |= set(v.tags) + self.test_flavors |= set(v.flavors) + continue + # Ignore updates to finalized flags. if k in self.finalized: continue @@ -568,6 +702,49 @@ class Files(SubContext): return d + @staticmethod + def aggregate(files): + """Given a mapping of path to Files, obtain aggregate results. + + Consumers may want to extract useful information from a collection of + Files describing paths. e.g. given the files info data for N paths, + recommend a single bug component based on the most frequent one. This + function provides logic for deriving aggregate knowledge from a + collection of path File metadata. + + Note: the intent of this function is to operate on the result of + :py:func:`mozbuild.frontend.reader.BuildReader.files_info`. The + :py:func:`mozbuild.frontend.context.Files` instances passed in are + thus the "collapsed" (``__iadd__``ed) results of all ``Files`` from all + moz.build files relevant to a specific path, not individual ``Files`` + instances from a single moz.build file. + """ + d = {} + + bug_components = Counter() + + for f in files.values(): + bug_component = f.get('BUG_COMPONENT') + if bug_component: + bug_components[bug_component] += 1 + + d['bug_component_counts'] = [] + for c, count in bug_components.most_common(): + component = (c.product, c.component) + d['bug_component_counts'].append((c, count)) + + if 'recommended_bug_component' not in d: + d['recommended_bug_component'] = component + recommended_count = count + elif count == recommended_count: + # Don't recommend a component if it doesn't have a clear lead. + d['recommended_bug_component'] = None + + # In case no bug components. + d.setdefault('recommended_bug_component', None) + + return d + # This defines functions that create sub-contexts. # @@ -626,12 +803,41 @@ VARIABLES = { file. """, 'export'), - 'ANDROID_RES_DIRS': (List, list, + 'ANDROID_APK_NAME': (unicode, unicode, + """The name of an Android APK file to generate. + """, 'export'), + + 'ANDROID_APK_PACKAGE': (unicode, unicode, + """The name of the Android package to generate R.java for, like org.mozilla.gecko. + """, 'export'), + + 'ANDROID_EXTRA_PACKAGES': (StrictOrderingOnAppendList, list, + """The name of extra Android packages to generate R.java for, like ['org.mozilla.other']. + """, 'export'), + + 'ANDROID_EXTRA_RES_DIRS': (ContextDerivedTypedListWithItems(Path, List), list, + """Android extra package resource directories. + + This variable contains a list of directories containing static files + to package into a 'res' directory and merge into an APK file. These + directories are packaged into the APK but are assumed to be static + unchecked dependencies that should not be otherwise re-distributed. + """, 'export'), + + 'ANDROID_RES_DIRS': (ContextDerivedTypedListWithItems(Path, List), list, """Android resource directories. - This variable contains a list of directories, each relative to - the srcdir, containing static files to package into a 'res' - directory and merge into an APK file. + This variable contains a list of directories containing static + files to package into a 'res' directory and merge into an APK + file. + """, 'export'), + + 'ANDROID_ASSETS_DIRS': (ContextDerivedTypedListWithItems(Path, List), list, + """Android assets directories. + + This variable contains a list of directories containing static + files to package into an 'assets' directory and merge into an + APK file. """, 'export'), 'ANDROID_ECLIPSE_PROJECT_TARGETS': (dict, dict, diff --git a/python/mozbuild/mozbuild/frontend/data.py b/python/mozbuild/mozbuild/frontend/data.py index 1cbffe03ed..4352c96db7 100644 --- a/python/mozbuild/mozbuild/frontend/data.py +++ b/python/mozbuild/mozbuild/frontend/data.py @@ -28,16 +28,14 @@ from ..util import ( group_unified_files, ) +from ..testing import ( + all_test_flavors, +) + class TreeMetadata(object): """Base class for all data being captured.""" - def __init__(self): - self._ack = False - - def ack(self): - self._ack = True - class ContextDerived(TreeMetadata): """Build object derived from a single Context instance. @@ -73,6 +71,12 @@ class ContextDerived(TreeMetadata): self.config = context.config + self._context = context + + @property + def install_target(self): + return self._context['FINAL_TARGET'] + @property def relobjdir(self): return mozpath.relpath(self.objdir, self.topobjdir) @@ -148,7 +152,6 @@ class XPIDLFile(ContextDerived): __slots__ = ( 'add_to_manifest', 'basename', - 'install_target', 'source_path', ) @@ -160,8 +163,6 @@ class XPIDLFile(ContextDerived): self.module = module self.add_to_manifest = add_to_manifest - self.install_target = context['FINAL_TARGET'] - class BaseDefines(ContextDerived): """Context derived container object for DEFINES/HOST_DEFINES, which are OrderedDicts. @@ -619,6 +620,8 @@ class TestManifest(ContextDerived): install_prefix=None, relpath=None, dupe_manifest=False): ContextDerived.__init__(self, context) + assert flavor in all_test_flavors() + self.path = path self.directory = mozpath.dirname(path) self.manifest = manifest @@ -959,3 +962,53 @@ class AndroidEclipseProjectData(object): cpe.ignore_warnings = ignore_warnings self._classpathentries.append(cpe) return cpe + + +class AndroidResDirs(ContextDerived): + """Represents Android resource directories.""" + + __slots__ = ( + 'paths', + ) + + def __init__(self, context, paths): + ContextDerived.__init__(self, context) + self.paths = paths + +class AndroidAssetsDirs(ContextDerived): + """Represents Android assets directories.""" + + __slots__ = ( + 'paths', + ) + + def __init__(self, context, paths): + ContextDerived.__init__(self, context) + self.paths = paths + +class AndroidExtraResDirs(ContextDerived): + """Represents Android extra resource directories. + + Extra resources are resources provided by libraries and including in a + packaged APK, but not otherwise redistributed. In practice, this means + resources included in Fennec but not in GeckoView. + """ + + __slots__ = ( + 'paths', + ) + + def __init__(self, context, paths): + ContextDerived.__init__(self, context) + self.paths = paths + +class AndroidExtraPackages(ContextDerived): + """Represents Android extra packages.""" + + __slots__ = ( + 'packages', + ) + + def __init__(self, context, packages): + ContextDerived.__init__(self, context) + self.packages = packages diff --git a/python/mozbuild/mozbuild/frontend/emitter.py b/python/mozbuild/mozbuild/frontend/emitter.py index 62c56dbbd0..540a568cf6 100644 --- a/python/mozbuild/mozbuild/frontend/emitter.py +++ b/python/mozbuild/mozbuild/frontend/emitter.py @@ -24,6 +24,10 @@ import reftest import mozinfo from .data import ( + AndroidAssetsDirs, + AndroidExtraPackages, + AndroidExtraResDirs, + AndroidResDirs, BrandingFiles, ConfigFileSubstitution, ContextWrapped, @@ -75,9 +79,14 @@ from .data import ( from .reader import SandboxValidationError +from ..testing import ( + TEST_MANIFESTS, + REFTEST_FLAVORS, + WEB_PATFORM_TESTS_FLAVORS, +) + from .context import ( Context, - ObjDirPath, SourcePath, ObjDirPath, Path, @@ -150,8 +159,6 @@ class TreeMetadataEmitter(LoggingMixin): for o in objs: self._object_count += 1 yield o - if not o._ack: - raise Exception('Unhandled object of type %s' % type(o)) for out in output: # Nothing in sub-contexts is currently of interest to us. Filter @@ -542,14 +549,26 @@ class TreeMetadataEmitter(LoggingMixin): for obj in self._process_xpidl(context): yield obj + # Check for manifest declarations in EXTRA_{PP_,}COMPONENTS. + extras = context.get('EXTRA_COMPONENTS', []) + context.get('EXTRA_PP_COMPONENTS', []) + if any(e.endswith('.js') for e in extras) and \ + not any(e.endswith('.manifest') for e in extras) and \ + not context.get('NO_JS_MANIFEST', False): + raise SandboxValidationError('A .js component was specified in EXTRA_COMPONENTS ' + 'or EXTRA_PP_COMPONENTS without a matching ' + '.manifest file. See ' + 'https://developer.mozilla.org/en/XPCOM/XPCOM_changes_in_Gecko_2.0 .', + context); + # Proxy some variables as-is until we have richer classes to represent # them. We should aim to keep this set small because it violates the # desired abstraction of the build definition away from makefiles. passthru = VariablePassthru(context) varlist = [ 'ALLOW_COMPILER_WARNINGS', + 'ANDROID_APK_NAME', + 'ANDROID_APK_PACKAGE', 'ANDROID_GENERATED_RESFILES', - 'ANDROID_RES_DIRS', 'DISABLE_STL_WRAPPING', 'EXTRA_COMPONENTS', 'EXTRA_DSO_LDOPTS', @@ -697,6 +716,24 @@ class TreeMetadataEmitter(LoggingMixin): for name, data in context.get('ANDROID_ECLIPSE_PROJECT_TARGETS', {}).items(): yield ContextWrapped(context, data) + for (symbol, cls) in [ + ('ANDROID_RES_DIRS', AndroidResDirs), + ('ANDROID_EXTRA_RES_DIRS', AndroidExtraResDirs), + ('ANDROID_ASSETS_DIRS', AndroidAssetsDirs)]: + paths = context.get(symbol) + if not paths: + continue + for p in paths: + if isinstance(p, SourcePath) and not os.path.isdir(p.full_path): + raise SandboxValidationError('Directory listed in ' + '%s is not a directory: \'%s\'' % + (symbol, p.full_path), context) + yield cls(context, paths) + + android_extra_packages = context.get('ANDROID_EXTRA_PACKAGES') + if android_extra_packages: + yield AndroidExtraPackages(context, android_extra_packages) + if passthru.variables: yield passthru @@ -845,15 +882,15 @@ class TreeMetadataEmitter(LoggingMixin): for f in generated_files: flags = generated_files[f] output = f + inputs = [] if flags.script: method = "main" + script = SourcePath(context, flags.script).full_path + # Deal with cases like "C:\\path\\to\\script.py:function". - if not flags.script.endswith('.py') and ':' in flags.script: - script, method = flags.script.rsplit(':', 1) - else: - script = flags.script - script = mozpath.join(context.srcdir, script) - inputs = [mozpath.join(context.srcdir, i) for i in flags.inputs] + if '.py:' in script: + script, method = script.rsplit('.py:', 1) + script += '.py' if not os.path.exists(script): raise SandboxValidationError( @@ -863,15 +900,18 @@ class TreeMetadataEmitter(LoggingMixin): raise SandboxValidationError( 'Script for generating %s does not end in .py: %s' % (f, script), context) - for i in inputs: - if not os.path.exists(i): + + for i in flags.inputs: + p = Path(context, i) + if (isinstance(p, SourcePath) and + not os.path.exists(p.full_path)): raise SandboxValidationError( 'Input for generating %s does not exist: %s' - % (f, i), context) + % (f, p.full_path), context) + inputs.append(p.full_path) else: script = None method = None - inputs = [] yield GeneratedFile(context, script, method, output, inputs) def _process_test_harness_files(self, context): @@ -945,49 +985,21 @@ class TreeMetadataEmitter(LoggingMixin): else 'USE_LIBS')) def _process_test_manifests(self, context): - # While there are multiple test manifests, the behavior is very similar - # across them. We enforce this by having common handling of all - # manifests and outputting a single class type with the differences - # described inside the instance. - # - # Keys are variable prefixes and values are tuples describing how these - # manifests should be handled: - # - # (flavor, install_prefix, package_tests) - # - # flavor identifies the flavor of this test. - # install_prefix is the path prefix of where to install the files in - # the tests directory. - # package_tests indicates whether to package test files into the test - # package; suites that compile the test files should not install - # them into the test package. - # - test_manifests = dict( - A11Y=('a11y', 'testing/mochitest', 'a11y', True), - BROWSER_CHROME=('browser-chrome', 'testing/mochitest', 'browser', True), - ANDROID_INSTRUMENTATION=('instrumentation', 'instrumentation', '.', False), - JETPACK_PACKAGE=('jetpack-package', 'testing/mochitest', 'jetpack-package', True), - JETPACK_ADDON=('jetpack-addon', 'testing/mochitest', 'jetpack-addon', False), - METRO_CHROME=('metro-chrome', 'testing/mochitest', 'metro', True), - MOCHITEST=('mochitest', 'testing/mochitest', 'tests', True), - MOCHITEST_CHROME=('chrome', 'testing/mochitest', 'chrome', True), - WEBRTC_SIGNALLING_TEST=('steeplechase', 'steeplechase', '.', True), - XPCSHELL_TESTS=('xpcshell', 'xpcshell', '.', True), - ) - for prefix, info in test_manifests.items(): + for prefix, info in TEST_MANIFESTS.items(): for path in context.get('%s_MANIFESTS' % prefix, []): for obj in self._process_test_manifest(context, info, path): yield obj - for flavor in ('crashtest', 'reftest'): + for flavor in REFTEST_FLAVORS: for path in context.get('%s_MANIFESTS' % flavor.upper(), []): for obj in self._process_reftest_manifest(context, flavor, path): yield obj - for path in context.get("WEB_PLATFORM_TESTS_MANIFESTS", []): - for obj in self._process_web_platform_tests_manifest(context, path): - yield obj + for flavor in WEB_PATFORM_TESTS_FLAVORS: + for path in context.get("%s_MANIFESTS" % flavor.upper().replace('-', '_'), []): + for obj in self._process_web_platform_tests_manifest(context, path): + yield obj def _process_test_manifest(self, context, info, manifest_path): flavor, install_root, install_subdir, package_tests = info @@ -1154,11 +1166,11 @@ class TreeMetadataEmitter(LoggingMixin): relpath=mozpath.join(manifest_reldir, mozpath.basename(manifest_path))) - for test in sorted(manifest.files): + for test, source_manifest in sorted(manifest.tests): obj.tests.append({ 'path': test, 'here': mozpath.dirname(test), - 'manifest': manifest_full_path, + 'manifest': source_manifest, 'name': mozpath.basename(test), 'head': '', 'tail': '', @@ -1253,6 +1265,7 @@ class TreeMetadataEmitter(LoggingMixin): # Some paths have a subconfigure, yet also have a moz.build. Those # shouldn't end up in self._external_paths. - self._external_paths -= { o.relobjdir } + if o.objdir: + self._external_paths -= { o.relobjdir } yield o diff --git a/python/mozbuild/mozbuild/frontend/mach_commands.py b/python/mozbuild/mozbuild/frontend/mach_commands.py index fe310fe159..cbecc11375 100644 --- a/python/mozbuild/mozbuild/frontend/mach_commands.py +++ b/python/mozbuild/mozbuild/frontend/mach_commands.py @@ -131,6 +131,34 @@ class MozbuildFileCommands(MachCommandBase): print(e.message) return 1 + @SubCommand('file-info', 'dep-tests', + 'Show test files marked as dependencies of these source files.') + @CommandArgument('-r', '--rev', + help='Version control revision to look up info from') + @CommandArgument('paths', nargs='+', + help='Paths whose data to query') + def file_info_test_deps(self, paths, rev=None): + try: + for p, m in self._get_files_info(paths, rev=rev).items(): + print('%s:' % mozpath.relpath(p, self.topsrcdir)) + if m.test_files: + print('\tTest file patterns:') + for p in m.test_files: + print('\t\t%s' % p) + if m.test_tags: + print('\tRelevant tags:') + for p in m.test_tags: + print('\t\t%s' % p) + if m.test_flavors: + print('\tRelevant flavors:') + for p in m.test_flavors: + print('\t\t%s' % p) + + except InvalidPathException as e: + print(e.message) + return 1 + + def _get_reader(self, finder): from mozbuild.frontend.reader import ( BuildReader, diff --git a/python/mozbuild/mozbuild/frontend/reader.py b/python/mozbuild/mozbuild/frontend/reader.py index 69596181a0..4eb597d3b6 100644 --- a/python/mozbuild/mozbuild/frontend/reader.py +++ b/python/mozbuild/mozbuild/frontend/reader.py @@ -25,7 +25,6 @@ import os import sys import textwrap import time -import tokenize import traceback import types @@ -41,6 +40,12 @@ from mozbuild.util import ( ReadOnlyDefaultDict, ) +from mozbuild.testing import ( + TEST_MANIFESTS, + REFTEST_FLAVORS, + WEB_PATFORM_TESTS_FLAVORS, +) + from mozbuild.backend.configenvironment import ConfigEnvironment from mozpack.files import FileFinder @@ -749,7 +754,7 @@ class BuildReaderError(Exception): self._print_exception(inner, s) def _print_keyerror(self, inner, s): - if inner.args[0] not in ('global_ns', 'local_ns'): + if not inner.args or inner.args[0] not in ('global_ns', 'local_ns'): self._print_exception(inner, s) return @@ -808,7 +813,7 @@ class BuildReaderError(Exception): s.write('variables and try again.\n') def _print_valueerror(self, inner, s): - if inner.args[0] not in ('global_ns', 'local_ns'): + if not inner.args or inner.args[0] not in ('global_ns', 'local_ns'): self._print_exception(inner, s) return @@ -1345,6 +1350,7 @@ class BuildReader(object): paths, _ = self.read_relevant_mozbuilds(paths) r = {} + test_ctx_reader = TestContextReader(self.config) for path, ctxs in paths.items(): flags = Files(Context()) @@ -1363,6 +1369,62 @@ class BuildReader(object): ('*' in pattern and mozpath.match(relpath, pattern)): flags += ctx + if not any([flags.test_tags, flags.test_files, flags.test_flavors]): + flags += test_ctx_reader.test_defaults_for_path(path, ctxs) + r[path] = flags return r + + +class TestContextReader(object): + """Helper to extract test patterns defaults from moz.build files. + + Given paths of interest and relevant contexts, populates a Files + object with patterns matching all tests mentioned in the given + contexts. + """ + def __init__(self, config): + self.config = config + + # This names the context keys that will end up emitting a test + # manifest. + self._test_manifest_contexts = set( + ['%s_MANIFESTS' % key for key in TEST_MANIFESTS] + + ['%s_MANIFESTS' % flavor.upper() for flavor in REFTEST_FLAVORS] + + ['%s_MANIFESTS' % flavor.upper().replace('-', '_') for flavor in WEB_PATFORM_TESTS_FLAVORS] + + # The emitter requires JAR_MANIFESTS in contexts if they exist on + # disk, so include them here. + ['JAR_MANIFESTS'] + ) + + + def test_defaults_for_path(self, path, ctxs): + # Using the emitter here creates a circular import (and crosses some + # abstraction boundaries), but it's a convenient way to get the build + # system's view of tests. + # Bug 1203266 tracks features that would allow improving this situation + # and removing this abuse of the TreeMetadataEmitter + from .emitter import TreeMetadataEmitter, TestManifest + emitter = TreeMetadataEmitter(self.config) + + result_context = Files(Context()) + + for ctx in ctxs: + test_context = Context(VARIABLES, self.config) + test_context.main_path = ctx.main_path + + # Clone just the keys that will result in test manifests. + manifest_keys = [key for key in ctx if key in self._test_manifest_contexts] + for key in manifest_keys: + test_context[key] = ctx[key] + for obj in emitter.emit_from_context(test_context): + if isinstance(obj, TestManifest): + for t in obj.tests: + if 'relpath' not in t: + # wpt manifests do not generate relpaths (bug 1207678). + t['relpath'] = mozpath.relpath(t['path'], + self.config.topsrcdir) + # Pull in the entire directory of tests. + result_context.test_files.add(mozpath.dirname(t['relpath']) + '/**') + return result_context diff --git a/python/mozbuild/mozbuild/mach_commands.py b/python/mozbuild/mozbuild/mach_commands.py index a24bb6a147..b5b436c173 100644 --- a/python/mozbuild/mozbuild/mach_commands.py +++ b/python/mozbuild/mozbuild/mach_commands.py @@ -528,7 +528,7 @@ class Build(MachCommandBase): # conditions, but that is for another day. @CommandArgument('-b', '--backend', nargs='+', choices=['RecursiveMake', 'AndroidEclipse', 'CppEclipse', - 'VisualStudio', 'FasterMake'], + 'VisualStudio', 'FasterMake', 'CompileDB'], help='Which backend to build.') def build_backend(self, backend, diff=False): python = self.virtualenv_manager.python_path @@ -645,13 +645,22 @@ class Warnings(MachCommandBase): @Command('warnings-summary', category='post-build', description='Show a summary of compiler warnings.') + @CommandArgument('-C', '--directory', default=None, + help='Change to a subdirectory of the build directory first.') @CommandArgument('report', default=None, nargs='?', help='Warnings report to display. If not defined, show the most ' 'recent report.') - def summary(self, report=None): + def summary(self, directory=None, report=None): database = self.database - type_counts = database.type_counts + if directory: + dirpath = self.join_ensure_dir(self.topsrcdir, directory) + if not dirpath: + return 1 + else: + dirpath = None + + type_counts = database.type_counts(dirpath) sorted_counts = sorted(type_counts.iteritems(), key=operator.itemgetter(1)) @@ -664,19 +673,41 @@ class Warnings(MachCommandBase): @Command('warnings-list', category='post-build', description='Show a list of compiler warnings.') + @CommandArgument('-C', '--directory', default=None, + help='Change to a subdirectory of the build directory first.') + @CommandArgument('--flags', default=None, nargs='+', + help='Which warnings flags to match.') @CommandArgument('report', default=None, nargs='?', help='Warnings report to display. If not defined, show the most ' 'recent report.') - def list(self, report=None): + def list(self, directory=None, flags=None, report=None): database = self.database by_name = sorted(database.warnings) - for warning in by_name: - filename = warning['filename'] + topsrcdir = mozpath.normpath(self.topsrcdir) - if filename.startswith(self.topsrcdir): - filename = filename[len(self.topsrcdir) + 1:] + if directory: + directory = mozpath.normsep(directory) + dirpath = self.join_ensure_dir(topsrcdir, directory) + if not dirpath: + return 1 + + if flags: + # Flatten lists of flags. + flags = set(itertools.chain(*[flaglist.split(',') for flaglist in flags])) + + for warning in by_name: + filename = mozpath.normsep(warning['filename']) + + if filename.startswith(topsrcdir): + filename = filename[len(topsrcdir) + 1:] + + if directory and not filename.startswith(directory): + continue + + if flags and warning['flag'] not in flags: + continue if warning['column'] is not None: print('%s:%d:%d [%s] %s' % (filename, warning['line'], @@ -685,6 +716,16 @@ class Warnings(MachCommandBase): print('%s:%d [%s] %s' % (filename, warning['line'], warning['flag'], warning['message'])) + def join_ensure_dir(self, dir1, dir2): + dir1 = mozpath.normpath(dir1) + dir2 = mozpath.normsep(dir2) + joined_path = mozpath.join(dir1, dir2) + if os.path.isdir(joined_path): + return joined_path + else: + print('Specified directory not found.') + return None + @CommandProvider class GTestCommands(MachCommandBase): @Command('gtest', category='testing', diff --git a/python/mozbuild/mozbuild/test/backend/data/branding-files/bar.ico b/python/mozbuild/mozbuild/test/backend/data/branding-files/bar.ico new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python/mozbuild/mozbuild/test/backend/data/branding-files/foo.ico b/python/mozbuild/mozbuild/test/backend/data/branding-files/foo.ico new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python/mozbuild/mozbuild/test/backend/data/branding-files/moz.build b/python/mozbuild/mozbuild/test/backend/data/branding-files/moz.build new file mode 100644 index 0000000000..c515db46d9 --- /dev/null +++ b/python/mozbuild/mozbuild/test/backend/data/branding-files/moz.build @@ -0,0 +1,14 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +BRANDING_FILES += [ + 'app.ico', + 'bar.ico', + 'sub/quux.png', +] +BRANDING_FILES['app.ico'].source = 'bar.ico' + +BRANDING_FILES.icons += [ + 'foo.ico', +] + diff --git a/python/mozbuild/mozbuild/test/backend/data/branding-files/sub/quux.png b/python/mozbuild/mozbuild/test/backend/data/branding-files/sub/quux.png new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python/mozbuild/mozbuild/test/backend/data/variable_passthru/moz.build b/python/mozbuild/mozbuild/test/backend/data/variable_passthru/moz.build index 0794630df9..a78f0897a6 100644 --- a/python/mozbuild/mozbuild/test/backend/data/variable_passthru/moz.build +++ b/python/mozbuild/mozbuild/test/backend/data/variable_passthru/moz.build @@ -2,7 +2,7 @@ # Any copyright is dedicated to the Public Domain. # http://creativecommons.org/publicdomain/zero/1.0/ -EXTRA_COMPONENTS = ['bar.js', 'foo.js'] +EXTRA_COMPONENTS = ['bar.js', 'dummy.manifest', 'foo.js'] EXTRA_PP_COMPONENTS = ['bar.pp.js', 'foo.pp.js'] NO_VISIBILITY_FLAGS = True diff --git a/python/mozbuild/mozbuild/test/backend/test_recursivemake.py b/python/mozbuild/mozbuild/test/backend/test_recursivemake.py index a3669f6fcf..56ca2ac48b 100644 --- a/python/mozbuild/mozbuild/test/backend/test_recursivemake.py +++ b/python/mozbuild/mozbuild/test/backend/test_recursivemake.py @@ -268,6 +268,7 @@ class TestRecursiveMakeBackend(BackendTester): ], 'EXTRA_COMPONENTS': [ 'EXTRA_COMPONENTS += bar.js', + 'EXTRA_COMPONENTS += dummy.manifest', 'EXTRA_COMPONENTS += foo.js', ], 'EXTRA_PP_COMPONENTS': [ @@ -417,6 +418,19 @@ class TestRecursiveMakeBackend(BackendTester): self.assertIn('res/tests/test.manifest', m) self.assertIn('res/tests/extra.manifest', m) + def test_branding_files(self): + """Ensure BRANDING_FILES is handled properly.""" + env = self._consume('branding-files', RecursiveMakeBackend) + + #BRANDING_FILES should appear in the dist_branding install manifest. + m = InstallManifest(path=os.path.join(env.topobjdir, + '_build_manifests', 'install', 'dist_branding')) + self.assertEqual(len(m), 4) + self.assertIn('app.ico', m) + self.assertIn('bar.ico', m) + self.assertIn('quux.png', m) + self.assertIn('icons/foo.ico', m) + def test_js_preference_files(self): """Ensure PREF_JS_EXPORTS is written out correctly.""" env = self._consume('js_preference_files', RecursiveMakeBackend) diff --git a/python/mozbuild/mozbuild/test/frontend/data/android-res-dirs/dir1/foo b/python/mozbuild/mozbuild/test/frontend/data/android-res-dirs/dir1/foo new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python/mozbuild/mozbuild/test/frontend/data/android-res-dirs/moz.build b/python/mozbuild/mozbuild/test/frontend/data/android-res-dirs/moz.build new file mode 100644 index 0000000000..7c55feb0c8 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/android-res-dirs/moz.build @@ -0,0 +1,9 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +ANDROID_RES_DIRS += [ + '/dir1', + '!/dir2', + '%/dir3', +] diff --git a/python/mozbuild/mozbuild/test/frontend/data/branding-files/moz.build b/python/mozbuild/mozbuild/test/frontend/data/branding-files/moz.build new file mode 100644 index 0000000000..13354e272f --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/branding-files/moz.build @@ -0,0 +1,15 @@ +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +BRANDING_FILES += [ + 'app.ico', + 'bar.ico', + 'baz.png', + 'foo.xpm', +] +BRANDING_FILES['app.ico'].source = 'test/bar.ico' + +BRANDING_FILES.icons += [ + 'quux.icns', +] + diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/default/module.js b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/default/module.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/default/moz.build b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/default/moz.build new file mode 100644 index 0000000000..5af39643f8 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/default/moz.build @@ -0,0 +1,5 @@ +XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell/xpcshell.ini'] + +EXTRA_JS_MODULES += [ + 'module.js', +] \ No newline at end of file diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/default/tests/xpcshell/test_default_mod.js b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/default/tests/xpcshell/test_default_mod.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/default/tests/xpcshell/xpcshell.ini b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/default/tests/xpcshell/xpcshell.ini new file mode 100644 index 0000000000..55c18a2505 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/default/tests/xpcshell/xpcshell.ini @@ -0,0 +1 @@ +[test_default_mod.js] \ No newline at end of file diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/moz.build b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/moz.build new file mode 100644 index 0000000000..faff2a1731 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/moz.build @@ -0,0 +1,4 @@ +DIRS += [ + 'default', + 'simple', +] \ No newline at end of file diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/base.cpp b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/base.cpp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/browser/browser.ini b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/browser/browser.ini new file mode 100644 index 0000000000..f284de0439 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/browser/browser.ini @@ -0,0 +1 @@ +[test_mod.js] \ No newline at end of file diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/browser/test_mod.js b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/browser/test_mod.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/moz.build b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/moz.build new file mode 100644 index 0000000000..cbce16e1d5 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/moz.build @@ -0,0 +1,22 @@ +with Files('src/*'): + IMPACTED_TESTS.files += [ + 'tests/test_general.html', + ] + +with Files('src/module.jsm'): + IMPACTED_TESTS.files += [ + 'browser/**.js', + ] + +with Files('base.cpp'): + IMPACTED_TESTS.files += [ + '/default/tests/xpcshell/test_default_mod.js', + 'tests/*', + ] + + +MOCHITEST_MANIFESTS += ['tests/mochitest.ini'] +BROWSER_CHROME_MANIFESTS += ['browser/browser.ini'] + +UNIFIED_SOURCES += ['base.cpp'] +DIRS += ['src'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/src/module.jsm b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/src/module.jsm new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/src/moz.build b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/src/moz.build new file mode 100644 index 0000000000..e0c49f1299 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/src/moz.build @@ -0,0 +1,3 @@ +EXTRA_JS_MODULES += [ + 'module.jsm', +] \ No newline at end of file diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/tests/mochitest.ini b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/tests/mochitest.ini new file mode 100644 index 0000000000..662566abdd --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/tests/mochitest.ini @@ -0,0 +1,2 @@ +[test_general.html] +[test_specific.html] \ No newline at end of file diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/tests/moz.build b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/tests/moz.build new file mode 100644 index 0000000000..8ef3a9fd86 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/tests/moz.build @@ -0,0 +1 @@ +MOCHITEST_MANIFESTS += ['mochitest.ini'] \ No newline at end of file diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/tests/test_general.html b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/tests/test_general.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/tests/test_specific.html b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/simple/tests/test_specific.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/moz.build b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/moz.build new file mode 100644 index 0000000000..0b7ca5a2b5 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/moz.build @@ -0,0 +1,15 @@ +with Files('src/submodule/**'): + IMPACTED_TESTS.tags += [ + 'submodule', + ] + +with Files('src/bar.jsm'): + IMPACTED_TESTS.flavors += [ + 'browser-chrome', + ] + IMPACTED_TESTS.files += [ + '**.js', + ] + +MOCHITEST_MANIFESTS += ['tests/mochitest.ini'] +XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell.ini'] diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/src/bar.jsm b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/src/bar.jsm new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/src/submodule/foo.js b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/src/submodule/foo.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/tests/mochitest.ini b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/tests/mochitest.ini new file mode 100644 index 0000000000..d40ca4d06f --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/tests/mochitest.ini @@ -0,0 +1,3 @@ +[test_simple.html] +[test_specific.html] +tags = submodule \ No newline at end of file diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/tests/test_bar.js b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/tests/test_bar.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/tests/test_simple.html b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/tests/test_simple.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/tests/test_specific.html b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/tests/test_specific.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/tests/xpcshell.ini b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/tests/xpcshell.ini new file mode 100644 index 0000000000..1275764c4c --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/files-test-metadata/tagged/tests/xpcshell.ini @@ -0,0 +1 @@ +[test_bar.js] \ No newline at end of file diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-emitted-includes/included-reftest.list b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-emitted-includes/included-reftest.list new file mode 100644 index 0000000000..1caf9cc391 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-emitted-includes/included-reftest.list @@ -0,0 +1 @@ +!= reftest2.html reftest2-ref.html \ No newline at end of file diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-emitted-includes/moz.build b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-emitted-includes/moz.build new file mode 100644 index 0000000000..39ad44c285 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-emitted-includes/moz.build @@ -0,0 +1 @@ +REFTEST_MANIFESTS += ['reftest.list'] \ No newline at end of file diff --git a/python/mozbuild/mozbuild/test/frontend/data/test-manifest-emitted-includes/reftest.list b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-emitted-includes/reftest.list new file mode 100644 index 0000000000..80caf8ffa4 --- /dev/null +++ b/python/mozbuild/mozbuild/test/frontend/data/test-manifest-emitted-includes/reftest.list @@ -0,0 +1,2 @@ +== reftest1.html reftest1-ref.html +include included-reftest.list diff --git a/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/moz.build b/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/moz.build index 131b154dfb..6616ea9131 100644 --- a/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/moz.build +++ b/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/moz.build @@ -2,7 +2,7 @@ # Any copyright is dedicated to the Public Domain. # http://creativecommons.org/publicdomain/zero/1.0/ -EXTRA_COMPONENTS=['fans.js', 'tans.js'] +EXTRA_COMPONENTS = ['dummy.manifest', 'fans.js', 'tans.js'] EXTRA_PP_COMPONENTS=['fans.pp.js', 'tans.pp.js'] DIST_INSTALL = False diff --git a/python/mozbuild/mozbuild/test/frontend/test_context.py b/python/mozbuild/mozbuild/test/frontend/test_context.py index f59c7de600..6987c6868e 100644 --- a/python/mozbuild/mozbuild/test/frontend/test_context.py +++ b/python/mozbuild/mozbuild/test/frontend/test_context.py @@ -8,9 +8,12 @@ import unittest from mozunit import main from mozbuild.frontend.context import ( + AbsolutePath, Context, + ContextDerivedTypedRecord, ContextDerivedTypedList, ContextDerivedTypedListWithItems, + Files, FUNCTIONS, ObjDirPath, Path, @@ -453,6 +456,18 @@ class TestPaths(unittest.TestCase): path = Path(path) self.assertIsInstance(path, ObjDirPath) + def test_absolute_path(self): + config = self.config + ctxt = Context(config=config) + ctxt.push_source(mozpath.join(config.topsrcdir, 'foo', 'moz.build')) + + path = AbsolutePath(ctxt, '%/qux') + self.assertEqual(path, '%/qux') + self.assertEqual(path.full_path, '/qux') + + with self.assertRaises(ValueError): + path = AbsolutePath(ctxt, '%qux') + def test_path_with_mixed_contexts(self): config = self.config ctxt1 = Context(config=config) @@ -565,5 +580,94 @@ class TestPaths(unittest.TestCase): self.assertEqual(l[p_str].foo, True) self.assertEqual(l[p_path].foo, True) +class TestTypedRecord(unittest.TestCase): + + def test_fields(self): + T = ContextDerivedTypedRecord(('field1', unicode), + ('field2', list)) + inst = T(None) + self.assertEqual(inst.field1, '') + self.assertEqual(inst.field2, []) + + inst.field1 = 'foo' + inst.field2 += ['bar'] + + self.assertEqual(inst.field1, 'foo') + self.assertEqual(inst.field2, ['bar']) + + with self.assertRaises(AttributeError): + inst.field3 = [] + + def test_coercion(self): + T = ContextDerivedTypedRecord(('field1', unicode), + ('field2', list)) + inst = T(None) + inst.field1 = 3 + inst.field2 += ('bar',) + self.assertEqual(inst.field1, '3') + self.assertEqual(inst.field2, ['bar']) + + with self.assertRaises(TypeError): + inst.field2 = object() + + +class TestFiles(unittest.TestCase): + def test_aggregate_empty(self): + c = Context({}) + + files = {'moz.build': Files(c, pattern='**')} + + self.assertEqual(Files.aggregate(files), { + 'bug_component_counts': [], + 'recommended_bug_component': None, + }) + + def test_single_bug_component(self): + c = Context({}) + f = Files(c, pattern='**') + f['BUG_COMPONENT'] = (u'Product1', u'Component1') + + files = {'moz.build': f} + self.assertEqual(Files.aggregate(files), { + 'bug_component_counts': [((u'Product1', u'Component1'), 1)], + 'recommended_bug_component': (u'Product1', u'Component1'), + }) + + def test_multiple_bug_components(self): + c = Context({}) + f1 = Files(c, pattern='**') + f1['BUG_COMPONENT'] = (u'Product1', u'Component1') + + f2 = Files(c, pattern='**') + f2['BUG_COMPONENT'] = (u'Product2', u'Component2') + + files = {'a': f1, 'b': f2, 'c': f1} + self.assertEqual(Files.aggregate(files), { + 'bug_component_counts': [ + ((u'Product1', u'Component1'), 2), + ((u'Product2', u'Component2'), 1), + ], + 'recommended_bug_component': (u'Product1', u'Component1'), + }) + + def test_no_recommended_bug_component(self): + """If there is no clear count winner, we don't recommend a bug component.""" + c = Context({}) + f1 = Files(c, pattern='**') + f1['BUG_COMPONENT'] = (u'Product1', u'Component1') + + f2 = Files(c, pattern='**') + f2['BUG_COMPONENT'] = (u'Product2', u'Component2') + + files = {'a': f1, 'b': f2} + self.assertEqual(Files.aggregate(files), { + 'bug_component_counts': [ + ((u'Product1', u'Component1'), 1), + ((u'Product2', u'Component2'), 1), + ], + 'recommended_bug_component': None, + }) + + if __name__ == '__main__': main() diff --git a/python/mozbuild/mozbuild/test/frontend/test_emitter.py b/python/mozbuild/mozbuild/test/frontend/test_emitter.py index fea7962c49..e46191ad63 100644 --- a/python/mozbuild/mozbuild/test/frontend/test_emitter.py +++ b/python/mozbuild/mozbuild/test/frontend/test_emitter.py @@ -10,6 +10,8 @@ import unittest from mozunit import main from mozbuild.frontend.data import ( + AndroidResDirs, + BrandingFiles, ConfigFileSubstitution, Defines, DistFiles, @@ -70,11 +72,7 @@ class TestEmitterBasic(unittest.TestCase): def read_topsrcdir(self, reader, filter_common=True): emitter = TreeMetadataEmitter(reader.config) - def ack(obj): - obj.ack() - return obj - - objs = list(ack(o) for o in emitter.emit(reader.read_topsrcdir())) + objs = list(emitter.emit(reader.read_topsrcdir())) self.assertGreater(len(objs), 0) filtered = [] @@ -156,7 +154,7 @@ class TestEmitterBasic(unittest.TestCase): wanted = { 'ALLOW_COMPILER_WARNINGS': True, 'DISABLE_STL_WRAPPING': True, - 'EXTRA_COMPONENTS': ['fans.js', 'tans.js'], + 'EXTRA_COMPONENTS': ['dummy.manifest', 'fans.js', 'tans.js'], 'EXTRA_PP_COMPONENTS': ['fans.pp.js', 'tans.pp.js'], 'NO_DIST_INSTALL': True, 'VISIBILITY_FLAGS': '', @@ -343,6 +341,23 @@ class TestEmitterBasic(unittest.TestCase): overwrite = resources._children['overwrite'] self.assertEqual(overwrite._strings, ['new.res']) + def test_branding_files(self): + reader = self.reader('branding-files') + objs = self.read_topsrcdir(reader) + + self.assertEqual(len(objs), 1) + self.assertIsInstance(objs[0], BrandingFiles) + + files = objs[0].files + + self.assertEqual(files._strings, ['app.ico', 'bar.ico', 'baz.png', 'foo.xpm']) + self.assertEqual(files['app.ico'].source, 'test/bar.ico') + + self.assertIn('icons', files._children) + icons = files._children['icons'] + + self.assertEqual(icons._strings, ['quux.icns']) + def test_preferences_js(self): reader = self.reader('js_preference_files') objs = self.read_topsrcdir(reader) @@ -448,6 +463,24 @@ class TestEmitterBasic(unittest.TestCase): paths = sorted([v[0] for v in o.installs.values()]) self.assertEqual(paths, expected) + def test_test_manifest_includes(self): + """Ensure that manifest objects from the emitter list a correct manifest. + """ + reader = self.reader('test-manifest-emitted-includes') + [obj] = self.read_topsrcdir(reader) + + # Expected manifest leafs for our tests. + expected_manifests = { + 'reftest1.html': 'reftest.list', + 'reftest1-ref.html': 'reftest.list', + 'reftest2.html': 'included-reftest.list', + 'reftest2-ref.html': 'included-reftest.list', + } + + for t in obj.tests: + self.assertTrue(t['manifest'].endswith(expected_manifests[t['name']])) + + def test_test_manifest_keys_extracted(self): """Ensure all metadata from test manifests is extracted.""" reader = self.reader('test-manifest-keys-extracted') @@ -855,5 +888,21 @@ class TestEmitterBasic(unittest.TestCase): reader = self.reader('dist-files-missing') self.read_topsrcdir(reader) + def test_android_res_dirs(self): + """Test that ANDROID_RES_DIRS works properly.""" + reader = self.reader('android-res-dirs') + objs = self.read_topsrcdir(reader) + + self.assertEqual(len(objs), 1) + self.assertIsInstance(objs[0], AndroidResDirs) + + # Android resource directories are ordered. + expected = [ + mozpath.join(reader.config.topsrcdir, 'dir1'), + mozpath.join(reader.config.topobjdir, 'dir2'), + '/dir3', + ] + self.assertEquals([p.full_path for p in objs[0].paths], expected) + if __name__ == '__main__': main() diff --git a/python/mozbuild/mozbuild/test/frontend/test_reader.py b/python/mozbuild/mozbuild/test/frontend/test_reader.py index 2fb9622dfb..3260cb5ec8 100644 --- a/python/mozbuild/mozbuild/test/frontend/test_reader.py +++ b/python/mozbuild/mozbuild/test/frontend/test_reader.py @@ -29,6 +29,14 @@ data_path = mozpath.join(data_path, 'data') class TestBuildReader(unittest.TestCase): + def setUp(self): + self._old_env = dict(os.environ) + os.environ.pop('MOZ_OBJDIR', None) + + def tearDown(self): + os.environ.clear() + os.environ.update(self._old_env) + def config(self, name, **kwargs): path = mozpath.join(data_path, name) @@ -378,6 +386,77 @@ class TestBuildReader(unittest.TestCase): self.assertEqual(v['bug_component/final/subcomponent/bar']['BUG_COMPONENT'], BugzillaComponent('Another', 'Component')) + def test_file_test_deps(self): + reader = self.reader('files-test-metadata') + + expected = { + 'simple/src/module.jsm': set(['simple/tests/test_general.html', + 'simple/browser/**.js']), + 'simple/base.cpp': set(['simple/tests/*', + 'default/tests/xpcshell/test_default_mod.js']), + } + + v = reader.files_info([ + 'simple/src/module.jsm', + 'simple/base.cpp', + ]) + + for path, pattern_set in expected.items(): + self.assertEqual(v[path].test_files, + expected[path]) + + def test_file_test_deps_default(self): + reader = self.reader('files-test-metadata') + v = reader.files_info([ + 'default/module.js', + ]) + + expected = { + 'default/module.js': set(['default/tests/xpcshell/**']), + } + + for path, pattern_set in expected.items(): + self.assertEqual(v[path].test_files, + expected[path]) + + def test_file_test_deps_tags(self): + reader = self.reader('files-test-metadata') + v = reader.files_info([ + 'tagged/src/bar.jsm', + 'tagged/src/submodule/foo.js', + ]) + + expected_patterns = { + 'tagged/src/submodule/foo.js': set([]), + 'tagged/src/bar.jsm': set(['tagged/**.js']), + } + + for path, pattern_set in expected_patterns.items(): + self.assertEqual(v[path].test_files, + expected_patterns[path]) + + expected_tags = { + 'tagged/src/submodule/foo.js': set(['submodule']), + 'tagged/src/bar.jsm': set([]), + } + for path, pattern_set in expected_tags.items(): + self.assertEqual(v[path].test_tags, + expected_tags[path]) + + expected_flavors = { + 'tagged/src/bar.jsm': set(['browser-chrome']), + 'tagged/src/submodule/foo.js': set([]), + } + for path, pattern_set in expected_flavors.items(): + self.assertEqual(v[path].test_flavors, + expected_flavors[path]) + + def test_invalid_flavor(self): + reader = self.reader('invalid-files-flavor') + + with self.assertRaises(BuildReaderError): + reader.files_info(['foo.js']) + if __name__ == '__main__': main() diff --git a/python/mozbuild/mozbuild/testing.py b/python/mozbuild/mozbuild/testing.py index 0ca0e7c78b..dcc9167e8d 100644 --- a/python/mozbuild/mozbuild/testing.py +++ b/python/mozbuild/mozbuild/testing.py @@ -212,3 +212,51 @@ class TestResolver(MozbuildObject): honor_install_to_subdir=True) else: yield test + +# These definitions provide a single source of truth for modules attempting +# to get a view of all tests for a build. Used by the emitter to figure out +# how to read/install manifests and by test dependency annotations in Files() +# entries to enumerate test flavors. + +# While there are multiple test manifests, the behavior is very similar +# across them. We enforce this by having common handling of all +# manifests and outputting a single class type with the differences +# described inside the instance. +# +# Keys are variable prefixes and values are tuples describing how these +# manifests should be handled: +# +# (flavor, install_prefix, package_tests) +# +# flavor identifies the flavor of this test. +# install_prefix is the path prefix of where to install the files in +# the tests directory. +# package_tests indicates whether to package test files into the test +# package; suites that compile the test files should not install +# them into the test package. +# +TEST_MANIFESTS = dict( + A11Y=('a11y', 'testing/mochitest', 'a11y', True), + BROWSER_CHROME=('browser-chrome', 'testing/mochitest', 'browser', True), + ANDROID_INSTRUMENTATION=('instrumentation', 'instrumentation', '.', False), + JETPACK_PACKAGE=('jetpack-package', 'testing/mochitest', 'jetpack-package', True), + JETPACK_ADDON=('jetpack-addon', 'testing/mochitest', 'jetpack-addon', False), + METRO_CHROME=('metro-chrome', 'testing/mochitest', 'metro', True), + MOCHITEST=('mochitest', 'testing/mochitest', 'tests', True), + MOCHITEST_CHROME=('chrome', 'testing/mochitest', 'chrome', True), + MOCHITEST_WEBAPPRT_CONTENT=('webapprt-content', 'testing/mochitest', 'webapprtContent', True), + MOCHITEST_WEBAPPRT_CHROME=('webapprt-chrome', 'testing/mochitest', 'webapprtChrome', True), + WEBRTC_SIGNALLING_TEST=('steeplechase', 'steeplechase', '.', True), + XPCSHELL_TESTS=('xpcshell', 'xpcshell', '.', True), +) + +# Reftests have their own manifest format and are processed separately. +REFTEST_FLAVORS = ('crashtest', 'reftest') + +# Web platform tests have their own manifest format and are processed separately. +WEB_PATFORM_TESTS_FLAVORS = ('web-platform-tests',) + +def all_test_flavors(): + return ([v[0] for v in TEST_MANIFESTS.values()] + + list(REFTEST_FLAVORS) + + list(WEB_PATFORM_TESTS_FLAVORS)) diff --git a/python/mozbuild/mozbuild/util.py b/python/mozbuild/mozbuild/util.py index 1f59426448..bb099c69d7 100644 --- a/python/mozbuild/mozbuild/util.py +++ b/python/mozbuild/mozbuild/util.py @@ -985,3 +985,20 @@ def group_unified_files(files, unified_prefix, unified_suffix, files)): just_the_filenames = list(filter_out_dummy(unified_group)) yield '%s%d.%s' % (unified_prefix, i, unified_suffix), just_the_filenames + + +class DefinesAction(argparse.Action): + '''An ArgumentParser action to handle -Dvar[=value] type of arguments.''' + def __call__(self, parser, namespace, values, option_string): + defines = getattr(namespace, self.dest) + if defines is None: + defines = {} + values = values.split('=', 1) + if len(values) == 1: + name, value = values[0], 1 + else: + name, value = values + if value.isdigit(): + value = int(value) + defines[name] = value + setattr(namespace, self.dest, defines) diff --git a/python/mozbuild/mozpack/chrome/flags.py b/python/mozbuild/mozpack/chrome/flags.py index c54018f11b..8c5c9a54c0 100644 --- a/python/mozbuild/mozpack/chrome/flags.py +++ b/python/mozbuild/mozpack/chrome/flags.py @@ -2,6 +2,8 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. +from __future__ import absolute_import + import re from distutils.version import LooseVersion from mozpack.errors import errors diff --git a/python/mozbuild/mozpack/chrome/manifest.py b/python/mozbuild/mozpack/chrome/manifest.py index a20de1a364..cf4c7648c7 100644 --- a/python/mozbuild/mozpack/chrome/manifest.py +++ b/python/mozbuild/mozpack/chrome/manifest.py @@ -2,6 +2,8 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. +from __future__ import absolute_import + import re import os from urlparse import urlparse diff --git a/python/mozbuild/mozpack/copier.py b/python/mozbuild/mozpack/copier.py index 3840135719..5010e6d11a 100644 --- a/python/mozbuild/mozpack/copier.py +++ b/python/mozbuild/mozpack/copier.py @@ -2,6 +2,8 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. +from __future__ import absolute_import + import os import stat diff --git a/python/mozbuild/mozpack/errors.py b/python/mozbuild/mozpack/errors.py index 7418f0f239..8b4b80072b 100644 --- a/python/mozbuild/mozpack/errors.py +++ b/python/mozbuild/mozpack/errors.py @@ -2,6 +2,8 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. +from __future__ import absolute_import + import sys from contextlib import contextmanager diff --git a/python/mozbuild/mozpack/executables.py b/python/mozbuild/mozpack/executables.py index 7a95009436..c31337c6ea 100644 --- a/python/mozbuild/mozpack/executables.py +++ b/python/mozbuild/mozpack/executables.py @@ -2,6 +2,8 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. +from __future__ import absolute_import + import os import struct import subprocess diff --git a/python/mozbuild/mozpack/files.py b/python/mozbuild/mozpack/files.py index c67bfde5b2..6c84e98ee5 100644 --- a/python/mozbuild/mozpack/files.py +++ b/python/mozbuild/mozpack/files.py @@ -2,6 +2,8 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. +from __future__ import absolute_import + import errno import os import platform @@ -409,12 +411,15 @@ class PreprocessedFile(BaseFile): File class for a file that is preprocessed. PreprocessedFile.copy() runs the preprocessor on the file to create the output. ''' - def __init__(self, path, depfile_path, marker, defines, extra_depends=None): + def __init__(self, path, depfile_path, marker, defines, extra_depends=None, + silence_missing_directive_warnings=False): self.path = path self.depfile = depfile_path self.marker = marker self.defines = defines self.extra_depends = list(extra_depends or []) + self.silence_missing_directive_warnings = \ + silence_missing_directive_warnings def copy(self, dest, skip_if_older=True): ''' @@ -463,6 +468,7 @@ class PreprocessedFile(BaseFile): if self.depfile: deps_out = FileAvoidWrite(self.depfile) pp = Preprocessor(defines=self.defines, marker=self.marker) + pp.setSilenceDirectiveWarnings(self.silence_missing_directive_warnings) with open(self.path, 'rU') as input: pp.processFile(input=input, output=dest, depfile=deps_out) diff --git a/python/mozbuild/mozpack/hg.py b/python/mozbuild/mozpack/hg.py index 3971ec6fd5..79876061f4 100644 --- a/python/mozbuild/mozpack/hg.py +++ b/python/mozbuild/mozpack/hg.py @@ -27,6 +27,8 @@ # do not wish to do so, delete this exception statement from your # version. +from __future__ import absolute_import + import mercurial.error as error import mercurial.hg as hg import mercurial.ui as hgui diff --git a/python/mozbuild/mozpack/manifests.py b/python/mozbuild/mozpack/manifests.py index 475fce27a9..c7b522e2eb 100644 --- a/python/mozbuild/mozpack/manifests.py +++ b/python/mozbuild/mozpack/manifests.py @@ -2,7 +2,7 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -from __future__ import unicode_literals +from __future__ import absolute_import, unicode_literals from contextlib import contextmanager import json @@ -156,9 +156,11 @@ class InstallManifest(object): continue if record_type == self.PREPROCESS: - dest, source, deps, marker, defines = fields[1:] + dest, source, deps, marker, defines, warnings = fields[1:] + self.add_preprocess(source, dest, deps, marker, - self._decode_field_entry(defines)) + self._decode_field_entry(defines), + silence_missing_directive_warnings=bool(int(warnings))) continue raise UnreadableInstallManifest('Unknown record type: %d' % @@ -281,14 +283,21 @@ class InstallManifest(object): self._add_entry(mozpath.join(base, pattern, dest), (self.PATTERN_COPY, base, pattern, dest)) - def add_preprocess(self, source, dest, deps, marker='#', defines={}): + def add_preprocess(self, source, dest, deps, marker='#', defines={}, + silence_missing_directive_warnings=False): """Add a preprocessed file to this manifest. ``source`` will be passed through preprocessor.py, and the output will be written to ``dest``. """ - self._add_entry(dest, - (self.PREPROCESS, source, deps, marker, self._encode_field_entry(defines))) + self._add_entry(dest, ( + self.PREPROCESS, + source, + deps, + marker, + self._encode_field_entry(defines), + '1' if silence_missing_directive_warnings else '0', + )) def _add_entry(self, dest, entry): if dest in self._dests: @@ -296,12 +305,15 @@ class InstallManifest(object): self._dests[dest] = entry - def populate_registry(self, registry): + def populate_registry(self, registry, defines_override={}): """Populate a mozpack.copier.FileRegistry instance with data from us. The caller supplied a FileRegistry instance (or at least something that conforms to its interface) and that instance is populated with data from this manifest. + + Defines can be given to override the ones in the manifest for + preprocessing. """ for dest in sorted(self._dests): entry = self._dests[dest] @@ -340,11 +352,15 @@ class InstallManifest(object): continue if install_type == self.PREPROCESS: + defines = self._decode_field_entry(entry[4]) + if defines_override: + defines.update(defines_override) registry.add(dest, PreprocessedFile(entry[1], depfile_path=entry[2], marker=entry[3], - defines=self._decode_field_entry(entry[4]), - extra_depends=self._source_files)) + defines=defines, + extra_depends=self._source_files, + silence_missing_directive_warnings=bool(int(entry[5])))) continue diff --git a/python/mozbuild/mozpack/mozjar.py b/python/mozbuild/mozpack/mozjar.py index 750432fe68..f0904bcda9 100644 --- a/python/mozbuild/mozpack/mozjar.py +++ b/python/mozbuild/mozpack/mozjar.py @@ -2,6 +2,8 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. +from __future__ import absolute_import + from io import BytesIO import struct import zlib diff --git a/python/mozbuild/mozpack/packager/__init__.py b/python/mozbuild/mozpack/packager/__init__.py index 1d12827c37..e2384caaa6 100644 --- a/python/mozbuild/mozpack/packager/__init__.py +++ b/python/mozbuild/mozpack/packager/__init__.py @@ -2,6 +2,8 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. +from __future__ import absolute_import + from mozbuild.preprocessor import Preprocessor import re import os diff --git a/python/mozbuild/mozpack/packager/formats.py b/python/mozbuild/mozpack/packager/formats.py index 39a46b622e..64586472e8 100644 --- a/python/mozbuild/mozpack/packager/formats.py +++ b/python/mozbuild/mozpack/packager/formats.py @@ -2,6 +2,8 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. +from __future__ import absolute_import + from mozpack.chrome.manifest import ( Manifest, ManifestInterfaces, diff --git a/python/mozbuild/mozpack/packager/l10n.py b/python/mozbuild/mozpack/packager/l10n.py index 08664cd55f..f0229252e0 100644 --- a/python/mozbuild/mozpack/packager/l10n.py +++ b/python/mozbuild/mozpack/packager/l10n.py @@ -2,6 +2,8 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. +from __future__ import absolute_import + ''' Replace localized parts of a packaged directory with data from a langpack directory. diff --git a/python/mozbuild/mozpack/packager/unpack.py b/python/mozbuild/mozpack/packager/unpack.py index 59a6a9b951..3323a04855 100644 --- a/python/mozbuild/mozpack/packager/unpack.py +++ b/python/mozbuild/mozpack/packager/unpack.py @@ -2,6 +2,8 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. +from __future__ import absolute_import + import mozpack.path as mozpath from mozpack.files import ( FileFinder, diff --git a/python/mozbuild/mozpack/path.py b/python/mozbuild/mozpack/path.py index 1b71ace78e..ecdc62265b 100644 --- a/python/mozbuild/mozpack/path.py +++ b/python/mozbuild/mozpack/path.py @@ -2,6 +2,8 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. +from __future__ import absolute_import + import posixpath import os import re diff --git a/python/mozbuild/mozpack/unify.py b/python/mozbuild/mozpack/unify.py index 215ae35434..53e1859285 100644 --- a/python/mozbuild/mozpack/unify.py +++ b/python/mozbuild/mozpack/unify.py @@ -2,6 +2,8 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. +from __future__ import absolute_import + from mozpack.files import ( BaseFinder, JarFinder, diff --git a/testing/mochitest/moz.build b/testing/mochitest/moz.build index 21541a3d91..3e23bc0d59 100644 --- a/testing/mochitest/moz.build +++ b/testing/mochitest/moz.build @@ -76,12 +76,6 @@ TEST_HARNESS_FILES.testing.mochitest += [ 'server.js', ] -if CONFIG['MOZ_BUILD_APP'] == 'mobile/android': - TEST_HARNESS_FILES.testing.mochitest += [ - '/mobile/android/base/tests/robocop.ini', - '/mobile/android/base/tests/robocop_autophone.ini', - ] - TEST_HARNESS_FILES.testing.mochitest.pywebsocket += [ 'pywebsocket/standalone.py', ] diff --git a/testing/mochitest/roboextender/Makefile.in b/testing/mochitest/roboextender/Makefile.in index 9bfaee1d3b..b36e8be2f7 100644 --- a/testing/mochitest/roboextender/Makefile.in +++ b/testing/mochitest/roboextender/Makefile.in @@ -3,7 +3,7 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -TESTPATH = $(topsrcdir)/mobile/android/base/tests/roboextender +TESTPATH = $(topsrcdir)/mobile/android/tests/browser/robocop/roboextender include $(DEPTH)/config/autoconf.mk diff --git a/toolkit/commonjs/sdk/panel.js b/toolkit/commonjs/sdk/panel.js index 97441114c6..cf0a9324bf 100644 --- a/toolkit/commonjs/sdk/panel.js +++ b/toolkit/commonjs/sdk/panel.js @@ -26,6 +26,7 @@ const { contract } = require("./util/contract"); const { on, off, emit, setListeners } = require("./event/core"); const { EventTarget } = require("./event/target"); const domPanel = require("./panel/utils"); +const { getDocShell } = require('./frame/utils'); const { events } = require("./panel/events"); const systemEvents = require("./system/events"); const { filter, pipe, stripListeners } = require("./event/utils"); @@ -73,9 +74,26 @@ let panelContract = contract(merge({ contentStyleFile: merge(Object.create(loaderContract.rules.contentScriptFile), { msg: 'The `contentStyleFile` option must be a local URL or an array of URLs' }), - contextMenu: boolean + contextMenu: boolean, + allow: { + is: ['object', 'undefined', 'null'], + map: function (allow) { return { script: !allow || allow.script !== false }} + }, }, displayContract.rules, loaderContract.rules)); +function Allow(panel) { + return { + get script() { return getDocShell(viewFor(panel).backgroundFrame).allowJavascript; }, + set script(value) { return setScriptState(panel, value); }, + }; +} + +function setScriptState(panel, value) { + let view = viewFor(panel); + getDocShell(view.backgroundFrame).allowJavascript = value; + getDocShell(view.viewFrame).allowJavascript = value; + view.setAttribute("sdkscriptenabled", "" + value); +} function isDisposed(panel) !views.has(panel); @@ -147,7 +165,8 @@ const Panel = Class({ } // Setup view - let view = domPanel.make(); + let viewOptions = {allowJavascript: !model.allow || (model.allow.script !== false)}; + let view = domPanel.make(null, viewOptions); panels.set(view, this); views.set(this, view); @@ -212,6 +231,12 @@ const Panel = Class({ workerFor(this).detach(); }, + get allow() { return Allow(this); }, + set allow(value) { + let allowJavascript = panelContract({ allow: value }).allow.script; + return setScriptState(this, value); + }, + /* Public API: Panel.isShowing */ get isShowing() !isDisposed(this) && domPanel.isOpen(viewFor(this)), diff --git a/toolkit/commonjs/sdk/panel/utils.js b/toolkit/commonjs/sdk/panel/utils.js index c9a39e97be..54e5eaec0e 100644 --- a/toolkit/commonjs/sdk/panel/utils.js +++ b/toolkit/commonjs/sdk/panel/utils.js @@ -14,7 +14,7 @@ const { platform } = require("../system"); const { getMostRecentBrowserWindow, getOwnerBrowserWindow, getHiddenWindow, getScreenPixelsPerCSSPixel } = require("../window/utils"); -const { create: createFrame, swapFrameLoaders } = require("../frame/utils"); +const { create: createFrame, swapFrameLoaders, getDocShell } = require("../frame/utils"); const { window: addonWindow } = require("../addon/window"); const { isNil } = require("../lang/type"); const { data } = require('../self'); @@ -233,10 +233,11 @@ function setupPanelFrame(frame) { } } -function make(document) { +function make(document, options) { document = document || getMostRecentBrowserWindow().document; let panel = document.createElementNS(XUL_NS, "panel"); panel.setAttribute("type", "arrow"); + panel.setAttribute("sdkscriptenabled", "" + options.allowJavascript); // Note that panel is a parent of `viewFrame` who's `docShell` will be // configured at creation time. If `panel` and there for `viewFrame` won't @@ -245,7 +246,7 @@ function make(document) { attach(panel, document); let frameOptions = { - allowJavascript: true, + allowJavascript: options.allowJavascript, allowPlugins: true, allowAuth: true, allowWindowControl: false, @@ -270,8 +271,16 @@ function make(document) { // See Bug 886329 if (target !== this) return; - try { swapFrameLoaders(backgroundFrame, viewFrame); } - catch(error) { console.exception(error); } + try { + swapFrameLoaders(backgroundFrame, viewFrame); + // We need to re-set this because... swapFrameLoaders. Or something. + let shouldEnableScript = panel.getAttribute("sdkscriptenabled") == "true"; + getDocShell(backgroundFrame).allowJavascript = shouldEnableScript; + getDocShell(viewFrame).allowJavascript = shouldEnableScript; + } + catch(error) { + console.exception(error); + } events.emit(type, { subject: panel }); } @@ -315,6 +324,7 @@ function make(document) { panel.backgroundFrame = backgroundFrame; + panel.viewFrame = viewFrame; // Store event listener on the panel instance so that it won't be GC-ed // while panel is alive. @@ -340,8 +350,10 @@ function detach(panel) { exports.detach = detach; function dispose(panel) { - panel.backgroundFrame.parentNode.removeChild(panel.backgroundFrame); + panel.backgroundFrame.remove(); + panel.viewFrame.remove(); panel.backgroundFrame = null; + panel.viewFrame = null; events.off("document-element-inserted", panel.onContentChange); panel.onContentChange = null; detach(panel); diff --git a/toolkit/mozapps/installer/upload-files.mk b/toolkit/mozapps/installer/upload-files.mk index 5405cbac80..01e987b53e 100644 --- a/toolkit/mozapps/installer/upload-files.mk +++ b/toolkit/mozapps/installer/upload-files.mk @@ -470,6 +470,7 @@ INNER_MAKE_PACKAGE = \ ( cd $(STAGEPATH)$(MOZ_PKG_DIR)$(_BINPATH) && \ unzip -o $(_ABS_DIST)/gecko.ap_ && \ rm $(_ABS_DIST)/gecko.ap_ && \ + $(ZIP) -r9D $(_ABS_DIST)/gecko.ap_ assets && \ $(ZIP) $(if $(ALREADY_SZIPPED),-0 ,$(if $(MOZ_ENABLE_SZIP),-0 ))$(_ABS_DIST)/gecko.ap_ $(ASSET_SO_LIBRARIES) && \ $(ZIP) -r9D $(_ABS_DIST)/gecko.ap_ $(DIST_FILES) -x $(NON_DIST_FILES) $(SZIP_LIBRARIES) && \ $(if $(filter-out ./,$(OMNIJAR_DIR)), \ diff --git a/xulrunner/app/Makefile.in b/xulrunner/app/Makefile.in index 8486ebb2d9..98e377d69c 100644 --- a/xulrunner/app/Makefile.in +++ b/xulrunner/app/Makefile.in @@ -38,19 +38,6 @@ endif #export:: brand.dtd.in # $(call py_action,preprocessor,$(DEFINES) $(ACDEFINES) $^ -o brand.dtd) -export:: - $(NSINSTALL) -D $(DIST)/branding -ifeq ($(OS_ARCH),WINNT) - cp $(srcdir)/xulrunner.ico $(DIST)/branding/xulrunner.ico - cp $(srcdir)/xulrunner.ico $(DIST)/branding/app.ico - cp $(srcdir)/document.ico $(DIST)/branding/document.ico -endif -ifdef MOZ_WIDGET_GTK - cp $(srcdir)/default16.png $(DIST)/branding/default16.png - cp $(srcdir)/default32.png $(DIST)/branding/default32.png - cp $(srcdir)/default48.png $(DIST)/branding/default48.png -endif - ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT)) FRAMEWORK_NAME = XUL diff --git a/xulrunner/app/moz.build b/xulrunner/app/moz.build index c51dee448e..0195a43990 100644 --- a/xulrunner/app/moz.build +++ b/xulrunner/app/moz.build @@ -58,3 +58,17 @@ JS_PREFERENCE_FILES += [ 'xulrunner.js', ] +if CONFIG['OS_ARCH'] == 'WINNT': + BRANDING_FILES += [ + 'app.ico', + 'document.ico', + 'xulrunner.ico', + ] + BRANDING_FILES['app.ico'].source = 'xulrunner.ico' + +if CONFIG['MOZ_WIDGET_GTK']: + BRANDING_FILES += [ + 'default16.png', + 'default32.png', + 'default48.png', + ]