diff --git a/0001-Python-Build-Use-r-instead-of-rU-file-read-modes.patch b/0001-Python-Build-Use-r-instead-of-rU-file-read-modes.patch deleted file mode 100644 index a20d39afbb377d5ede166a4a48a07611fb9a6a7c..0000000000000000000000000000000000000000 --- a/0001-Python-Build-Use-r-instead-of-rU-file-read-modes.patch +++ /dev/null @@ -1,109 +0,0 @@ -From 0e790bd2eb846e90495eb81952cf35cc7fc8766a Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Franti=C5=A1ek=20Zatloukal?= -Date: Mon, 27 Jun 2022 19:55:16 +0100 -Subject: [PATCH] Python/Build: Use r instead of rU file read modes - -Fixes Python 3.11 build ---- - python/mozbuild/mozbuild/action/process_define_files.py | 2 +- - python/mozbuild/mozbuild/backend/base.py | 2 +- - python/mozbuild/mozbuild/preprocessor.py | 6 +++--- - python/mozbuild/mozbuild/util.py | 4 ++-- - python/mozbuild/mozpack/files.py | 4 ++-- - 5 files changed, 9 insertions(+), 9 deletions(-) - -diff --git a/python/mozbuild/mozbuild/action/process_define_files.py b/python/mozbuild/mozbuild/action/process_define_files.py -index f1d401a..aca59d0 100644 ---- a/python/mozbuild/mozbuild/action/process_define_files.py -+++ b/python/mozbuild/mozbuild/action/process_define_files.py -@@ -36,7 +36,7 @@ def process_define_file(output, input): - ) and not config.substs.get("JS_STANDALONE"): - config = PartialConfigEnvironment(mozpath.join(topobjdir, "js", "src")) - -- with open(path, "rU") as input: -+ with open(path, "r") as input: - r = re.compile( - "^\s*#\s*(?P[a-z]+)(?:\s+(?P\S+)(?:\s+(?P\S+))?)?", re.U - ) -diff --git a/python/mozbuild/mozbuild/backend/base.py b/python/mozbuild/mozbuild/backend/base.py -index 7bc1986..b64a709 100644 ---- a/python/mozbuild/mozbuild/backend/base.py -+++ b/python/mozbuild/mozbuild/backend/base.py -@@ -272,7 +272,7 @@ class BuildBackend(LoggingMixin): - return status - - @contextmanager -- def _write_file(self, path=None, fh=None, readmode="rU"): -+ def _write_file(self, path=None, fh=None, readmode="r"): - """Context manager to write a file. - - This is a glorified wrapper around FileAvoidWrite with integration to -diff --git a/python/mozbuild/mozbuild/preprocessor.py b/python/mozbuild/mozbuild/preprocessor.py -index f7820b9..857f1a6 100644 ---- a/python/mozbuild/mozbuild/preprocessor.py -+++ b/python/mozbuild/mozbuild/preprocessor.py -@@ -531,7 +531,7 @@ class Preprocessor: - - if args: - for f in args: -- with io.open(f, "rU", encoding="utf-8") as input: -+ with io.open(f, "r", encoding="utf-8") as input: - self.processFile(input=input, output=out) - if depfile: - mk = Makefile() -@@ -860,7 +860,7 @@ class Preprocessor: - args = self.applyFilters(args) - if not os.path.isabs(args): - args = os.path.join(self.curdir, args) -- args = io.open(args, "rU", encoding="utf-8") -+ args = io.open(args, "r", encoding="utf-8") - except Preprocessor.Error: - raise - except Exception: -@@ -914,7 +914,7 @@ class Preprocessor: - def preprocess(includes=[sys.stdin], defines={}, output=sys.stdout, marker="#"): - pp = Preprocessor(defines=defines, marker=marker) - for f in includes: -- with io.open(f, "rU", encoding="utf-8") as input: -+ with io.open(f, "r", encoding="utf-8") as input: - pp.processFile(input=input, output=output) - return pp.includes - -diff --git a/python/mozbuild/mozbuild/util.py b/python/mozbuild/mozbuild/util.py -index 071daec..b59aabb 100644 ---- a/python/mozbuild/mozbuild/util.py -+++ b/python/mozbuild/mozbuild/util.py -@@ -236,7 +236,7 @@ class FileAvoidWrite(BytesIO): - still occur, as well as diff capture if requested. - """ - -- def __init__(self, filename, capture_diff=False, dry_run=False, readmode="rU"): -+ def __init__(self, filename, capture_diff=False, dry_run=False, readmode="r"): - BytesIO.__init__(self) - self.name = filename - assert type(capture_diff) == bool -diff --git a/python/mozbuild/mozpack/files.py b/python/mozbuild/mozpack/files.py -index 8150e72..001c497 100644 ---- a/python/mozbuild/mozpack/files.py -+++ b/python/mozbuild/mozpack/files.py -@@ -554,7 +554,7 @@ class PreprocessedFile(BaseFile): - pp = Preprocessor(defines=self.defines, marker=self.marker) - pp.setSilenceDirectiveWarnings(self.silence_missing_directive_warnings) - -- with _open(self.path, "rU") as input: -+ with _open(self.path, "r") as input: - with _open(os.devnull, "w") as output: - pp.processFile(input=input, output=output) - -@@ -611,7 +611,7 @@ class PreprocessedFile(BaseFile): - pp = Preprocessor(defines=self.defines, marker=self.marker) - pp.setSilenceDirectiveWarnings(self.silence_missing_directive_warnings) - -- with _open(self.path, "rU") as input: -+ with _open(self.path, "r") as input: - pp.processFile(input=input, output=dest, depfile=deps_out) - - dest.close() --- -2.36.1 - diff --git a/D134330.diff b/D134330.diff new file mode 100644 index 0000000000000000000000000000000000000000..730400d52964c6f9f04f928f6de42751da3fbab4 --- /dev/null +++ b/D134330.diff @@ -0,0 +1,73 @@ +diff --git a/build/moz.configure/toolchain.configure b/build/moz.configure/toolchain.configure +--- a/build/moz.configure/toolchain.configure ++++ b/build/moz.configure/toolchain.configure +@@ -1511,11 +1511,11 @@ + + + option( + "--enable-linker", + nargs=1, +- help="Select the linker {bfd, gold, ld64, lld, lld-*}{|}", ++ help="Select the linker {bfd, gold, ld64, lld, lld-*, mold}{|}", + default=enable_linker_default, + when=is_linker_option_enabled, + ) + + +@@ -1546,11 +1546,11 @@ + + def is_valid_linker(linker): + if target.kernel == "Darwin": + valid_linkers = ("ld64", "lld") + else: +- valid_linkers = ("bfd", "gold", "lld") ++ valid_linkers = ("bfd", "gold", "lld", "mold") + if linker in valid_linkers: + return True + if "lld" in valid_linkers and linker.startswith("lld-"): + return True + return False +@@ -1591,10 +1591,13 @@ + kind = "ld64" + + elif retcode != 0: + return None + ++ elif "mold" in stdout: ++ kind = "mold" ++ + elif "GNU ld" in stdout: + # We are using the normal linker + kind = "bfd" + + elif "GNU gold" in stdout: +@@ -1697,11 +1700,11 @@ + # There's a wrinkle with MinGW: linker configuration is not enabled, so + # `select_linker` is never invoked. Hard-code around it. + @depends(select_linker, target, c_compiler) + def gcc_use_gnu_ld(select_linker, target, c_compiler): + if select_linker is not None and target.kernel != "Darwin": +- return select_linker.KIND in ("bfd", "gold", "lld") ++ return select_linker.KIND in ("bfd", "gold", "lld", "mold") + if target.kernel == "WINNT" and c_compiler.type == "clang": + return True + return None + + +diff --git a/toolkit/moz.configure b/toolkit/moz.configure +--- a/toolkit/moz.configure ++++ b/toolkit/moz.configure +@@ -1444,11 +1444,11 @@ + ) + + @depends("--enable-release", enable_linker) + def default_elfhack(release, linker): + # Disable elfhack when explicitly building with --enable-linker=lld +- if linker and linker.origin != "default" and linker[0] == "lld": ++ if linker and linker.origin != "default" and linker[0] in ("lld", "mold"): + return False + return bool(release) + + with only_when(has_elfhack): + option( + diff --git a/firefox-102.6.0esr.source.tar.xz b/firefox-102.9.0esr.source.tar.xz similarity index 100% rename from firefox-102.6.0esr.source.tar.xz rename to firefox-102.9.0esr.source.tar.xz diff --git a/firefox-102.6.0esr.source.tar.xzab b/firefox-102.9.0esr.source.tar.xzaa similarity index 90% rename from firefox-102.6.0esr.source.tar.xzab rename to firefox-102.9.0esr.source.tar.xzaa index 0249ea7d57b8bd8307167d0900b94c44c32c2043..4490f75866faf431d27b81140c5d8dd38f3d2938 100644 Binary files a/firefox-102.6.0esr.source.tar.xzab and b/firefox-102.9.0esr.source.tar.xzaa differ diff --git a/firefox-102.6.0esr.source.tar.xzaa b/firefox-102.9.0esr.source.tar.xzab similarity index 88% rename from firefox-102.6.0esr.source.tar.xzaa rename to firefox-102.9.0esr.source.tar.xzab index 061b4450e2615644b7c2ceba0a6724ad45a49348..e3a7a70c2c3e7e53b58b91269a3e83abd5f1639d 100644 Binary files a/firefox-102.6.0esr.source.tar.xzaa and b/firefox-102.9.0esr.source.tar.xzab differ diff --git a/firefox-102.6.0esr.source.tar.xzac b/firefox-102.9.0esr.source.tar.xzac similarity index 88% rename from firefox-102.6.0esr.source.tar.xzac rename to firefox-102.9.0esr.source.tar.xzac index e4966ad50010d58ca78a7a949217a500197e7ff0..1522eb647ecbca1e9b792aa31a3415ca1612388c 100644 Binary files a/firefox-102.6.0esr.source.tar.xzac and b/firefox-102.9.0esr.source.tar.xzac differ diff --git a/firefox-102.6.0esr.source.tar.xzad b/firefox-102.9.0esr.source.tar.xzad similarity index 39% rename from firefox-102.6.0esr.source.tar.xzad rename to firefox-102.9.0esr.source.tar.xzad index 1bec47caebe036265f6e9ede5d073dbf92e72944..0f8f43eba883822ac74e0792d80fdee2097a1e9e 100644 Binary files a/firefox-102.6.0esr.source.tar.xzad and b/firefox-102.9.0esr.source.tar.xzad differ diff --git a/mozjs102.spec b/mozjs102.spec index 9b2c7c78d537f692e36c2deabccfff2f67f13faf..5a683ead322a6ad7f2e161e03e192468e1f1f70b 100644 --- a/mozjs102.spec +++ b/mozjs102.spec @@ -18,8 +18,12 @@ %global big_endian 1 %endif +%ifarch riscv64 +%global build_mold 1 +%endif + Name: mozjs%{major} -Version: 102.6.0 +Version: 102.9.0 Release: 1 Summary: SpiderMonkey JavaScript library License: MPL-2.0 AND Apache-2.0 AND BSD-3-Clause AND BSD-2-Clause AND MIT AND GPL-3.0-or-later @@ -29,10 +33,10 @@ Source0: https://ftp.mozilla.org/pub/firefox/releases/%{version}esr/sourc # Known failures with system libicu Source1: known_failures.txt -Source10: firefox-102.6.0esr.source.tar.xzaa -Source11: firefox-102.6.0esr.source.tar.xzab -Source12: firefox-102.6.0esr.source.tar.xzac -Source13: firefox-102.6.0esr.source.tar.xzad +Source10: firefox-102.9.0esr.source.tar.xzaa +Source11: firefox-102.9.0esr.source.tar.xzab +Source12: firefox-102.9.0esr.source.tar.xzac +Source13: firefox-102.9.0esr.source.tar.xzad # Patches from mozjs68, rebased for mozjs78: Patch01: fix-soname.patch @@ -48,7 +52,6 @@ Patch13: tests-Use-native-TemporaryDirectory.patch # Build fixes Patch14: init_patch.patch Patch15: remove-sloppy-m4-detection-from-bundled-autoconf.patch -Patch16: 0001-Python-Build-Use-r-instead-of-rU-file-read-modes.patch # TODO: Check with mozilla for cause of these fails and re-enable spidermonkey compile time checks if needed Patch20: spidermonkey_checks_disable.patch @@ -56,6 +59,14 @@ Patch20: spidermonkey_checks_disable.patch # s390x/ppc64 fixes Patch21: 0001-Skip-failing-tests-on-ppc64-and-s390x.patch +# riscv64 sipdermonkey jit +Patch22: spidermonkey-riscv64-plct.patch + +# mold +%if 0%{?build_mold} +Patch23: D134330.diff +%endif + BuildRequires: cargo BuildRequires: ccache BuildRequires: clang-devel @@ -79,6 +90,9 @@ BuildRequires: python3-six BuildRequires: readline-devel BuildRequires: wget BuildRequires: zip +%if 0%{?build_mold} +BuildRequires: mold +%endif %description SpiderMonkey is the code-name for Mozilla Firefox's C++ implementation of @@ -126,7 +140,7 @@ export CARGO_PROFILE_RELEASE_LTO=true export M4=m4 export AWK=awk export AC_MACRODIR=%{_builddir}/firefox-%{version}/build/autoconf/ - + sh ../../build/autoconf/autoconf.sh --localdir=%{_builddir}/firefox-%{version}/js/src configure.in > configure chmod +x configure @@ -141,7 +155,11 @@ chmod +x configure --enable-optimize \ --disable-debug \ --enable-pie \ - --disable-jemalloc + --disable-jemalloc \ +%if 0%{?build_mold} + --enable-linker=mold \ +%endif + %{nil} %make_build @@ -156,7 +174,7 @@ case `uname -i` in i386 | ppc | s390 | sparc ) wordsize="32" ;; - x86_64 | ppc64 | s390x | sparc64 ) + x86_64 | ppc64 | s390x | sparc64 | riscv64 ) wordsize="64" ;; *) @@ -232,6 +250,10 @@ ln -s libmozjs-%{major}.so.0 %{buildroot}%{_libdir}/libmozjs-%{major}.so %{_includedir}/mozjs-%{major}/ %changelog +* Thu Mar 16 2023 Jingwiw - 102.9.0-1 +- upgrade to 102.9.0 +- add sipdermonkey jit support for riscv64 + * Mon Jan 02 2023 lin zhang - 102.6.0-1 - Initial package. diff --git a/spidermonkey-riscv64-plct.patch b/spidermonkey-riscv64-plct.patch new file mode 100644 index 0000000000000000000000000000000000000000..353f75819ff9a6ee1132b0088b86c714bd2a6e82 --- /dev/null +++ b/spidermonkey-riscv64-plct.patch @@ -0,0 +1,36157 @@ +diff --git a/config/check_macroassembler_style.py b/config/check_macroassembler_style.py +index 20375e9e26..96aae7b890 100644 +--- a/config/check_macroassembler_style.py ++++ b/config/check_macroassembler_style.py +@@ -29,9 +29,9 @@ import re + import sys + + architecture_independent = set(["generic"]) +-all_unsupported_architectures_names = set(["mips32", "mips64", "mips_shared"]) +-all_architecture_names = set(["x86", "x64", "arm", "arm64", "loong64"]) +-all_shared_architecture_names = set(["x86_shared", "arm", "arm64", "loong64"]) ++all_unsupported_architectures_names = set(["mips32", "mips64", "mips_shared", "riscv64"]) ++all_architecture_names = set(["x86", "x64", "arm", "arm64", "loong64", "riscv64"]) ++all_shared_architecture_names = set(["x86_shared", "arm", "arm64", "loong64", "riscv64"]) + + reBeforeArg = "(?<=[(,\s])" + reArgType = "(?P[\w\s:*&<>]+)" +@@ -321,7 +321,6 @@ def check_style(): + for diffline in difflines: + ok = False + print(diffline, end="") +- + return ok + + +diff --git a/js/moz.configure b/js/moz.configure +index 7a241cac76..aa691c07f0 100644 +--- a/js/moz.configure ++++ b/js/moz.configure +@@ -184,7 +184,7 @@ def report_deprecated(value): + # ======================================================= + option( + "--enable-simulator", +- choices=("arm", "arm64", "mips32", "mips64", "loong64"), ++ choices=("arm", "arm64", "mips32", "mips64", "loong64", "riscv64"), + nargs=1, + help="Enable a JIT code simulator for the specified architecture", + ) +@@ -201,7 +201,7 @@ def simulator(jit_enabled, simulator_enabled, target): + if target.cpu != "x86": + die("The %s simulator only works on x86." % sim_cpu) + +- if sim_cpu in ("arm64", "mips64", "loong64"): ++ if sim_cpu in ("arm64", "mips64", "loong64", "riscv64"): + if target.cpu != "x86_64" and target.cpu != "aarch64": + die("The %s simulator only works on x86-64 or arm64." % sim_cpu) + +@@ -214,12 +214,14 @@ set_config("JS_SIMULATOR_ARM64", simulator.arm64) + set_config("JS_SIMULATOR_MIPS32", simulator.mips32) + set_config("JS_SIMULATOR_MIPS64", simulator.mips64) + set_config("JS_SIMULATOR_LOONG64", simulator.loong64) ++set_config("JS_SIMULATOR_RISCV64", simulator.riscv64) + set_define("JS_SIMULATOR", depends_if(simulator)(lambda x: True)) + set_define("JS_SIMULATOR_ARM", simulator.arm) + set_define("JS_SIMULATOR_ARM64", simulator.arm64) + set_define("JS_SIMULATOR_MIPS32", simulator.mips32) + set_define("JS_SIMULATOR_MIPS64", simulator.mips64) + set_define("JS_SIMULATOR_LOONG64", simulator.loong64) ++set_define("JS_SIMULATOR_RISCV64", simulator.riscv64) + + + @depends("--enable-jit", simulator, target) +@@ -244,6 +246,7 @@ set_config("JS_CODEGEN_ARM64", jit_codegen.arm64) + set_config("JS_CODEGEN_MIPS32", jit_codegen.mips32) + set_config("JS_CODEGEN_MIPS64", jit_codegen.mips64) + set_config("JS_CODEGEN_LOONG64", jit_codegen.loong64) ++set_config("JS_CODEGEN_RISCV64", jit_codegen.riscv64) + set_config("JS_CODEGEN_X86", jit_codegen.x86) + set_config("JS_CODEGEN_X64", jit_codegen.x64) + set_define("JS_CODEGEN_NONE", jit_codegen.none) +@@ -252,6 +255,7 @@ set_define("JS_CODEGEN_ARM64", jit_codegen.arm64) + set_define("JS_CODEGEN_MIPS32", jit_codegen.mips32) + set_define("JS_CODEGEN_MIPS64", jit_codegen.mips64) + set_define("JS_CODEGEN_LOONG64", jit_codegen.loong64) ++set_define("JS_CODEGEN_RISCV64", jit_codegen.riscv64) + set_define("JS_CODEGEN_X86", jit_codegen.x86) + set_define("JS_CODEGEN_X64", jit_codegen.x64) + +@@ -453,6 +457,23 @@ set_config("JS_DISASM_ARM", jit_disasm_arm) + set_define("JS_DISASM_ARM", jit_disasm_arm) + + ++@depends("--enable-jit", "--enable-jitspew", simulator, target, moz_debug) ++def jit_disasm_riscv(jit_enabled, spew, simulator, target, debug): ++ if not jit_enabled: ++ return ++ ++ if simulator and (debug or spew): ++ if getattr(simulator, "riscv64", None): ++ return True ++ ++ if target.cpu == "riscv64" and (debug or spew): ++ return True ++ ++ ++set_config("JS_DISASM_RISCV64", jit_disasm_riscv) ++set_define("JS_DISASM_RISCV64", jit_disasm_riscv) ++ ++ + @depends("--enable-jit", "--enable-jitspew", simulator, target, moz_debug) + def jit_disasm_arm64(jit_enabled, spew, simulator, target, debug): + if not jit_enabled: +diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp +index 77408db873..ddad1c3938 100644 +--- a/js/src/builtin/TestingFunctions.cpp ++++ b/js/src/builtin/TestingFunctions.cpp +@@ -456,6 +456,24 @@ static bool GetBuildConfiguration(JSContext* cx, unsigned argc, Value* vp) { + return false; + } + ++#ifdef JS_CODEGEN_RISCV64 ++ value = BooleanValue(true); ++#else ++ value = BooleanValue(false); ++#endif ++ if (!JS_SetProperty(cx, info, "riscv64", value)) { ++ return false; ++ } ++ ++#ifdef JS_SIMULATOR_RISCV64 ++ value = BooleanValue(true); ++#else ++ value = BooleanValue(false); ++#endif ++ if (!JS_SetProperty(cx, info, "riscv64-simulator", value)) { ++ return false; ++ } ++ + #ifdef MOZ_ASAN + value = BooleanValue(true); + #else +diff --git a/js/src/jit-test/tests/ion/bug1433496.js b/js/src/jit-test/tests/ion/bug1433496.js +index e5ef04c8e0..e7ea1d5d08 100644 +--- a/js/src/jit-test/tests/ion/bug1433496.js ++++ b/js/src/jit-test/tests/ion/bug1433496.js +@@ -1,4 +1,4 @@ +-// |jit-test| --spectre-mitigations=on; skip-if: getBuildConfiguration()['mips32'] || getBuildConfiguration()['mips64'] ++// |jit-test| --spectre-mitigations=on; skip-if: getBuildConfiguration()['mips32'] || getBuildConfiguration()['mips64'] || getBuildConfiguration()['riscv64'] + function f() { + return arguments[arguments.length]; + } +diff --git a/js/src/jit/Assembler.h b/js/src/jit/Assembler.h +index 04dcdc647e..57b8cc392d 100644 +--- a/js/src/jit/Assembler.h ++++ b/js/src/jit/Assembler.h +@@ -21,6 +21,8 @@ + # include "jit/mips64/Assembler-mips64.h" + #elif defined(JS_CODEGEN_LOONG64) + # include "jit/loong64/Assembler-loong64.h" ++#elif defined(JS_CODEGEN_RISCV64) ++# include "jit/riscv64/Assembler-riscv64.h" + #elif defined(JS_CODEGEN_NONE) + # include "jit/none/Assembler-none.h" + #else +diff --git a/js/src/jit/BaselineBailouts.cpp b/js/src/jit/BaselineBailouts.cpp +index 93ec841714..18edd6fce8 100644 +--- a/js/src/jit/BaselineBailouts.cpp ++++ b/js/src/jit/BaselineBailouts.cpp +@@ -9,7 +9,9 @@ + + #include "builtin/ModuleObject.h" + #include "debugger/DebugAPI.h" ++#if defined(JS_SIMULATOR_ARM) + #include "jit/arm/Simulator-arm.h" ++#endif + #include "jit/Bailouts.h" + #include "jit/BaselineFrame.h" + #include "jit/BaselineIC.h" +@@ -21,11 +23,9 @@ + #include "jit/JitFrames.h" + #include "jit/JitRuntime.h" + #include "jit/JitSpewer.h" +-#include "jit/loong64/Simulator-loong64.h" +-#include "jit/mips32/Simulator-mips32.h" +-#include "jit/mips64/Simulator-mips64.h" + #include "jit/RematerializedFrame.h" + #include "jit/SharedICRegisters.h" ++#include "jit/Simulator.h" + #include "js/friend/StackLimits.h" // js::AutoCheckRecursionLimit, js::ReportOverRecursed + #include "js/Utility.h" + #include "util/Memory.h" +diff --git a/js/src/jit/BaselineCodeGen.cpp b/js/src/jit/BaselineCodeGen.cpp +index 9cd48d5448..e506492713 100644 +--- a/js/src/jit/BaselineCodeGen.cpp ++++ b/js/src/jit/BaselineCodeGen.cpp +@@ -528,6 +528,8 @@ bool BaselineCodeGen::emitOutOfLinePostBarrierSlot() { + masm.push(ra); + #elif defined(JS_CODEGEN_LOONG64) + masm.push(ra); ++#elif defined(JS_CODEGEN_RISCV64) ++ masm.push(ra); + #endif + masm.pushValue(R0); + +diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp +index 1e3c7ed1c7..f72b15b359 100644 +--- a/js/src/jit/CodeGenerator.cpp ++++ b/js/src/jit/CodeGenerator.cpp +@@ -11464,7 +11464,7 @@ void CodeGenerator::visitOutOfLineStoreElementHole( + // Also note: this branch does not need Spectre mitigations, doing that for + // the capacity check below is sufficient. + #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) || \ +- defined(JS_CODEGEN_LOONG64) ++ defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) + // Had to reimplement for MIPS because there are no flags. + Address initLength(elements, ObjectElements::offsetOfInitializedLength()); + masm.branch32(Assembler::NotEqual, initLength, indexReg, ool->callStub()); +diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h +index 393fd33fa6..ff6e89e283 100644 +--- a/js/src/jit/CodeGenerator.h ++++ b/js/src/jit/CodeGenerator.h +@@ -26,6 +26,8 @@ + # include "jit/mips64/CodeGenerator-mips64.h" + #elif defined(JS_CODEGEN_LOONG64) + # include "jit/loong64/CodeGenerator-loong64.h" ++#elif defined(JS_CODEGEN_RISCV64) ++# include "jit/riscv64/CodeGenerator-riscv64.h" + #elif defined(JS_CODEGEN_NONE) + # include "jit/none/CodeGenerator-none.h" + #else +diff --git a/js/src/jit/FlushICache.h b/js/src/jit/FlushICache.h +index 6ef08c63d3..2552a831fd 100644 +--- a/js/src/jit/FlushICache.h ++++ b/js/src/jit/FlushICache.h +@@ -24,7 +24,7 @@ inline void FlushICache(void* code, size_t size) { + + #elif (defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64)) || \ + (defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)) || \ +- defined(JS_CODEGEN_LOONG64) ++ defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) + + // Invalidate the given code range from the icache. This will also flush the + // execution context for this core. If this code is to be executed on another +@@ -42,7 +42,7 @@ inline void FlushICache(void* code, size_t size) { MOZ_CRASH(); } + + #if (defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)) || \ + (defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)) || \ +- defined(JS_CODEGEN_LOONG64) ++ defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) + + inline void FlushExecutionContext() { + // No-op. Execution context is coherent with instruction cache. +diff --git a/js/src/jit/JitFrames.cpp b/js/src/jit/JitFrames.cpp +index 0001bab1f5..c162d23a70 100644 +--- a/js/src/jit/JitFrames.cpp ++++ b/js/src/jit/JitFrames.cpp +@@ -2294,7 +2294,12 @@ uintptr_t MachineState::read(Register reg) const { + + template + T MachineState::read(FloatRegister reg) const { ++#if !defined(JS_CODEGEN_RISCV64) + MOZ_ASSERT(reg.size() == sizeof(T)); ++#else ++ // RISCV64 always store FloatRegister as 64bit. ++ MOZ_ASSERT(reg.size() == sizeof(double)); ++#endif + + #if !defined(JS_CODEGEN_NONE) + if (state_.is()) { +diff --git a/js/src/jit/JitOptions.cpp b/js/src/jit/JitOptions.cpp +index 6831001334..44a087621c 100644 +--- a/js/src/jit/JitOptions.cpp ++++ b/js/src/jit/JitOptions.cpp +@@ -254,7 +254,7 @@ DefaultJitOptions::DefaultJitOptions() { + } + + #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) || \ +- defined(JS_CODEGEN_LOONG64) ++ defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) + SET_DEFAULT(spectreIndexMasking, false); + SET_DEFAULT(spectreObjectMitigations, false); + SET_DEFAULT(spectreStringMitigations, false); +diff --git a/js/src/jit/LIR.h b/js/src/jit/LIR.h +index 1d9420de24..c565a65a57 100644 +--- a/js/src/jit/LIR.h ++++ b/js/src/jit/LIR.h +@@ -552,12 +552,18 @@ class LDefinition { + Type type() const { return (Type)((bits_ >> TYPE_SHIFT) & TYPE_MASK); } + + static bool isFloatRegCompatible(Type type, FloatRegister reg) { ++#ifdef JS_CODEGEN_RISCV64 ++ if (type == FLOAT32 || type == DOUBLE) { ++ return reg.isSingle() || reg.isDouble(); ++ } ++#else + if (type == FLOAT32) { + return reg.isSingle(); + } + if (type == DOUBLE) { + return reg.isDouble(); + } ++#endif + MOZ_ASSERT(type == SIMD128); + return reg.isSimd128(); + } +@@ -1948,6 +1954,8 @@ AnyRegister LAllocation::toRegister() const { + # include "jit/arm64/LIR-arm64.h" + #elif defined(JS_CODEGEN_LOONG64) + # include "jit/loong64/LIR-loong64.h" ++#elif defined(JS_CODEGEN_RISCV64) ++# include "jit/riscv64/LIR-riscv64.h" + #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) + # if defined(JS_CODEGEN_MIPS32) + # include "jit/mips32/LIR-mips32.h" +diff --git a/js/src/jit/LIROps.yaml b/js/src/jit/LIROps.yaml +index 05f25291db..4facc17373 100644 +--- a/js/src/jit/LIROps.yaml ++++ b/js/src/jit/LIROps.yaml +@@ -3693,3 +3693,32 @@ + - name: WasmAtomicExchangeI64 + gen_boilerplate: false + #endif ++ ++#ifdef JS_CODEGEN_RISCV64 ++- name: DivOrModI64 ++ gen_boilerplate: false ++ ++- name: UDivOrMod ++ gen_boilerplate: false ++ ++- name: UDivOrModI64 ++ gen_boilerplate: false ++ ++- name: ModMaskI ++ gen_boilerplate: false ++ ++- name: WasmTruncateToInt64 ++ gen_boilerplate: false ++ ++- name: Int64ToFloatingPoint ++ gen_boilerplate: false ++ ++- name: WasmCompareExchangeI64 ++ gen_boilerplate: false ++ ++- name: WasmAtomicBinopI64 ++ gen_boilerplate: false ++ ++- name: WasmAtomicExchangeI64 ++ gen_boilerplate: false ++#endif +diff --git a/js/src/jit/Label.h b/js/src/jit/Label.h +index c1ed1c6b25..bf78d3c5b8 100644 +--- a/js/src/jit/Label.h ++++ b/js/src/jit/Label.h +@@ -27,7 +27,7 @@ struct LabelBase { + void operator=(const LabelBase& label) = delete; + + #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) || \ +- defined(JS_CODEGEN_LOONG64) ++ defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) + public: + #endif + static const uint32_t INVALID_OFFSET = 0x7fffffff; // UINT31_MAX. +diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h +index a04d09c446..2e652949b7 100644 +--- a/js/src/jit/Lowering.h ++++ b/js/src/jit/Lowering.h +@@ -25,6 +25,8 @@ + # include "jit/mips64/Lowering-mips64.h" + #elif defined(JS_CODEGEN_LOONG64) + # include "jit/loong64/Lowering-loong64.h" ++#elif defined(JS_CODEGEN_RISCV64) ++# include "jit/riscv64/Lowering-riscv64.h" + #elif defined(JS_CODEGEN_NONE) + # include "jit/none/Lowering-none.h" + #else +diff --git a/js/src/jit/MacroAssembler-inl.h b/js/src/jit/MacroAssembler-inl.h +index 8f7963622b..aadeaf4dfa 100644 +--- a/js/src/jit/MacroAssembler-inl.h ++++ b/js/src/jit/MacroAssembler-inl.h +@@ -39,6 +39,8 @@ + # include "jit/mips64/MacroAssembler-mips64-inl.h" + #elif defined(JS_CODEGEN_LOONG64) + # include "jit/loong64/MacroAssembler-loong64-inl.h" ++#elif defined(JS_CODEGEN_RISCV64) ++# include "jit/riscv64/MacroAssembler-riscv64-inl.h" + #elif !defined(JS_CODEGEN_NONE) + # error "Unknown architecture!" + #endif +diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp +index 433378c5e8..37e7e3b89f 100644 +--- a/js/src/jit/MacroAssembler.cpp ++++ b/js/src/jit/MacroAssembler.cpp +@@ -2418,7 +2418,7 @@ void MacroAssembler::outOfLineTruncateSlow(FloatRegister src, Register dest, + + #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \ + defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) || \ +- defined(JS_CODEGEN_LOONG64) ++ defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) + ScratchDoubleScope fpscratch(*this); + if (widenFloatToDouble) { + convertFloat32ToDouble(src, fpscratch); +@@ -2457,7 +2457,7 @@ void MacroAssembler::outOfLineTruncateSlow(FloatRegister src, Register dest, + + #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \ + defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) || \ +- defined(JS_CODEGEN_LOONG64) ++ defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) + // Nothing + #elif defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) + if (widenFloatToDouble) { +@@ -4154,6 +4154,8 @@ void MacroAssembler::emitPreBarrierFastPath(JSRuntime* rt, MIRType type, + ma_dsll(temp1, temp1, temp3); + #elif JS_CODEGEN_LOONG64 + as_sll_d(temp1, temp1, temp3); ++#elif JS_CODEGEN_RISCV64 ++ sll(temp1, temp1, temp3); + #elif JS_CODEGEN_NONE + MOZ_CRASH(); + #else +diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h +index 9fbd2833b0..6677d4255e 100644 +--- a/js/src/jit/MacroAssembler.h ++++ b/js/src/jit/MacroAssembler.h +@@ -27,6 +27,8 @@ + # include "jit/mips64/MacroAssembler-mips64.h" + #elif defined(JS_CODEGEN_LOONG64) + # include "jit/loong64/MacroAssembler-loong64.h" ++#elif defined(JS_CODEGEN_RISCV64) ++# include "jit/riscv64/MacroAssembler-riscv64.h" + #elif defined(JS_CODEGEN_NONE) + # include "jit/none/MacroAssembler-none.h" + #else +@@ -94,8 +96,8 @@ + // } + // ////}}} check_macroassembler_style + +-#define ALL_ARCH mips32, mips64, arm, arm64, x86, x64, loong64 +-#define ALL_SHARED_ARCH arm, arm64, loong64, x86_shared, mips_shared ++#define ALL_ARCH mips32, mips64, arm, arm64, x86, x64, loong64, riscv64 ++#define ALL_SHARED_ARCH arm, arm64, loong64, x86_shared, mips_shared, riscv64 + + // * How this macro works: + // +@@ -142,6 +144,7 @@ + #define DEFINED_ON_mips64 + #define DEFINED_ON_mips_shared + #define DEFINED_ON_loong64 ++#define DEFINED_ON_riscv64 + #define DEFINED_ON_none + + // Specialize for each architecture. +@@ -174,6 +177,9 @@ + #elif defined(JS_CODEGEN_LOONG64) + # undef DEFINED_ON_loong64 + # define DEFINED_ON_loong64 define ++#elif defined(JS_CODEGEN_RISCV64) ++# undef DEFINED_ON_riscv64 ++# define DEFINED_ON_riscv64 define + #elif defined(JS_CODEGEN_NONE) + # undef DEFINED_ON_none + # define DEFINED_ON_none crash +@@ -490,10 +496,10 @@ class MacroAssembler : public MacroAssemblerSpecific { + + // The size of the area used by PushRegsInMask. + size_t PushRegsInMaskSizeInBytes(LiveRegisterSet set) +- DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared); ++ DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared, riscv64); + + void PushRegsInMask(LiveRegisterSet set) +- DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared); ++ DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared, riscv64); + void PushRegsInMask(LiveGeneralRegisterSet set); + + // Like PushRegsInMask, but instead of pushing the registers, store them to +@@ -504,12 +510,12 @@ class MacroAssembler : public MacroAssemblerSpecific { + // must point to either the lowest address in the save area, or some address + // below that. + void storeRegsInMask(LiveRegisterSet set, Address dest, Register scratch) +- DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared); ++ DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared, riscv64); + + void PopRegsInMask(LiveRegisterSet set); + void PopRegsInMask(LiveGeneralRegisterSet set); + void PopRegsInMaskIgnore(LiveRegisterSet set, LiveRegisterSet ignore) +- DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared); ++ DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared, riscv64); + + // =============================================================== + // Stack manipulation functions -- single registers/values. +@@ -542,7 +548,7 @@ class MacroAssembler : public MacroAssemblerSpecific { + void Pop(FloatRegister t) PER_SHARED_ARCH; + void Pop(const ValueOperand& val) PER_SHARED_ARCH; + void PopFlags() DEFINED_ON(x86_shared); +- void PopStackPtr() DEFINED_ON(arm, mips_shared, x86_shared, loong64); ++ void PopStackPtr() DEFINED_ON(arm, mips_shared, x86_shared, loong64, riscv64); + void popRooted(VMFunctionData::RootType rootType, Register cellReg, + const ValueOperand& valueReg); + +@@ -600,8 +606,8 @@ class MacroAssembler : public MacroAssemblerSpecific { + void callAndPushReturnAddress(Label* label) DEFINED_ON(x86_shared); + + // These do not adjust framePushed(). +- void pushReturnAddress() DEFINED_ON(mips_shared, arm, arm64, loong64); +- void popReturnAddress() DEFINED_ON(mips_shared, arm, arm64, loong64); ++ void pushReturnAddress() DEFINED_ON(mips_shared, arm, arm64, loong64, riscv64); ++ void popReturnAddress() DEFINED_ON(mips_shared, arm, arm64, loong64, riscv64); + + // Useful for dealing with two-valued returns. + void moveRegPair(Register src0, Register src1, Register dst0, Register dst1, +@@ -632,10 +638,10 @@ class MacroAssembler : public MacroAssemblerSpecific { + // Note: "Near" applies to ARM64 where the target must be within 1 MB (this is + // release-asserted). + CodeOffset moveNearAddressWithPatch(Register dest) +- DEFINED_ON(x86, x64, arm, arm64, loong64, mips_shared); ++ DEFINED_ON(x86, x64, arm, arm64, loong64, mips_shared, riscv64); + static void patchNearAddressMove(CodeLocationLabel loc, + CodeLocationLabel target) +- DEFINED_ON(x86, x64, arm, arm64, loong64, mips_shared); ++ DEFINED_ON(x86, x64, arm, arm64, loong64, mips_shared, riscv64); + + public: + // =============================================================== +@@ -997,11 +1003,11 @@ class MacroAssembler : public MacroAssemblerSpecific { + inline void xorPtr(Imm32 imm, Register dest) PER_ARCH; + + inline void and64(const Operand& src, Register64 dest) +- DEFINED_ON(x64, mips64, loong64); ++ DEFINED_ON(x64, mips64, loong64, riscv64); + inline void or64(const Operand& src, Register64 dest) +- DEFINED_ON(x64, mips64, loong64); ++ DEFINED_ON(x64, mips64, loong64, riscv64); + inline void xor64(const Operand& src, Register64 dest) +- DEFINED_ON(x64, mips64, loong64); ++ DEFINED_ON(x64, mips64, loong64, riscv64); + + // =============================================================== + // Swap instructions +@@ -1035,17 +1041,17 @@ class MacroAssembler : public MacroAssemblerSpecific { + inline void addPtr(ImmWord imm, Register dest) PER_ARCH; + inline void addPtr(ImmPtr imm, Register dest); + inline void addPtr(Imm32 imm, const Address& dest) +- DEFINED_ON(mips_shared, arm, arm64, x86, x64, loong64); ++ DEFINED_ON(mips_shared, arm, arm64, x86, x64, loong64, riscv64); + inline void addPtr(Imm32 imm, const AbsoluteAddress& dest) + DEFINED_ON(x86, x64); + inline void addPtr(const Address& src, Register dest) +- DEFINED_ON(mips_shared, arm, arm64, x86, x64, loong64); ++ DEFINED_ON(mips_shared, arm, arm64, x86, x64, loong64, riscv64); + + inline void add64(Register64 src, Register64 dest) PER_ARCH; + inline void add64(Imm32 imm, Register64 dest) PER_ARCH; + inline void add64(Imm64 imm, Register64 dest) PER_ARCH; + inline void add64(const Operand& src, Register64 dest) +- DEFINED_ON(x64, mips64, loong64); ++ DEFINED_ON(x64, mips64, loong64, riscv64); + + inline void addFloat32(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH; + +@@ -1064,16 +1070,16 @@ class MacroAssembler : public MacroAssemblerSpecific { + + inline void subPtr(Register src, Register dest) PER_ARCH; + inline void subPtr(Register src, const Address& dest) +- DEFINED_ON(mips_shared, arm, arm64, x86, x64, loong64); ++ DEFINED_ON(mips_shared, arm, arm64, x86, x64, loong64, riscv64); + inline void subPtr(Imm32 imm, Register dest) PER_ARCH; + inline void subPtr(ImmWord imm, Register dest) DEFINED_ON(x64); + inline void subPtr(const Address& addr, Register dest) +- DEFINED_ON(mips_shared, arm, arm64, x86, x64, loong64); ++ DEFINED_ON(mips_shared, arm, arm64, x86, x64, loong64, riscv64); + + inline void sub64(Register64 src, Register64 dest) PER_ARCH; + inline void sub64(Imm64 imm, Register64 dest) PER_ARCH; + inline void sub64(const Operand& src, Register64 dest) +- DEFINED_ON(x64, mips64, loong64); ++ DEFINED_ON(x64, mips64, loong64, riscv64); + + inline void subFloat32(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH; + +@@ -1089,10 +1095,11 @@ class MacroAssembler : public MacroAssemblerSpecific { + + inline void mul64(const Operand& src, const Register64& dest) DEFINED_ON(x64); + inline void mul64(const Operand& src, const Register64& dest, +- const Register temp) DEFINED_ON(x64, mips64, loong64); ++ const Register temp) ++ DEFINED_ON(x64, mips64, loong64, riscv64); + inline void mul64(Imm64 imm, const Register64& dest) PER_ARCH; + inline void mul64(Imm64 imm, const Register64& dest, const Register temp) +- DEFINED_ON(x86, x64, arm, mips32, mips64, loong64); ++ DEFINED_ON(x86, x64, arm, mips32, mips64, loong64, riscv64); + inline void mul64(const Register64& src, const Register64& dest, + const Register temp) PER_ARCH; + inline void mul64(const Register64& src1, const Register64& src2, +@@ -1106,14 +1113,14 @@ class MacroAssembler : public MacroAssemblerSpecific { + inline void mulDouble(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH; + + inline void mulDoublePtr(ImmPtr imm, Register temp, FloatRegister dest) +- DEFINED_ON(mips_shared, arm, arm64, x86, x64, loong64); ++ DEFINED_ON(mips_shared, arm, arm64, x86, x64, loong64, riscv64); + + // Perform an integer division, returning the integer part rounded toward + // zero. rhs must not be zero, and the division must not overflow. + // + // On ARM, the chip must have hardware division instructions. + inline void quotient32(Register rhs, Register srcDest, bool isUnsigned) +- DEFINED_ON(mips_shared, arm, arm64, loong64); ++ DEFINED_ON(mips_shared, arm, arm64, loong64, riscv64); + + // As above, but srcDest must be eax and tempEdx must be edx. + inline void quotient32(Register rhs, Register srcDest, Register tempEdx, +@@ -1124,7 +1131,7 @@ class MacroAssembler : public MacroAssemblerSpecific { + // + // On ARM, the chip must have hardware division instructions. + inline void remainder32(Register rhs, Register srcDest, bool isUnsigned) +- DEFINED_ON(mips_shared, arm, arm64, loong64); ++ DEFINED_ON(mips_shared, arm, arm64, loong64, riscv64); + + // As above, but srcDest must be eax and tempEdx must be edx. + inline void remainder32(Register rhs, Register srcDest, Register tempEdx, +@@ -1139,7 +1146,7 @@ class MacroAssembler : public MacroAssemblerSpecific { + // rhs is preserved, srdDest is clobbered. + void flexibleRemainder32(Register rhs, Register srcDest, bool isUnsigned, + const LiveRegisterSet& volatileLiveRegs) +- DEFINED_ON(mips_shared, arm, arm64, x86_shared, loong64); ++ DEFINED_ON(mips_shared, arm, arm64, x86_shared, loong64, riscv64); + + // Perform an integer division, returning the integer part rounded toward + // zero. rhs must not be zero, and the division must not overflow. +@@ -1150,7 +1157,7 @@ class MacroAssembler : public MacroAssemblerSpecific { + // rhs is preserved, srdDest is clobbered. + void flexibleQuotient32(Register rhs, Register srcDest, bool isUnsigned, + const LiveRegisterSet& volatileLiveRegs) +- DEFINED_ON(mips_shared, arm, arm64, x86_shared, loong64); ++ DEFINED_ON(mips_shared, arm, arm64, x86_shared, loong64, riscv64); + + // Perform an integer division, returning the integer part rounded toward + // zero. rhs must not be zero, and the division must not overflow. The +@@ -1163,7 +1170,7 @@ class MacroAssembler : public MacroAssemblerSpecific { + void flexibleDivMod32(Register rhs, Register srcDest, Register remOutput, + bool isUnsigned, + const LiveRegisterSet& volatileLiveRegs) +- DEFINED_ON(mips_shared, arm, arm64, x86_shared, loong64); ++ DEFINED_ON(mips_shared, arm, arm64, x86_shared, loong64, riscv64); + + inline void divFloat32(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH; + inline void divDouble(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH; +@@ -1370,7 +1377,7 @@ class MacroAssembler : public MacroAssemblerSpecific { + + template + inline void cmp32Set(Condition cond, T1 lhs, T2 rhs, Register dest) +- DEFINED_ON(x86_shared, arm, arm64, mips32, mips64, loong64); ++ DEFINED_ON(x86_shared, arm, arm64, mips32, mips64, loong64, riscv64); + + // Only the NotEqual and Equal conditions are allowed. + inline void cmp64Set(Condition cond, Address lhs, Imm64 rhs, +@@ -1405,10 +1412,10 @@ class MacroAssembler : public MacroAssemblerSpecific { + + inline void branch32(Condition cond, const AbsoluteAddress& lhs, Register rhs, + Label* label) +- DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64); ++ DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64); + inline void branch32(Condition cond, const AbsoluteAddress& lhs, Imm32 rhs, + Label* label) +- DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64); ++ DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64); + + inline void branch32(Condition cond, const BaseIndex& lhs, Register rhs, + Label* label) DEFINED_ON(arm, x86_shared); +@@ -1422,7 +1429,7 @@ class MacroAssembler : public MacroAssemblerSpecific { + + inline void branch32(Condition cond, wasm::SymbolicAddress lhs, Imm32 rhs, + Label* label) +- DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64); ++ DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64); + + // The supported condition are Equal, NotEqual, LessThan(orEqual), + // GreaterThan(orEqual), Below(orEqual) and Above(orEqual). When a fail label +@@ -1473,14 +1480,14 @@ class MacroAssembler : public MacroAssemblerSpecific { + + inline void branchPtr(Condition cond, const AbsoluteAddress& lhs, + Register rhs, Label* label) +- DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64); ++ DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64); + inline void branchPtr(Condition cond, const AbsoluteAddress& lhs, ImmWord rhs, + Label* label) +- DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64); ++ DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64); + + inline void branchPtr(Condition cond, wasm::SymbolicAddress lhs, Register rhs, + Label* label) +- DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64); ++ DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64); + + // Given a pointer to a GC Cell, retrieve the StoreBuffer pointer from its + // chunk header, or nullptr if it is in the tenured heap. +@@ -1488,7 +1495,7 @@ class MacroAssembler : public MacroAssemblerSpecific { + + void branchPtrInNurseryChunk(Condition cond, Register ptr, Register temp, + Label* label) +- DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64); ++ DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64); + void branchPtrInNurseryChunk(Condition cond, const Address& address, + Register temp, Label* label) DEFINED_ON(x86); + void branchValueIsNurseryCell(Condition cond, const Address& address, +@@ -1510,10 +1517,10 @@ class MacroAssembler : public MacroAssemblerSpecific { + // x64 variants will do this only in the int64_t range. + inline void branchTruncateFloat32MaybeModUint32(FloatRegister src, + Register dest, Label* fail) +- DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64); ++ DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64); + inline void branchTruncateDoubleMaybeModUint32(FloatRegister src, + Register dest, Label* fail) +- DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64); ++ DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64); + + // Truncate a double/float32 to intptr and when it doesn't fit jump to the + // failure label. +@@ -1526,10 +1533,10 @@ class MacroAssembler : public MacroAssemblerSpecific { + // failure label. + inline void branchTruncateFloat32ToInt32(FloatRegister src, Register dest, + Label* fail) +- DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64); ++ DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64); + inline void branchTruncateDoubleToInt32(FloatRegister src, Register dest, + Label* fail) +- DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64); ++ DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64); + + inline void branchDouble(DoubleCondition cond, FloatRegister lhs, + FloatRegister rhs, Label* label) PER_SHARED_ARCH; +@@ -1586,7 +1593,7 @@ class MacroAssembler : public MacroAssemblerSpecific { + Label* label) PER_SHARED_ARCH; + inline void branchTest32(Condition cond, const AbsoluteAddress& lhs, + Imm32 rhs, Label* label) +- DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64); ++ DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64); + + template + inline void branchTestPtr(Condition cond, Register lhs, Register rhs, +@@ -1747,7 +1754,7 @@ class MacroAssembler : public MacroAssemblerSpecific { + inline void branchTestInt32(Condition cond, Register tag, + Label* label) PER_SHARED_ARCH; + inline void branchTestDouble(Condition cond, Register tag, Label* label) +- DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared); ++ DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared, riscv64); + inline void branchTestNumber(Condition cond, Register tag, + Label* label) PER_SHARED_ARCH; + inline void branchTestBoolean(Condition cond, Register tag, +@@ -1779,7 +1786,7 @@ class MacroAssembler : public MacroAssemblerSpecific { + Label* label) PER_SHARED_ARCH; + inline void branchTestUndefined(Condition cond, const ValueOperand& value, + Label* label) +- DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared); ++ DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared, riscv64); + + inline void branchTestInt32(Condition cond, const Address& address, + Label* label) PER_SHARED_ARCH; +@@ -1787,7 +1794,7 @@ class MacroAssembler : public MacroAssemblerSpecific { + Label* label) PER_SHARED_ARCH; + inline void branchTestInt32(Condition cond, const ValueOperand& value, + Label* label) +- DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared); ++ DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared, riscv64); + + inline void branchTestDouble(Condition cond, const Address& address, + Label* label) PER_SHARED_ARCH; +@@ -1795,11 +1802,11 @@ class MacroAssembler : public MacroAssemblerSpecific { + Label* label) PER_SHARED_ARCH; + inline void branchTestDouble(Condition cond, const ValueOperand& value, + Label* label) +- DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared); ++ DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared, riscv64); + + inline void branchTestNumber(Condition cond, const ValueOperand& value, + Label* label) +- DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared); ++ DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared, riscv64); + + inline void branchTestBoolean(Condition cond, const Address& address, + Label* label) PER_SHARED_ARCH; +@@ -1807,7 +1814,7 @@ class MacroAssembler : public MacroAssemblerSpecific { + Label* label) PER_SHARED_ARCH; + inline void branchTestBoolean(Condition cond, const ValueOperand& value, + Label* label) +- DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared); ++ DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared, riscv64); + + inline void branchTestString(Condition cond, const Address& address, + Label* label) PER_SHARED_ARCH; +@@ -1815,7 +1822,7 @@ class MacroAssembler : public MacroAssemblerSpecific { + Label* label) PER_SHARED_ARCH; + inline void branchTestString(Condition cond, const ValueOperand& value, + Label* label) +- DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared); ++ DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared, riscv64); + + inline void branchTestSymbol(Condition cond, const Address& address, + Label* label) PER_SHARED_ARCH; +@@ -1823,7 +1830,7 @@ class MacroAssembler : public MacroAssemblerSpecific { + Label* label) PER_SHARED_ARCH; + inline void branchTestSymbol(Condition cond, const ValueOperand& value, + Label* label) +- DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared); ++ DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared, riscv64); + + inline void branchTestBigInt(Condition cond, const Address& address, + Label* label) PER_SHARED_ARCH; +@@ -1831,7 +1838,7 @@ class MacroAssembler : public MacroAssemblerSpecific { + Label* label) PER_SHARED_ARCH; + inline void branchTestBigInt(Condition cond, const ValueOperand& value, + Label* label) +- DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared); ++ DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared, riscv64); + + inline void branchTestNull(Condition cond, const Address& address, + Label* label) PER_SHARED_ARCH; +@@ -1839,7 +1846,7 @@ class MacroAssembler : public MacroAssemblerSpecific { + Label* label) PER_SHARED_ARCH; + inline void branchTestNull(Condition cond, const ValueOperand& value, + Label* label) +- DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared); ++ DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared, riscv64); + + // Clobbers the ScratchReg on x64. + inline void branchTestObject(Condition cond, const Address& address, +@@ -1848,7 +1855,7 @@ class MacroAssembler : public MacroAssemblerSpecific { + Label* label) PER_SHARED_ARCH; + inline void branchTestObject(Condition cond, const ValueOperand& value, + Label* label) +- DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared); ++ DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared, riscv64); + + inline void branchTestGCThing(Condition cond, const Address& address, + Label* label) PER_SHARED_ARCH; +@@ -1859,7 +1866,7 @@ class MacroAssembler : public MacroAssemblerSpecific { + + inline void branchTestPrimitive(Condition cond, const ValueOperand& value, + Label* label) +- DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared); ++ DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared, riscv64); + + inline void branchTestMagic(Condition cond, const Address& address, + Label* label) PER_SHARED_ARCH; +@@ -1868,7 +1875,7 @@ class MacroAssembler : public MacroAssemblerSpecific { + template + inline void branchTestMagic(Condition cond, const ValueOperand& value, + L label) +- DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared); ++ DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared, riscv64); + + inline void branchTestMagic(Condition cond, const Address& valaddr, + JSWhyMagic why, Label* label) PER_ARCH; +@@ -1886,17 +1893,17 @@ class MacroAssembler : public MacroAssemblerSpecific { + // The type of the value should match the type of the method. + inline void branchTestInt32Truthy(bool truthy, const ValueOperand& value, + Label* label) +- DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared); ++ DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared, riscv64); + inline void branchTestDoubleTruthy(bool truthy, FloatRegister reg, + Label* label) PER_SHARED_ARCH; + inline void branchTestBooleanTruthy(bool truthy, const ValueOperand& value, + Label* label) PER_ARCH; + inline void branchTestStringTruthy(bool truthy, const ValueOperand& value, + Label* label) +- DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared); ++ DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared, riscv64); + inline void branchTestBigIntTruthy(bool truthy, const ValueOperand& value, + Label* label) +- DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared); ++ DEFINED_ON(arm, arm64, mips32, mips64, loong64, x86_shared, riscv64); + + // Create an unconditional branch to the address given as argument. + inline void branchToComputedAddress(const BaseIndex& address) PER_ARCH; +@@ -1911,7 +1918,7 @@ class MacroAssembler : public MacroAssemblerSpecific { + template + void branchValueIsNurseryCellImpl(Condition cond, const T& value, + Register temp, Label* label) +- DEFINED_ON(arm64, x64, mips64, loong64); ++ DEFINED_ON(arm64, x64, mips64, loong64, riscv64); + + template + inline void branchTestUndefinedImpl(Condition cond, const T& t, Label* label) +@@ -1998,11 +2005,11 @@ class MacroAssembler : public MacroAssemblerSpecific { + + inline void cmp32Move32(Condition cond, Register lhs, Register rhs, + Register src, Register dest) +- DEFINED_ON(arm, arm64, loong64, mips_shared, x86_shared); ++ DEFINED_ON(arm, arm64, loong64, mips_shared, x86_shared, riscv64); + + inline void cmp32Move32(Condition cond, Register lhs, const Address& rhs, + Register src, Register dest) +- DEFINED_ON(arm, arm64, loong64, mips_shared, x86_shared); ++ DEFINED_ON(arm, arm64, loong64, mips_shared, x86_shared, riscv64); + + inline void cmpPtrMovePtr(Condition cond, Register lhs, Register rhs, + Register src, Register dest) PER_ARCH; +@@ -2012,36 +2019,36 @@ class MacroAssembler : public MacroAssemblerSpecific { + + inline void cmp32Load32(Condition cond, Register lhs, const Address& rhs, + const Address& src, Register dest) +- DEFINED_ON(arm, arm64, loong64, mips_shared, x86_shared); ++ DEFINED_ON(arm, arm64, loong64, mips_shared, x86_shared, riscv64); + + inline void cmp32Load32(Condition cond, Register lhs, Register rhs, + const Address& src, Register dest) +- DEFINED_ON(arm, arm64, loong64, mips_shared, x86_shared); ++ DEFINED_ON(arm, arm64, loong64, mips_shared, x86_shared, riscv64); + + inline void cmp32LoadPtr(Condition cond, const Address& lhs, Imm32 rhs, + const Address& src, Register dest) +- DEFINED_ON(arm, arm64, loong64, mips_shared, x86, x64); ++ DEFINED_ON(arm, arm64, loong64, mips_shared, x86, x64, riscv64); + + inline void cmp32MovePtr(Condition cond, Register lhs, Imm32 rhs, + Register src, Register dest) +- DEFINED_ON(arm, arm64, loong64, mips_shared, x86, x64); ++ DEFINED_ON(arm, arm64, loong64, mips_shared, x86, x64, riscv64); + + inline void test32LoadPtr(Condition cond, const Address& addr, Imm32 mask, + const Address& src, Register dest) +- DEFINED_ON(arm, arm64, loong64, mips_shared, x86, x64); ++ DEFINED_ON(arm, arm64, loong64, mips_shared, x86, x64, riscv64); + + inline void test32MovePtr(Condition cond, const Address& addr, Imm32 mask, + Register src, Register dest) +- DEFINED_ON(arm, arm64, loong64, mips_shared, x86, x64); ++ DEFINED_ON(arm, arm64, loong64, mips_shared, x86, x64, riscv64); + + // Conditional move for Spectre mitigations. + inline void spectreMovePtr(Condition cond, Register src, Register dest) +- DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64); ++ DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64); + + // Zeroes dest if the condition is true. + inline void spectreZeroRegister(Condition cond, Register scratch, + Register dest) +- DEFINED_ON(arm, arm64, mips_shared, x86_shared, loong64); ++ DEFINED_ON(arm, arm64, mips_shared, x86_shared, loong64, riscv64); + + // Performs a bounds check and zeroes the index register if out-of-bounds + // (to mitigate Spectre). +@@ -2053,17 +2060,17 @@ class MacroAssembler : public MacroAssemblerSpecific { + public: + inline void spectreBoundsCheck32(Register index, Register length, + Register maybeScratch, Label* failure) +- DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64); ++ DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64); + inline void spectreBoundsCheck32(Register index, const Address& length, + Register maybeScratch, Label* failure) +- DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64); ++ DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64); + + inline void spectreBoundsCheckPtr(Register index, Register length, + Register maybeScratch, Label* failure) +- DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64); ++ DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64); + inline void spectreBoundsCheckPtr(Register index, const Address& length, + Register maybeScratch, Label* failure) +- DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64); ++ DEFINED_ON(arm, arm64, mips_shared, x86, x64, loong64, riscv64); + + // ======================================================================== + // Canonicalization primitives. +@@ -2077,10 +2084,10 @@ class MacroAssembler : public MacroAssemblerSpecific { + // ======================================================================== + // Memory access primitives. + inline void storeUncanonicalizedDouble(FloatRegister src, const Address& dest) +- DEFINED_ON(x86_shared, arm, arm64, mips32, mips64, loong64); ++ DEFINED_ON(x86_shared, arm, arm64, mips32, mips64, loong64, riscv64); + inline void storeUncanonicalizedDouble(FloatRegister src, + const BaseIndex& dest) +- DEFINED_ON(x86_shared, arm, arm64, mips32, mips64, loong64); ++ DEFINED_ON(x86_shared, arm, arm64, mips32, mips64, loong64, riscv64); + inline void storeUncanonicalizedDouble(FloatRegister src, const Operand& dest) + DEFINED_ON(x86_shared); + +@@ -2094,10 +2101,10 @@ class MacroAssembler : public MacroAssemblerSpecific { + + inline void storeUncanonicalizedFloat32(FloatRegister src, + const Address& dest) +- DEFINED_ON(x86_shared, arm, arm64, mips32, mips64, loong64); ++ DEFINED_ON(x86_shared, arm, arm64, mips32, mips64, loong64, riscv64); + inline void storeUncanonicalizedFloat32(FloatRegister src, + const BaseIndex& dest) +- DEFINED_ON(x86_shared, arm, arm64, mips32, mips64, loong64); ++ DEFINED_ON(x86_shared, arm, arm64, mips32, mips64, loong64, riscv64); + inline void storeUncanonicalizedFloat32(FloatRegister src, + const Operand& dest) + DEFINED_ON(x86_shared); +@@ -3498,10 +3505,10 @@ class MacroAssembler : public MacroAssemblerSpecific { + + // temp required on x86 and x64; must be undefined on mips64 and loong64. + void convertUInt64ToFloat32(Register64 src, FloatRegister dest, Register temp) +- DEFINED_ON(arm64, mips64, loong64, x64, x86); ++ DEFINED_ON(arm64, mips64, loong64, x64, x86, riscv64); + + void convertInt64ToFloat32(Register64 src, FloatRegister dest) +- DEFINED_ON(arm64, mips64, loong64, x64, x86); ++ DEFINED_ON(arm64, mips64, loong64, x64, x86, riscv64); + + bool convertUInt64ToDoubleNeedsTemp() PER_ARCH; + +@@ -3554,19 +3561,19 @@ class MacroAssembler : public MacroAssemblerSpecific { + + void wasmBoundsCheck32(Condition cond, Register index, + Register boundsCheckLimit, Label* ok) +- DEFINED_ON(arm, arm64, mips32, mips64, x86_shared, loong64); ++ DEFINED_ON(arm, arm64, mips32, mips64, x86_shared, loong64, riscv64); + + void wasmBoundsCheck32(Condition cond, Register index, + Address boundsCheckLimit, Label* ok) +- DEFINED_ON(arm, arm64, mips32, mips64, x86_shared, loong64); ++ DEFINED_ON(arm, arm64, mips32, mips64, x86_shared, loong64, riscv64); + + void wasmBoundsCheck64(Condition cond, Register64 index, + Register64 boundsCheckLimit, Label* ok) +- DEFINED_ON(arm64, mips64, x64, x86, arm, loong64); ++ DEFINED_ON(arm64, mips64, x64, x86, arm, loong64, riscv64); + + void wasmBoundsCheck64(Condition cond, Register64 index, + Address boundsCheckLimit, Label* ok) +- DEFINED_ON(arm64, mips64, x64, x86, arm, loong64); ++ DEFINED_ON(arm64, mips64, x64, x86, arm, loong64, riscv64); + + // Each wasm load/store instruction appends its own wasm::Trap::OutOfBounds. + void wasmLoad(const wasm::MemoryAccessDesc& access, Operand srcAddr, +@@ -3586,16 +3593,16 @@ class MacroAssembler : public MacroAssemblerSpecific { + // Scalar::Int64. + void wasmLoad(const wasm::MemoryAccessDesc& access, Register memoryBase, + Register ptr, Register ptrScratch, AnyRegister output) +- DEFINED_ON(arm, loong64, mips_shared); ++ DEFINED_ON(arm, loong64, riscv64, mips_shared); + void wasmLoadI64(const wasm::MemoryAccessDesc& access, Register memoryBase, + Register ptr, Register ptrScratch, Register64 output) +- DEFINED_ON(arm, mips32, mips64, loong64); ++ DEFINED_ON(arm, mips32, mips64, loong64, riscv64); + void wasmStore(const wasm::MemoryAccessDesc& access, AnyRegister value, + Register memoryBase, Register ptr, Register ptrScratch) +- DEFINED_ON(arm, loong64, mips_shared); ++ DEFINED_ON(arm, loong64, riscv64, mips_shared); + void wasmStoreI64(const wasm::MemoryAccessDesc& access, Register64 value, + Register memoryBase, Register ptr, Register ptrScratch) +- DEFINED_ON(arm, mips32, mips64, loong64); ++ DEFINED_ON(arm, mips32, mips64, loong64, riscv64); + + // These accept general memoryBase + ptr + offset (in `access`); the offset is + // always smaller than the guard region. They will insert an additional add +@@ -3659,7 +3666,7 @@ class MacroAssembler : public MacroAssemblerSpecific { + void oolWasmTruncateCheckF64ToI32(FloatRegister input, Register output, + TruncFlags flags, wasm::BytecodeOffset off, + Label* rejoin) +- DEFINED_ON(arm, arm64, x86_shared, mips_shared, loong64); ++ DEFINED_ON(arm, arm64, x86_shared, mips_shared, loong64, riscv64); + + void wasmTruncateFloat32ToUInt32(FloatRegister input, Register output, + bool isSaturating, Label* oolEntry) PER_ARCH; +@@ -3669,35 +3676,35 @@ class MacroAssembler : public MacroAssemblerSpecific { + void oolWasmTruncateCheckF32ToI32(FloatRegister input, Register output, + TruncFlags flags, wasm::BytecodeOffset off, + Label* rejoin) +- DEFINED_ON(arm, arm64, x86_shared, mips_shared, loong64); ++ DEFINED_ON(arm, arm64, x86_shared, mips_shared, loong64, riscv64); + + // The truncate-to-int64 methods will always bind the `oolRejoin` label + // after the last emitted instruction. + void wasmTruncateDoubleToInt64(FloatRegister input, Register64 output, + bool isSaturating, Label* oolEntry, + Label* oolRejoin, FloatRegister tempDouble) +- DEFINED_ON(arm64, x86, x64, mips64, loong64); ++ DEFINED_ON(arm64, x86, x64, mips64, loong64, riscv64); + void wasmTruncateDoubleToUInt64(FloatRegister input, Register64 output, + bool isSaturating, Label* oolEntry, + Label* oolRejoin, FloatRegister tempDouble) +- DEFINED_ON(arm64, x86, x64, mips64, loong64); ++ DEFINED_ON(arm64, x86, x64, mips64, loong64, riscv64); + void oolWasmTruncateCheckF64ToI64(FloatRegister input, Register64 output, + TruncFlags flags, wasm::BytecodeOffset off, + Label* rejoin) +- DEFINED_ON(arm, arm64, x86_shared, mips_shared, loong64); ++ DEFINED_ON(arm, arm64, x86_shared, mips_shared, loong64, riscv64); + + void wasmTruncateFloat32ToInt64(FloatRegister input, Register64 output, + bool isSaturating, Label* oolEntry, + Label* oolRejoin, FloatRegister tempDouble) +- DEFINED_ON(arm64, x86, x64, mips64, loong64); ++ DEFINED_ON(arm64, x86, x64, mips64, loong64, riscv64); + void wasmTruncateFloat32ToUInt64(FloatRegister input, Register64 output, + bool isSaturating, Label* oolEntry, + Label* oolRejoin, FloatRegister tempDouble) +- DEFINED_ON(arm64, x86, x64, mips64, loong64); ++ DEFINED_ON(arm64, x86, x64, mips64, loong64, riscv64); + void oolWasmTruncateCheckF32ToI64(FloatRegister input, Register64 output, + TruncFlags flags, wasm::BytecodeOffset off, + Label* rejoin) +- DEFINED_ON(arm, arm64, x86_shared, mips_shared, loong64); ++ DEFINED_ON(arm, arm64, x86_shared, mips_shared, loong64, riscv64); + + void loadWasmGlobalPtr(uint32_t globalDataOffset, Register dest); + +@@ -3755,7 +3762,7 @@ class MacroAssembler : public MacroAssemblerSpecific { + // convention, which requires predictable high bits. In practice, this means + // that the 32-bit value will be zero-extended or sign-extended to 64 bits as + // appropriate for the platform. +- void widenInt32(Register r) DEFINED_ON(arm64, x64, mips64, loong64); ++ void widenInt32(Register r) DEFINED_ON(arm64, x64, mips64, loong64, riscv64); + + // As enterFakeExitFrame(), but using register conventions appropriate for + // wasm stubs. +@@ -3814,13 +3821,13 @@ class MacroAssembler : public MacroAssemblerSpecific { + const Address& mem, Register expected, + Register replacement, Register valueTemp, + Register offsetTemp, Register maskTemp, Register output) +- DEFINED_ON(mips_shared, loong64); ++ DEFINED_ON(mips_shared, loong64, riscv64); + + void compareExchange(Scalar::Type type, const Synchronization& sync, + const BaseIndex& mem, Register expected, + Register replacement, Register valueTemp, + Register offsetTemp, Register maskTemp, Register output) +- DEFINED_ON(mips_shared, loong64); ++ DEFINED_ON(mips_shared, loong64, riscv64); + + // x86: `expected` and `output` must be edx:eax; `replacement` is ecx:ebx. + // x64: `output` must be rax. +@@ -3830,12 +3837,12 @@ class MacroAssembler : public MacroAssemblerSpecific { + void compareExchange64(const Synchronization& sync, const Address& mem, + Register64 expected, Register64 replacement, + Register64 output) +- DEFINED_ON(arm, arm64, x64, x86, mips64, loong64); ++ DEFINED_ON(arm, arm64, x64, x86, mips64, loong64, riscv64); + + void compareExchange64(const Synchronization& sync, const BaseIndex& mem, + Register64 expected, Register64 replacement, + Register64 output) +- DEFINED_ON(arm, arm64, x64, x86, mips64, loong64); ++ DEFINED_ON(arm, arm64, x64, x86, mips64, loong64, riscv64); + + // Exchange with memory. Return the value initially in memory. + // MIPS: `valueTemp`, `offsetTemp` and `maskTemp` must be defined for 8-bit +@@ -3852,12 +3859,12 @@ class MacroAssembler : public MacroAssemblerSpecific { + void atomicExchange(Scalar::Type type, const Synchronization& sync, + const Address& mem, Register value, Register valueTemp, + Register offsetTemp, Register maskTemp, Register output) +- DEFINED_ON(mips_shared, loong64); ++ DEFINED_ON(mips_shared, loong64, riscv64); + + void atomicExchange(Scalar::Type type, const Synchronization& sync, + const BaseIndex& mem, Register value, Register valueTemp, + Register offsetTemp, Register maskTemp, Register output) +- DEFINED_ON(mips_shared, loong64); ++ DEFINED_ON(mips_shared, loong64, riscv64); + + // x86: `value` must be ecx:ebx; `output` must be edx:eax. + // ARM: `value` and `output` must be distinct and (even,odd) pairs. +@@ -3865,11 +3872,11 @@ class MacroAssembler : public MacroAssemblerSpecific { + + void atomicExchange64(const Synchronization& sync, const Address& mem, + Register64 value, Register64 output) +- DEFINED_ON(arm, arm64, x64, x86, mips64, loong64); ++ DEFINED_ON(arm, arm64, x64, x86, mips64, loong64, riscv64); + + void atomicExchange64(const Synchronization& sync, const BaseIndex& mem, + Register64 value, Register64 output) +- DEFINED_ON(arm, arm64, x64, x86, mips64, loong64); ++ DEFINED_ON(arm, arm64, x64, x86, mips64, loong64, riscv64); + + // Read-modify-write with memory. Return the value in memory before the + // operation. +@@ -3905,12 +3912,12 @@ class MacroAssembler : public MacroAssemblerSpecific { + void atomicFetchOp(Scalar::Type type, const Synchronization& sync, + AtomicOp op, Register value, const Address& mem, + Register valueTemp, Register offsetTemp, Register maskTemp, +- Register output) DEFINED_ON(mips_shared, loong64); ++ Register output) DEFINED_ON(mips_shared, loong64, riscv64); + + void atomicFetchOp(Scalar::Type type, const Synchronization& sync, + AtomicOp op, Register value, const BaseIndex& mem, + Register valueTemp, Register offsetTemp, Register maskTemp, +- Register output) DEFINED_ON(mips_shared, loong64); ++ Register output) DEFINED_ON(mips_shared, loong64, riscv64); + + // x86: + // `temp` must be ecx:ebx; `output` must be edx:eax. +@@ -3925,7 +3932,7 @@ class MacroAssembler : public MacroAssemblerSpecific { + void atomicFetchOp64(const Synchronization& sync, AtomicOp op, + Register64 value, const Address& mem, Register64 temp, + Register64 output) +- DEFINED_ON(arm, arm64, x64, mips64, loong64); ++ DEFINED_ON(arm, arm64, x64, mips64, loong64, riscv64); + + void atomicFetchOp64(const Synchronization& sync, AtomicOp op, + const Address& value, const Address& mem, +@@ -3934,7 +3941,7 @@ class MacroAssembler : public MacroAssemblerSpecific { + void atomicFetchOp64(const Synchronization& sync, AtomicOp op, + Register64 value, const BaseIndex& mem, Register64 temp, + Register64 output) +- DEFINED_ON(arm, arm64, x64, mips64, loong64); ++ DEFINED_ON(arm, arm64, x64, mips64, loong64, riscv64); + + void atomicFetchOp64(const Synchronization& sync, AtomicOp op, + const Address& value, const BaseIndex& mem, +@@ -3952,14 +3959,14 @@ class MacroAssembler : public MacroAssemblerSpecific { + + void atomicEffectOp64(const Synchronization& sync, AtomicOp op, + Register64 value, const Address& mem, Register64 temp) +- DEFINED_ON(arm, arm64, mips64, loong64); ++ DEFINED_ON(arm, arm64, mips64, loong64, riscv64); + + void atomicEffectOp64(const Synchronization& sync, AtomicOp op, + Register64 value, const BaseIndex& mem) DEFINED_ON(x64); + + void atomicEffectOp64(const Synchronization& sync, AtomicOp op, + Register64 value, const BaseIndex& mem, Register64 temp) +- DEFINED_ON(arm, arm64, mips64, loong64); ++ DEFINED_ON(arm, arm64, mips64, loong64, riscv64); + + // 64-bit atomic load. On 64-bit systems, use regular load with + // Synchronization::Load, not this method. +@@ -4011,13 +4018,15 @@ class MacroAssembler : public MacroAssemblerSpecific { + const Address& mem, Register expected, + Register replacement, Register valueTemp, + Register offsetTemp, Register maskTemp, +- Register output) DEFINED_ON(mips_shared, loong64); ++ Register output) ++ DEFINED_ON(mips_shared, loong64, riscv64); + + void wasmCompareExchange(const wasm::MemoryAccessDesc& access, + const BaseIndex& mem, Register expected, + Register replacement, Register valueTemp, + Register offsetTemp, Register maskTemp, +- Register output) DEFINED_ON(mips_shared, loong64); ++ Register output) ++ DEFINED_ON(mips_shared, loong64, riscv64); + + void wasmAtomicExchange(const wasm::MemoryAccessDesc& access, + const Address& mem, Register value, Register output) +@@ -4031,13 +4040,13 @@ class MacroAssembler : public MacroAssemblerSpecific { + const Address& mem, Register value, + Register valueTemp, Register offsetTemp, + Register maskTemp, Register output) +- DEFINED_ON(mips_shared, loong64); ++ DEFINED_ON(mips_shared, loong64, riscv64); + + void wasmAtomicExchange(const wasm::MemoryAccessDesc& access, + const BaseIndex& mem, Register value, + Register valueTemp, Register offsetTemp, + Register maskTemp, Register output) +- DEFINED_ON(mips_shared, loong64); ++ DEFINED_ON(mips_shared, loong64, riscv64); + + void wasmAtomicFetchOp(const wasm::MemoryAccessDesc& access, AtomicOp op, + Register value, const Address& mem, Register temp, +@@ -4058,13 +4067,14 @@ class MacroAssembler : public MacroAssemblerSpecific { + void wasmAtomicFetchOp(const wasm::MemoryAccessDesc& access, AtomicOp op, + Register value, const Address& mem, Register valueTemp, + Register offsetTemp, Register maskTemp, +- Register output) DEFINED_ON(mips_shared, loong64); ++ Register output) ++ DEFINED_ON(mips_shared, loong64, riscv64); + + void wasmAtomicFetchOp(const wasm::MemoryAccessDesc& access, AtomicOp op, + Register value, const BaseIndex& mem, + Register valueTemp, Register offsetTemp, + Register maskTemp, Register output) +- DEFINED_ON(mips_shared, loong64); ++ DEFINED_ON(mips_shared, loong64, riscv64); + + // Read-modify-write with memory. Return no value. + // +@@ -4090,12 +4100,14 @@ class MacroAssembler : public MacroAssemblerSpecific { + void wasmAtomicEffectOp(const wasm::MemoryAccessDesc& access, AtomicOp op, + Register value, const Address& mem, + Register valueTemp, Register offsetTemp, +- Register maskTemp) DEFINED_ON(mips_shared, loong64); ++ Register maskTemp) ++ DEFINED_ON(mips_shared, loong64, riscv64); + + void wasmAtomicEffectOp(const wasm::MemoryAccessDesc& access, AtomicOp op, + Register value, const BaseIndex& mem, + Register valueTemp, Register offsetTemp, +- Register maskTemp) DEFINED_ON(mips_shared, loong64); ++ Register maskTemp) ++ DEFINED_ON(mips_shared, loong64, riscv64); + + // 64-bit wide operations. + +@@ -4155,12 +4167,12 @@ class MacroAssembler : public MacroAssemblerSpecific { + void wasmAtomicFetchOp64(const wasm::MemoryAccessDesc& access, AtomicOp op, + Register64 value, const Address& mem, + Register64 temp, Register64 output) +- DEFINED_ON(arm, arm64, mips32, mips64, loong64, x64); ++ DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, x64); + + void wasmAtomicFetchOp64(const wasm::MemoryAccessDesc& access, AtomicOp op, + Register64 value, const BaseIndex& mem, + Register64 temp, Register64 output) +- DEFINED_ON(arm, arm64, mips32, mips64, loong64, x64); ++ DEFINED_ON(arm, arm64, mips32, mips64, loong64, riscv64, x64); + + void wasmAtomicFetchOp64(const wasm::MemoryAccessDesc& access, AtomicOp op, + const Address& value, const Address& mem, +@@ -4212,13 +4224,15 @@ class MacroAssembler : public MacroAssemblerSpecific { + const Address& mem, Register expected, + Register replacement, Register valueTemp, + Register offsetTemp, Register maskTemp, Register temp, +- AnyRegister output) DEFINED_ON(mips_shared, loong64); ++ AnyRegister output) ++ DEFINED_ON(mips_shared, loong64, riscv64); + + void compareExchangeJS(Scalar::Type arrayType, const Synchronization& sync, + const BaseIndex& mem, Register expected, + Register replacement, Register valueTemp, + Register offsetTemp, Register maskTemp, Register temp, +- AnyRegister output) DEFINED_ON(mips_shared, loong64); ++ AnyRegister output) ++ DEFINED_ON(mips_shared, loong64, riscv64); + + void atomicExchangeJS(Scalar::Type arrayType, const Synchronization& sync, + const Address& mem, Register value, Register temp, +@@ -4231,13 +4245,14 @@ class MacroAssembler : public MacroAssemblerSpecific { + void atomicExchangeJS(Scalar::Type arrayType, const Synchronization& sync, + const Address& mem, Register value, Register valueTemp, + Register offsetTemp, Register maskTemp, Register temp, +- AnyRegister output) DEFINED_ON(mips_shared, loong64); ++ AnyRegister output) ++ DEFINED_ON(mips_shared, loong64, riscv64); + + void atomicExchangeJS(Scalar::Type arrayType, const Synchronization& sync, + const BaseIndex& mem, Register value, + Register valueTemp, Register offsetTemp, + Register maskTemp, Register temp, AnyRegister output) +- DEFINED_ON(mips_shared, loong64); ++ DEFINED_ON(mips_shared, loong64, riscv64); + + void atomicFetchOpJS(Scalar::Type arrayType, const Synchronization& sync, + AtomicOp op, Register value, const Address& mem, +@@ -4263,13 +4278,13 @@ class MacroAssembler : public MacroAssemblerSpecific { + AtomicOp op, Register value, const Address& mem, + Register valueTemp, Register offsetTemp, + Register maskTemp, Register temp, AnyRegister output) +- DEFINED_ON(mips_shared, loong64); ++ DEFINED_ON(mips_shared, loong64, riscv64); + + void atomicFetchOpJS(Scalar::Type arrayType, const Synchronization& sync, + AtomicOp op, Register value, const BaseIndex& mem, + Register valueTemp, Register offsetTemp, + Register maskTemp, Register temp, AnyRegister output) +- DEFINED_ON(mips_shared, loong64); ++ DEFINED_ON(mips_shared, loong64, riscv64); + + void atomicEffectOpJS(Scalar::Type arrayType, const Synchronization& sync, + AtomicOp op, Register value, const Address& mem, +@@ -4290,12 +4305,14 @@ class MacroAssembler : public MacroAssemblerSpecific { + void atomicEffectOpJS(Scalar::Type arrayType, const Synchronization& sync, + AtomicOp op, Register value, const Address& mem, + Register valueTemp, Register offsetTemp, +- Register maskTemp) DEFINED_ON(mips_shared, loong64); ++ Register maskTemp) ++ DEFINED_ON(mips_shared, loong64, riscv64); + + void atomicEffectOpJS(Scalar::Type arrayType, const Synchronization& sync, + AtomicOp op, Register value, const BaseIndex& mem, + Register valueTemp, Register offsetTemp, +- Register maskTemp) DEFINED_ON(mips_shared, loong64); ++ Register maskTemp) ++ DEFINED_ON(mips_shared, loong64, riscv64); + + void atomicIsLockFreeJS(Register value, Register output); + +@@ -4999,7 +5016,7 @@ class MacroAssembler : public MacroAssemblerSpecific { + inline void addStackPtrTo(T t); + + void subFromStackPtr(Imm32 imm32) +- DEFINED_ON(mips32, mips64, loong64, arm, x86, x64); ++ DEFINED_ON(mips32, mips64, loong64, arm, x86, x64, riscv64); + void subFromStackPtr(Register reg); + + template +diff --git a/js/src/jit/MoveEmitter.h b/js/src/jit/MoveEmitter.h +index a51cbc100a..bfad15ab9f 100644 +--- a/js/src/jit/MoveEmitter.h ++++ b/js/src/jit/MoveEmitter.h +@@ -19,6 +19,8 @@ + # include "jit/mips64/MoveEmitter-mips64.h" + #elif defined(JS_CODEGEN_LOONG64) + # include "jit/loong64/MoveEmitter-loong64.h" ++#elif defined(JS_CODEGEN_RISCV64) ++# include "jit/riscv64/MoveEmitter-riscv64.h" + #elif defined(JS_CODEGEN_NONE) + # include "jit/none/MoveEmitter-none.h" + #else +diff --git a/js/src/jit/RegisterAllocator.h b/js/src/jit/RegisterAllocator.h +index 49a3de0ab8..39472646a6 100644 +--- a/js/src/jit/RegisterAllocator.h ++++ b/js/src/jit/RegisterAllocator.h +@@ -304,9 +304,10 @@ class RegisterAllocator { + public: + template + static void takeWasmRegisters(TakeableSet& regs) { +-#if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM) || \ +- defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_MIPS32) || \ +- defined(JS_CODEGEN_MIPS64) || defined(JS_CODEGEN_LOONG64) ++#if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM) || \ ++ defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_MIPS32) || \ ++ defined(JS_CODEGEN_MIPS64) || defined(JS_CODEGEN_LOONG64) || \ ++ defined(JS_CODEGEN_RISCV64) + regs.take(HeapReg); + #endif + MOZ_ASSERT(!regs.has(FramePointer)); +diff --git a/js/src/jit/RegisterSets.h b/js/src/jit/RegisterSets.h +index 4212a2d682..9ffc48f651 100644 +--- a/js/src/jit/RegisterSets.h ++++ b/js/src/jit/RegisterSets.h +@@ -1318,9 +1318,8 @@ inline LiveGeneralRegisterSet SavedNonVolatileRegisters( + result.add(Register::FromCode(Registers::lr)); + #elif defined(JS_CODEGEN_ARM64) + result.add(Register::FromCode(Registers::lr)); +-#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) +- result.add(Register::FromCode(Registers::ra)); +-#elif defined(JS_CODEGEN_LOONG64) ++#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) || \ ++ defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) + result.add(Register::FromCode(Registers::ra)); + #endif + +diff --git a/js/src/jit/Registers.h b/js/src/jit/Registers.h +index 2c1cec0771..574a84e26b 100644 +--- a/js/src/jit/Registers.h ++++ b/js/src/jit/Registers.h +@@ -22,6 +22,8 @@ + # include "jit/mips64/Architecture-mips64.h" + #elif defined(JS_CODEGEN_LOONG64) + # include "jit/loong64/Architecture-loong64.h" ++#elif defined(JS_CODEGEN_RISCV64) ++# include "jit/riscv64/Architecture-riscv64.h" + #elif defined(JS_CODEGEN_NONE) + # include "jit/none/Architecture-none.h" + #else +diff --git a/js/src/jit/SharedICHelpers-inl.h b/js/src/jit/SharedICHelpers-inl.h +index 60a77956f0..7969ec4988 100644 +--- a/js/src/jit/SharedICHelpers-inl.h ++++ b/js/src/jit/SharedICHelpers-inl.h +@@ -19,6 +19,8 @@ + # include "jit/mips-shared/SharedICHelpers-mips-shared-inl.h" + #elif defined(JS_CODEGEN_LOONG64) + # include "jit/loong64/SharedICHelpers-loong64-inl.h" ++#elif defined(JS_CODEGEN_RISCV64) ++# include "jit/riscv64/SharedICHelpers-riscv64-inl.h" + #elif defined(JS_CODEGEN_NONE) + # include "jit/none/SharedICHelpers-none-inl.h" + #else +diff --git a/js/src/jit/SharedICHelpers.h b/js/src/jit/SharedICHelpers.h +index da8378ebae..c671f6f98f 100644 +--- a/js/src/jit/SharedICHelpers.h ++++ b/js/src/jit/SharedICHelpers.h +@@ -19,6 +19,8 @@ + # include "jit/mips-shared/SharedICHelpers-mips-shared.h" + #elif defined(JS_CODEGEN_LOONG64) + # include "jit/loong64/SharedICHelpers-loong64.h" ++#elif defined(JS_CODEGEN_RISCV64) ++# include "jit/riscv64/SharedICHelpers-riscv64.h" + #elif defined(JS_CODEGEN_NONE) + # include "jit/none/SharedICHelpers-none.h" + #else +diff --git a/js/src/jit/SharedICRegisters.h b/js/src/jit/SharedICRegisters.h +index e29f21c28d..9d01f25230 100644 +--- a/js/src/jit/SharedICRegisters.h ++++ b/js/src/jit/SharedICRegisters.h +@@ -21,6 +21,8 @@ + # include "jit/mips64/SharedICRegisters-mips64.h" + #elif defined(JS_CODEGEN_LOONG64) + # include "jit/loong64/SharedICRegisters-loong64.h" ++#elif defined(JS_CODEGEN_RISCV64) ++# include "jit/riscv64/SharedICRegisters-riscv64.h" + #elif defined(JS_CODEGEN_NONE) + # include "jit/none/SharedICRegisters-none.h" + #else +diff --git a/js/src/jit/Simulator.h b/js/src/jit/Simulator.h +index babe2b9c19..18b98662f6 100644 +--- a/js/src/jit/Simulator.h ++++ b/js/src/jit/Simulator.h +@@ -17,6 +17,8 @@ + # include "jit/mips64/Simulator-mips64.h" + #elif defined(JS_SIMULATOR_LOONG64) + # include "jit/loong64/Simulator-loong64.h" ++#elif defined(JS_SIMULATOR_RISCV64) ++# include "jit/riscv64/Simulator-riscv64.h" + #elif defined(JS_SIMULATOR) + # error "Unexpected simulator platform" + #endif +diff --git a/js/src/jit/moz.build b/js/src/jit/moz.build +index 4f2765c060..c9f75d057e 100644 +--- a/js/src/jit/moz.build ++++ b/js/src/jit/moz.build +@@ -233,6 +233,31 @@ elif CONFIG["JS_CODEGEN_LOONG64"]: + if CONFIG["JS_SIMULATOR_LOONG64"]: + UNIFIED_SOURCES += ["loong64/Simulator-loong64.cpp"] + ++elif CONFIG["JS_CODEGEN_RISCV64"]: ++ UNIFIED_SOURCES += [ ++ "riscv64/Architecture-riscv64.cpp", ++ "riscv64/Assembler-riscv64.cpp", ++ "riscv64/AssemblerMatInt.cpp.cpp", ++ "riscv64/CodeGenerator-riscv64.cpp", ++ "riscv64/constant/Base-constant-riscv.cpp", ++ "riscv64/disasm/Disasm-riscv64.cpp", ++ "riscv64/extension/base-assembler-riscv.cc", ++ "riscv64/extension/base-riscv-i.cc", ++ "riscv64/extension/extension-riscv-a.cc", ++ "riscv64/extension/extension-riscv-c.cc", ++ "riscv64/extension/extension-riscv-d.cc", ++ "riscv64/extension/extension-riscv-f.cc", ++ "riscv64/extension/extension-riscv-m.cc", ++ "riscv64/extension/extension-riscv-v.cc", ++ "riscv64/extension/extension-riscv-zicsr.cc", ++ "riscv64/extension/extension-riscv-zifencei.cc", ++ "riscv64/Lowering-riscv64.cpp", ++ "riscv64/MacroAssembler-riscv64.cpp", ++ "riscv64/MoveEmitter-riscv64.cpp", ++ "riscv64/Trampoline-riscv64.cpp", ++ ] ++ if CONFIG["JS_SIMULATOR_RISCV64"]: ++ UNIFIED_SOURCES += ["riscv64/Simulator-riscv64.cpp"] + + # Generate jit/MIROpsGenerated.h from jit/MIROps.yaml + GeneratedFile( +diff --git a/js/src/jit/riscv64/Architecture-riscv64.cpp b/js/src/jit/riscv64/Architecture-riscv64.cpp +new file mode 100644 +index 0000000000..ea4a364b92 +--- /dev/null ++++ b/js/src/jit/riscv64/Architecture-riscv64.cpp +@@ -0,0 +1,100 @@ ++/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- ++ * vim: set ts=8 sts=2 et sw=2 tw=80: ++ * 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/riscv64/Architecture-riscv64.h" ++ ++#include "jit/FlushICache.h" // js::jit::FlushICache ++#include "jit/RegisterSets.h" ++#include "jit/Simulator.h" ++namespace js { ++namespace jit { ++Registers::Code Registers::FromName(const char* name) { ++ for (size_t i = 0; i < Total; i++) { ++ if (strcmp(GetName(i), name) == 0) { ++ return Code(i); ++ } ++ } ++ ++ return Invalid; ++} ++ ++FloatRegisters::Code FloatRegisters::FromName(const char* name) { ++ for (size_t i = 0; i < Total; i++) { ++ if (strcmp(GetName(i), name) == 0) { ++ return Code(i); ++ } ++ } ++ ++ return Invalid; ++} ++ ++FloatRegisterSet FloatRegister::ReduceSetForPush(const FloatRegisterSet& s) { ++#ifdef ENABLE_WASM_SIMD ++# error "Needs more careful logic if SIMD is enabled" ++#endif ++ ++ 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(); ++} ++ ++FloatRegister FloatRegister::singleOverlay() const { ++ MOZ_ASSERT(!isInvalid()); ++ if (kind_ == Codes::Double) { ++ return FloatRegister(encoding_, Codes::Single); ++ } ++ return *this; ++} ++ ++FloatRegister FloatRegister::doubleOverlay() const { ++ MOZ_ASSERT(!isInvalid()); ++ if (kind_ != Codes::Double) { ++ return FloatRegister(encoding_, Codes::Double); ++ } ++ return *this; ++} ++ ++uint32_t FloatRegister::GetPushSizeInBytes( ++ const TypedRegisterSet& s) { ++#ifdef ENABLE_WASM_SIMD ++# error "Needs more careful logic if SIMD is enabled" ++#endif ++ ++ return s.size() * sizeof(double); ++} ++void FlushICache(void* code, size_t size) { ++#if defined(JS_SIMULATOR) ++ js::jit::SimulatorProcess::FlushICache(code, size); ++ ++#elif defined(__linux__) ++# if defined(__GNUC__) ++ intptr_t end = reinterpret_cast(code) + size; ++ __builtin___clear_cache(reinterpret_cast(code), ++ reinterpret_cast(end)); ++ ++# else ++ _flush_cache(reinterpret_cast(code), size, BCACHE); ++# endif ++#else ++# error "Unsupported platform" ++#endif ++} ++ ++bool CPUFlagsHaveBeenComputed() { ++ // TODO Add CPU flags support ++ // Flags were computed above. ++ return true; ++} ++ ++} // namespace jit ++} // namespace js +diff --git a/js/src/jit/riscv64/Architecture-riscv64.h b/js/src/jit/riscv64/Architecture-riscv64.h +new file mode 100644 +index 0000000000..e53273f2e2 +--- /dev/null ++++ b/js/src/jit/riscv64/Architecture-riscv64.h +@@ -0,0 +1,513 @@ ++/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- ++ * vim: set ts=8 sts=2 et sw=2 tw=80: ++ * 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_riscv64_Architecture_riscv64_h ++#define jit_riscv64_Architecture_riscv64_h ++ ++// JitSpewer.h is included through MacroAssembler implementations for other ++// platforms, so include it here to avoid inadvertent build bustage. ++#include "mozilla/MathAlgorithms.h" ++ ++#include ++#include ++ ++#include "jit/JitSpewer.h" ++#include "jit/shared/Architecture-shared.h" ++#include "js/Utility.h" ++ ++namespace js { ++namespace jit { ++ ++static const uint32_t SimdMemoryAlignment = ++ 16; // Make it 4 to avoid a bunch of div-by-zero warnings ++ ++// RISCV64 has 32 64-bit integer registers, x0 though x31. ++// The program counter is not accessible as a register. ++ ++// RISCV INT Register Convention: ++// Name Alias Usage ++// x0 zero hardwired to 0, ignores writes ++// x1 ra return address for calls ++// x2 sp stack pointer ++// x3 gp global pointer ++// x4 tp thread pointer ++// x5-x7 t0-t2 temporary register 0 ++// x8 fp/s0 Callee-saved register 0 or frame pointer ++// x9 s1 Callee-saved register 1 ++// x10-x11 a0-a1 return value or function argument ++// x12-x17 a2-a7 function argument 2 ++// x18-x27 s2-s11 Callee-saved register ++// x28-x31 t3-t6 temporary register 3 ++ ++// RISCV-64 FP Register Convention: ++// Name Alias Usage ++// $f0-$f7 $ft0-$ft7 Temporary registers ++// $f8-$f9 $fs0-$fs1 Callee-saved registers ++// $f10-$f11 $fa0-$fa1 Return values ++// $f12-$f17 $fa2-$fa7 Args values ++// $f18-$f27 $fs2-$fs11 Callee-saved registers ++// $f28-$f31 $ft8-$ft11 Temporary registers ++class Registers { ++ public: ++ enum RegisterID { ++ x0 = 0, ++ x1, ++ x2, ++ x3, ++ x4, ++ x5, ++ x6, ++ x7, ++ x8, ++ x9, ++ x10, ++ x11, ++ x12, ++ x13, ++ x14, ++ x15, ++ x16, ++ x17, ++ x18, ++ x19, ++ x20, ++ x21, ++ x22, ++ x23, ++ x24, ++ x25, ++ x26, ++ x27, ++ x28, ++ x29, ++ x30, ++ x31, ++ zero = x0, ++ ra = x1, ++ sp = x2, ++ gp = x3, ++ tp = x4, ++ t0 = x5, ++ t1 = x6, ++ t2 = x7, ++ fp = x8, ++ s1 = x9, ++ a0 = x10, ++ a1 = x11, ++ a2 = x12, ++ a3 = x13, ++ a4 = x14, ++ a5 = x15, ++ a6 = x16, ++ a7 = x17, ++ s2 = x18, ++ s3 = x19, ++ s4 = x20, ++ s5 = x21, ++ s6 = x22, ++ s7 = x23, ++ s8 = x24, ++ s9 = x25, ++ s10 = x26, ++ s11 = x27, ++ t3 = x28, ++ t4 = x29, ++ t5 = x30, ++ t6 = x31, ++ invalid_reg, ++ }; ++ typedef uint8_t Code; ++ typedef RegisterID Encoding; ++ union RegisterContent { ++ uintptr_t r; ++ }; ++ ++ typedef uint32_t SetType; ++ ++ static uint32_t SetSize(SetType x) { ++ static_assert(sizeof(SetType) == 4, "SetType must be 32 bits"); ++ return mozilla::CountPopulation32(x); ++ } ++ static uint32_t FirstBit(SetType x) { ++ return mozilla::CountTrailingZeroes32(x); ++ } ++ static uint32_t LastBit(SetType x) { ++ return 31 - mozilla::CountLeadingZeroes32(x); ++ } ++ static const char* GetName(uint32_t code) { ++ static const char* const Names[] = { ++ "zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2", "fp", "s1", "a0", ++ "a1", "a2", "a3", "a4", "a5", "a6", "a7", "s2", "s3", "s4", "s5", ++ "s6", "s7", "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6"}; ++ static_assert(Total == std::size(Names), "Table is the correct size"); ++ if (code >= Total) { ++ return "invalid"; ++ } ++ return Names[code]; ++ } ++ ++ static Code FromName(const char*); ++ ++ static const Encoding StackPointer = sp; ++ static const Encoding Invalid = invalid_reg; ++ static const uint32_t Total = 32; ++ static const uint32_t TotalPhys = 32; ++ static const uint32_t Allocatable = 24; ++ static const SetType NoneMask = 0x0; ++ static const SetType AllMask = 0xFFFFFFFF; ++ static const SetType ArgRegMask = ++ (1 << Registers::a0) | (1 << Registers::a1) | (1 << Registers::a2) | ++ (1 << Registers::a3) | (1 << Registers::a4) | (1 << Registers::a5) | ++ (1 << Registers::a6) | (1 << Registers::a7); ++ ++ static const SetType VolatileMask = ++ ArgRegMask | (1 << Registers::t0) | (1 << Registers::t1) | ++ (1 << Registers::t2) | (1 << Registers::t3) | (1 << Registers::t4) | ++ (1 << Registers::t5) | (1 << Registers::t6); ++ ++ // We use this constant to save registers when entering functions. This ++ // is why $ra is added here even though it is not "Non Volatile". ++ static const SetType NonVolatileMask = ++ (1 << Registers::ra) | (1 << Registers::fp) | (1 << Registers::s1) | ++ (1 << Registers::s2) | (1 << Registers::s3) | (1 << Registers::s4) | ++ (1 << Registers::s5) | (1 << Registers::s6) | (1 << Registers::s7) | ++ (1 << Registers::s8) | (1 << Registers::s9) | (1 << Registers::s10) | ++ (1 << Registers::s11); ++ ++ static const SetType NonAllocatableMask = ++ (1 << Registers::zero) | // Always be zero. ++ (1 << Registers::t4) | // Scratch reg ++ (1 << Registers::t5) | // Scratch reg ++ (1 << Registers::t6) | // Scratch reg or call reg ++ (1 << Registers::s11) | // Scratch reg ++ (1 << Registers::ra) | (1 << Registers::tp) | (1 << Registers::sp) | ++ (1 << Registers::fp) | (1 << Registers::gp); ++ ++ static const SetType AllocatableMask = AllMask & ~NonAllocatableMask; ++ ++ // Registers returned from a JS -> JS call. ++ static const SetType JSCallMask = (1 << Registers::a2); ++ ++ // Registers returned from a JS -> C call. ++ static const SetType CallMask = (1 << Registers::a0); ++ ++ static const SetType WrapperMask = VolatileMask; ++}; ++ ++// Smallest integer type that can hold a register bitmask. ++typedef uint32_t PackedRegisterMask; ++ ++class FloatRegisters { ++ public: ++ enum FPRegisterID { ++ f0 = 0, ++ f1, ++ f2, ++ f3, ++ f4, ++ f5, ++ f6, ++ f7, ++ f8, ++ f9, ++ f10, ++ f11, ++ f12, ++ f13, ++ f14, ++ f15, ++ f16, ++ f17, ++ f18, ++ f19, ++ f20, ++ f21, ++ f22, ++ f23, ++ f24, ++ f25, ++ f26, ++ f27, ++ f28, ++ f29, ++ f30, ++ f31, ++ invalid_reg, ++ ft0 = f0, ++ ft1 = f1, ++ ft2 = f2, ++ ft3 = f3, ++ ft4 = f4, ++ ft5 = f5, ++ ft6 = f6, ++ ft7 = f7, ++ fs0 = f8, ++ fs1 = f9, ++ fa0 = f10, ++ fa1 = f11, ++ fa2 = f12, ++ fa3 = f13, ++ fa4 = f14, ++ fa5 = f15, ++ fa6 = f16, ++ fa7 = f17, ++ fs2 = f18, ++ fs3 = f19, ++ fs4 = f20, ++ fs5 = f21, ++ fs6 = f22, ++ fs7 = f23, ++ fs8 = f24, ++ fs9 = f25, ++ fs10 = f26, ++ fs11 = f27, // Scratch register ++ ft8 = f28, ++ ft9 = f29, ++ ft10 = f30, // Scratch register ++ ft11 = f31 ++ }; ++ ++ enum Kind : uint8_t { Double, NumTypes, Single }; ++ ++ typedef FPRegisterID Code; ++ typedef FPRegisterID Encoding; ++ union RegisterContent { ++ float s; ++ double d; ++ }; ++ ++ static const char* GetName(uint32_t code) { ++ static const char* const Names[] = { ++ "ft0", "ft1", "ft2", "ft3", "ft4", "ft5", "ft6", "ft7", ++ "fs0", "fs2", "fa0", "fa1", "fa2", "fa3", "fa4", "fa5", ++ "fa6", "fa7", "fs2", "fs3", "fs4", "fs5", "fs6", "fs7", ++ "fs8", "fs9", "fs10", "fs11", "ft8", "ft9", "ft10", "ft11"}; ++ static_assert(TotalPhys == std::size(Names), "Table is the correct size"); ++ if (code >= Total) { ++ return "invalid"; ++ } ++ return Names[code]; ++ } ++ ++ static Code FromName(const char* name); ++ ++ typedef uint32_t SetType; ++ ++ static const Code Invalid = invalid_reg; ++ static const uint32_t Total = 32; ++ static const uint32_t TotalPhys = 32; ++ static const uint32_t Allocatable = 23; ++ static const SetType AllPhysMask = 0xFFFFFFFF; ++ static const SetType AllMask = 0xFFFFFFFF; ++ static const SetType AllDoubleMask = AllMask; ++ // Single values are stored as 64 bits values (NaN-boxed) when pushing them to ++ // the stack, we do not require making distinctions between the 2 types, and ++ // therefore the masks are overlapping.See The RISC-V Instruction Set Manual ++ // for 14.2 NaN Boxing of Narrower Values. ++ static const SetType AllSingleMask = AllMask; ++ static const SetType NonVolatileMask = ++ SetType((1 << FloatRegisters::fs0) | (1 << FloatRegisters::fs1) | ++ (1 << FloatRegisters::fs2) | (1 << FloatRegisters::fs3) | ++ (1 << FloatRegisters::fs4) | (1 << FloatRegisters::fs5) | ++ (1 << FloatRegisters::fs6) | (1 << FloatRegisters::fs7) | ++ (1 << FloatRegisters::fs8) | (1 << FloatRegisters::fs9) | ++ (1 << FloatRegisters::fs10) | (1 << FloatRegisters::fs11)); ++ static const SetType VolatileMask = AllMask & ~NonVolatileMask; ++ ++ // fs11/ft10 is the scratch register. ++ static const SetType NonAllocatableMask = ++ SetType((1 << FloatRegisters::fs11) | (1 << FloatRegisters::ft10)); ++ ++ static const SetType AllocatableMask = AllMask & ~NonAllocatableMask; ++}; ++ ++template ++class TypedRegisterSet; ++ ++struct FloatRegister { ++ public: ++ typedef FloatRegisters Codes; ++ typedef Codes::Code Code; ++ typedef Codes::Encoding Encoding; ++ typedef Codes::SetType SetType; ++ ++ static uint32_t SetSize(SetType x) { ++ static_assert(sizeof(SetType) == 4, "SetType must be 32 bits"); ++ x &= FloatRegisters::AllPhysMask; ++ return mozilla::CountPopulation32(x); ++ } ++ ++ static uint32_t FirstBit(SetType x) { ++ static_assert(sizeof(SetType) == 4, "SetType"); ++ return mozilla::CountTrailingZeroes64(x); ++ } ++ static uint32_t LastBit(SetType x) { ++ static_assert(sizeof(SetType) == 4, "SetType"); ++ return 31 - mozilla::CountLeadingZeroes64(x); ++ } ++ ++ static FloatRegister FromCode(uint32_t i) { ++ uint32_t code = i & 0x1f; ++ return FloatRegister(Code(code)); ++ } ++ bool isSimd128() const { return false; } ++ bool isInvalid() const { return invalid_; } ++ FloatRegister asSingle() const { ++ MOZ_ASSERT(!invalid_); ++ return FloatRegister(Encoding(encoding_), FloatRegisters::Single); ++ } ++ FloatRegister asDouble() const { ++ MOZ_ASSERT(!invalid_); ++ return FloatRegister(Encoding(encoding_), FloatRegisters::Double); ++ } ++ FloatRegister asSimd128() const { MOZ_CRASH(); } ++ constexpr Code code() const { ++ MOZ_ASSERT(!invalid_); ++ return encoding_; ++ } ++ Encoding encoding() const { return encoding_; } ++ const char* name() const { return FloatRegisters::GetName(code()); } ++ bool volatile_() const { ++ MOZ_ASSERT(!invalid_); ++ return !!((SetType(1) << code()) & FloatRegisters::VolatileMask); ++ } ++ bool operator!=(FloatRegister other) const { return code() != other.code(); } ++ bool operator==(FloatRegister other) const { return code() == other.code(); } ++ bool aliases(FloatRegister other) const { ++ return other.encoding_ == encoding_; ++ } ++ uint32_t numAliased() const { return 1; } ++ FloatRegister aliased(uint32_t aliasIdx) const { ++ MOZ_ASSERT(aliasIdx == 0); ++ return *this; ++ } ++ // Ensure that two floating point registers' types are equivalent. ++ bool equiv(FloatRegister other) const { ++ MOZ_ASSERT(!invalid_); ++ return kind_ == other.kind_; ++ } ++ constexpr uint32_t size() const { ++ MOZ_ASSERT(!invalid_); ++ if (kind_ == FloatRegisters::Double) { ++ return sizeof(double); ++ } ++ MOZ_ASSERT(kind_ == FloatRegisters::Single); ++ return sizeof(float); ++ } ++ uint32_t numAlignedAliased() { return numAliased(); } ++ FloatRegister alignedAliased(uint32_t aliasIdx) { ++ MOZ_ASSERT(aliasIdx < numAliased()); ++ return aliased(aliasIdx); ++ } ++ SetType alignedOrDominatedAliasedSet() const { return SetType(1) << code(); } ++ static constexpr RegTypeName DefaultType = RegTypeName::Float64; ++ ++ template ++ static SetType LiveAsIndexableSet(SetType s) { ++ return SetType(0); ++ } ++ ++ template ++ static SetType AllocatableAsIndexableSet(SetType s) { ++ static_assert(Name != RegTypeName::Any, "Allocatable set are not iterable"); ++ printf("AllocatableAsIndexableSet\n"); ++ return LiveAsIndexableSet(s); ++ } ++ ++ FloatRegister singleOverlay() const; ++ FloatRegister doubleOverlay() const; ++ ++ static TypedRegisterSet ReduceSetForPush( ++ const TypedRegisterSet& s); ++ ++ uint32_t getRegisterDumpOffsetInBytes() { ++#ifdef ENABLE_WASM_SIMD ++# error "Needs more careful logic if SIMD is enabled" ++#endif ++ ++ return code() * sizeof(double); ++ } ++ static Code FromName(const char* name); ++ ++ // This is used in static initializers, so produce a bogus value instead of ++ // crashing. ++ static uint32_t GetPushSizeInBytes(const TypedRegisterSet& s); ++ ++ private: ++ typedef Codes::Kind Kind; ++ // These fields only hold valid values: an invalid register is always ++ // represented as a valid encoding and kind with the invalid_ bit set. ++ Encoding encoding_; // 32 encodings ++ Kind kind_; // Double, Single; more later ++ bool invalid_; ++ ++ public: ++ constexpr FloatRegister(Encoding encoding, Kind kind) ++ : encoding_(encoding), kind_(kind), invalid_(false) { ++ MOZ_ASSERT(uint32_t(encoding) < Codes::Total); ++ } ++ ++ constexpr FloatRegister(Encoding encoding) ++ : encoding_(encoding), kind_(FloatRegisters::Double), invalid_(false) { ++ MOZ_ASSERT(uint32_t(encoding) < Codes::Total); ++ } ++ ++ constexpr FloatRegister() ++ : encoding_(FloatRegisters::invalid_reg), ++ kind_(FloatRegisters::Double), ++ invalid_(true) {} ++ ++ bool isSingle() const { ++ MOZ_ASSERT(!invalid_); ++ return kind_ == FloatRegisters::Single; ++ } ++ bool isDouble() const { ++ MOZ_ASSERT(!invalid_); ++ return kind_ == FloatRegisters::Double; ++ } ++ ++ Encoding code() { return encoding_; } ++}; ++ ++template <> ++inline FloatRegister::SetType ++FloatRegister::LiveAsIndexableSet(SetType set) { ++ return set & FloatRegisters::AllSingleMask; ++} ++ ++template <> ++inline FloatRegister::SetType ++FloatRegister::LiveAsIndexableSet(SetType set) { ++ return set & FloatRegisters::AllDoubleMask; ++} ++ ++template <> ++inline FloatRegister::SetType ++FloatRegister::LiveAsIndexableSet(SetType set) { ++ return set; ++} ++ ++inline bool hasUnaliasedDouble() { return false; } ++inline bool hasMultiAlias() { return false; } ++ ++static const uint32_t ShadowStackSpace = 0; ++static const uint32_t JumpImmediateRange = INT32_MAX; ++ ++#ifdef JS_NUNBOX32 ++static const int32_t NUNBOX32_TYPE_OFFSET = 4; ++static const int32_t NUNBOX32_PAYLOAD_OFFSET = 0; ++#endif ++ ++static const uint32_t SpillSlotSize = ++ std::max(sizeof(Registers::RegisterContent), ++ sizeof(FloatRegisters::RegisterContent)); ++ ++inline uint32_t GetRISCV64Flags() { return 0; } ++ ++} // namespace jit ++} // namespace js ++ ++#endif /* jit_riscv64_Architecture_riscv64_h */ +diff --git a/js/src/jit/riscv64/Assembler-riscv64.cpp b/js/src/jit/riscv64/Assembler-riscv64.cpp +new file mode 100644 +index 0000000000..b09af2bac1 +--- /dev/null ++++ b/js/src/jit/riscv64/Assembler-riscv64.cpp +@@ -0,0 +1,1546 @@ ++/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- ++ * vim: set ts=8 sts=2 et sw=2 tw=80: ++ * 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/. */ ++ ++// Copyright (c) 1994-2006 Sun Microsystems Inc. ++// All Rights Reserved. ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions are ++// met: ++// ++// - Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// ++// - Redistribution in binary form must reproduce the above copyright ++// notice, this list of conditions and the following disclaimer in the ++// documentation and/or other materials provided with the distribution. ++// ++// - Neither the name of Sun Microsystems or the names of contributors may ++// be used to endorse or promote products derived from this software without ++// specific prior written permission. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ ++// The original source code covered by the above license above has been ++// modified significantly by Google Inc. ++// Copyright 2021 the V8 project authors. All rights reserved. ++#include "jit/riscv64/Assembler-riscv64.h" ++ ++#include "mozilla/DebugOnly.h" ++#include "mozilla/Maybe.h" ++ ++#include "gc/Marking.h" ++#include "jit/AutoWritableJitCode.h" ++#include "jit/ExecutableAllocator.h" ++#include "jit/riscv64/disasm/Disasm-riscv64.h" ++#include "vm/Realm.h" ++ ++using mozilla::DebugOnly; ++namespace js { ++namespace jit { ++ ++#define UNIMPLEMENTED_RISCV() MOZ_CRASH("RISC_V not implemented"); ++ ++bool Assembler::FLAG_riscv_debug = false; ++ ++void Assembler::nop() { addi(ToRegister(0), ToRegister(0), 0); } ++ ++// Size of the instruction stream, in bytes. ++size_t Assembler::size() const { return m_buffer.size(); } ++ ++bool Assembler::swapBuffer(wasm::Bytes& bytes) { ++ // For now, specialize to the one use case. As long as wasm::Bytes is a ++ // Vector, not a linked-list of chunks, there's not much we can do other ++ // than copy. ++ MOZ_ASSERT(bytes.empty()); ++ if (!bytes.resize(bytesNeeded())) { ++ return false; ++ } ++ m_buffer.executableCopy(bytes.begin()); ++ return true; ++} ++ ++// Size of the relocation table, in bytes. ++size_t Assembler::jumpRelocationTableBytes() const { ++ return jumpRelocations_.length(); ++} ++ ++size_t Assembler::dataRelocationTableBytes() const { ++ return dataRelocations_.length(); ++} ++// Size of the data table, in bytes. ++size_t Assembler::bytesNeeded() const { ++ return size() + jumpRelocationTableBytes() + dataRelocationTableBytes(); ++} ++ ++void Assembler::executableCopy(uint8_t* buffer) { ++ MOZ_ASSERT(isFinished); ++ m_buffer.executableCopy(buffer); ++} ++ ++uint32_t Assembler::AsmPoolMaxOffset = 1024; ++ ++uint32_t Assembler::GetPoolMaxOffset() { ++ static bool isSet = false; ++ if (!isSet) { ++ char* poolMaxOffsetStr = getenv("ASM_POOL_MAX_OFFSET"); ++ uint32_t poolMaxOffset; ++ if (poolMaxOffsetStr && ++ sscanf(poolMaxOffsetStr, "%u", &poolMaxOffset) == 1) { ++ AsmPoolMaxOffset = poolMaxOffset; ++ } ++ isSet = true; ++ } ++ return AsmPoolMaxOffset; ++} ++ ++// Pool callbacks stuff: ++void Assembler::InsertIndexIntoTag(uint8_t* load_, uint32_t index) { ++ MOZ_CRASH("Unimplement"); ++} ++ ++void Assembler::PatchConstantPoolLoad(void* loadAddr, void* constPoolAddr) { ++ MOZ_CRASH("Unimplement"); ++} ++ ++void Assembler::processCodeLabels(uint8_t* rawCode) { ++ for (const CodeLabel& label : codeLabels_) { ++ Bind(rawCode, label); ++ } ++} ++ ++void Assembler::WritePoolGuard(BufferOffset branch, Instruction* dest, ++ BufferOffset afterPool) { ++ int32_t off = afterPool.getOffset() - branch.getOffset(); ++ if (!is_int21(off) || !((off & 0x1) == 0)) { ++ printf("%d\n", off); ++ MOZ_CRASH("imm invalid"); ++ } ++ // JAL encode is ++ // 31 | 30 21 | 20 | 19 12 | 11 7 | 6 0 | ++ // imm[20] | imm[10:1] | imm[11] | imm[19:12] | rd | opcode| ++ // 1 10 1 8 5 7 ++ // offset[20:1] dest JAL ++ int32_t imm20 = (off & 0xff000) | // bits 19-12 ++ ((off & 0x800) << 9) | // bit 11 ++ ((off & 0x7fe) << 20) | // bits 10-1 ++ ((off & 0x100000) << 11); // bit 20 ++ Instr instr = JAL | (imm20 & kImm20Mask); ++ dest->SetInstructionBits(instr); ++ DEBUG_PRINTF("%p(%x): ", dest, branch.getOffset()); ++ disassembleInstr(dest->InstructionBits(), JitSpew_Codegen); ++} ++ ++void Assembler::WritePoolHeader(uint8_t* start, Pool* p, bool isNatural) { ++ static_assert(sizeof(PoolHeader) == 4); ++ ++ // Get the total size of the pool. ++ const uintptr_t totalPoolSize = sizeof(PoolHeader) + p->getPoolSize(); ++ const uintptr_t totalPoolInstructions = totalPoolSize / kInstrSize; ++ ++ MOZ_ASSERT((totalPoolSize & 0x3) == 0); ++ MOZ_ASSERT(totalPoolInstructions < (1 << 15)); ++ ++ PoolHeader header(totalPoolInstructions, isNatural); ++ *(PoolHeader*)start = header; ++} ++ ++void Assembler::copyJumpRelocationTable(uint8_t* dest) { ++ if (jumpRelocations_.length()) { ++ memcpy(dest, jumpRelocations_.buffer(), jumpRelocations_.length()); ++ } ++} ++ ++void Assembler::copyDataRelocationTable(uint8_t* dest) { ++ if (dataRelocations_.length()) { ++ memcpy(dest, dataRelocations_.buffer(), dataRelocations_.length()); ++ } ++} ++ ++void Assembler::RV_li(Register rd, int64_t imm) { ++ UseScratchRegisterScope temps(this); ++ if (RecursiveLiCount(imm) > GeneralLiCount(imm, temps.hasAvailable())) { ++ GeneralLi(rd, imm); ++ } else { ++ RecursiveLi(rd, imm); ++ } ++} ++ ++int Assembler::RV_li_count(int64_t imm, bool is_get_temp_reg) { ++ if (RecursiveLiCount(imm) > GeneralLiCount(imm, is_get_temp_reg)) { ++ return GeneralLiCount(imm, is_get_temp_reg); ++ } else { ++ return RecursiveLiCount(imm); ++ } ++} ++ ++void Assembler::GeneralLi(Register rd, int64_t imm) { ++ // 64-bit imm is put in the register rd. ++ // In most cases the imm is 32 bit and 2 instructions are generated. If a ++ // temporary register is available, in the worst case, 6 instructions are ++ // generated for a full 64-bit immediate. If temporay register is not ++ // available the maximum will be 8 instructions. If imm is more than 32 bits ++ // and a temp register is available, imm is divided into two 32-bit parts, ++ // low_32 and up_32. Each part is built in a separate register. low_32 is ++ // built before up_32. If low_32 is negative (upper 32 bits are 1), 0xffffffff ++ // is subtracted from up_32 before up_32 is built. This compensates for 32 ++ // bits of 1's in the lower when the two registers are added. If no temp is ++ // available, the upper 32 bit is built in rd, and the lower 32 bits are ++ // devided to 3 parts (11, 11, and 10 bits). The parts are shifted and added ++ // to the upper part built in rd. ++ if (is_int32(imm + 0x800)) { ++ // 32-bit case. Maximum of 2 instructions generated ++ int64_t high_20 = ((imm + 0x800) >> 12); ++ int64_t low_12 = imm << 52 >> 52; ++ if (high_20) { ++ lui(rd, (int32_t)high_20); ++ if (low_12) { ++ addi(rd, rd, low_12); ++ } ++ } else { ++ addi(rd, zero_reg, low_12); ++ } ++ return; ++ } else { ++ UseScratchRegisterScope temps(this); ++ // 64-bit case: divide imm into two 32-bit parts, upper and lower ++ int64_t up_32 = imm >> 32; ++ int64_t low_32 = imm & 0xffffffffull; ++ Register temp_reg = rd; ++ // Check if a temporary register is available ++ if (up_32 == 0 || low_32 == 0) { ++ // No temp register is needed ++ } else { ++ BlockTrampolinePoolScope block_trampoline_pool(this, 0); ++ temp_reg = temps.hasAvailable() ? temps.Acquire() : InvalidReg; ++ } ++ if (temp_reg != InvalidReg) { ++ // keep track of hardware behavior for lower part in sim_low ++ int64_t sim_low = 0; ++ // Build lower part ++ if (low_32 != 0) { ++ int64_t high_20 = ((low_32 + 0x800) >> 12); ++ int64_t low_12 = low_32 & 0xfff; ++ if (high_20) { ++ // Adjust to 20 bits for the case of overflow ++ high_20 &= 0xfffff; ++ sim_low = ((high_20 << 12) << 32) >> 32; ++ lui(rd, (int32_t)high_20); ++ if (low_12) { ++ sim_low += (low_12 << 52 >> 52) | low_12; ++ addi(rd, rd, low_12); ++ } ++ } else { ++ sim_low = low_12; ++ ori(rd, zero_reg, low_12); ++ } ++ } ++ if (sim_low & 0x100000000) { ++ // Bit 31 is 1. Either an overflow or a negative 64 bit ++ if (up_32 == 0) { ++ // Positive number, but overflow because of the add 0x800 ++ slli(rd, rd, 32); ++ srli(rd, rd, 32); ++ return; ++ } ++ // low_32 is a negative 64 bit after the build ++ up_32 = (up_32 - 0xffffffff) & 0xffffffff; ++ } ++ if (up_32 == 0) { ++ return; ++ } ++ // Build upper part in a temporary register ++ if (low_32 == 0) { ++ // Build upper part in rd ++ temp_reg = rd; ++ } ++ int64_t high_20 = (up_32 + 0x800) >> 12; ++ int64_t low_12 = up_32 & 0xfff; ++ if (high_20) { ++ // Adjust to 20 bits for the case of overflow ++ high_20 &= 0xfffff; ++ lui(temp_reg, (int32_t)high_20); ++ if (low_12) { ++ addi(temp_reg, temp_reg, low_12); ++ } ++ } else { ++ ori(temp_reg, zero_reg, low_12); ++ } ++ // Put it at the bgining of register ++ slli(temp_reg, temp_reg, 32); ++ if (low_32 != 0) { ++ add(rd, rd, temp_reg); ++ } ++ return; ++ } ++ // No temp register. Build imm in rd. ++ // Build upper 32 bits first in rd. Divide lower 32 bits parts and add ++ // parts to the upper part by doing shift and add. ++ // First build upper part in rd. ++ int64_t high_20 = (up_32 + 0x800) >> 12; ++ int64_t low_12 = up_32 & 0xfff; ++ if (high_20) { ++ // Adjust to 20 bits for the case of overflow ++ high_20 &= 0xfffff; ++ lui(rd, (int32_t)high_20); ++ if (low_12) { ++ addi(rd, rd, low_12); ++ } ++ } else { ++ ori(rd, zero_reg, low_12); ++ } ++ // upper part already in rd. Each part to be added to rd, has maximum of 11 ++ // bits, and always starts with a 1. rd is shifted by the size of the part ++ // plus the number of zeros between the parts. Each part is added after the ++ // left shift. ++ uint32_t mask = 0x80000000; ++ int32_t shift_val = 0; ++ int32_t i; ++ for (i = 0; i < 32; i++) { ++ if ((low_32 & mask) == 0) { ++ mask >>= 1; ++ shift_val++; ++ if (i == 31) { ++ // rest is zero ++ slli(rd, rd, shift_val); ++ } ++ continue; ++ } ++ // The first 1 seen ++ int32_t part; ++ if ((i + 11) < 32) { ++ // Pick 11 bits ++ part = ((uint32_t)(low_32 << i) >> i) >> (32 - (i + 11)); ++ slli(rd, rd, shift_val + 11); ++ ori(rd, rd, part); ++ i += 10; ++ mask >>= 11; ++ } else { ++ part = (uint32_t)(low_32 << i) >> i; ++ slli(rd, rd, shift_val + (32 - i)); ++ ori(rd, rd, part); ++ break; ++ } ++ shift_val = 0; ++ } ++ } ++} ++ ++int Assembler::GeneralLiCount(int64_t imm, bool is_get_temp_reg) { ++ int count = 0; ++ // imitate Assembler::RV_li ++ if (is_int32(imm + 0x800)) { ++ // 32-bit case. Maximum of 2 instructions generated ++ int64_t high_20 = ((imm + 0x800) >> 12); ++ int64_t low_12 = imm << 52 >> 52; ++ if (high_20) { ++ count++; ++ if (low_12) { ++ count++; ++ } ++ } else { ++ count++; ++ } ++ return count; ++ } else { ++ // 64-bit case: divide imm into two 32-bit parts, upper and lower ++ int64_t up_32 = imm >> 32; ++ int64_t low_32 = imm & 0xffffffffull; ++ // Check if a temporary register is available ++ if (is_get_temp_reg) { ++ // keep track of hardware behavior for lower part in sim_low ++ int64_t sim_low = 0; ++ // Build lower part ++ if (low_32 != 0) { ++ int64_t high_20 = ((low_32 + 0x800) >> 12); ++ int64_t low_12 = low_32 & 0xfff; ++ if (high_20) { ++ // Adjust to 20 bits for the case of overflow ++ high_20 &= 0xfffff; ++ sim_low = ((high_20 << 12) << 32) >> 32; ++ count++; ++ if (low_12) { ++ sim_low += (low_12 << 52 >> 52) | low_12; ++ count++; ++ } ++ } else { ++ sim_low = low_12; ++ count++; ++ } ++ } ++ if (sim_low & 0x100000000) { ++ // Bit 31 is 1. Either an overflow or a negative 64 bit ++ if (up_32 == 0) { ++ // Positive number, but overflow because of the add 0x800 ++ count++; ++ count++; ++ return count; ++ } ++ // low_32 is a negative 64 bit after the build ++ up_32 = (up_32 - 0xffffffff) & 0xffffffff; ++ } ++ if (up_32 == 0) { ++ return count; ++ } ++ int64_t high_20 = (up_32 + 0x800) >> 12; ++ int64_t low_12 = up_32 & 0xfff; ++ if (high_20) { ++ // Adjust to 20 bits for the case of overflow ++ high_20 &= 0xfffff; ++ count++; ++ if (low_12) { ++ count++; ++ } ++ } else { ++ count++; ++ } ++ // Put it at the bgining of register ++ count++; ++ if (low_32 != 0) { ++ count++; ++ } ++ return count; ++ } ++ // No temp register. Build imm in rd. ++ // Build upper 32 bits first in rd. Divide lower 32 bits parts and add ++ // parts to the upper part by doing shift and add. ++ // First build upper part in rd. ++ int64_t high_20 = (up_32 + 0x800) >> 12; ++ int64_t low_12 = up_32 & 0xfff; ++ if (high_20) { ++ // Adjust to 20 bits for the case of overflow ++ high_20 &= 0xfffff; ++ count++; ++ if (low_12) { ++ count++; ++ } ++ } else { ++ count++; ++ } ++ // upper part already in rd. Each part to be added to rd, has maximum of 11 ++ // bits, and always starts with a 1. rd is shifted by the size of the part ++ // plus the number of zeros between the parts. Each part is added after the ++ // left shift. ++ uint32_t mask = 0x80000000; ++ int32_t i; ++ for (i = 0; i < 32; i++) { ++ if ((low_32 & mask) == 0) { ++ mask >>= 1; ++ if (i == 31) { ++ // rest is zero ++ count++; ++ } ++ continue; ++ } ++ // The first 1 seen ++ if ((i + 11) < 32) { ++ // Pick 11 bits ++ count++; ++ count++; ++ i += 10; ++ mask >>= 11; ++ } else { ++ count++; ++ count++; ++ break; ++ } ++ } ++ } ++ return count; ++} ++ ++void Assembler::li_ptr(Register rd, int64_t imm) { ++ m_buffer.enterNoNops(); ++ m_buffer.assertNoPoolAndNoNops(); ++ // Initialize rd with an address ++ // Pointers are 48 bits ++ // 6 fixed instructions are generated ++ DEBUG_PRINTF("li_ptr(%d, %lx <%ld>)\n", ToNumber(rd), imm, imm); ++ MOZ_ASSERT((imm & 0xfff0000000000000ll) == 0); ++ int64_t a6 = imm & 0x3f; // bits 0:5. 6 bits ++ int64_t b11 = (imm >> 6) & 0x7ff; // bits 6:11. 11 bits ++ int64_t high_31 = (imm >> 17) & 0x7fffffff; // 31 bits ++ int64_t high_20 = ((high_31 + 0x800) >> 12); // 19 bits ++ int64_t low_12 = high_31 & 0xfff; // 12 bits ++ lui(rd, (int32_t)high_20); ++ addi(rd, rd, low_12); // 31 bits in rd. ++ slli(rd, rd, 11); // Space for next 11 bis ++ ori(rd, rd, b11); // 11 bits are put in. 42 bit in rd ++ slli(rd, rd, 6); // Space for next 6 bits ++ ori(rd, rd, a6); // 6 bits are put in. 48 bis in rd ++ m_buffer.leaveNoNops(); ++} ++ ++void Assembler::li_constant(Register rd, int64_t imm) { ++ m_buffer.enterNoNops(); ++ m_buffer.assertNoPoolAndNoNops(); ++ DEBUG_PRINTF("li_constant(%d, %lx <%ld>)\n", ToNumber(rd), imm, imm); ++ lui(rd, (imm + (1LL << 47) + (1LL << 35) + (1LL << 23) + (1LL << 11)) >> ++ 48); // Bits 63:48 ++ addiw(rd, rd, ++ (imm + (1LL << 35) + (1LL << 23) + (1LL << 11)) << 16 >> ++ 52); // Bits 47:36 ++ slli(rd, rd, 12); ++ addi(rd, rd, (imm + (1LL << 23) + (1LL << 11)) << 28 >> 52); // Bits 35:24 ++ slli(rd, rd, 12); ++ addi(rd, rd, (imm + (1LL << 11)) << 40 >> 52); // Bits 23:12 ++ slli(rd, rd, 12); ++ addi(rd, rd, imm << 52 >> 52); // Bits 11:0 ++ m_buffer.leaveNoNops(); ++} ++ ++ABIArg ABIArgGenerator::next(MIRType type) { ++ switch (type) { ++ case MIRType::Int32: ++ case MIRType::Int64: ++ case MIRType::Pointer: ++ case MIRType::RefOrNull: ++ case MIRType::StackResults: { ++ if (intRegIndex_ == NumIntArgRegs) { ++ current_ = ABIArg(stackOffset_); ++ stackOffset_ += sizeof(uintptr_t); ++ break; ++ } ++ current_ = ABIArg(Register::FromCode(intRegIndex_ + a0.encoding())); ++ intRegIndex_++; ++ break; ++ } ++ case MIRType::Float32: ++ case MIRType::Double: { ++ if (floatRegIndex_ == NumFloatArgRegs) { ++ current_ = ABIArg(stackOffset_); ++ stackOffset_ += sizeof(double); ++ break; ++ } ++ current_ = ABIArg(FloatRegister( ++ FloatRegisters::Encoding(floatRegIndex_ + fa0.encoding()), ++ type == MIRType::Double ? FloatRegisters::Double ++ : FloatRegisters::Single)); ++ floatRegIndex_++; ++ break; ++ } ++ case MIRType::Simd128: { ++ MOZ_CRASH("RISCV64 does not support simd yet."); ++ break; ++ } ++ default: ++ MOZ_CRASH("Unexpected argument type"); ++ } ++ return current_; ++} ++ ++bool Assembler::oom() const { ++ return AssemblerShared::oom() || m_buffer.oom() || jumpRelocations_.oom() || ++ dataRelocations_.oom() || !enoughLabelCache_; ++} ++ ++int Assembler::disassembleInstr(Instr instr, bool enable_spew) { ++ if (!FLAG_riscv_debug && !enable_spew) return -1; ++ disasm::NameConverter converter; ++ disasm::Disassembler disasm(converter); ++ EmbeddedVector disasm_buffer; ++ ++ int size = ++ disasm.InstructionDecode(disasm_buffer, reinterpret_cast(&instr)); ++ DEBUG_PRINTF("%s\n", disasm_buffer.start()); ++ if (enable_spew) { ++ JitSpew(JitSpew_Codegen, "%s", disasm_buffer.start()); ++ } ++ return size; ++} ++ ++uintptr_t Assembler::target_address_at(Instruction* pc) { ++ Instruction* instr0 = pc; ++ DEBUG_PRINTF("target_address_at: pc: 0x%p\t", instr0); ++ Instruction* instr1 = pc + 1 * kInstrSize; ++ Instruction* instr2 = pc + 2 * kInstrSize; ++ Instruction* instr3 = pc + 3 * kInstrSize; ++ Instruction* instr4 = pc + 4 * kInstrSize; ++ Instruction* instr5 = pc + 5 * kInstrSize; ++ ++ // Interpret instructions for address generated by li: See listing in ++ // Assembler::set_target_address_at() just below. ++ if (IsLui(*reinterpret_cast(instr0)) && ++ IsAddi(*reinterpret_cast(instr1)) && ++ IsSlli(*reinterpret_cast(instr2)) && ++ IsOri(*reinterpret_cast(instr3)) && ++ IsSlli(*reinterpret_cast(instr4)) && ++ IsOri(*reinterpret_cast(instr5))) { ++ // Assemble the 64 bit value. ++ int64_t addr = (int64_t)(instr0->Imm20UValue() << kImm20Shift) + ++ (int64_t)instr1->Imm12Value(); ++ MOZ_ASSERT(instr2->Imm12Value() == 11); ++ addr <<= 11; ++ addr |= (int64_t)instr3->Imm12Value(); ++ MOZ_ASSERT(instr4->Imm12Value() == 6); ++ addr <<= 6; ++ addr |= (int64_t)instr5->Imm12Value(); ++ ++ DEBUG_PRINTF("addr: %lx\n", addr); ++ return static_cast(addr); ++ } ++ // We should never get here, force a bad address if we do. ++ MOZ_CRASH("RISC-V UNREACHABLE"); ++} ++ ++void Assembler::PatchDataWithValueCheck(CodeLocationLabel label, ++ ImmPtr newValue, ImmPtr expectedValue) { ++ PatchDataWithValueCheck(label, PatchedImmPtr(newValue.value), ++ PatchedImmPtr(expectedValue.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)); ++} ++ ++uint64_t Assembler::ExtractLoad64Value(Instruction* inst0) { ++ DEBUG_PRINTF("\tExtractLoad64Value: \tpc:%p ", inst0); ++ if (IsJal(*reinterpret_cast(inst0))) { ++ int offset = inst0->Imm20JValue(); ++ inst0 = inst0 + offset; ++ } ++ Instruction* instr1 = inst0 + 1 * kInstrSize; ++ if (IsAddiw(*reinterpret_cast(instr1))) { ++ // Li64 ++ Instruction* instr2 = inst0 + 2 * kInstrSize; ++ Instruction* instr3 = inst0 + 3 * kInstrSize; ++ Instruction* instr4 = inst0 + 4 * kInstrSize; ++ Instruction* instr5 = inst0 + 5 * kInstrSize; ++ Instruction* instr6 = inst0 + 6 * kInstrSize; ++ Instruction* instr7 = inst0 + 7 * kInstrSize; ++ if (IsLui(*reinterpret_cast(inst0)) && ++ IsAddiw(*reinterpret_cast(instr1)) && ++ IsSlli(*reinterpret_cast(instr2)) && ++ IsAddi(*reinterpret_cast(instr3)) && ++ IsSlli(*reinterpret_cast(instr4)) && ++ IsAddi(*reinterpret_cast(instr5)) && ++ IsSlli(*reinterpret_cast(instr6)) && ++ IsAddi(*reinterpret_cast(instr7))) { ++ int64_t imm = (int64_t)(inst0->Imm20UValue() << kImm20Shift) + ++ (int64_t)instr1->Imm12Value(); ++ MOZ_ASSERT(instr2->Imm12Value() == 12); ++ imm <<= 12; ++ imm += (int64_t)instr3->Imm12Value(); ++ MOZ_ASSERT(instr4->Imm12Value() == 12); ++ imm <<= 12; ++ imm += (int64_t)instr5->Imm12Value(); ++ MOZ_ASSERT(instr6->Imm12Value() == 12); ++ imm <<= 12; ++ imm += (int64_t)instr7->Imm12Value(); ++ DEBUG_PRINTF("imm:%lx\n", imm); ++ return imm; ++ } else { ++ FLAG_riscv_debug = true; ++ disassembleInstr(inst0->InstructionBits()); ++ disassembleInstr(instr1->InstructionBits()); ++ disassembleInstr(instr2->InstructionBits()); ++ disassembleInstr(instr3->InstructionBits()); ++ disassembleInstr(instr4->InstructionBits()); ++ disassembleInstr(instr5->InstructionBits()); ++ disassembleInstr(instr6->InstructionBits()); ++ disassembleInstr(instr7->InstructionBits()); ++ MOZ_CRASH(); ++ } ++ } else { ++ DEBUG_PRINTF("\n"); ++ Instruction* instrf1 = (inst0 - 1 * kInstrSize); ++ Instruction* instr2 = inst0 + 2 * kInstrSize; ++ Instruction* instr3 = inst0 + 3 * kInstrSize; ++ Instruction* instr4 = inst0 + 4 * kInstrSize; ++ Instruction* instr5 = inst0 + 5 * kInstrSize; ++ Instruction* instr6 = inst0 + 6 * kInstrSize; ++ Instruction* instr7 = inst0 + 7 * kInstrSize; ++ disassembleInstr(instrf1->InstructionBits()); ++ disassembleInstr(inst0->InstructionBits()); ++ disassembleInstr(instr1->InstructionBits()); ++ disassembleInstr(instr2->InstructionBits()); ++ disassembleInstr(instr3->InstructionBits()); ++ disassembleInstr(instr4->InstructionBits()); ++ disassembleInstr(instr5->InstructionBits()); ++ disassembleInstr(instr6->InstructionBits()); ++ disassembleInstr(instr7->InstructionBits()); ++ MOZ_ASSERT(IsAddi(*reinterpret_cast(instr1))); ++ // Li48 ++ return target_address_at(inst0); ++ } ++} ++ ++void Assembler::UpdateLoad64Value(Instruction* pc, uint64_t value) { ++ DEBUG_PRINTF("\tUpdateLoad64Value: pc: %p\tvalue: %lx\n", pc, value); ++ Instruction* instr1 = pc + 1 * kInstrSize; ++ if (IsJal(*reinterpret_cast(pc))) { ++ pc = pc + pc->Imm20JValue(); ++ instr1 = pc + 1 * kInstrSize; ++ } ++ if (IsAddiw(*reinterpret_cast(instr1))) { ++ Instruction* instr0 = pc; ++ Instruction* instr2 = pc + 2 * kInstrSize; ++ Instruction* instr3 = pc + 3 * kInstrSize; ++ Instruction* instr4 = pc + 4 * kInstrSize; ++ Instruction* instr5 = pc + 5 * kInstrSize; ++ Instruction* instr6 = pc + 6 * kInstrSize; ++ Instruction* instr7 = pc + 7 * kInstrSize; ++ MOZ_ASSERT(IsLui(*reinterpret_cast(pc)) && ++ IsAddiw(*reinterpret_cast(instr1)) && ++ IsSlli(*reinterpret_cast(instr2)) && ++ IsAddi(*reinterpret_cast(instr3)) && ++ IsSlli(*reinterpret_cast(instr4)) && ++ IsAddi(*reinterpret_cast(instr5)) && ++ IsSlli(*reinterpret_cast(instr6)) && ++ IsAddi(*reinterpret_cast(instr7))); ++ // lui(rd, (imm + (1LL << 47) + (1LL << 35) + (1LL << 23) + (1LL << 11)) >> ++ // 48); // Bits 63:48 ++ // addiw(rd, rd, ++ // (imm + (1LL << 35) + (1LL << 23) + (1LL << 11)) << 16 >> ++ // 52); // Bits 47:36 ++ // slli(rd, rd, 12); ++ // addi(rd, rd, (imm + (1LL << 23) + (1LL << 11)) << 28 >> 52); // Bits ++ // 35:24 slli(rd, rd, 12); addi(rd, rd, (imm + (1LL << 11)) << 40 >> 52); // ++ // Bits 23:12 slli(rd, rd, 12); addi(rd, rd, imm << 52 >> 52); // Bits 11:0 ++ *reinterpret_cast(instr0) &= 0xfff; ++ *reinterpret_cast(instr0) |= ++ (((value + (1LL << 47) + (1LL << 35) + (1LL << 23) + (1LL << 11)) >> 48) ++ << 12); ++ *reinterpret_cast(instr1) &= 0xfffff; ++ *reinterpret_cast(instr1) |= ++ (((value + (1LL << 35) + (1LL << 23) + (1LL << 11)) << 16 >> 52) << 20); ++ *reinterpret_cast(instr3) &= 0xfffff; ++ *reinterpret_cast(instr3) |= ++ (((value + (1LL << 23) + (1LL << 11)) << 28 >> 52) << 20); ++ *reinterpret_cast(instr5) &= 0xfffff; ++ *reinterpret_cast(instr5) |= ++ (((value + (1LL << 11)) << 40 >> 52) << 20); ++ *reinterpret_cast(instr7) &= 0xfffff; ++ *reinterpret_cast(instr7) |= ((value << 52 >> 52) << 20); ++ disassembleInstr(instr0->InstructionBits()); ++ disassembleInstr(instr1->InstructionBits()); ++ disassembleInstr(instr2->InstructionBits()); ++ disassembleInstr(instr3->InstructionBits()); ++ disassembleInstr(instr4->InstructionBits()); ++ disassembleInstr(instr5->InstructionBits()); ++ disassembleInstr(instr6->InstructionBits()); ++ disassembleInstr(instr7->InstructionBits()); ++ MOZ_ASSERT(ExtractLoad64Value(pc) == value); ++ } else { ++ Instruction* instr0 = pc; ++ Instruction* instr2 = pc + 2 * kInstrSize; ++ Instruction* instr3 = pc + 3 * kInstrSize; ++ Instruction* instr4 = pc + 4 * kInstrSize; ++ Instruction* instr5 = pc + 5 * kInstrSize; ++ Instruction* instr6 = pc + 6 * kInstrSize; ++ Instruction* instr7 = pc + 7 * kInstrSize; ++ disassembleInstr(instr0->InstructionBits()); ++ disassembleInstr(instr1->InstructionBits()); ++ disassembleInstr(instr2->InstructionBits()); ++ disassembleInstr(instr3->InstructionBits()); ++ disassembleInstr(instr4->InstructionBits()); ++ disassembleInstr(instr5->InstructionBits()); ++ disassembleInstr(instr6->InstructionBits()); ++ disassembleInstr(instr7->InstructionBits()); ++ MOZ_ASSERT(IsAddi(*reinterpret_cast(instr1))); ++ set_target_value_at(pc, value); ++ } ++} ++ ++void Assembler::set_target_value_at(Instruction* pc, uint64_t target) { ++ DEBUG_PRINTF("\tset_target_value_at: pc: %p\ttarget: %lx\n", pc, target); ++ uint32_t* p = reinterpret_cast(pc); ++ MOZ_ASSERT((target & 0xffff000000000000ll) == 0); ++#ifdef DEBUG ++ // Check we have the result from a li macro-instruction. ++ Instruction* instr0 = pc; ++ Instruction* instr1 = pc + 1 * kInstrSize; ++ Instruction* instr3 = pc + 3 * kInstrSize; ++ Instruction* instr5 = pc + 5 * kInstrSize; ++ MOZ_ASSERT(IsLui(*reinterpret_cast(instr0)) && ++ IsAddi(*reinterpret_cast(instr1)) && ++ IsOri(*reinterpret_cast(instr3)) && ++ IsOri(*reinterpret_cast(instr5))); ++#endif ++ int64_t a6 = target & 0x3f; // bits 0:6. 6 bits ++ int64_t b11 = (target >> 6) & 0x7ff; // bits 6:11. 11 bits ++ int64_t high_31 = (target >> 17) & 0x7fffffff; // 31 bits ++ int64_t high_20 = ((high_31 + 0x800) >> 12); // 19 bits ++ int64_t low_12 = high_31 & 0xfff; // 12 bits ++ *p = *p & 0xfff; ++ *p = *p | ((int32_t)high_20 << 12); ++ *(p + 1) = *(p + 1) & 0xfffff; ++ *(p + 1) = *(p + 1) | ((int32_t)low_12 << 20); ++ *(p + 2) = *(p + 2) & 0xfffff; ++ *(p + 2) = *(p + 2) | (11 << 20); ++ *(p + 3) = *(p + 3) & 0xfffff; ++ *(p + 3) = *(p + 3) | ((int32_t)b11 << 20); ++ *(p + 4) = *(p + 4) & 0xfffff; ++ *(p + 4) = *(p + 4) | (6 << 20); ++ *(p + 5) = *(p + 5) & 0xfffff; ++ *(p + 5) = *(p + 5) | ((int32_t)a6 << 20); ++ MOZ_ASSERT(target_address_at(pc) == target); ++} ++ ++void Assembler::WriteLoad64Instructions(Instruction* inst0, Register reg, ++ uint64_t value) { ++ DEBUG_PRINTF("\tWriteLoad64Instructions\n"); ++ // Initialize rd with an address ++ // Pointers are 48 bits ++ // 6 fixed instructions are generated ++ MOZ_ASSERT((value & 0xfff0000000000000ll) == 0); ++ int64_t a6 = value & 0x3f; // bits 0:5. 6 bits ++ int64_t b11 = (value >> 6) & 0x7ff; // bits 6:11. 11 bits ++ int64_t high_31 = (value >> 17) & 0x7fffffff; // 31 bits ++ int64_t high_20 = ((high_31 + 0x800) >> 12); // 19 bits ++ int64_t low_12 = high_31 & 0xfff; // 12 bits ++ Instr lui_ = LUI | (reg.code() << kRdShift) | ++ ((int32_t)high_20 << kImm20Shift); // lui(rd, (int32_t)high_20); ++ *reinterpret_cast(inst0) = lui_; ++ ++ Instr addi_ = ++ OP_IMM | (reg.code() << kRdShift) | (0b000 << kFunct3Shift) | ++ (reg.code() << kRs1Shift) | ++ (low_12 << kImm12Shift); // addi(rd, rd, low_12); // 31 bits in rd. ++ *reinterpret_cast(inst0 + 1 * kInstrSize) = addi_; ++ ++ Instr slli_ = ++ OP_IMM | (reg.code() << kRdShift) | (0b001 << kFunct3Shift) | ++ (reg.code() << kRs1Shift) | ++ (11 << kImm12Shift); // slli(rd, rd, 11); // Space for next 11 bis ++ *reinterpret_cast(inst0 + 2 * kInstrSize) = slli_; ++ ++ Instr ori_b11 = OP_IMM | (reg.code() << kRdShift) | (0b110 << kFunct3Shift) | ++ (reg.code() << kRs1Shift) | ++ (b11 << kImm12Shift); // ori(rd, rd, b11); // 11 bits ++ // are put in. 42 bit in rd ++ *reinterpret_cast(inst0 + 3 * kInstrSize) = ori_b11; ++ ++ slli_ = OP_IMM | (reg.code() << kRdShift) | (0b001 << kFunct3Shift) | ++ (reg.code() << kRs1Shift) | ++ (6 << kImm12Shift); // slli(rd, rd, 6); // Space for next 11 bis ++ *reinterpret_cast(inst0 + 4 * kInstrSize) = ++ slli_; // slli(rd, rd, 6); // Space for next 6 bits ++ ++ Instr ori_a6 = OP_IMM | (reg.code() << kRdShift) | (0b110 << kFunct3Shift) | ++ (reg.code() << kRs1Shift) | ++ (a6 << kImm12Shift); // ori(rd, rd, a6); // 6 bits are ++ // put in. 48 bis in rd ++ *reinterpret_cast(inst0 + 5 * kInstrSize) = ori_a6; ++ disassembleInstr((inst0 + 0 * kInstrSize)->InstructionBits()); ++ disassembleInstr((inst0 + 1 * kInstrSize)->InstructionBits()); ++ disassembleInstr((inst0 + 2 * kInstrSize)->InstructionBits()); ++ disassembleInstr((inst0 + 3 * kInstrSize)->InstructionBits()); ++ disassembleInstr((inst0 + 4 * kInstrSize)->InstructionBits()); ++ disassembleInstr((inst0 + 5 * kInstrSize)->InstructionBits()); ++ disassembleInstr((inst0 + 6 * kInstrSize)->InstructionBits()); ++ MOZ_ASSERT(ExtractLoad64Value(inst0) == value); ++} ++ ++// This just stomps over memory with 32 bits of raw data. Its purpose is to ++// overwrite the call of JITed code with 32 bits worth of an offset. This will ++// is only meant to function on code that has been invalidated, so it should ++// be totally safe. Since that instruction will never be executed again, a ++// ICache flush should not be necessary ++void Assembler::PatchWrite_Imm32(CodeLocationLabel label, Imm32 imm) { ++ // Raw is going to be the return address. ++ uint32_t* raw = (uint32_t*)label.raw(); ++ // Overwrite the 4 bytes before the return address, which will ++ // end up being the call instruction. ++ *(raw - 1) = imm.value; ++} ++ ++void Assembler::target_at_put(BufferOffset pos, BufferOffset target_pos, ++ bool trampoline) { ++ if (m_buffer.oom()) { ++ return; ++ } ++ DEBUG_PRINTF("\ttarget_at_put: %p (%d) to %p (%d)\n", ++ reinterpret_cast(editSrc(pos)), pos.getOffset(), ++ reinterpret_cast(editSrc(pos)) + target_pos.getOffset() - ++ pos.getOffset(), ++ target_pos.getOffset()); ++ Instruction* instruction = editSrc(pos); ++ Instr instr = instruction->InstructionBits(); ++ switch (instruction->InstructionOpcodeType()) { ++ case BRANCH: { ++ instr = SetBranchOffset(pos.getOffset(), target_pos.getOffset(), instr); ++ instr_at_put(pos, instr); ++ } break; ++ case JAL: { ++ MOZ_ASSERT(IsJal(instr)); ++ instr = SetJalOffset(pos.getOffset(), target_pos.getOffset(), instr); ++ instr_at_put(pos, instr); ++ } break; ++ case LUI: { ++ set_target_value_at(instruction, ++ reinterpret_cast(editSrc(target_pos))); ++ } break; ++ case AUIPC: { ++ Instr instr_auipc = instr; ++ Instr instr_I = ++ editSrc(BufferOffset(pos.getOffset() + 4))->InstructionBits(); ++ MOZ_ASSERT(IsJalr(instr_I) || IsAddi(instr_I)); ++ ++ intptr_t offset = target_pos.getOffset() - pos.getOffset(); ++ if (is_int21(offset) && IsJalr(instr_I) && trampoline) { ++ MOZ_ASSERT(is_int21(offset) && ((offset & 1) == 0)); ++ Instr instr = JAL; ++ instr = SetJalOffset(pos.getOffset(), target_pos.getOffset(), instr); ++ MOZ_ASSERT(IsJal(instr)); ++ MOZ_ASSERT(JumpOffset(instr) == offset); ++ instr_at_put(pos, instr); ++ instr_at_put(BufferOffset(pos.getOffset() + 4), kNopByte); ++ } else { ++ MOZ_RELEASE_ASSERT(is_int32(offset + 0x800)); ++ MOZ_ASSERT(instruction->RdValue() == ++ editSrc(BufferOffset(pos.getOffset() + 4))->Rs1Value()); ++ int32_t Hi20 = (((int32_t)offset + 0x800) >> 12); ++ int32_t Lo12 = (int32_t)offset << 20 >> 20; ++ ++ instr_auipc = ++ (instr_auipc & ~kImm31_12Mask) | ((Hi20 & kImm19_0Mask) << 12); ++ instr_at_put(pos, instr_auipc); ++ ++ const int kImm31_20Mask = ((1 << 12) - 1) << 20; ++ const int kImm11_0Mask = ((1 << 12) - 1); ++ instr_I = (instr_I & ~kImm31_20Mask) | ((Lo12 & kImm11_0Mask) << 20); ++ instr_at_put(BufferOffset(pos.getOffset() + 4), instr_I); ++ } ++ } break; ++ default: ++ UNIMPLEMENTED_RISCV(); ++ break; ++ } ++} ++ ++const int kEndOfChain = -1; ++const int32_t kEndOfJumpChain = 0; ++ ++int Assembler::target_at(BufferOffset pos, bool is_internal) { ++ if (oom()) { ++ return kEndOfChain; ++ } ++ Instruction* instruction = editSrc(pos); ++ Instruction* instruction2 = nullptr; ++ if (IsAuipc(instruction->InstructionBits())) { ++ instruction2 = editSrc(BufferOffset(pos.getOffset() + kInstrSize)); ++ } ++ return target_at(instruction, pos, is_internal, instruction2); ++} ++ ++int Assembler::target_at(Instruction* instruction, BufferOffset pos, ++ bool is_internal, Instruction* instruction2) { ++ DEBUG_PRINTF("\t target_at: %p(%x)\n\t", ++ reinterpret_cast(instruction), pos.getOffset()); ++ disassembleInstr(instruction->InstructionBits()); ++ Instr instr = instruction->InstructionBits(); ++ switch (instruction->InstructionOpcodeType()) { ++ case BRANCH: { ++ int32_t imm13 = BranchOffset(instr); ++ if (imm13 == kEndOfJumpChain) { ++ // EndOfChain sentinel is returned directly, not relative to pc or pos. ++ return kEndOfChain; ++ } else { ++ DEBUG_PRINTF("\t target_at: %d %d\n", imm13, pos.getOffset() + imm13); ++ return pos.getOffset() + imm13; ++ } ++ } ++ case JAL: { ++ int32_t imm21 = JumpOffset(instr); ++ if (imm21 == kEndOfJumpChain) { ++ // EndOfChain sentinel is returned directly, not relative to pc or pos. ++ return kEndOfChain; ++ } else { ++ DEBUG_PRINTF("\t target_at: %d %d\n", imm21, pos.getOffset() + imm21); ++ return pos.getOffset() + imm21; ++ } ++ } ++ case JALR: { ++ int32_t imm12 = instr >> 20; ++ if (imm12 == kEndOfJumpChain) { ++ // EndOfChain sentinel is returned directly, not relative to pc or pos. ++ return kEndOfChain; ++ } else { ++ DEBUG_PRINTF("\t target_at: %d %d\n", imm12, pos.getOffset() + imm12); ++ return pos.getOffset() + imm12; ++ } ++ } ++ case LUI: { ++ uintptr_t imm = target_address_at(instruction); ++ uintptr_t instr_address = reinterpret_cast(instruction); ++ if (imm == kEndOfJumpChain) { ++ return kEndOfChain; ++ } else { ++ MOZ_ASSERT(instr_address - imm < INT_MAX); ++ int32_t delta = static_cast(instr_address - imm); ++ MOZ_ASSERT(pos.getOffset() > delta); ++ return pos.getOffset() - delta; ++ } ++ } ++ case AUIPC: { ++ MOZ_ASSERT(instruction2 != nullptr); ++ Instr instr_auipc = instr; ++ Instr instr_I = instruction2->InstructionBits(); ++ MOZ_ASSERT(IsJalr(instr_I) || IsAddi(instr_I)); ++ int32_t offset = BrachlongOffset(instr_auipc, instr_I); ++ if (offset == kEndOfJumpChain) return kEndOfChain; ++ DEBUG_PRINTF("\t target_at: %d %d\n", offset, pos.getOffset() + offset); ++ return offset + pos.getOffset(); ++ } ++ default: { ++ UNIMPLEMENTED_RISCV(); ++ } ++ } ++} ++ ++uint32_t Assembler::next_link(Label* L, bool is_internal) { ++ MOZ_ASSERT(L->used()); ++ BufferOffset pos(L); ++ int link = target_at(pos, is_internal); ++ if (link == kEndOfChain) { ++ L->reset(); ++ return LabelBase::INVALID_OFFSET; ++ } else { ++ MOZ_ASSERT(link >= 0); ++ DEBUG_PRINTF("next: %p to offset %d\n", L, link); ++ L->use(link); ++ return link; ++ } ++} ++ ++void Assembler::bind(Label* label, BufferOffset boff) { ++ JitSpew(JitSpew_Codegen, ".set Llabel %p %d", label, currentOffset()); ++ DEBUG_PRINTF(".set Llabel %p\n", label); ++ // If our caller didn't give us an explicit target to bind to ++ // then we want to bind to the location of the next instruction ++ BufferOffset dest = boff.assigned() ? boff : nextOffset(); ++ if (label->used()) { ++ uint32_t next; ++ ++ // A used label holds a link to branch that uses it. ++ do { ++ BufferOffset b(label); ++ DEBUG_PRINTF("\tbind next:%d\n", b.getOffset()); ++ // Even a 0 offset may be invalid if we're out of memory. ++ if (oom()) { ++ return; ++ } ++ int fixup_pos = b.getOffset(); ++ int dist = dest.getOffset() - fixup_pos; ++ next = next_link(label, false); ++ DEBUG_PRINTF("\t%p fixup: %d next: %d\n", label, fixup_pos, next); ++ DEBUG_PRINTF("\t fixup: %d dest: %d dist: %d %d %d\n", fixup_pos, ++ dest.getOffset(), dist, nextOffset().getOffset(), ++ currentOffset()); ++ Instruction* instruction = editSrc(b); ++ Instr instr = instruction->InstructionBits(); ++ if (IsBranch(instr)) { ++ if (dist > kMaxBranchOffset) { ++ MOZ_ASSERT(next != LabelBase::INVALID_OFFSET); ++ MOZ_RELEASE_ASSERT((next - fixup_pos) <= kMaxBranchOffset); ++ MOZ_ASSERT(IsAuipc(editSrc(BufferOffset(next))->InstructionBits())); ++ MOZ_ASSERT( ++ IsJalr(editSrc(BufferOffset(next + 4))->InstructionBits())); ++ DEBUG_PRINTF("\t\ttrampolining: %d\n", next); ++ } else { ++ target_at_put(b, dest); ++ BufferOffset deadline(b.getOffset() + ++ ImmBranchMaxForwardOffset(CondBranchRangeType)); ++ m_buffer.unregisterBranchDeadline(CondBranchRangeType, deadline); ++ } ++ } else if (IsJal(instr)) { ++ if (dist > kMaxJumpOffset) { ++ MOZ_ASSERT(next != LabelBase::INVALID_OFFSET); ++ MOZ_RELEASE_ASSERT((next - fixup_pos) <= kMaxJumpOffset); ++ MOZ_ASSERT(IsAuipc(editSrc(BufferOffset(next))->InstructionBits())); ++ MOZ_ASSERT( ++ IsJalr(editSrc(BufferOffset(next + 4))->InstructionBits())); ++ DEBUG_PRINTF("\t\ttrampolining: %d\n", next); ++ } else { ++ target_at_put(b, dest); ++ BufferOffset deadline( ++ b.getOffset() + ImmBranchMaxForwardOffset(UncondBranchRangeType)); ++ m_buffer.unregisterBranchDeadline(UncondBranchRangeType, deadline); ++ } ++ } else { ++ MOZ_ASSERT(IsAuipc(instr)); ++ target_at_put(b, dest); ++ } ++ } while (next != LabelBase::INVALID_OFFSET); ++ } ++ label->bind(dest.getOffset()); ++} ++ ++void Assembler::Bind(uint8_t* rawCode, const CodeLabel& label) { ++ if (label.patchAt().bound()) { ++ auto mode = label.linkMode(); ++ intptr_t offset = label.patchAt().offset(); ++ intptr_t target = label.target().offset(); ++ ++ if (mode == CodeLabel::RawPointer) { ++ *reinterpret_cast(rawCode + offset) = rawCode + target; ++ } else { ++ MOZ_ASSERT(mode == CodeLabel::MoveImmediate || ++ mode == CodeLabel::JumpImmediate); ++ Instruction* inst = (Instruction*)(rawCode + offset); ++ Assembler::UpdateLoad64Value(inst, (uint64_t)(rawCode + target)); ++ } ++ } ++} ++ ++bool Assembler::is_near(Label* L) { ++ MOZ_ASSERT(L->bound()); ++ return is_intn((currentOffset() - L->offset()), kJumpOffsetBits); ++} ++ ++bool Assembler::is_near(Label* L, OffsetSize bits) { ++ if (L == nullptr || !L->bound()) return true; ++ return is_intn((currentOffset() - L->offset()), bits); ++} ++ ++bool Assembler::is_near_branch(Label* L) { ++ MOZ_ASSERT(L->bound()); ++ return is_intn((currentOffset() - L->offset()), kBranchOffsetBits); ++} ++ ++int32_t Assembler::branch_long_offset(Label* L) { ++ if (oom()) { ++ return kEndOfJumpChain; ++ } ++ intptr_t target_pos; ++ BufferOffset next_instr_offset = nextInstrOffset(2); ++ DEBUG_PRINTF("\tbranch_long_offset: %p to (%d)\n", L, ++ next_instr_offset.getOffset()); ++ if (L->bound()) { ++ JitSpew(JitSpew_Codegen, ".use Llabel %p on %d", L, ++ next_instr_offset.getOffset()); ++ target_pos = L->offset(); ++ } else { ++ if (L->used()) { ++ LabelCahe::Ptr p = label_cache_.lookup(L->offset()); ++ MOZ_ASSERT(p); ++ MOZ_ASSERT(p->key() == L->offset()); ++ target_pos = p->value().getOffset(); ++ target_at_put(BufferOffset(target_pos), next_instr_offset); ++ DEBUG_PRINTF("\tLabel %p added to link: %d\n", L, ++ next_instr_offset.getOffset()); ++ bool ok = label_cache_.put(L->offset(), next_instr_offset); ++ if (!ok) { ++ NoEnoughLabelCache(); ++ } ++ return kEndOfJumpChain; ++ } else { ++ JitSpew(JitSpew_Codegen, ".use Llabel %p on %d", L, ++ next_instr_offset.getOffset()); ++ L->use(next_instr_offset.getOffset()); ++ DEBUG_PRINTF("\tLabel %p added to link: %d\n", L, ++ next_instr_offset.getOffset()); ++ bool ok = label_cache_.putNew(L->offset(), next_instr_offset); ++ if (!ok) { ++ NoEnoughLabelCache(); ++ } ++ return kEndOfJumpChain; ++ } ++ } ++ intptr_t offset = target_pos - next_instr_offset.getOffset(); ++ MOZ_ASSERT((offset & 3) == 0); ++ MOZ_ASSERT(is_int32(offset)); ++ return static_cast(offset); ++} ++ ++int32_t Assembler::branch_offset_helper(Label* L, OffsetSize bits) { ++ if (oom()) { ++ return kEndOfJumpChain; ++ } ++ int32_t target_pos; ++ BufferOffset next_instr_offset = nextInstrOffset(); ++ DEBUG_PRINTF("\tbranch_offset_helper: %p to %d\n", L, ++ next_instr_offset.getOffset()); ++ // This is the last possible branch target. ++ if (L->bound()) { ++ JitSpew(JitSpew_Codegen, ".use Llabel %p on %d", L, ++ next_instr_offset.getOffset()); ++ target_pos = L->offset(); ++ } else { ++ BufferOffset deadline(next_instr_offset.getOffset() + ++ ImmBranchMaxForwardOffset(bits)); ++ DEBUG_PRINTF("\tregisterBranchDeadline %d\n", deadline.getOffset()); ++ m_buffer.registerBranchDeadline(OffsetSizeToImmBranchRangeType(bits), ++ deadline); ++ if (L->used()) { ++ LabelCahe::Ptr p = label_cache_.lookup(L->offset()); ++ MOZ_ASSERT(p); ++ MOZ_ASSERT(p->key() == L->offset()); ++ target_pos = p->value().getOffset(); ++ target_at_put(BufferOffset(target_pos), next_instr_offset); ++ DEBUG_PRINTF("\tLabel %p added to link: %d\n", L, ++ next_instr_offset.getOffset()); ++ bool ok = label_cache_.put(L->offset(), next_instr_offset); ++ if (!ok) { ++ NoEnoughLabelCache(); ++ } ++ return kEndOfJumpChain; ++ } else { ++ JitSpew(JitSpew_Codegen, ".use Llabel %p on %d", L, ++ next_instr_offset.getOffset()); ++ L->use(next_instr_offset.getOffset()); ++ bool ok = label_cache_.putNew(L->offset(), next_instr_offset); ++ if (!ok) { ++ NoEnoughLabelCache(); ++ } ++ DEBUG_PRINTF("\tLabel %p added to link: %d\n", L, ++ next_instr_offset.getOffset()); ++ return kEndOfJumpChain; ++ } ++ } ++ ++ int32_t offset = target_pos - next_instr_offset.getOffset(); ++ DEBUG_PRINTF("\toffset = %d\n", offset); ++ MOZ_ASSERT(is_intn(offset, bits)); ++ MOZ_ASSERT((offset & 1) == 0); ++ return offset; ++} ++ ++Assembler::Condition Assembler::InvertCondition(Condition cond) { ++ switch (cond) { ++ case Equal: ++ return NotEqual; ++ case NotEqual: ++ return Equal; ++ case Zero: ++ return NonZero; ++ case NonZero: ++ return Zero; ++ case LessThan: ++ return GreaterThanOrEqual; ++ case LessThanOrEqual: ++ return GreaterThan; ++ case GreaterThan: ++ return LessThanOrEqual; ++ case GreaterThanOrEqual: ++ return LessThan; ++ case Above: ++ return BelowOrEqual; ++ case AboveOrEqual: ++ return Below; ++ case Below: ++ return AboveOrEqual; ++ case BelowOrEqual: ++ return Above; ++ case Signed: ++ return NotSigned; ++ case NotSigned: ++ return Signed; ++ default: ++ MOZ_CRASH("unexpected condition"); ++ } ++} ++ ++Assembler::DoubleCondition Assembler::InvertCondition(DoubleCondition cond) { ++ switch (cond) { ++ case DoubleOrdered: ++ return DoubleUnordered; ++ case DoubleEqual: ++ return DoubleNotEqualOrUnordered; ++ case DoubleNotEqual: ++ return DoubleEqualOrUnordered; ++ case DoubleGreaterThan: ++ return DoubleLessThanOrEqualOrUnordered; ++ case DoubleGreaterThanOrEqual: ++ return DoubleLessThanOrUnordered; ++ case DoubleLessThan: ++ return DoubleGreaterThanOrEqualOrUnordered; ++ case DoubleLessThanOrEqual: ++ return DoubleGreaterThanOrUnordered; ++ case DoubleUnordered: ++ return DoubleOrdered; ++ case DoubleEqualOrUnordered: ++ return DoubleNotEqual; ++ case DoubleNotEqualOrUnordered: ++ return DoubleEqual; ++ case DoubleGreaterThanOrUnordered: ++ return DoubleLessThanOrEqual; ++ case DoubleGreaterThanOrEqualOrUnordered: ++ return DoubleLessThan; ++ case DoubleLessThanOrUnordered: ++ return DoubleGreaterThanOrEqual; ++ case DoubleLessThanOrEqualOrUnordered: ++ return DoubleGreaterThan; ++ default: ++ MOZ_CRASH("unexpected condition"); ++ } ++} ++ ++// Break / Trap instructions. ++void Assembler::break_(uint32_t code, bool break_as_stop) { ++ // We need to invalidate breaks that could be stops as well because the ++ // simulator expects a char pointer after the stop instruction. ++ // See constants-mips.h for explanation. ++ MOZ_ASSERT( ++ (break_as_stop && code <= kMaxStopCode && code > kMaxTracepointCode) || ++ (!break_as_stop && (code > kMaxStopCode || code <= kMaxTracepointCode))); ++ ++ // since ebreak does not allow additional immediate field, we use the ++ // immediate field of lui instruction immediately following the ebreak to ++ // encode the "code" info ++ ebreak(); ++ MOZ_ASSERT(is_uint20(code)); ++ lui(zero_reg, code); ++} ++ ++void Assembler::ToggleToJmp(CodeLocationLabel inst_) { ++ Instruction* inst = (Instruction*)inst_.raw(); ++ MOZ_ASSERT(IsAddi(inst->InstructionBits())); ++ int32_t offset = inst->Imm12Value(); ++ MOZ_ASSERT(is_int12(offset)); ++ Instr jal_ = JAL | (0b000 << kFunct3Shift) | ++ (offset & 0xff000) | // bits 19-12 ++ ((offset & 0x800) << 9) | // bit 11 ++ ((offset & 0x7fe) << 20) | // bits 10-1 ++ ((offset & 0x100000) << 11); // bit 20 ++ // jal(zero, offset); ++ *reinterpret_cast(inst) = jal_; ++} ++ ++void Assembler::ToggleToCmp(CodeLocationLabel inst_) { ++ Instruction* inst = (Instruction*)inst_.raw(); ++ ++ // toggledJump is allways used for short jumps. ++ MOZ_ASSERT(IsJal(inst->InstructionBits())); ++ // Replace "jal zero_reg, offset" with "addi $zero, $zero, offset" ++ int32_t offset = inst->Imm20JValue(); ++ MOZ_ASSERT(is_int12(offset)); ++ Instr addi_ = OP_IMM | (0b000 << kFunct3Shift) | ++ (offset << kImm12Shift); // addi(zero, zero, low_12); ++ *reinterpret_cast(inst) = addi_; ++} ++ ++bool Assembler::reserve(size_t size) { ++ // This buffer uses fixed-size chunks so there's no point in reserving ++ // now vs. on-demand. ++ return !oom(); ++} ++ ++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, ++ mozilla::Maybe& awjc, ++ JitCode* code, Instruction* inst) { ++ void* ptr = (void*)Assembler::ExtractLoad64Value(inst); ++ void* prior = ptr; ++ ++ // Data relocations can be for Values or for raw pointers. If a Value is ++ // zero-tagged, we can trace it as if it were a raw pointer. If a Value ++ // is not zero-tagged, we have to interpret it as a Value to ensure that the ++ // tag bits are masked off to recover the actual pointer. ++ uintptr_t word = reinterpret_cast(ptr); ++ if (word >> JSVAL_TAG_SHIFT) { ++ // This relocation is a Value with a non-zero tag. ++ Value v = Value::fromRawBits(word); ++ TraceManuallyBarrieredEdge(trc, &v, "jit-masm-value"); ++ ptr = (void*)v.bitsAsPunboxPointer(); ++ } else { ++ // This relocation is a raw pointer or a Value with a zero tag. ++ // No barrier needed since these are constants. ++ TraceManuallyBarrieredGenericPointerEdge( ++ trc, reinterpret_cast(&ptr), "jit-masm-ptr"); ++ } ++ ++ if (ptr != prior) { ++ if (awjc.isNothing()) { ++ awjc.emplace(code); ++ } ++ Assembler::UpdateLoad64Value(inst, uint64_t(ptr)); ++ } ++} ++ ++/* static */ ++void Assembler::TraceDataRelocations(JSTracer* trc, JitCode* code, ++ CompactBufferReader& reader) { ++ mozilla::Maybe awjc; ++ while (reader.more()) { ++ size_t offset = reader.readUnsigned(); ++ Instruction* inst = (Instruction*)(code->raw() + offset); ++ TraceOneDataRelocation(trc, awjc, code, inst); ++ } ++} ++ ++UseScratchRegisterScope::UseScratchRegisterScope(Assembler* assembler) ++ : available_(assembler->GetScratchRegisterList()), ++ old_available_(*available_) {} ++ ++UseScratchRegisterScope::~UseScratchRegisterScope() { ++ *available_ = old_available_; ++} ++ ++Register UseScratchRegisterScope::Acquire() { ++ MOZ_ASSERT(available_ != nullptr); ++ MOZ_ASSERT(!available_->empty()); ++ Register index = GeneralRegisterSet::FirstRegister(available_->bits()); ++ available_->takeRegisterIndex(index); ++ return index; ++} ++ ++bool UseScratchRegisterScope::hasAvailable() const { ++ return (available_->size()) != 0; ++} ++ ++void Assembler::retarget(Label* label, Label* target) { ++ spew("retarget %p -> %p", label, target); ++ if (label->used() && !oom()) { ++ if (target->bound()) { ++ bind(label, BufferOffset(target)); ++ } else if (target->used()) { ++ // The target is not bound but used. Prepend label's branch list ++ // onto target's. ++ int32_t next; ++ BufferOffset labelBranchOffset(label); ++ ++ // Find the head of the use chain for label. ++ do { ++ next = next_link(label, false); ++ labelBranchOffset = BufferOffset(next); ++ } while (next != LabelBase::INVALID_OFFSET); ++ ++ // Then patch the head of label's use chain to the tail of ++ // target's use chain, prepending the entire use chain of target. ++ target->use(label->offset()); ++ target_at_put(labelBranchOffset, BufferOffset(target)); ++ MOZ_CRASH("check"); ++ } else { ++ // The target is unbound and unused. We can just take the head of ++ // the list hanging off of label, and dump that into target. ++ target->use(label->offset()); ++ } ++ } ++ label->reset(); ++} ++ ++bool Assembler::appendRawCode(const uint8_t* code, size_t numBytes) { ++ if (m_buffer.oom()) { ++ return false; ++ } ++ while (numBytes > SliceSize) { ++ m_buffer.putBytes(SliceSize, code); ++ numBytes -= SliceSize; ++ code += SliceSize; ++ } ++ m_buffer.putBytes(numBytes, code); ++ return !m_buffer.oom(); ++} ++ ++void Assembler::ToggleCall(CodeLocationLabel inst_, bool enabled) { ++ Instruction* i0 = (Instruction*)inst_.raw(); ++ Instruction* i1 = (Instruction*)(inst_.raw() + 1 * kInstrSize); ++ Instruction* i2 = (Instruction*)(inst_.raw() + 2 * kInstrSize); ++ Instruction* i3 = (Instruction*)(inst_.raw() + 3 * kInstrSize); ++ Instruction* i4 = (Instruction*)(inst_.raw() + 4 * kInstrSize); ++ Instruction* i5 = (Instruction*)(inst_.raw() + 5 * kInstrSize); ++ Instruction* i6 = (Instruction*)(inst_.raw() + 6 * kInstrSize); ++ ++ MOZ_ASSERT(IsLui(i0->InstructionBits())); ++ MOZ_ASSERT(IsAddi(i1->InstructionBits())); ++ MOZ_ASSERT(IsSlli(i2->InstructionBits())); ++ MOZ_ASSERT(IsOri(i3->InstructionBits())); ++ MOZ_ASSERT(IsSlli(i4->InstructionBits())); ++ MOZ_ASSERT(IsOri(i5->InstructionBits())); ++ if (enabled) { ++ Instr jalr_ = JALR | (ra.code() << kRdShift) | (0x0 << kFunct3Shift) | ++ (i5->RdValue() << kRs1Shift) | (0x0 << kImm12Shift); ++ *((Instr*)i6) = jalr_; ++ } else { ++ *((Instr*)i6) = kNopByte; ++ } ++} ++ ++void Assembler::PatchShortRangeBranchToVeneer(Buffer* buffer, unsigned rangeIdx, ++ BufferOffset deadline, ++ BufferOffset veneer) { ++ if (buffer->oom()) { ++ return; ++ } ++ DEBUG_PRINTF("\tPatchShortRangeBranchToVeneer\n"); ++ // Reconstruct the position of the branch from (rangeIdx, deadline). ++ ImmBranchRangeType branchRange = static_cast(rangeIdx); ++ BufferOffset branch(deadline.getOffset() - ++ ImmBranchMaxForwardOffset(branchRange)); ++ Instruction* branchInst = buffer->getInst(branch); ++ Instruction* veneerInst_1 = buffer->getInst(veneer); ++ Instruction* veneerInst_2 = ++ buffer->getInst(BufferOffset(veneer.getOffset() + 4)); ++ // Verify that the branch range matches what's encoded. ++ DEBUG_PRINTF("\t%p(%x): ", branchInst, branch.getOffset()); ++ disassembleInstr(branchInst->InstructionBits(), JitSpew_Codegen); ++ DEBUG_PRINTF("\t instert veneer %x, branch:%x deadline: %x\n", ++ veneer.getOffset(), branch.getOffset(), deadline.getOffset()); ++ MOZ_ASSERT(branchRange <= UncondBranchRangeType); ++ MOZ_ASSERT(branchInst->GetImmBranchRangeType() == branchRange); ++ // emit a long jump slot ++ Instr auipc = AUIPC | (t6.code() << kRdShift) | (0x0 << kImm20Shift); ++ Instr jalr = JALR | (zero_reg.code() << kRdShift) | (0x0 << kFunct3Shift) | ++ (t6.code() << kRs1Shift) | (0x0 << kImm12Shift); ++ ++ // We want to insert veneer after branch in the linked list of instructions ++ // that use the same unbound label. ++ // The veneer should be an unconditional branch. ++ int32_t nextElemOffset = target_at(buffer->getInst(branch), branch, false); ++ int32_t dist; ++ // If offset is 0, this is the end of the linked list. ++ if (nextElemOffset != kEndOfChain) { ++ // Make the offset relative to veneer so it targets the same instruction ++ // as branchInst. ++ dist = nextElemOffset - veneer.getOffset(); ++ } else { ++ dist = 0; ++ } ++ int32_t Hi20 = (((int32_t)dist + 0x800) >> 12); ++ int32_t Lo12 = (int32_t)dist << 20 >> 20; ++ auipc = SetAuipcOffset(Hi20, auipc); ++ jalr = SetJalrOffset(Lo12, jalr); ++ // insert veneer ++ veneerInst_1->SetInstructionBits(auipc); ++ veneerInst_2->SetInstructionBits(jalr); ++ // Now link branchInst to veneer. ++ if (IsBranch(branchInst->InstructionBits())) { ++ branchInst->SetInstructionBits(SetBranchOffset( ++ branch.getOffset(), veneer.getOffset(), branchInst->InstructionBits())); ++ } else { ++ MOZ_ASSERT(IsJal(branchInst->InstructionBits())); ++ branchInst->SetInstructionBits(SetJalOffset( ++ branch.getOffset(), veneer.getOffset(), branchInst->InstructionBits())); ++ } ++ DEBUG_PRINTF("\tfix to veneer:"); ++ disassembleInstr(branchInst->InstructionBits()); ++} ++} // namespace jit ++} // namespace js +diff --git a/js/src/jit/riscv64/Assembler-riscv64.h b/js/src/jit/riscv64/Assembler-riscv64.h +new file mode 100644 +index 0000000000..4086b38ff7 +--- /dev/null ++++ b/js/src/jit/riscv64/Assembler-riscv64.h +@@ -0,0 +1,685 @@ ++/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- ++ * vim: set ts=8 sts=2 et sw=2 tw=80: ++ * 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/. */ ++ ++// Copyright (c) 1994-2006 Sun Microsystems Inc. ++// All Rights Reserved. ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions are ++// met: ++// ++// - Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// ++// - Redistribution in binary form must reproduce the above copyright ++// notice, this list of conditions and the following disclaimer in the ++// documentation and/or other materials provided with the distribution. ++// ++// - Neither the name of Sun Microsystems or the names of contributors may ++// be used to endorse or promote products derived from this software without ++// specific prior written permission. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ ++// The original source code covered by the above license above has been ++// modified significantly by Google Inc. ++// Copyright 2021 the V8 project authors. All rights reserved. ++ ++#ifndef jit_riscv64_Assembler_riscv64_h ++#define jit_riscv64_Assembler_riscv64_h ++ ++#include "mozilla/Assertions.h" ++#include "mozilla/Sprintf.h" ++ ++#include ++ ++#include "jit/CompactBuffer.h" ++#include "jit/JitCode.h" ++#include "jit/JitSpewer.h" ++#include "jit/Registers.h" ++#include "jit/RegisterSets.h" ++#include "jit/riscv64/Architecture-riscv64.h" ++#include "jit/riscv64/constant/Constant-riscv64.h" ++#include "jit/riscv64/extension/base-assembler-riscv.h" ++#include "jit/riscv64/extension/base-riscv-i.h" ++#include "jit/riscv64/extension/extension-riscv-a.h" ++#include "jit/riscv64/extension/extension-riscv-c.h" ++#include "jit/riscv64/extension/extension-riscv-d.h" ++#include "jit/riscv64/extension/extension-riscv-f.h" ++#include "jit/riscv64/extension/extension-riscv-m.h" ++#include "jit/riscv64/extension/extension-riscv-v.h" ++#include "jit/riscv64/extension/extension-riscv-zicsr.h" ++#include "jit/riscv64/extension/extension-riscv-zifencei.h" ++#include "jit/riscv64/Register-riscv64.h" ++#include "jit/shared/Assembler-shared.h" ++#include "jit/shared/Disassembler-shared.h" ++#include "jit/shared/IonAssemblerBufferWithConstantPools.h" ++#include "js/HashTable.h" ++#include "wasm/WasmTypeDecls.h" ++namespace js { ++namespace jit { ++ ++struct ScratchFloat32Scope : public AutoFloatRegisterScope { ++ explicit ScratchFloat32Scope(MacroAssembler& masm) ++ : AutoFloatRegisterScope(masm, ScratchFloat32Reg) {} ++}; ++ ++struct ScratchDoubleScope : public AutoFloatRegisterScope { ++ explicit ScratchDoubleScope(MacroAssembler& masm) ++ : AutoFloatRegisterScope(masm, ScratchDoubleReg) {} ++}; ++ ++struct ScratchRegisterScope : public AutoRegisterScope { ++ explicit ScratchRegisterScope(MacroAssembler& masm) ++ : AutoRegisterScope(masm, ScratchRegister) {} ++}; ++ ++class MacroAssembler; ++ ++inline Imm32 Imm64::secondHalf() const { return hi(); } ++inline Imm32 Imm64::firstHalf() const { return low(); } ++ ++static constexpr uint32_t ABIStackAlignment = 8; ++static constexpr uint32_t CodeAlignment = 16; ++static constexpr uint32_t JitStackAlignment = 8; ++static constexpr uint32_t JitStackValueAlignment = ++ JitStackAlignment / sizeof(Value); ++static const uint32_t WasmStackAlignment = 16; ++static const uint32_t WasmTrapInstructionLength = 2 * sizeof(uint32_t); ++// See comments in wasm::GenerateFunctionPrologue. The difference between these ++// is the size of the largest callable prologue on the platform. ++static constexpr uint32_t WasmCheckedCallEntryOffset = 0u; ++static constexpr uint32_t WasmCheckedTailEntryOffset = 20u; ++ ++static const Scale ScalePointer = TimesEight; ++ ++class Assembler; ++ ++static constexpr int32_t SliceSize = 1024; ++ ++typedef js::jit::AssemblerBufferWithConstantPools< ++ SliceSize, 4, Instruction, Assembler, NumShortBranchRangeTypes> ++ Buffer; ++ ++class Assembler : public AssemblerShared, ++ public AssemblerRISCVI, ++ public AssemblerRISCVA, ++ public AssemblerRISCVF, ++ public AssemblerRISCVD, ++ public AssemblerRISCVM, ++ public AssemblerRISCVC, ++ public AssemblerRISCVZicsr, ++ public AssemblerRISCVZifencei { ++ GeneralRegisterSet scratch_register_list_; ++ ++ static constexpr int kInvalidSlotPos = -1; ++ ++#ifdef JS_JITSPEW ++ Sprinter* printer; ++#endif ++ bool enoughLabelCache_ = true; ++ ++ protected: ++ using LabelOffset = int32_t; ++ using LabelCahe = ++ HashMap, ++ js::SystemAllocPolicy>; ++ LabelCahe label_cache_; ++ void NoEnoughLabelCache() { enoughLabelCache_ = false; } ++ CompactBufferWriter jumpRelocations_; ++ CompactBufferWriter dataRelocations_; ++ Buffer m_buffer; ++ bool isFinished = false; ++ Instruction* editSrc(BufferOffset bo) { return m_buffer.getInst(bo); } ++ ++ struct RelativePatch { ++ // the offset within the code buffer where the value is loaded that ++ // we want to fix-up ++ BufferOffset offset; ++ void* target; ++ RelocationKind kind; ++ ++ RelativePatch(BufferOffset offset, void* target, RelocationKind kind) ++ : offset(offset), target(target), kind(kind) {} ++ }; ++ ++ js::Vector jumps_; ++ ++ void addPendingJump(BufferOffset src, ImmPtr target, RelocationKind kind) { ++ enoughMemory_ &= jumps_.append(RelativePatch(src, target.value, kind)); ++ if (kind == RelocationKind::JITCODE) { ++ jumpRelocations_.writeUnsigned(src.getOffset()); ++ } ++ } ++ ++ void addLongJump(BufferOffset src, BufferOffset dst) { ++ CodeLabel cl; ++ cl.patchAt()->bind(src.getOffset()); ++ cl.target()->bind(dst.getOffset()); ++ cl.setLinkMode(CodeLabel::JumpImmediate); ++ addCodeLabel(std::move(cl)); ++ } ++ ++ public: ++ static bool FLAG_riscv_debug; ++ ++ Assembler() ++ : scratch_register_list_((1 << t5.code()) | (1 << t4.code()) | ++ (1 << t6.code())), ++#ifdef JS_JITSPEW ++ printer(nullptr), ++#endif ++ m_buffer(/*guardSize*/ 2, /*headerSize*/ 2, /*instBufferAlign*/ 8, ++ /*poolMaxOffset*/ GetPoolMaxOffset(), /*pcBias*/ 8, ++ /*alignFillInst*/ kNopByte, /*nopFillInst*/ kNopByte), ++ isFinished(false) { ++ } ++ static uint32_t NopFill; ++ static uint32_t AsmPoolMaxOffset; ++ static uint32_t GetPoolMaxOffset(); ++ bool reserve(size_t size); ++ bool oom() const; ++ void setPrinter(Sprinter* sp) { ++#ifdef JS_JITSPEW ++ printer = sp; ++#endif ++ } ++ void finish() { ++ MOZ_ASSERT(!isFinished); ++ isFinished = true; ++ } ++ void enterNoPool(size_t maxInst) { m_buffer.enterNoPool(maxInst); } ++ void leaveNoPool() { m_buffer.leaveNoPool(); } ++ bool swapBuffer(wasm::Bytes& bytes); ++ // Size of the instruction stream, in bytes. ++ size_t size() const; ++ // Size of the data table, in bytes. ++ size_t bytesNeeded() const; ++ // Size of the jump relocation table, in bytes. ++ size_t jumpRelocationTableBytes() const; ++ size_t dataRelocationTableBytes() const; ++ void copyJumpRelocationTable(uint8_t* dest); ++ void copyDataRelocationTable(uint8_t* dest); ++ // Copy the assembly code to the given buffer, and perform any pending ++ // relocations relying on the target address. ++ void executableCopy(uint8_t* buffer); ++ // API for speaking with the IonAssemblerBufferWithConstantPools generate an ++ // initial placeholder instruction that we want to later fix up. ++ static void InsertIndexIntoTag(uint8_t* load, uint32_t index); ++ static void PatchConstantPoolLoad(void* loadAddr, void* constPoolAddr); ++ // We're not tracking short-range branches for ARM for now. ++ static void PatchShortRangeBranchToVeneer(Buffer*, unsigned rangeIdx, ++ BufferOffset deadline, ++ BufferOffset veneer); ++ struct PoolHeader { ++ uint32_t data; ++ ++ struct Header { ++ // The size should take into account the pool header. ++ // The size is in units of Instruction (4bytes), not byte. ++ union { ++ struct { ++ uint32_t size : 15; ++ ++ // "Natural" guards are part of the normal instruction stream, ++ // while "non-natural" guards are inserted for the sole purpose ++ // of skipping around a pool. ++ uint32_t isNatural : 1; ++ uint32_t ONES : 16; ++ }; ++ uint32_t data; ++ }; ++ ++ Header(int size_, bool isNatural_) ++ : size(size_), isNatural(isNatural_), ONES(0xffff) {} ++ ++ Header(uint32_t data) : data(data) { ++ static_assert(sizeof(Header) == sizeof(uint32_t)); ++ MOZ_ASSERT(ONES == 0xffff); ++ } ++ ++ uint32_t raw() const { ++ static_assert(sizeof(Header) == sizeof(uint32_t)); ++ return data; ++ } ++ }; ++ ++ PoolHeader(int size_, bool isNatural_) ++ : data(Header(size_, isNatural_).raw()) {} ++ ++ uint32_t size() const { ++ Header tmp(data); ++ return tmp.size; ++ } ++ ++ uint32_t isNatural() const { ++ Header tmp(data); ++ return tmp.isNatural; ++ } ++ }; ++ ++ static void WritePoolHeader(uint8_t* start, Pool* p, bool isNatural); ++ static void WritePoolGuard(BufferOffset branch, Instruction* inst, ++ BufferOffset dest); ++ void processCodeLabels(uint8_t* rawCode); ++ BufferOffset nextOffset() { return m_buffer.nextOffset(); } ++ // Get the buffer offset of the next inserted instruction. This may flush ++ // constant pools. ++ BufferOffset nextInstrOffset(int numInstr = 1) { ++ return m_buffer.nextInstrOffset(numInstr); ++ } ++ void comment(const char* msg) { spew("; %s", msg); } ++ ++#ifdef JS_JITSPEW ++ inline void spew(const char* fmt, ...) MOZ_FORMAT_PRINTF(2, 3) { ++ if (MOZ_UNLIKELY(printer || JitSpewEnabled(JitSpew_Codegen))) { ++ va_list va; ++ va_start(va, fmt); ++ spew(fmt, va); ++ va_end(va); ++ } ++ } ++ ++#else ++ MOZ_ALWAYS_INLINE void spew(const char* fmt, ...) MOZ_FORMAT_PRINTF(2, 3) {} ++#endif ++ ++#ifdef JS_JITSPEW ++ MOZ_COLD void spew(const char* fmt, va_list va) MOZ_FORMAT_PRINTF(2, 0) { ++ // Buffer to hold the formatted string. Note that this may contain ++ // '%' characters, so do not pass it directly to printf functions. ++ char buf[200]; ++ ++ int i = VsprintfLiteral(buf, fmt, va); ++ if (i > -1) { ++ if (printer) { ++ printer->printf("%s\n", buf); ++ } ++ js::jit::JitSpew(js::jit::JitSpew_Codegen, "%s", buf); ++ } ++ } ++#endif ++ ++ enum Condition { ++ Overflow = overflow, ++ Below = Uless, ++ BelowOrEqual = Uless_equal, ++ Above = Ugreater, ++ AboveOrEqual = Ugreater_equal, ++ Equal = equal, ++ NotEqual = not_equal, ++ GreaterThan = greater, ++ GreaterThanOrEqual = greater_equal, ++ LessThan = less, ++ LessThanOrEqual = less_equal, ++ Always = cc_always, ++ CarrySet, ++ CarryClear, ++ Signed, ++ NotSigned, ++ Zero, ++ NonZero, ++ }; ++ ++ enum DoubleCondition { ++ // These conditions will only evaluate to true if the comparison is ordered ++ // - i.e. neither operand is NaN. ++ DoubleOrdered, ++ DoubleEqual, ++ DoubleNotEqual, ++ DoubleGreaterThan, ++ DoubleGreaterThanOrEqual, ++ DoubleLessThan, ++ DoubleLessThanOrEqual, ++ // If either operand is NaN, these conditions always evaluate to true. ++ DoubleUnordered, ++ DoubleEqualOrUnordered, ++ DoubleNotEqualOrUnordered, ++ DoubleGreaterThanOrUnordered, ++ DoubleGreaterThanOrEqualOrUnordered, ++ DoubleLessThanOrUnordered, ++ DoubleLessThanOrEqualOrUnordered, ++ FIRST_UNORDERED = DoubleUnordered, ++ LAST_UNORDERED = DoubleLessThanOrEqualOrUnordered ++ }; ++ ++ Register getStackPointer() const { return StackPointer; } ++ void flushBuffer() {} ++ static int disassembleInstr(Instr instr, bool enable_spew = false); ++ int target_at(BufferOffset pos, bool is_internal); ++ static int target_at(Instruction* instruction, BufferOffset pos, ++ bool is_internal, Instruction* instruction2 = nullptr); ++ uint32_t next_link(Label* label, bool is_internal); ++ static uintptr_t target_address_at(Instruction* pos); ++ static void set_target_value_at(Instruction* pc, uint64_t target); ++ void target_at_put(BufferOffset pos, BufferOffset target_pos, ++ bool trampoline = false); ++ virtual int32_t branch_offset_helper(Label* L, OffsetSize bits); ++ int32_t branch_long_offset(Label* L); ++ ++ // Determines if Label is bound and near enough so that branch instruction ++ // can be used to reach it, instead of jump instruction. ++ bool is_near(Label* L); ++ bool is_near(Label* L, OffsetSize bits); ++ bool is_near_branch(Label* L); ++ ++ void nopAlign(int m) { ++ MOZ_ASSERT(m >= 4 && (m & (m - 1)) == 0); ++ while ((currentOffset() & (m - 1)) != 0) { ++ nop(); ++ } ++ } ++ virtual void emit(Instr x) { ++ MOZ_ASSERT(hasCreator()); ++ m_buffer.putInt(x); ++#ifdef DEBUG ++ if (!oom()) { ++ DEBUG_PRINTF( ++ "0x%lx(%lx):", ++ (uint64_t)editSrc(BufferOffset(currentOffset() - sizeof(Instr))), ++ currentOffset() - sizeof(Instr)); ++ disassembleInstr(x, JitSpewEnabled(JitSpew_Codegen)); ++ } ++#endif ++ } ++ virtual void emit(ShortInstr x) { MOZ_CRASH(); } ++ virtual void emit(uint64_t x) { MOZ_CRASH(); } ++ virtual void emit(uint32_t x) { ++ DEBUG_PRINTF( ++ "0x%lx(%lx): uint32_t: %d\n", ++ (uint64_t)editSrc(BufferOffset(currentOffset() - sizeof(Instr))), ++ currentOffset() - sizeof(Instr), x); ++ m_buffer.putInt(x); ++ } ++ ++ void instr_at_put(BufferOffset offset, Instr instr) { ++ DEBUG_PRINTF("\t[instr_at_put\n"); ++ DEBUG_PRINTF("\t%p %d \n\t", editSrc(offset), offset.getOffset()); ++ disassembleInstr(editSrc(offset)->InstructionBits()); ++ DEBUG_PRINTF("\t"); ++ *reinterpret_cast(editSrc(offset)) = instr; ++ disassembleInstr(editSrc(offset)->InstructionBits()); ++ DEBUG_PRINTF("\t]\n"); ++ } ++ ++ static Condition InvertCondition(Condition); ++ ++ static DoubleCondition InvertCondition(DoubleCondition); ++ ++ static uint64_t ExtractLoad64Value(Instruction* inst0); ++ static void UpdateLoad64Value(Instruction* inst0, uint64_t value); ++ static void PatchDataWithValueCheck(CodeLocationLabel label, ImmPtr newValue, ++ ImmPtr expectedValue); ++ static void PatchDataWithValueCheck(CodeLocationLabel label, ++ PatchedImmPtr newValue, ++ PatchedImmPtr expectedValue); ++ static void PatchWrite_Imm32(CodeLocationLabel label, Imm32 imm); ++ ++ static void 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. ++ // WriteLoad64Instructions will emit 6 instrs to load a addr. ++ Assembler::WriteLoad64Instructions(inst, ScratchRegister, (uint64_t)dest); ++ Instr jalr_ = JALR | (ra.code() << kRdShift) | (0x0 << kFunct3Shift) | ++ (ScratchRegister.code() << kRs1Shift) | (0x0 << kImm12Shift); ++ *reinterpret_cast(inst + 6 * kInstrSize) = jalr_; ++ } ++ static void WriteLoad64Instructions(Instruction* inst0, Register reg, ++ uint64_t value); ++ ++ static uint32_t PatchWrite_NearCallSize() { return 7 * sizeof(uint32_t); } ++ ++ static void TraceJumpRelocations(JSTracer* trc, JitCode* code, ++ CompactBufferReader& reader); ++ static void TraceDataRelocations(JSTracer* trc, JitCode* code, ++ CompactBufferReader& reader); ++ ++ static void ToggleToJmp(CodeLocationLabel inst_); ++ static void ToggleToCmp(CodeLocationLabel inst_); ++ static void ToggleCall(CodeLocationLabel inst_, bool enable); ++ ++ static void Bind(uint8_t* rawCode, const CodeLabel& label); ++ // label operations ++ void bind(Label* label, BufferOffset boff = BufferOffset()); ++ void bind(CodeLabel* label) { label->target()->bind(currentOffset()); } ++ uint32_t currentOffset() { return nextOffset().getOffset(); } ++ void retarget(Label* label, Label* target); ++ static uint32_t NopSize() { return 4; } ++ ++ static uintptr_t GetPointer(uint8_t* instPtr) { ++ Instruction* inst = (Instruction*)instPtr; ++ return Assembler::ExtractLoad64Value(inst); ++ } ++ ++ static bool HasRoundInstruction(RoundingMode) { return false; } ++ ++ void verifyHeapAccessDisassembly(uint32_t begin, uint32_t end, ++ const Disassembler::HeapAccess& heapAccess) { ++ MOZ_CRASH(); ++ } ++ ++ void setUnlimitedBuffer() { m_buffer.setUnlimited(); } ++ ++ GeneralRegisterSet* GetScratchRegisterList() { ++ return &scratch_register_list_; ++ } ++ ++ void EmitConstPoolWithJumpIfNeeded(size_t margin = 0) {} ++ ++ // As opposed to x86/x64 version, the data relocation has to be executed ++ // before to recover the pointer, and not after. ++ void writeDataRelocation(ImmGCPtr ptr) { ++ // Raw GC pointer relocations and Value relocations both end up in ++ // TraceOneDataRelocation. ++ if (ptr.value) { ++ if (gc::IsInsideNursery(ptr.value)) { ++ embedsNurseryPointers_ = true; ++ } ++ dataRelocations_.writeUnsigned(nextOffset().getOffset()); ++ } ++ } ++ ++ bool appendRawCode(const uint8_t* code, size_t numBytes); ++ ++ void assertNoGCThings() const { ++#ifdef DEBUG ++ MOZ_ASSERT(dataRelocations_.length() == 0); ++ for (auto& j : jumps_) { ++ MOZ_ASSERT(j.kind == RelocationKind::HARDCODED); ++ } ++#endif ++ } ++ ++ // Assembler Pseudo Instructions (Tables 25.2, 25.3, RISC-V Unprivileged ISA) ++ void break_(uint32_t code, bool break_as_stop = false); ++ void nop(); ++ void RV_li(Register rd, intptr_t imm); ++ static int RV_li_count(int64_t imm, bool is_get_temp_reg = false); ++ void GeneralLi(Register rd, int64_t imm); ++ static int GeneralLiCount(intptr_t imm, bool is_get_temp_reg = false); ++ void RecursiveLiImpl(Register rd, intptr_t imm); ++ void RecursiveLi(Register rd, intptr_t imm); ++ static int RecursiveLiCount(intptr_t imm); ++ static int RecursiveLiImplCount(intptr_t imm); ++ // Returns the number of instructions required to load the immediate ++ static int li_estimate(intptr_t imm, bool is_get_temp_reg = false); ++ // Loads an immediate, always using 8 instructions, regardless of the value, ++ // so that it can be modified later. ++ void li_constant(Register rd, intptr_t imm); ++ void li_ptr(Register rd, intptr_t imm); ++}; ++ ++class ABIArgGenerator { ++ public: ++ ABIArgGenerator() ++ : intRegIndex_(0), floatRegIndex_(0), stackOffset_(0), current_() {} ++ ABIArg next(MIRType); ++ ABIArg& current() { return current_; } ++ uint32_t stackBytesConsumedSoFar() const { return stackOffset_; } ++ void increaseStackOffset(uint32_t bytes) { stackOffset_ += bytes; } ++ ++ protected: ++ unsigned intRegIndex_; ++ unsigned floatRegIndex_; ++ uint32_t stackOffset_; ++ ABIArg current_; ++}; ++ ++class BlockTrampolinePoolScope { ++ public: ++ explicit BlockTrampolinePoolScope(Assembler* assem, int margin) ++ : assem_(assem) { ++ assem_->enterNoPool(margin); ++ } ++ ~BlockTrampolinePoolScope() { assem_->leaveNoPool(); } ++ ++ private: ++ Assembler* assem_; ++ BlockTrampolinePoolScope() = delete; ++ BlockTrampolinePoolScope(const BlockTrampolinePoolScope&) = delete; ++ BlockTrampolinePoolScope& operator=(const BlockTrampolinePoolScope&) = delete; ++}; ++class UseScratchRegisterScope { ++ public: ++ explicit UseScratchRegisterScope(Assembler* assembler); ++ ~UseScratchRegisterScope(); ++ ++ Register Acquire(); ++ bool hasAvailable() const; ++ void Include(const GeneralRegisterSet& list) { ++ *available_ = GeneralRegisterSet::Intersect(*available_, list); ++ } ++ void Exclude(const GeneralRegisterSet& list) { ++ *available_ = GeneralRegisterSet::Subtract(*available_, list); ++ } ++ ++ private: ++ GeneralRegisterSet* available_; ++ GeneralRegisterSet old_available_; ++}; ++ ++// Class Operand represents a shifter operand in data processing instructions. ++class Operand { ++ public: ++ enum Tag { REG, FREG, MEM, IMM }; ++ Operand(FloatRegister freg) : tag(FREG), rm_(freg.code()) {} ++ ++ explicit Operand(Register base, Imm32 off) ++ : tag(MEM), rm_(base.code()), offset_(off.value) {} ++ ++ explicit Operand(Register base, int32_t off) ++ : tag(MEM), rm_(base.code()), offset_(off) {} ++ ++ explicit Operand(const Address& addr) ++ : tag(MEM), rm_(addr.base.code()), offset_(addr.offset) {} ++ ++ explicit Operand(intptr_t immediate) : tag(IMM), rm_() { value_ = immediate; } ++ // Register. ++ Operand(const Register rm) : tag(REG), rm_(rm.code()) {} ++ // Return true if this is a register operand. ++ bool is_reg() const { return tag == REG; } ++ bool is_freg() const { return tag == FREG; } ++ bool is_mem() const { return tag == MEM; } ++ bool is_imm() const { return tag == IMM; } ++ inline intptr_t immediate() const { ++ MOZ_ASSERT(is_imm()); ++ return value_; ++ } ++ bool IsImmediate() const { return !is_reg(); } ++ Register rm() const { return Register::FromCode(rm_); } ++ int32_t offset() const { ++ MOZ_ASSERT(is_mem()); ++ return offset_; ++ } ++ ++ FloatRegister toFReg() const { ++ MOZ_ASSERT(tag == FREG); ++ return FloatRegister::FromCode(rm_); ++ } ++ ++ Register toReg() const { ++ MOZ_ASSERT(tag == REG); ++ return Register::FromCode(rm_); ++ } ++ ++ Address toAddress() const { ++ MOZ_ASSERT(tag == MEM); ++ return Address(Register::FromCode(rm_), offset()); ++ } ++ ++ private: ++ Tag tag; ++ uint32_t rm_; ++ int32_t offset_; ++ intptr_t value_; // valid if rm_ == no_reg ++ ++ friend class Assembler; ++ friend class MacroAssembler; ++}; ++ ++static const uint32_t NumIntArgRegs = 8; ++static const uint32_t NumFloatArgRegs = 8; ++static inline bool GetIntArgReg(uint32_t usedIntArgs, Register* out) { ++ if (usedIntArgs < NumIntArgRegs) { ++ *out = Register::FromCode(a0.code() + usedIntArgs); ++ return true; ++ } ++ return false; ++} ++ ++static inline bool GetFloatArgReg(uint32_t usedFloatArgs, FloatRegister* out) { ++ if (usedFloatArgs < NumFloatArgRegs) { ++ *out = FloatRegister::FromCode(fa0.code() + usedFloatArgs); ++ 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 ++#endif /* jit_riscv64_Assembler_riscv64_h */ +diff --git a/js/src/jit/riscv64/AssemblerMatInt.cpp b/js/src/jit/riscv64/AssemblerMatInt.cpp +new file mode 100644 +index 0000000000..81c7fa7c40 +--- /dev/null ++++ b/js/src/jit/riscv64/AssemblerMatInt.cpp +@@ -0,0 +1,217 @@ ++/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- ++ * vim: set ts=8 sts=2 et sw=2 tw=80: ++ * 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/. */ ++//===- RISCVMatInt.cpp - Immediate materialisation -------------*- C++ ++//-*--===// ++// ++// Part of the LLVM Project, under the Apache License v2.0 with LLVM ++// Exceptions. See https://llvm.org/LICENSE.txt for license information. ++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception ++// ++//===----------------------------------------------------------------------===// ++#include "mozilla/MathAlgorithms.h" ++#include "mozilla/Maybe.h" ++ ++#include "gc/Marking.h" ++#include "jit/AutoWritableJitCode.h" ++#include "jit/ExecutableAllocator.h" ++#include "jit/riscv64/Assembler-riscv64.h" ++#include "jit/riscv64/disasm/Disasm-riscv64.h" ++#include "vm/Realm.h" ++namespace js { ++namespace jit { ++void Assembler::RecursiveLi(Register rd, int64_t val) { ++ if (val > 0 && RecursiveLiImplCount(val) > 2) { ++ unsigned LeadingZeros = mozilla::CountLeadingZeroes64((uint64_t)val); ++ uint64_t ShiftedVal = (uint64_t)val << LeadingZeros; ++ int countFillZero = RecursiveLiImplCount(ShiftedVal) + 1; ++ if (countFillZero < RecursiveLiImplCount(val)) { ++ RecursiveLiImpl(rd, ShiftedVal); ++ srli(rd, rd, LeadingZeros); ++ return; ++ } ++ } ++ RecursiveLiImpl(rd, val); ++} ++ ++int Assembler::RecursiveLiCount(int64_t val) { ++ if (val > 0 && RecursiveLiImplCount(val) > 2) { ++ unsigned LeadingZeros = mozilla::CountLeadingZeroes64((uint64_t)val); ++ uint64_t ShiftedVal = (uint64_t)val << LeadingZeros; ++ // Fill in the bits that will be shifted out with 1s. An example where ++ // this helps is trailing one masks with 32 or more ones. This will ++ // generate ADDI -1 and an SRLI. ++ int countFillZero = RecursiveLiImplCount(ShiftedVal) + 1; ++ if (countFillZero < RecursiveLiImplCount(val)) { ++ return countFillZero; ++ } ++ } ++ return RecursiveLiImplCount(val); ++} ++ ++inline int64_t signExtend(uint64_t V, int N) { ++ return int64_t(V << (64 - N)) >> (64 - N); ++} ++ ++void Assembler::RecursiveLiImpl(Register rd, int64_t Val) { ++ if (is_int32(Val)) { ++ // Depending on the active bits in the immediate Value v, the following ++ // instruction sequences are emitted: ++ // ++ // v == 0 : ADDI ++ // v[0,12) != 0 && v[12,32) == 0 : ADDI ++ // v[0,12) == 0 && v[12,32) != 0 : LUI ++ // v[0,32) != 0 : LUI+ADDI(W) ++ int64_t Hi20 = ((Val + 0x800) >> 12) & 0xFFFFF; ++ int64_t Lo12 = Val << 52 >> 52; ++ ++ if (Hi20) { ++ lui(rd, (int32_t)Hi20); ++ } ++ ++ if (Lo12 || Hi20 == 0) { ++ if (Hi20) { ++ addiw(rd, rd, Lo12); ++ } else { ++ addi(rd, zero_reg, Lo12); ++ } ++ } ++ return; ++ } ++ ++ // In the worst case, for a full 64-bit constant, a sequence of 8 ++ // instructions (i.e., LUI+ADDIW+SLLI+ADDI+SLLI+ADDI+SLLI+ADDI) has to be ++ // emitted. Note that the first two instructions (LUI+ADDIW) can contribute ++ // up to 32 bits while the following ADDI instructions contribute up to 12 ++ // bits each. ++ // ++ // On the first glance, implementing this seems to be possible by simply ++ // emitting the most significant 32 bits (LUI+ADDIW) followed by as many ++ // left shift (SLLI) and immediate additions (ADDI) as needed. However, due ++ // to the fact that ADDI performs a sign extended addition, doing it like ++ // that would only be possible when at most 11 bits of the ADDI instructions ++ // are used. Using all 12 bits of the ADDI instructions, like done by GAS, ++ // actually requires that the constant is processed starting with the least ++ // significant bit. ++ // ++ // In the following, constants are processed from LSB to MSB but instruction ++ // emission is performed from MSB to LSB by recursively calling ++ // generateInstSeq. In each recursion, first the lowest 12 bits are removed ++ // from the constant and the optimal shift amount, which can be greater than ++ // 12 bits if the constant is sparse, is determined. Then, the shifted ++ // remaining constant is processed recursively and gets emitted as soon as ++ // it fits into 32 bits. The emission of the shifts and additions is ++ // subsequently performed when the recursion returns. ++ ++ int64_t Lo12 = Val << 52 >> 52; ++ int64_t Hi52 = ((uint64_t)Val + 0x800ull) >> 12; ++ int ShiftAmount = 12 + mozilla::CountTrailingZeroes64((uint64_t)Hi52); ++ Hi52 = signExtend(Hi52 >> (ShiftAmount - 12), 64 - ShiftAmount); ++ ++ // If the remaining bits don't fit in 12 bits, we might be able to reduce ++ // the shift amount in order to use LUI which will zero the lower 12 bits. ++ bool Unsigned = false; ++ if (ShiftAmount > 12 && !is_int12(Hi52)) { ++ if (is_int32((uint64_t)Hi52 << 12)) { ++ // Reduce the shift amount and add zeros to the LSBs so it will match ++ // LUI. ++ ShiftAmount -= 12; ++ Hi52 = (uint64_t)Hi52 << 12; ++ } ++ } ++ RecursiveLi(rd, Hi52); ++ ++ if (Unsigned) { ++ } else { ++ slli(rd, rd, ShiftAmount); ++ } ++ if (Lo12) { ++ addi(rd, rd, Lo12); ++ } ++} ++ ++int Assembler::RecursiveLiImplCount(int64_t Val) { ++ int count = 0; ++ if (is_int32(Val)) { ++ // Depending on the active bits in the immediate Value v, the following ++ // instruction sequences are emitted: ++ // ++ // v == 0 : ADDI ++ // v[0,12) != 0 && v[12,32) == 0 : ADDI ++ // v[0,12) == 0 && v[12,32) != 0 : LUI ++ // v[0,32) != 0 : LUI+ADDI(W) ++ int64_t Hi20 = ((Val + 0x800) >> 12) & 0xFFFFF; ++ int64_t Lo12 = Val << 52 >> 52; ++ ++ if (Hi20) { ++ // lui(rd, (int32_t)Hi20); ++ count++; ++ } ++ ++ if (Lo12 || Hi20 == 0) { ++ // unsigned AddiOpc = (IsRV64 && Hi20) ? RISCV::ADDIW : RISCV::ADDI; ++ // Res.push_back(RISCVMatInt::Inst(AddiOpc, Lo12)); ++ count++; ++ } ++ return count; ++ } ++ ++ // In the worst case, for a full 64-bit constant, a sequence of 8 ++ // instructions (i.e., LUI+ADDIW+SLLI+ADDI+SLLI+ADDI+SLLI+ADDI) has to be ++ // emitted. Note that the first two instructions (LUI+ADDIW) can contribute ++ // up to 32 bits while the following ADDI instructions contribute up to 12 ++ // bits each. ++ // ++ // On the first glance, implementing this seems to be possible by simply ++ // emitting the most significant 32 bits (LUI+ADDIW) followed by as many ++ // left shift (SLLI) and immediate additions (ADDI) as needed. However, due ++ // to the fact that ADDI performs a sign extended addition, doing it like ++ // that would only be possible when at most 11 bits of the ADDI instructions ++ // are used. Using all 12 bits of the ADDI instructions, like done by GAS, ++ // actually requires that the constant is processed starting with the least ++ // significant bit. ++ // ++ // In the following, constants are processed from LSB to MSB but instruction ++ // emission is performed from MSB to LSB by recursively calling ++ // generateInstSeq. In each recursion, first the lowest 12 bits are removed ++ // from the constant and the optimal shift amount, which can be greater than ++ // 12 bits if the constant is sparse, is determined. Then, the shifted ++ // remaining constant is processed recursively and gets emitted as soon as ++ // it fits into 32 bits. The emission of the shifts and additions is ++ // subsequently performed when the recursion returns. ++ ++ int64_t Lo12 = Val << 52 >> 52; ++ int64_t Hi52 = ((uint64_t)Val + 0x800ull) >> 12; ++ int ShiftAmount = 12 + mozilla::CountTrailingZeroes64((uint64_t)Hi52); ++ Hi52 = signExtend(Hi52 >> (ShiftAmount - 12), 64 - ShiftAmount); ++ ++ // If the remaining bits don't fit in 12 bits, we might be able to reduce ++ // the shift amount in order to use LUI which will zero the lower 12 bits. ++ bool Unsigned = false; ++ if (ShiftAmount > 12 && !is_int12(Hi52)) { ++ if (is_int32((uint64_t)Hi52 << 12)) { ++ // Reduce the shift amount and add zeros to the LSBs so it will match ++ // LUI. ++ ShiftAmount -= 12; ++ Hi52 = (uint64_t)Hi52 << 12; ++ } ++ } ++ ++ count += RecursiveLiImplCount(Hi52); ++ ++ if (Unsigned) { ++ } else { ++ // slli(rd, rd, ShiftAmount); ++ count++; ++ } ++ if (Lo12) { ++ // addi(rd, rd, Lo12); ++ count++; ++ } ++ return count; ++} ++ ++} // namespace jit ++} // namespace js +diff --git a/js/src/jit/riscv64/CodeGenerator-riscv64.cpp b/js/src/jit/riscv64/CodeGenerator-riscv64.cpp +new file mode 100644 +index 0000000000..8b63e4a03a +--- /dev/null ++++ b/js/src/jit/riscv64/CodeGenerator-riscv64.cpp +@@ -0,0 +1,2871 @@ ++/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- ++ * vim: set ts=8 sts=2 et sw=2 tw=80: ++ * 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/riscv64/CodeGenerator-riscv64.h" ++ ++#include "mozilla/MathAlgorithms.h" ++ ++#include "jsnum.h" ++ ++#include "jit/CodeGenerator.h" ++#include "jit/InlineScriptTree.h" ++#include "jit/JitRuntime.h" ++#include "jit/MIR.h" ++#include "jit/MIRGraph.h" ++#include "vm/JSContext.h" ++#include "vm/Realm.h" ++#include "vm/Shape.h" ++ ++#include "jit/shared/CodeGenerator-shared-inl.h" ++#include "vm/JSScript-inl.h" ++ ++using namespace js; ++using namespace js::jit; ++ ++using JS::GenericNaN; ++using mozilla::FloorLog2; ++using mozilla::NegativeInfinity; ++ ++// shared ++CodeGeneratorRiscv64::CodeGeneratorRiscv64(MIRGenerator* gen, LIRGraph* graph, ++ MacroAssembler* masm) ++ : CodeGeneratorShared(gen, graph, masm) {} ++ ++Operand CodeGeneratorRiscv64::ToOperand(const LAllocation& a) { ++ if (a.isGeneralReg()) { ++ return Operand(a.toGeneralReg()->reg()); ++ } ++ if (a.isFloatReg()) { ++ return Operand(a.toFloatReg()->reg()); ++ } ++ return Operand(ToAddress(a)); ++} ++ ++Operand CodeGeneratorRiscv64::ToOperand(const LAllocation* a) { ++ return ToOperand(*a); ++} ++ ++Operand CodeGeneratorRiscv64::ToOperand(const LDefinition* def) { ++ return ToOperand(def->output()); ++} ++ ++#ifdef JS_PUNBOX64 ++Operand CodeGeneratorRiscv64::ToOperandOrRegister64( ++ const LInt64Allocation input) { ++ return ToOperand(input.value()); ++} ++#else ++Register64 CodeGeneratorRiscv64::ToOperandOrRegister64( ++ const LInt64Allocation input) { ++ return ToRegister64(input); ++} ++#endif ++ ++void CodeGeneratorRiscv64::branchToBlock(FloatFormat fmt, FloatRegister lhs, ++ FloatRegister rhs, MBasicBlock* mir, ++ Assembler::DoubleCondition cond) { ++ // Skip past trivial blocks. ++ Label* label = skipTrivialBlocks(mir)->lir()->label(); ++ if (fmt == DoubleFloat) { ++ masm.branchDouble(cond, lhs, rhs, label); ++ } else { ++ masm.branchFloat(cond, lhs, rhs, label); ++ } ++} ++ ++void OutOfLineBailout::accept(CodeGeneratorRiscv64* codegen) { ++ codegen->visitOutOfLineBailout(this); ++} ++ ++MoveOperand CodeGeneratorRiscv64::toMoveOperand(LAllocation a) const { ++ if (a.isGeneralReg()) { ++ return MoveOperand(ToRegister(a)); ++ } ++ if (a.isFloatReg()) { ++ return MoveOperand(ToFloatRegister(a)); ++ } ++ MoveOperand::Kind kind = ++ a.isStackArea() ? MoveOperand::EFFECTIVE_ADDRESS : MoveOperand::MEMORY; ++ Address address = ToAddress(a); ++ MOZ_ASSERT((address.offset & 3) == 0); ++ ++ return MoveOperand(address, kind); ++} ++ ++void CodeGeneratorRiscv64::bailoutFrom(Label* label, LSnapshot* snapshot) { ++ MOZ_ASSERT_IF(!masm.oom(), label->used()); ++ MOZ_ASSERT_IF(!masm.oom(), !label->bound()); ++ ++ encode(snapshot); ++ ++ InlineScriptTree* tree = snapshot->mir()->block()->trackedTree(); ++ OutOfLineBailout* ool = new (alloc()) OutOfLineBailout(snapshot); ++ addOutOfLineCode(ool, ++ new (alloc()) BytecodeSite(tree, tree->script()->code())); ++ ++ masm.retarget(label, ool->entry()); ++} ++ ++void CodeGeneratorRiscv64::bailout(LSnapshot* snapshot) { ++ Label label; ++ masm.jump(&label); ++ bailoutFrom(&label, snapshot); ++} ++ ++bool CodeGeneratorRiscv64::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); ++ ++ TrampolinePtr handler = gen->jitRuntime()->getGenericBailoutHandler(); ++ masm.jump(handler); ++ } ++ ++ return !masm.oom(); ++} ++ ++class js::jit::OutOfLineTableSwitch ++ : public OutOfLineCodeBase { ++ MTableSwitch* mir_; ++ CodeLabel jumpLabel_; ++ ++ void accept(CodeGeneratorRiscv64* codegen) { ++ codegen->visitOutOfLineTableSwitch(this); ++ } ++ ++ public: ++ OutOfLineTableSwitch(MTableSwitch* mir) : mir_(mir) {} ++ ++ MTableSwitch* mir() const { return mir_; } ++ ++ CodeLabel* jumpLabel() { return &jumpLabel_; } ++}; ++ ++void CodeGeneratorRiscv64::emitTableSwitchDispatch(MTableSwitch* mir, ++ Register index, ++ Register base) { ++ Label* defaultcase = skipTrivialBlocks(mir->getDefault())->lir()->label(); ++ ++ // Lower value with low value ++ if (mir->low() != 0) { ++ masm.subPtr(Imm32(mir->low()), index); ++ } ++ ++ // Jump to default case if input is out of range ++ int32_t cases = mir->numCases(); ++ masm.branchPtr(Assembler::AboveOrEqual, index, ImmWord(cases), defaultcase); ++ ++ // To fill in the CodeLabels for the case entries, we need to first ++ // generate the case entries (we don't yet know their offsets in the ++ // instruction stream). ++ OutOfLineTableSwitch* ool = new (alloc()) OutOfLineTableSwitch(mir); ++ addOutOfLineCode(ool, mir); ++ ++ // Compute the position where a pointer to the right case stands. ++ masm.ma_li(base, ool->jumpLabel()); ++ ++ BaseIndex pointer(base, index, ScalePointer); ++ ++ // Jump to the right case ++ masm.branchToComputedAddress(pointer); ++} ++ ++void CodeGenerator::visitWasmHeapBase(LWasmHeapBase* ins) { ++ MOZ_ASSERT(ins->instance()->isBogus()); ++ masm.movePtr(HeapReg, ToRegister(ins->output())); ++} ++ ++template ++void CodeGeneratorRiscv64::emitWasmLoad(T* lir) { ++ const MWasmLoad* mir = lir->mir(); ++ UseScratchRegisterScope temps(&masm); ++ Register scratch2 = temps.Acquire(); ++ ++ Register ptr = ToRegister(lir->ptr()); ++ Register ptrScratch = InvalidReg; ++ if (!lir->ptrCopy()->isBogusTemp()) { ++ ptrScratch = ToRegister(lir->ptrCopy()); ++ } ++ ++ if (mir->base()->type() == MIRType::Int32) { ++ masm.move32To64ZeroExtend(ptr, Register64(scratch2)); ++ ptr = scratch2; ++ ptrScratch = ptrScratch != InvalidReg ? scratch2 : InvalidReg; ++ } ++ ++ // ptr is a GPR and is either a 32-bit value zero-extended to 64-bit, or a ++ // true 64-bit value. ++ masm.wasmLoad(mir->access(), HeapReg, ptr, ptrScratch, ++ ToAnyRegister(lir->output())); ++} ++ ++template ++void CodeGeneratorRiscv64::emitWasmStore(T* lir) { ++ const MWasmStore* mir = lir->mir(); ++ UseScratchRegisterScope temps(&masm); ++ Register scratch2 = temps.Acquire(); ++ ++ Register ptr = ToRegister(lir->ptr()); ++ Register ptrScratch = InvalidReg; ++ if (!lir->ptrCopy()->isBogusTemp()) { ++ ptrScratch = ToRegister(lir->ptrCopy()); ++ } ++ ++ if (mir->base()->type() == MIRType::Int32) { ++ masm.move32To64ZeroExtend(ptr, Register64(scratch2)); ++ ptr = scratch2; ++ ptrScratch = ptrScratch != InvalidReg ? scratch2 : InvalidReg; ++ } ++ ++ // ptr is a GPR and is either a 32-bit value zero-extended to 64-bit, or a ++ // true 64-bit value. ++ masm.wasmStore(mir->access(), ToAnyRegister(lir->value()), HeapReg, ptr, ++ ptrScratch); ++} ++ ++void CodeGeneratorRiscv64::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))); ++ ++ // Jump to the invalidator which will replace the current frame. ++ TrampolinePtr thunk = gen->jitRuntime()->getInvalidationThunk(); ++ ++ masm.jump(thunk); ++} ++ ++void CodeGeneratorRiscv64::visitOutOfLineBailout(OutOfLineBailout* ool) { ++ // Push snapshotOffset and make sure stack is aligned. ++ masm.subPtr(Imm32(sizeof(Value)), StackPointer); ++ masm.storePtr(ImmWord(ool->snapshot()->snapshotOffset()), ++ Address(StackPointer, 0)); ++ ++ masm.jump(&deoptLabel_); ++} ++ ++void CodeGeneratorRiscv64::visitOutOfLineTableSwitch( ++ OutOfLineTableSwitch* ool) { ++ MTableSwitch* mir = ool->mir(); ++ masm.nop(); ++ masm.haltingAlign(sizeof(void*)); ++ masm.bind(ool->jumpLabel()); ++ masm.addCodeLabel(*ool->jumpLabel()); ++ BlockTrampolinePoolScope block_trampoline_pool( ++ &masm, mir->numCases() * sizeof(uint64_t)); ++ for (size_t i = 0; i < mir->numCases(); i++) { ++ LBlock* caseblock = skipTrivialBlocks(mir->getCase(i))->lir(); ++ Label* caseheader = caseblock->label(); ++ uint32_t caseoffset = caseheader->offset(); ++ ++ // The entries of the jump table need to be absolute addresses and thus ++ // must be patched after codegen is finished. ++ CodeLabel cl; ++ masm.writeCodePointer(&cl); ++ cl.target()->bind(caseoffset); ++ masm.addCodeLabel(cl); ++ } ++} ++ ++void CodeGeneratorRiscv64::visitOutOfLineWasmTruncateCheck( ++ OutOfLineWasmTruncateCheck* ool) { ++ FloatRegister input = ool->input(); ++ Register output = ool->output(); ++ Register64 output64 = ool->output64(); ++ MIRType fromType = ool->fromType(); ++ MIRType toType = ool->toType(); ++ Label* oolRejoin = ool->rejoin(); ++ TruncFlags flags = ool->flags(); ++ wasm::BytecodeOffset off = ool->bytecodeOffset(); ++ ++ if (fromType == MIRType::Float32) { ++ if (toType == MIRType::Int32) { ++ masm.oolWasmTruncateCheckF32ToI32(input, output, flags, off, oolRejoin); ++ } else if (toType == MIRType::Int64) { ++ masm.oolWasmTruncateCheckF32ToI64(input, output64, flags, off, oolRejoin); ++ } else { ++ MOZ_CRASH("unexpected type"); ++ } ++ } else if (fromType == MIRType::Double) { ++ if (toType == MIRType::Int32) { ++ masm.oolWasmTruncateCheckF64ToI32(input, output, flags, off, oolRejoin); ++ } else if (toType == MIRType::Int64) { ++ masm.oolWasmTruncateCheckF64ToI64(input, output64, flags, off, oolRejoin); ++ } else { ++ MOZ_CRASH("unexpected type"); ++ } ++ } else { ++ MOZ_CRASH("unexpected type"); ++ } ++} ++ ++ValueOperand CodeGeneratorRiscv64::ToValue(LInstruction* ins, size_t pos) { ++ return ValueOperand(ToRegister(ins->getOperand(pos))); ++} ++ ++ValueOperand CodeGeneratorRiscv64::ToTempValue(LInstruction* ins, size_t pos) { ++ return ValueOperand(ToRegister(ins->getTemp(pos))); ++} ++ ++void CodeGenerator::visitBox(LBox* box) { ++ const LAllocation* in = box->getOperand(0); ++ ValueOperand result = ToOutValue(box); ++ ++ masm.moveValue(TypedOrValueRegister(box->type(), ToAnyRegister(in)), result); ++} ++ ++void CodeGenerator::visitUnbox(LUnbox* unbox) { ++ MUnbox* mir = unbox->mir(); ++ ++ Register result = ToRegister(unbox->output()); ++ ++ if (mir->fallible()) { ++ const ValueOperand value = ToValue(unbox, LUnbox::Input); ++ Label bail; ++ switch (mir->type()) { ++ case MIRType::Int32: ++ masm.fallibleUnboxInt32(value, result, &bail); ++ break; ++ case MIRType::Boolean: ++ masm.fallibleUnboxBoolean(value, result, &bail); ++ break; ++ case MIRType::Object: ++ masm.fallibleUnboxObject(value, result, &bail); ++ break; ++ case MIRType::String: ++ masm.fallibleUnboxString(value, result, &bail); ++ break; ++ case MIRType::Symbol: ++ masm.fallibleUnboxSymbol(value, result, &bail); ++ break; ++ case MIRType::BigInt: ++ masm.fallibleUnboxBigInt(value, result, &bail); ++ break; ++ default: ++ MOZ_CRASH("Given MIRType cannot be unboxed."); ++ } ++ bailoutFrom(&bail, unbox->snapshot()); ++ return; ++ } ++ ++ LAllocation* input = unbox->getOperand(LUnbox::Input); ++ if (input->isRegister()) { ++ Register inputReg = ToRegister(input); ++ switch (mir->type()) { ++ case MIRType::Int32: ++ masm.unboxInt32(inputReg, result); ++ break; ++ case MIRType::Boolean: ++ masm.unboxBoolean(inputReg, result); ++ break; ++ case MIRType::Object: ++ masm.unboxObject(inputReg, result); ++ break; ++ case MIRType::String: ++ masm.unboxString(inputReg, result); ++ break; ++ case MIRType::Symbol: ++ masm.unboxSymbol(inputReg, result); ++ break; ++ case MIRType::BigInt: ++ masm.unboxBigInt(inputReg, result); ++ break; ++ default: ++ MOZ_CRASH("Given MIRType cannot be unboxed."); ++ } ++ return; ++ } ++ ++ Address inputAddr = ToAddress(input); ++ switch (mir->type()) { ++ case MIRType::Int32: ++ masm.unboxInt32(inputAddr, result); ++ break; ++ case MIRType::Boolean: ++ masm.unboxBoolean(inputAddr, result); ++ break; ++ case MIRType::Object: ++ masm.unboxObject(inputAddr, result); ++ break; ++ case MIRType::String: ++ masm.unboxString(inputAddr, result); ++ break; ++ case MIRType::Symbol: ++ masm.unboxSymbol(inputAddr, result); ++ break; ++ case MIRType::BigInt: ++ masm.unboxBigInt(inputAddr, result); ++ break; ++ default: ++ MOZ_CRASH("Given MIRType cannot be unboxed."); ++ } ++} ++ ++void CodeGeneratorRiscv64::splitTagForTest(const ValueOperand& value, ++ ScratchTagScope& tag) { ++ masm.splitTag(value.valueReg(), tag); ++} ++ ++void CodeGenerator::visitCompareI64(LCompareI64* lir) { ++ MCompare* mir = lir->mir(); ++ const mozilla::DebugOnly type = mir->compareType(); ++ MOZ_ASSERT(type == MCompare::Compare_Int64 || ++ type == MCompare::Compare_UInt64); ++ ++ const LInt64Allocation lhs = lir->getInt64Operand(LCompareI64::Lhs); ++ const LInt64Allocation rhs = lir->getInt64Operand(LCompareI64::Rhs); ++ Register lhsReg = ToRegister64(lhs).reg; ++ Register output = ToRegister(lir->output()); ++ bool isSigned = mir->compareType() == MCompare::Compare_Int64; ++ Assembler::Condition cond = JSOpToCondition(lir->jsop(), isSigned); ++ ++ if (IsConstant(rhs)) { ++ masm.cmpPtrSet(cond, lhsReg, ImmWord(ToInt64(rhs)), output); ++ } else if (rhs.value().isGeneralReg()) { ++ masm.cmpPtrSet(cond, lhsReg, ToRegister64(rhs).reg, output); ++ } else { ++ masm.cmpPtrSet(cond, lhsReg, ToAddress(rhs.value()), output); ++ } ++} ++ ++void CodeGenerator::visitCompareI64AndBranch(LCompareI64AndBranch* lir) { ++ MCompare* mir = lir->cmpMir(); ++ const mozilla::DebugOnly type = mir->compareType(); ++ MOZ_ASSERT(type == MCompare::Compare_Int64 || ++ type == MCompare::Compare_UInt64); ++ ++ const LInt64Allocation lhs = lir->getInt64Operand(LCompareI64::Lhs); ++ const LInt64Allocation rhs = lir->getInt64Operand(LCompareI64::Rhs); ++ Register lhsReg = ToRegister64(lhs).reg; ++ bool isSigned = mir->compareType() == MCompare::Compare_Int64; ++ Assembler::Condition cond = JSOpToCondition(lir->jsop(), isSigned); ++ ++ if (IsConstant(rhs)) { ++ emitBranch(lhsReg, ImmWord(ToInt64(rhs)), cond, lir->ifTrue(), ++ lir->ifFalse()); ++ } else if (rhs.value().isGeneralReg()) { ++ emitBranch(lhsReg, ToRegister64(rhs).reg, cond, lir->ifTrue(), ++ lir->ifFalse()); ++ } else { ++ emitBranch(lhsReg, ToAddress(rhs.value()), cond, lir->ifTrue(), ++ lir->ifFalse()); ++ } ++} ++ ++void CodeGenerator::visitCompare(LCompare* comp) { ++ MCompare* mir = comp->mir(); ++ Assembler::Condition cond = JSOpToCondition(mir->compareType(), comp->jsop()); ++ const LAllocation* left = comp->getOperand(0); ++ const LAllocation* right = comp->getOperand(1); ++ const LDefinition* def = comp->getDef(0); ++ ++ if (mir->compareType() == MCompare::Compare_Object || ++ mir->compareType() == MCompare::Compare_Symbol || ++ mir->compareType() == MCompare::Compare_UIntPtr || ++ mir->compareType() == MCompare::Compare_RefOrNull) { ++ if (right->isConstant()) { ++ MOZ_ASSERT(mir->compareType() == MCompare::Compare_UIntPtr); ++ masm.cmpPtrSet(cond, ToRegister(left), Imm32(ToInt32(right)), ++ ToRegister(def)); ++ } else if (right->isGeneralReg()) { ++ masm.cmpPtrSet(cond, ToRegister(left), ToRegister(right), ++ ToRegister(def)); ++ } else { ++ masm.cmpPtrSet(cond, ToRegister(left), ToAddress(right), ToRegister(def)); ++ } ++ return; ++ } ++ ++ 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 CodeGenerator::visitCompareAndBranch(LCompareAndBranch* comp) { ++ const MCompare* mir = comp->cmpMir(); ++ const MCompare::CompareType type = mir->compareType(); ++ const LAllocation* lhs = comp->left(); ++ const LAllocation* rhs = comp->right(); ++ MBasicBlock* ifTrue = comp->ifTrue(); ++ MBasicBlock* ifFalse = comp->ifFalse(); ++ Register lhsReg = ToRegister(lhs); ++ const Assembler::Condition cond = JSOpToCondition(type, comp->jsop()); ++ ++ if (type == MCompare::Compare_Object || type == MCompare::Compare_Symbol || ++ type == MCompare::Compare_UIntPtr || ++ type == MCompare::Compare_RefOrNull) { ++ if (rhs->isConstant()) { ++ emitBranch(ToRegister(lhs), Imm32(ToInt32(rhs)), cond, ifTrue, ifFalse); ++ } else if (rhs->isGeneralReg()) { ++ emitBranch(lhsReg, ToRegister(rhs), cond, ifTrue, ifFalse); ++ } else { ++ MOZ_CRASH("NYI"); ++ } ++ return; ++ } ++ ++ if (rhs->isConstant()) { ++ emitBranch(lhsReg, Imm32(ToInt32(comp->right())), cond, ifTrue, ifFalse); ++ } else if (comp->right()->isGeneralReg()) { ++ emitBranch(lhsReg, ToRegister(rhs), cond, ifTrue, ifFalse); ++ } else { ++ // TODO(loong64): emitBranch with 32-bit comparision ++ ScratchRegisterScope scratch(masm); ++ masm.load32(ToAddress(rhs), scratch); ++ emitBranch(lhsReg, Register(scratch), cond, ifTrue, ifFalse); ++ } ++} ++ ++void CodeGenerator::visitDivOrModI64(LDivOrModI64* lir) { ++ Register lhs = ToRegister(lir->lhs()); ++ Register rhs = ToRegister(lir->rhs()); ++ Register output = ToRegister(lir->output()); ++ ++ Label done; ++ ++ // Handle divide by zero. ++ if (lir->canBeDivideByZero()) { ++ Label nonZero; ++ masm.ma_b(rhs, rhs, &nonZero, Assembler::NonZero); ++ masm.wasmTrap(wasm::Trap::IntegerDivideByZero, lir->bytecodeOffset()); ++ masm.bind(&nonZero); ++ } ++ ++ // Handle an integer overflow exception from INT64_MIN / -1. ++ if (lir->canBeNegativeOverflow()) { ++ Label notOverflow; ++ masm.branchPtr(Assembler::NotEqual, lhs, ImmWord(INT64_MIN), ¬Overflow); ++ masm.branchPtr(Assembler::NotEqual, rhs, ImmWord(-1), ¬Overflow); ++ if (lir->mir()->isMod()) { ++ masm.ma_xor(output, output, Operand(output)); ++ } else { ++ masm.wasmTrap(wasm::Trap::IntegerOverflow, lir->bytecodeOffset()); ++ } ++ masm.jump(&done); ++ masm.bind(¬Overflow); ++ } ++ ++ if (lir->mir()->isMod()) { ++ masm.ma_mod64(output, lhs, rhs); ++ } else { ++ masm.ma_div64(output, lhs, rhs); ++ } ++ ++ masm.bind(&done); ++} ++ ++void CodeGenerator::visitUDivOrModI64(LUDivOrModI64* lir) { ++ Register lhs = ToRegister(lir->lhs()); ++ Register rhs = ToRegister(lir->rhs()); ++ Register output = ToRegister(lir->output()); ++ ++ Label done; ++ ++ // Prevent divide by zero. ++ if (lir->canBeDivideByZero()) { ++ Label nonZero; ++ masm.ma_b(rhs, rhs, &nonZero, Assembler::NonZero); ++ masm.wasmTrap(wasm::Trap::IntegerDivideByZero, lir->bytecodeOffset()); ++ masm.bind(&nonZero); ++ } ++ ++ if (lir->mir()->isMod()) { ++ masm.ma_modu64(output, lhs, rhs); ++ } else { ++ masm.ma_divu64(output, lhs, rhs); ++ } ++ ++ masm.bind(&done); ++} ++ ++void CodeGeneratorRiscv64::emitBigIntDiv(LBigIntDiv* ins, Register dividend, ++ Register divisor, Register output, ++ Label* fail) { ++ // Callers handle division by zero and integer overflow. ++ masm.ma_div64(/* result= */ dividend, dividend, divisor); ++ ++ // Create and return the result. ++ masm.newGCBigInt(output, divisor, initialBigIntHeap(), fail); ++ masm.initializeBigInt(output, dividend); ++} ++ ++void CodeGeneratorRiscv64::emitBigIntMod(LBigIntMod* ins, Register dividend, ++ Register divisor, Register output, ++ Label* fail) { ++ // Callers handle division by zero and integer overflow. ++ masm.ma_mod64(/* result= */ dividend, dividend, divisor); ++ ++ // Create and return the result. ++ masm.newGCBigInt(output, divisor, initialBigIntHeap(), fail); ++ masm.initializeBigInt(output, dividend); ++} ++ ++void CodeGenerator::visitWasmLoadI64(LWasmLoadI64* lir) { ++ const MWasmLoad* mir = lir->mir(); ++ ++ Register ptrScratch = InvalidReg; ++ if (!lir->ptrCopy()->isBogusTemp()) { ++ ptrScratch = ToRegister(lir->ptrCopy()); ++ } ++ ++ masm.wasmLoadI64(mir->access(), HeapReg, ToRegister(lir->ptr()), ptrScratch, ++ ToOutRegister64(lir)); ++} ++ ++void CodeGenerator::visitWasmStoreI64(LWasmStoreI64* lir) { ++ const MWasmStore* mir = lir->mir(); ++ ++ Register ptrScratch = InvalidReg; ++ if (!lir->ptrCopy()->isBogusTemp()) { ++ ptrScratch = ToRegister(lir->ptrCopy()); ++ } ++ ++ masm.wasmStoreI64(mir->access(), ToRegister64(lir->value()), HeapReg, ++ ToRegister(lir->ptr()), ptrScratch); ++} ++ ++void CodeGenerator::visitWasmSelectI64(LWasmSelectI64* lir) { ++ MOZ_ASSERT(lir->mir()->type() == MIRType::Int64); ++ ++ Register cond = ToRegister(lir->condExpr()); ++ const LInt64Allocation falseExpr = lir->falseExpr(); ++ ++ Register64 out = ToOutRegister64(lir); ++ MOZ_ASSERT(ToRegister64(lir->trueExpr()) == out, ++ "true expr is reused for input"); ++ ++ if (falseExpr.value().isRegister()) { ++ masm.moveIfZero(out.reg, ToRegister(falseExpr.value()), cond); ++ } else { ++ Label done; ++ masm.ma_b(cond, cond, &done, Assembler::NonZero, ShortJump); ++ masm.loadPtr(ToAddress(falseExpr.value()), out.reg); ++ masm.bind(&done); ++ } ++} ++ ++void CodeGenerator::visitWasmReinterpretFromI64(LWasmReinterpretFromI64* lir) { ++ MOZ_ASSERT(lir->mir()->type() == MIRType::Double); ++ MOZ_ASSERT(lir->mir()->input()->type() == MIRType::Int64); ++ masm.fmv_d_x(ToFloatRegister(lir->output()), ToRegister(lir->input())); ++} ++ ++void CodeGenerator::visitWasmReinterpretToI64(LWasmReinterpretToI64* lir) { ++ MOZ_ASSERT(lir->mir()->type() == MIRType::Int64); ++ MOZ_ASSERT(lir->mir()->input()->type() == MIRType::Double); ++ masm.fmv_x_d(ToRegister(lir->output()), ToFloatRegister(lir->input())); ++} ++ ++void CodeGenerator::visitExtendInt32ToInt64(LExtendInt32ToInt64* lir) { ++ const LAllocation* input = lir->getOperand(0); ++ Register output = ToRegister(lir->output()); ++ ++ if (lir->mir()->isUnsigned()) { ++ masm.move32To64ZeroExtend(ToRegister(input), Register64(output)); ++ } else { ++ masm.slliw(output, ToRegister(input), 0); ++ } ++} ++ ++void CodeGenerator::visitWrapInt64ToInt32(LWrapInt64ToInt32* lir) { ++ const LAllocation* input = lir->getOperand(0); ++ Register output = ToRegister(lir->output()); ++ ++ if (lir->mir()->bottomHalf()) { ++ if (input->isMemory()) { ++ masm.load32(ToAddress(input), output); ++ } else { ++ masm.slliw(output, ToRegister(input), 0); ++ } ++ } else { ++ MOZ_CRASH("Not implemented."); ++ } ++} ++ ++void CodeGenerator::visitSignExtendInt64(LSignExtendInt64* lir) { ++ Register64 input = ToRegister64(lir->getInt64Operand(0)); ++ Register64 output = ToOutRegister64(lir); ++ switch (lir->mode()) { ++ case MSignExtendInt64::Byte: ++ masm.move32To64SignExtend(input.reg, output); ++ masm.move8SignExtend(output.reg, output.reg); ++ break; ++ case MSignExtendInt64::Half: ++ masm.move32To64SignExtend(input.reg, output); ++ masm.move16SignExtend(output.reg, output.reg); ++ break; ++ case MSignExtendInt64::Word: ++ masm.move32To64SignExtend(input.reg, output); ++ break; ++ } ++} ++ ++void CodeGenerator::visitWasmExtendU32Index(LWasmExtendU32Index* lir) { ++ Register input = ToRegister(lir->input()); ++ Register output = ToRegister(lir->output()); ++ MOZ_ASSERT(input == output); ++ masm.move32To64ZeroExtend(input, Register64(output)); ++} ++ ++void CodeGenerator::visitWasmWrapU32Index(LWasmWrapU32Index* lir) { ++ Register input = ToRegister(lir->input()); ++ Register output = ToRegister(lir->output()); ++ MOZ_ASSERT(input == output); ++ masm.move64To32(Register64(input), output); ++} ++ ++void CodeGenerator::visitClzI64(LClzI64* lir) { ++ Register64 input = ToRegister64(lir->getInt64Operand(0)); ++ Register64 output = ToOutRegister64(lir); ++ masm.clz64(input, output.reg); ++} ++ ++void CodeGenerator::visitCtzI64(LCtzI64* lir) { ++ Register64 input = ToRegister64(lir->getInt64Operand(0)); ++ Register64 output = ToOutRegister64(lir); ++ masm.ctz64(input, output.reg); ++} ++ ++void CodeGenerator::visitNotI64(LNotI64* lir) { ++ Register64 input = ToRegister64(lir->getInt64Operand(0)); ++ Register output = ToRegister(lir->output()); ++ ++ masm.ma_cmp_set(output, input.reg, zero, Assembler::Equal); ++} ++ ++void CodeGenerator::visitWasmTruncateToInt64(LWasmTruncateToInt64* lir) { ++ FloatRegister input = ToFloatRegister(lir->input()); ++ Register64 output = ToOutRegister64(lir); ++ ++ MWasmTruncateToInt64* mir = lir->mir(); ++ MIRType fromType = mir->input()->type(); ++ ++ MOZ_ASSERT(fromType == MIRType::Double || fromType == MIRType::Float32); ++ ++ auto* ool = new (alloc()) OutOfLineWasmTruncateCheck(mir, input, output); ++ addOutOfLineCode(ool, mir); ++ ++ Label* oolEntry = ool->entry(); ++ Label* oolRejoin = ool->rejoin(); ++ bool isSaturating = mir->isSaturating(); ++ ++ if (fromType == MIRType::Double) { ++ if (mir->isUnsigned()) { ++ masm.wasmTruncateDoubleToUInt64(input, output, isSaturating, oolEntry, ++ oolRejoin, InvalidFloatReg); ++ } else { ++ masm.wasmTruncateDoubleToInt64(input, output, isSaturating, oolEntry, ++ oolRejoin, InvalidFloatReg); ++ } ++ } else { ++ if (mir->isUnsigned()) { ++ masm.wasmTruncateFloat32ToUInt64(input, output, isSaturating, oolEntry, ++ oolRejoin, InvalidFloatReg); ++ } else { ++ masm.wasmTruncateFloat32ToInt64(input, output, isSaturating, oolEntry, ++ oolRejoin, InvalidFloatReg); ++ } ++ } ++} ++ ++void CodeGenerator::visitInt64ToFloatingPoint(LInt64ToFloatingPoint* lir) { ++ Register64 input = ToRegister64(lir->getInt64Operand(0)); ++ FloatRegister output = ToFloatRegister(lir->output()); ++ ++ MIRType outputType = lir->mir()->type(); ++ MOZ_ASSERT(outputType == MIRType::Double || outputType == MIRType::Float32); ++ ++ if (outputType == MIRType::Double) { ++ if (lir->mir()->isUnsigned()) { ++ masm.convertUInt64ToDouble(input, output, Register::Invalid()); ++ } else { ++ masm.convertInt64ToDouble(input, output); ++ } ++ } else { ++ if (lir->mir()->isUnsigned()) { ++ masm.convertUInt64ToFloat32(input, output, Register::Invalid()); ++ } else { ++ masm.convertInt64ToFloat32(input, output); ++ } ++ } ++} ++ ++void CodeGenerator::visitTestI64AndBranch(LTestI64AndBranch* lir) { ++ Register64 input = ToRegister64(lir->getInt64Operand(0)); ++ MBasicBlock* ifTrue = lir->ifTrue(); ++ MBasicBlock* ifFalse = lir->ifFalse(); ++ ++ emitBranch(input.reg, Imm32(0), Assembler::NonZero, ifTrue, ifFalse); ++} ++ ++void CodeGenerator::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 CodeGenerator::visitMinMaxD(LMinMaxD* ins) { ++ FloatRegister first = ToFloatRegister(ins->first()); ++ FloatRegister second = ToFloatRegister(ins->second()); ++ ++ MOZ_ASSERT(first == ToFloatRegister(ins->output())); ++ ++ if (ins->mir()->isMax()) { ++ masm.maxDouble(second, first, true); ++ } else { ++ masm.minDouble(second, first, true); ++ } ++} ++ ++void CodeGenerator::visitMinMaxF(LMinMaxF* ins) { ++ FloatRegister first = ToFloatRegister(ins->first()); ++ FloatRegister second = ToFloatRegister(ins->second()); ++ ++ MOZ_ASSERT(first == ToFloatRegister(ins->output())); ++ ++ if (ins->mir()->isMax()) { ++ masm.maxFloat32(second, first, true); ++ } else { ++ masm.minFloat32(second, first, true); ++ } ++} ++ ++void CodeGenerator::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_add32(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs))); ++ } else { ++ masm.addw(ToRegister(dest), ToRegister(lhs), ToRegister(rhs)); ++ } ++ return; ++ } ++ ++ Label overflow; ++ if (rhs->isConstant()) { ++ masm.ma_add32TestOverflow(ToRegister(dest), ToRegister(lhs), ++ Imm32(ToInt32(rhs)), &overflow); ++ } else { ++ masm.ma_add32TestOverflow(ToRegister(dest), ToRegister(lhs), ++ ToRegister(rhs), &overflow); ++ } ++ ++ bailoutFrom(&overflow, ins->snapshot()); ++} ++ ++void CodeGenerator::visitAddI64(LAddI64* lir) { ++ const LInt64Allocation lhs = lir->getInt64Operand(LAddI64::Lhs); ++ const LInt64Allocation rhs = lir->getInt64Operand(LAddI64::Rhs); ++ ++ MOZ_ASSERT(ToOutRegister64(lir) == ToRegister64(lhs)); ++ ++ if (IsConstant(rhs)) { ++ masm.add64(Imm64(ToInt64(rhs)), ToRegister64(lhs)); ++ return; ++ } ++ ++ masm.add64(ToOperandOrRegister64(rhs), ToRegister64(lhs)); ++} ++ ++void CodeGenerator::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_sub32(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs))); ++ } else { ++ masm.ma_sub32(ToRegister(dest), ToRegister(lhs), ToRegister(rhs)); ++ } ++ return; ++ } ++ ++ Label overflow; ++ if (rhs->isConstant()) { ++ masm.ma_sub32TestOverflow(ToRegister(dest), ToRegister(lhs), ++ Imm32(ToInt32(rhs)), &overflow); ++ } else { ++ masm.ma_sub32TestOverflow(ToRegister(dest), ToRegister(lhs), ++ ToRegister(rhs), &overflow); ++ } ++ ++ bailoutFrom(&overflow, ins->snapshot()); ++} ++ ++void CodeGenerator::visitSubI64(LSubI64* lir) { ++ const LInt64Allocation lhs = lir->getInt64Operand(LSubI64::Lhs); ++ const LInt64Allocation rhs = lir->getInt64Operand(LSubI64::Rhs); ++ ++ MOZ_ASSERT(ToOutRegister64(lir) == ToRegister64(lhs)); ++ ++ if (IsConstant(rhs)) { ++ masm.sub64(Imm64(ToInt64(rhs)), ToRegister64(lhs)); ++ return; ++ } ++ ++ masm.sub64(ToOperandOrRegister64(rhs), ToRegister64(lhs)); ++} ++ ++void CodeGenerator::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_sub32(dest, zero, src); ++ break; ++ case 0: ++ masm.move32(zero, dest); ++ break; ++ case 1: ++ masm.move32(src, dest); ++ break; ++ case 2: ++ if (mul->canOverflow()) { ++ Label mulTwoOverflow; ++ masm.ma_add32TestOverflow(dest, src, src, &mulTwoOverflow); ++ ++ bailoutFrom(&mulTwoOverflow, ins->snapshot()); ++ } else { ++ masm.addw(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.slliw(dest, src, shift % 32); ++ 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) { ++ ScratchRegisterScope scratch(masm); ++ // dest = lhs * pow(2, shift) ++ masm.slliw(dest, src, shift % 32); ++ // 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.sraiw(scratch, dest, shift % 32); ++ bailoutCmp32(Assembler::NotEqual, src, Register(scratch), ++ ins->snapshot()); ++ return; ++ } ++ } ++ ++ if (mul->canOverflow()) { ++ Label mulConstOverflow; ++ masm.ma_mul32TestOverflow(dest, ToRegister(lhs), Imm32(ToInt32(rhs)), ++ &mulConstOverflow); ++ ++ bailoutFrom(&mulConstOverflow, ins->snapshot()); ++ } else { ++ masm.ma_mul32(dest, src, Imm32(ToInt32(rhs))); ++ } ++ break; ++ } ++ } else { ++ Label multRegOverflow; ++ ++ if (mul->canOverflow()) { ++ masm.ma_mul32TestOverflow(dest, ToRegister(lhs), ToRegister(rhs), ++ &multRegOverflow); ++ bailoutFrom(&multRegOverflow, ins->snapshot()); ++ } else { ++ masm.mulw(dest, ToRegister(lhs), ToRegister(rhs)); ++ } ++ ++ 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 ++ UseScratchRegisterScope temps(&masm); ++ Register scratch = temps.Acquire(); ++ masm.or_(scratch, ToRegister(lhs), ToRegister(rhs)); ++ bailoutCmp32(Assembler::Signed, scratch, scratch, ins->snapshot()); ++ ++ masm.bind(&done); ++ } ++ } ++} ++ ++void CodeGenerator::visitMulI64(LMulI64* lir) { ++ const LInt64Allocation lhs = lir->getInt64Operand(LMulI64::Lhs); ++ const LInt64Allocation rhs = lir->getInt64Operand(LMulI64::Rhs); ++ const Register64 output = ToOutRegister64(lir); ++ ++ if (IsConstant(rhs)) { ++ int64_t constant = ToInt64(rhs); ++ switch (constant) { ++ case -1: ++ masm.neg64(ToRegister64(lhs)); ++ return; ++ case 0: ++ masm.xor64(ToRegister64(lhs), ToRegister64(lhs)); ++ return; ++ case 1: ++ // nop ++ return; ++ default: ++ if (constant > 0) { ++ if (mozilla::IsPowerOfTwo(static_cast(constant + 1))) { ++ masm.move64(ToRegister64(lhs), output); ++ masm.lshift64(Imm32(FloorLog2(constant + 1)), output); ++ masm.sub64(ToRegister64(lhs), output); ++ return; ++ } else if (mozilla::IsPowerOfTwo( ++ static_cast(constant - 1))) { ++ masm.move64(ToRegister64(lhs), output); ++ masm.lshift64(Imm32(FloorLog2(constant - 1u)), output); ++ masm.add64(ToRegister64(lhs), output); ++ return; ++ } ++ // Use shift if constant is power of 2. ++ int32_t shift = mozilla::FloorLog2(constant); ++ if (int64_t(1) << shift == constant) { ++ masm.lshift64(Imm32(shift), ToRegister64(lhs)); ++ return; ++ } ++ } ++ Register temp = ToTempRegisterOrInvalid(lir->temp()); ++ masm.mul64(Imm64(constant), ToRegister64(lhs), temp); ++ } ++ } else { ++ Register temp = ToTempRegisterOrInvalid(lir->temp()); ++ masm.mul64(ToOperandOrRegister64(rhs), ToRegister64(lhs), temp); ++ } ++} ++ ++void CodeGenerator::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->trapOnError()) { ++ Label nonZero; ++ masm.ma_b(rhs, rhs, &nonZero, Assembler::NonZero); ++ masm.wasmTrap(wasm::Trap::IntegerDivideByZero, mir->bytecodeOffset()); ++ masm.bind(&nonZero); ++ } else 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_branch(&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->trapOnError()) { ++ Label ok; ++ masm.ma_b(rhs, temp, &ok, Assembler::NotEqual); ++ masm.wasmTrap(wasm::Trap::IntegerOverflow, mir->bytecodeOffset()); ++ masm.bind(&ok); ++ } else 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_branch(&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.ma_div32(dest, lhs, rhs); ++ } else { ++ MOZ_ASSERT(mir->fallible()); ++ ++ Label remainderNonZero; ++ masm.ma_div_branch_overflow(dest, lhs, rhs, &remainderNonZero); ++ bailoutFrom(&remainderNonZero, ins->snapshot()); ++ } ++ ++ masm.bind(&done); ++} ++ ++void CodeGenerator::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.slliw(tmp, lhs, (32 - shift) % 32); ++ bailoutCmp32(Assembler::NonZero, tmp, tmp, ins->snapshot()); ++ } ++ ++ if (!mir->canBeNegativeDividend()) { ++ // Numerator is unsigned, so needs no adjusting. Do the shift. ++ masm.sraiw(dest, lhs, shift % 32); ++ 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.sraiw(tmp, lhs, 31); ++ masm.srliw(tmp, tmp, (32 - shift) % 32); ++ masm.add32(lhs, tmp); ++ } else { ++ masm.srliw(tmp, lhs, (32 - shift) % 32); ++ masm.add32(lhs, tmp); ++ } ++ ++ // Do the shift. ++ masm.sraiw(dest, tmp, shift % 32); ++ } else { ++ masm.move32(lhs, dest); ++ } ++} ++ ++void CodeGenerator::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_branch(&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()) { ++ if (mir->trapOnError()) { ++ Label nonZero; ++ masm.ma_b(rhs, rhs, &nonZero, Assembler::NonZero); ++ masm.wasmTrap(wasm::Trap::IntegerDivideByZero, mir->bytecodeOffset()); ++ masm.bind(&nonZero); ++ } else { ++ Label skip; ++ masm.ma_b(rhs, Imm32(0), &skip, Assembler::NotEqual, ShortJump); ++ masm.move32(Imm32(0), dest); ++ masm.ma_branch(&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_branch(&done, ShortJump); ++ masm.bind(&skip); ++ } else { ++ MOZ_ASSERT(mir->fallible()); ++ bailoutCmp32(Assembler::Equal, lhs, Imm32(0), ins->snapshot()); ++ } ++ masm.bind(¬Negative); ++ } ++ ++ masm.ma_mod32(dest, lhs, rhs); ++ ++ // 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 CodeGenerator::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_branch(&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 CodeGenerator::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 CodeGenerator::visitBitNotI(LBitNotI* ins) { ++ const LAllocation* input = ins->getOperand(0); ++ const LDefinition* dest = ins->getDef(0); ++ MOZ_ASSERT(!input->isConstant()); ++ ++ masm.nor(ToRegister(dest), ToRegister(input), zero); ++} ++ ++void CodeGenerator::visitBitNotI64(LBitNotI64* ins) { ++ const LAllocation* input = ins->getOperand(0); ++ MOZ_ASSERT(!input->isConstant()); ++ Register inputReg = ToRegister(input); ++ MOZ_ASSERT(inputReg == ToRegister(ins->output())); ++ masm.nor(inputReg, inputReg, zero); ++} ++ ++void CodeGenerator::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.or_(ToRegister(dest), ToRegister(lhs), ToRegister(rhs)); ++ masm.slliw(ToRegister(dest), ToRegister(dest), 0); ++ } ++ break; ++ case JSOp::BitXor: ++ if (rhs->isConstant()) { ++ masm.ma_xor(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs))); ++ } else { ++ masm.ma_xor(ToRegister(dest), ToRegister(lhs), ++ Operand(ToRegister(rhs))); ++ masm.slliw(ToRegister(dest), ToRegister(dest), 0); ++ } ++ break; ++ case JSOp::BitAnd: ++ if (rhs->isConstant()) { ++ masm.ma_and(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs))); ++ } else { ++ masm.and_(ToRegister(dest), ToRegister(lhs), ToRegister(rhs)); ++ masm.slliw(ToRegister(dest), ToRegister(dest), 0); ++ } ++ break; ++ default: ++ MOZ_CRASH("unexpected binary opcode"); ++ } ++} ++ ++void CodeGenerator::visitBitOpI64(LBitOpI64* lir) { ++ const LInt64Allocation lhs = lir->getInt64Operand(LBitOpI64::Lhs); ++ const LInt64Allocation rhs = lir->getInt64Operand(LBitOpI64::Rhs); ++ ++ MOZ_ASSERT(ToOutRegister64(lir) == ToRegister64(lhs)); ++ ++ switch (lir->bitop()) { ++ case JSOp::BitOr: ++ if (IsConstant(rhs)) { ++ masm.or64(Imm64(ToInt64(rhs)), ToRegister64(lhs)); ++ } else { ++ masm.or64(ToOperandOrRegister64(rhs), ToRegister64(lhs)); ++ } ++ break; ++ case JSOp::BitXor: ++ if (IsConstant(rhs)) { ++ masm.xor64(Imm64(ToInt64(rhs)), ToRegister64(lhs)); ++ } else { ++ masm.xor64(ToOperandOrRegister64(rhs), ToRegister64(lhs)); ++ } ++ break; ++ case JSOp::BitAnd: ++ if (IsConstant(rhs)) { ++ masm.and64(Imm64(ToInt64(rhs)), ToRegister64(lhs)); ++ } else { ++ masm.and64(ToOperandOrRegister64(rhs), ToRegister64(lhs)); ++ } ++ break; ++ default: ++ MOZ_CRASH("unexpected binary opcode"); ++ } ++} ++ ++void CodeGenerator::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.slliw(dest, lhs, shift % 32); ++ } else { ++ masm.move32(lhs, dest); ++ } ++ break; ++ case JSOp::Rsh: ++ if (shift) { ++ masm.sraiw(dest, lhs, shift % 32); ++ } else { ++ masm.move32(lhs, dest); ++ } ++ break; ++ case JSOp::Ursh: ++ if (shift) { ++ masm.srliw(dest, lhs, shift % 32); ++ } else { ++ // x >>> 0 can overflow. ++ if (ins->mir()->toUrsh()->fallible()) { ++ bailoutCmp32(Assembler::LessThan, lhs, Imm32(0), ins->snapshot()); ++ } ++ masm.move32(lhs, dest); ++ } ++ 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.sllw(dest, lhs, dest); ++ break; ++ case JSOp::Rsh: ++ masm.sraw(dest, lhs, dest); ++ break; ++ case JSOp::Ursh: ++ masm.srlw(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 CodeGenerator::visitShiftI64(LShiftI64* lir) { ++ const LInt64Allocation lhs = lir->getInt64Operand(LShiftI64::Lhs); ++ LAllocation* rhs = lir->getOperand(LShiftI64::Rhs); ++ ++ MOZ_ASSERT(ToOutRegister64(lir) == ToRegister64(lhs)); ++ ++ if (rhs->isConstant()) { ++ int32_t shift = int32_t(rhs->toConstant()->toInt64() & 0x3F); ++ switch (lir->bitop()) { ++ case JSOp::Lsh: ++ if (shift) { ++ masm.lshift64(Imm32(shift), ToRegister64(lhs)); ++ } ++ break; ++ case JSOp::Rsh: ++ if (shift) { ++ masm.rshift64Arithmetic(Imm32(shift), ToRegister64(lhs)); ++ } ++ break; ++ case JSOp::Ursh: ++ if (shift) { ++ masm.rshift64(Imm32(shift), ToRegister64(lhs)); ++ } ++ break; ++ default: ++ MOZ_CRASH("Unexpected shift op"); ++ } ++ return; ++ } ++ ++ switch (lir->bitop()) { ++ case JSOp::Lsh: ++ masm.lshift64(ToRegister(rhs), ToRegister64(lhs)); ++ break; ++ case JSOp::Rsh: ++ masm.rshift64Arithmetic(ToRegister(rhs), ToRegister64(lhs)); ++ break; ++ case JSOp::Ursh: ++ masm.rshift64(ToRegister(rhs), ToRegister64(lhs)); ++ break; ++ default: ++ MOZ_CRASH("Unexpected shift op"); ++ } ++} ++ ++void CodeGenerator::visitRotateI64(LRotateI64* lir) { ++ MRotate* mir = lir->mir(); ++ LAllocation* count = lir->count(); ++ ++ Register64 input = ToRegister64(lir->input()); ++ Register64 output = ToOutRegister64(lir); ++ Register temp = ToTempRegisterOrInvalid(lir->temp()); ++ ++ MOZ_ASSERT(input == output); ++ ++ if (count->isConstant()) { ++ int32_t c = int32_t(count->toConstant()->toInt64() & 0x3F); ++ if (!c) { ++ return; ++ } ++ if (mir->isLeftRotate()) { ++ masm.rotateLeft64(Imm32(c), input, output, temp); ++ } else { ++ masm.rotateRight64(Imm32(c), input, output, temp); ++ } ++ } else { ++ if (mir->isLeftRotate()) { ++ masm.rotateLeft64(ToRegister(count), input, output, temp); ++ } else { ++ masm.rotateRight64(ToRegister(count), input, output, temp); ++ } ++ } ++} ++ ++void CodeGenerator::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.srliw(temp, lhs, ToInt32(rhs) % 32); ++ } else { ++ masm.srlw(temp, lhs, ToRegister(rhs)); ++ } ++ ++ masm.convertUInt32ToDouble(temp, out); ++} ++ ++void CodeGenerator::visitClzI(LClzI* ins) { ++ Register input = ToRegister(ins->input()); ++ Register output = ToRegister(ins->output()); ++ ++ masm.Clz32(output, input); ++} ++ ++void CodeGenerator::visitCtzI(LCtzI* ins) { ++ Register input = ToRegister(ins->input()); ++ Register output = ToRegister(ins->output()); ++ ++ masm.Ctz32(output, input); ++} ++ ++void CodeGenerator::visitPopcntI(LPopcntI* ins) { ++ Register input = ToRegister(ins->input()); ++ Register output = ToRegister(ins->output()); ++ Register tmp = ToRegister(ins->temp0()); ++ ++ masm.Popcnt32(input, output, tmp); ++} ++ ++void CodeGenerator::visitPopcntI64(LPopcntI64* ins) { ++ Register64 input = ToRegister64(ins->getInt64Operand(0)); ++ Register64 output = ToOutRegister64(ins); ++ Register tmp = ToRegister(ins->getTemp(0)); ++ ++ masm.Popcnt64(input.scratchReg(), output.scratchReg(), tmp); ++} ++ ++void CodeGenerator::visitPowHalfD(LPowHalfD* ins) { ++ FloatRegister input = ToFloatRegister(ins->input()); ++ FloatRegister output = ToFloatRegister(ins->output()); ++ ScratchDoubleScope fpscratch(masm); ++ ++ Label done, skip; ++ ++ // Masm.pow(-Infinity, 0.5) == Infinity. ++ masm.loadConstantDouble(NegativeInfinity(), fpscratch); ++ UseScratchRegisterScope temps(&masm); ++ Register scratch = temps.Acquire(); ++ ++ masm.ma_compareF64(scratch, Assembler::DoubleNotEqualOrUnordered, input, ++ fpscratch); ++ masm.ma_branch(&skip, Assembler::Equal, scratch, Operand(1)); ++ // masm.ma_bc_d(input, fpscratch, &skip, Assembler::DoubleNotEqualOrUnordered, ++ // ShortJump); ++ masm.fneg_d(output, fpscratch); ++ masm.ma_branch(&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, fpscratch); ++ masm.fadd_d(output, input, fpscratch); ++ masm.fsqrt_d(output, output); ++ ++ masm.bind(&done); ++} ++ ++void CodeGenerator::visitMathD(LMathD* math) { ++ FloatRegister src1 = ToFloatRegister(math->getOperand(0)); ++ FloatRegister src2 = ToFloatRegister(math->getOperand(1)); ++ FloatRegister output = ToFloatRegister(math->getDef(0)); ++ ++ switch (math->jsop()) { ++ case JSOp::Add: ++ masm.fadd_d(output, src1, src2); ++ break; ++ case JSOp::Sub: ++ masm.fsub_d(output, src1, src2); ++ break; ++ case JSOp::Mul: ++ masm.fmul_d(output, src1, src2); ++ break; ++ case JSOp::Div: ++ masm.fdiv_d(output, src1, src2); ++ break; ++ default: ++ MOZ_CRASH("unexpected opcode"); ++ } ++} ++ ++void CodeGenerator::visitMathF(LMathF* math) { ++ FloatRegister src1 = ToFloatRegister(math->getOperand(0)); ++ FloatRegister src2 = ToFloatRegister(math->getOperand(1)); ++ FloatRegister output = ToFloatRegister(math->getDef(0)); ++ ++ switch (math->jsop()) { ++ case JSOp::Add: ++ masm.fadd_s(output, src1, src2); ++ break; ++ case JSOp::Sub: ++ masm.fsub_s(output, src1, src2); ++ break; ++ case JSOp::Mul: ++ masm.fmul_s(output, src1, src2); ++ break; ++ case JSOp::Div: ++ masm.fdiv_s(output, src1, src2); ++ break; ++ default: ++ MOZ_CRASH("unexpected opcode"); ++ } ++} ++ ++void CodeGenerator::visitTruncateDToInt32(LTruncateDToInt32* ins) { ++ emitTruncateDouble(ToFloatRegister(ins->input()), ToRegister(ins->output()), ++ ins->mir()); ++} ++ ++void CodeGenerator::visitTruncateFToInt32(LTruncateFToInt32* ins) { ++ emitTruncateFloat32(ToFloatRegister(ins->input()), ToRegister(ins->output()), ++ ins->mir()); ++} ++ ++void CodeGenerator::visitWasmBuiltinTruncateDToInt32( ++ LWasmBuiltinTruncateDToInt32* lir) { ++ emitTruncateDouble(ToFloatRegister(lir->getOperand(0)), ++ ToRegister(lir->getDef(0)), lir->mir()); ++} ++ ++void CodeGenerator::visitWasmBuiltinTruncateFToInt32( ++ LWasmBuiltinTruncateFToInt32* lir) { ++ emitTruncateFloat32(ToFloatRegister(lir->getOperand(0)), ++ ToRegister(lir->getDef(0)), lir->mir()); ++} ++ ++void CodeGenerator::visitWasmTruncateToInt32(LWasmTruncateToInt32* lir) { ++ auto input = ToFloatRegister(lir->input()); ++ auto output = ToRegister(lir->output()); ++ ++ MWasmTruncateToInt32* mir = lir->mir(); ++ MIRType fromType = mir->input()->type(); ++ ++ MOZ_ASSERT(fromType == MIRType::Double || fromType == MIRType::Float32); ++ ++ auto* ool = new (alloc()) OutOfLineWasmTruncateCheck(mir, input, output); ++ addOutOfLineCode(ool, mir); ++ ++ Label* oolEntry = ool->entry(); ++ if (mir->isUnsigned()) { ++ if (fromType == MIRType::Double) { ++ masm.wasmTruncateDoubleToUInt32(input, output, mir->isSaturating(), ++ oolEntry); ++ } else if (fromType == MIRType::Float32) { ++ masm.wasmTruncateFloat32ToUInt32(input, output, mir->isSaturating(), ++ oolEntry); ++ } else { ++ MOZ_CRASH("unexpected type"); ++ } ++ ++ masm.bind(ool->rejoin()); ++ return; ++ } ++ ++ if (fromType == MIRType::Double) { ++ masm.wasmTruncateDoubleToInt32(input, output, mir->isSaturating(), ++ oolEntry); ++ } else if (fromType == MIRType::Float32) { ++ masm.wasmTruncateFloat32ToInt32(input, output, mir->isSaturating(), ++ oolEntry); ++ } else { ++ MOZ_CRASH("unexpected type"); ++ } ++ ++ masm.bind(ool->rejoin()); ++} ++ ++void CodeGenerator::visitCopySignF(LCopySignF* ins) { ++ FloatRegister lhs = ToFloatRegister(ins->getOperand(0)); ++ FloatRegister rhs = ToFloatRegister(ins->getOperand(1)); ++ FloatRegister output = ToFloatRegister(ins->getDef(0)); ++ ++ masm.fsgnj_s(output, lhs, rhs); ++} ++ ++void CodeGenerator::visitCopySignD(LCopySignD* ins) { ++ FloatRegister lhs = ToFloatRegister(ins->getOperand(0)); ++ FloatRegister rhs = ToFloatRegister(ins->getOperand(1)); ++ FloatRegister output = ToFloatRegister(ins->getDef(0)); ++ ++ masm.fsgnj_d(output, lhs, rhs); ++} ++ ++void CodeGenerator::visitValue(LValue* value) { ++ const ValueOperand out = ToOutValue(value); ++ ++ masm.moveValue(value->value(), out); ++} ++ ++void CodeGenerator::visitDouble(LDouble* ins) { ++ const LDefinition* out = ins->getDef(0); ++ ++ masm.loadConstantDouble(ins->value(), ToFloatRegister(out)); ++} ++ ++void CodeGenerator::visitFloat32(LFloat32* ins) { ++ const LDefinition* out = ins->getDef(0); ++ masm.loadConstantFloat32(ins->value(), ToFloatRegister(out)); ++} ++ ++void CodeGenerator::visitTestDAndBranch(LTestDAndBranch* test) { ++ FloatRegister input = ToFloatRegister(test->input()); ++ ScratchDoubleScope fpscratch(masm); ++ ++ MBasicBlock* ifTrue = test->ifTrue(); ++ MBasicBlock* ifFalse = test->ifFalse(); ++ ++ masm.loadConstantDouble(0.0, fpscratch); ++ // If 0, or NaN, the result is false. ++ if (isNextBlock(ifFalse->lir())) { ++ branchToBlock(DoubleFloat, input, fpscratch, ifTrue, ++ Assembler::DoubleNotEqual); ++ } else { ++ branchToBlock(DoubleFloat, input, fpscratch, ifFalse, ++ Assembler::DoubleEqualOrUnordered); ++ jumpToBlock(ifTrue); ++ } ++} ++ ++void CodeGenerator::visitTestFAndBranch(LTestFAndBranch* test) { ++ FloatRegister input = ToFloatRegister(test->input()); ++ ScratchFloat32Scope fpscratch(masm); ++ ++ MBasicBlock* ifTrue = test->ifTrue(); ++ MBasicBlock* ifFalse = test->ifFalse(); ++ ++ masm.loadConstantFloat32(0.0f, fpscratch); ++ // If 0, or NaN, the result is false. ++ ++ if (isNextBlock(ifFalse->lir())) { ++ branchToBlock(SingleFloat, input, fpscratch, ifTrue, ++ Assembler::DoubleNotEqual); ++ } else { ++ branchToBlock(SingleFloat, input, fpscratch, ifFalse, ++ Assembler::DoubleEqualOrUnordered); ++ jumpToBlock(ifTrue); ++ } ++} ++ ++void CodeGenerator::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_compareF64(dest, cond, lhs, rhs); ++} ++ ++void CodeGenerator::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_compareF32(dest, cond, lhs, rhs); ++} ++ ++void CodeGenerator::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(DoubleFloat, lhs, rhs, ifTrue, cond); ++ } else { ++ branchToBlock(DoubleFloat, lhs, rhs, ifFalse, ++ Assembler::InvertCondition(cond)); ++ jumpToBlock(ifTrue); ++ } ++} ++ ++void CodeGenerator::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(SingleFloat, lhs, rhs, ifTrue, cond); ++ } else { ++ branchToBlock(SingleFloat, lhs, rhs, ifFalse, ++ Assembler::InvertCondition(cond)); ++ jumpToBlock(ifTrue); ++ } ++} ++ ++void CodeGenerator::visitBitAndAndBranch(LBitAndAndBranch* lir) { ++ ScratchRegisterScope scratch(masm); ++ if (lir->right()->isConstant()) { ++ masm.ma_and(scratch, ToRegister(lir->left()), Imm32(ToInt32(lir->right()))); ++ } else { ++ masm.ma_and(scratch, ToRegister(lir->left()), ToRegister(lir->right())); ++ } ++ emitBranch(scratch, Register(scratch), lir->cond(), lir->ifTrue(), ++ lir->ifFalse()); ++} ++ ++void CodeGenerator::visitWasmUint32ToDouble(LWasmUint32ToDouble* lir) { ++ masm.convertUInt32ToDouble(ToRegister(lir->input()), ++ ToFloatRegister(lir->output())); ++} ++ ++void CodeGenerator::visitWasmUint32ToFloat32(LWasmUint32ToFloat32* lir) { ++ masm.convertUInt32ToFloat32(ToRegister(lir->input()), ++ ToFloatRegister(lir->output())); ++} ++ ++void CodeGenerator::visitNotI(LNotI* ins) { ++ masm.cmp32Set(Assembler::Equal, ToRegister(ins->input()), Imm32(0), ++ ToRegister(ins->output())); ++} ++ ++void CodeGenerator::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()); ++ ScratchDoubleScope fpscratch(masm); ++ ++ masm.loadConstantDouble(0.0, fpscratch); ++ masm.ma_compareF64(dest, Assembler::DoubleEqualOrUnordered, in, fpscratch); ++} ++ ++void CodeGenerator::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()); ++ ScratchFloat32Scope fpscratch(masm); ++ ++ masm.loadConstantFloat32(0.0f, fpscratch); ++ masm.ma_compareF32(dest, Assembler::DoubleEqualOrUnordered, in, fpscratch); ++} ++ ++void CodeGenerator::visitMemoryBarrier(LMemoryBarrier* ins) { ++ masm.memoryBarrier(ins->type()); ++} ++ ++void CodeGenerator::visitWasmLoad(LWasmLoad* lir) { emitWasmLoad(lir); } ++ ++void CodeGenerator::visitWasmStore(LWasmStore* lir) { emitWasmStore(lir); } ++ ++void CodeGenerator::visitAsmJSLoadHeap(LAsmJSLoadHeap* ins) { ++ const MAsmJSLoadHeap* mir = ins->mir(); ++ const LAllocation* ptr = ins->ptr(); ++ const LDefinition* out = ins->output(); ++ const LAllocation* boundsCheckLimit = ins->boundsCheckLimit(); ++ ++ bool isSigned; ++ int size; ++ bool isFloat = false; ++ switch (mir->access().type()) { ++ 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; ++ } ++ ++ Label done, outOfRange; ++ masm.wasmBoundsCheck32(Assembler::AboveOrEqual, ptrReg, ++ ToRegister(boundsCheckLimit), &outOfRange); ++ // 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_branch(&done, ShortJump); ++ masm.bind(&outOfRange); ++ // Offset is out of range. Load default values. ++ if (isFloat) { ++ if (size == 32) { ++ masm.loadConstantFloat32(float(GenericNaN()), ToFloatRegister(out)); ++ } else { ++ masm.loadConstantDouble(GenericNaN(), ToFloatRegister(out)); ++ } ++ } else { ++ masm.move32(Imm32(0), ToRegister(out)); ++ } ++ masm.bind(&done); ++} ++ ++void CodeGenerator::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins) { ++ const MAsmJSStoreHeap* mir = ins->mir(); ++ const LAllocation* value = ins->value(); ++ const LAllocation* ptr = ins->ptr(); ++ const LAllocation* boundsCheckLimit = ins->boundsCheckLimit(); ++ ++ bool isSigned; ++ int size; ++ bool isFloat = false; ++ switch (mir->access().type()) { ++ 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) { ++ FloatRegister freg = ToFloatRegister(value); ++ Address addr(HeapReg, ptrImm); ++ if (size == 32) { ++ masm.storeFloat32(freg, addr); ++ } else { ++ masm.storeDouble(freg, addr); ++ } ++ } 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) { ++ FloatRegister freg = ToFloatRegister(value); ++ BaseIndex bi(HeapReg, ptrReg, TimesOne); ++ if (size == 32) { ++ masm.storeFloat32(freg, bi); ++ } else { ++ masm.storeDouble(freg, bi); ++ } ++ } else { ++ masm.ma_store(ToRegister(value), BaseIndex(HeapReg, ptrReg, TimesOne), ++ static_cast(size), ++ isSigned ? SignExtend : ZeroExtend); ++ } ++ return; ++ } ++ ++ Label outOfRange; ++ masm.wasmBoundsCheck32(Assembler::AboveOrEqual, ptrReg, ++ ToRegister(boundsCheckLimit), &outOfRange); ++ ++ // 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(&outOfRange); ++} ++ ++void CodeGenerator::visitWasmCompareExchangeHeap( ++ LWasmCompareExchangeHeap* ins) { ++ MWasmCompareExchangeHeap* mir = ins->mir(); ++ Register ptrReg = ToRegister(ins->ptr()); ++ BaseIndex srcAddr(HeapReg, ptrReg, TimesOne, mir->access().offset()); ++ MOZ_ASSERT(ins->addrTemp()->isBogusTemp()); ++ ++ Register oldval = ToRegister(ins->oldValue()); ++ Register newval = ToRegister(ins->newValue()); ++ Register valueTemp = ToTempRegisterOrInvalid(ins->valueTemp()); ++ Register offsetTemp = ToTempRegisterOrInvalid(ins->offsetTemp()); ++ Register maskTemp = ToTempRegisterOrInvalid(ins->maskTemp()); ++ ++ masm.wasmCompareExchange(mir->access(), srcAddr, oldval, newval, valueTemp, ++ offsetTemp, maskTemp, ToRegister(ins->output())); ++} ++ ++void CodeGenerator::visitWasmAtomicExchangeHeap(LWasmAtomicExchangeHeap* ins) { ++ MWasmAtomicExchangeHeap* mir = ins->mir(); ++ Register ptrReg = ToRegister(ins->ptr()); ++ Register value = ToRegister(ins->value()); ++ BaseIndex srcAddr(HeapReg, ptrReg, TimesOne, mir->access().offset()); ++ MOZ_ASSERT(ins->addrTemp()->isBogusTemp()); ++ ++ Register valueTemp = ToTempRegisterOrInvalid(ins->valueTemp()); ++ Register offsetTemp = ToTempRegisterOrInvalid(ins->offsetTemp()); ++ Register maskTemp = ToTempRegisterOrInvalid(ins->maskTemp()); ++ ++ masm.wasmAtomicExchange(mir->access(), srcAddr, value, valueTemp, offsetTemp, ++ maskTemp, ToRegister(ins->output())); ++} ++ ++void CodeGenerator::visitWasmAtomicBinopHeap(LWasmAtomicBinopHeap* ins) { ++ MOZ_ASSERT(ins->mir()->hasUses()); ++ MOZ_ASSERT(ins->addrTemp()->isBogusTemp()); ++ ++ MWasmAtomicBinopHeap* mir = ins->mir(); ++ Register ptrReg = ToRegister(ins->ptr()); ++ Register valueTemp = ToTempRegisterOrInvalid(ins->valueTemp()); ++ Register offsetTemp = ToTempRegisterOrInvalid(ins->offsetTemp()); ++ Register maskTemp = ToTempRegisterOrInvalid(ins->maskTemp()); ++ ++ BaseIndex srcAddr(HeapReg, ptrReg, TimesOne, mir->access().offset()); ++ ++ masm.wasmAtomicFetchOp(mir->access(), mir->operation(), ++ ToRegister(ins->value()), srcAddr, valueTemp, ++ offsetTemp, maskTemp, ToRegister(ins->output())); ++} ++ ++void CodeGenerator::visitWasmAtomicBinopHeapForEffect( ++ LWasmAtomicBinopHeapForEffect* ins) { ++ MOZ_ASSERT(!ins->mir()->hasUses()); ++ MOZ_ASSERT(ins->addrTemp()->isBogusTemp()); ++ ++ MWasmAtomicBinopHeap* mir = ins->mir(); ++ Register ptrReg = ToRegister(ins->ptr()); ++ Register valueTemp = ToTempRegisterOrInvalid(ins->valueTemp()); ++ Register offsetTemp = ToTempRegisterOrInvalid(ins->offsetTemp()); ++ Register maskTemp = ToTempRegisterOrInvalid(ins->maskTemp()); ++ ++ BaseIndex srcAddr(HeapReg, ptrReg, TimesOne, mir->access().offset()); ++ masm.wasmAtomicEffectOp(mir->access(), mir->operation(), ++ ToRegister(ins->value()), srcAddr, valueTemp, ++ offsetTemp, maskTemp); ++} ++ ++void CodeGenerator::visitWasmStackArg(LWasmStackArg* ins) { ++ const MWasmStackArg* 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 if (mir->input()->type() == MIRType::Double) { ++ masm.storeDouble(ToFloatRegister(ins->arg()), ++ Address(StackPointer, mir->spOffset())); ++ } else { ++ masm.storeFloat32(ToFloatRegister(ins->arg()), ++ Address(StackPointer, mir->spOffset())); ++ } ++ } ++} ++ ++void CodeGenerator::visitWasmStackArgI64(LWasmStackArgI64* ins) { ++ const MWasmStackArg* mir = ins->mir(); ++ Address dst(StackPointer, mir->spOffset()); ++ if (IsConstant(ins->arg())) { ++ masm.store64(Imm64(ToInt64(ins->arg())), dst); ++ } else { ++ masm.store64(ToRegister64(ins->arg()), dst); ++ } ++} ++ ++void CodeGenerator::visitWasmSelect(LWasmSelect* ins) { ++ MIRType mirType = ins->mir()->type(); ++ ++ Register cond = ToRegister(ins->condExpr()); ++ const LAllocation* falseExpr = ins->falseExpr(); ++ ++ if (mirType == MIRType::Int32 || mirType == MIRType::RefOrNull) { ++ Register out = ToRegister(ins->output()); ++ MOZ_ASSERT(ToRegister(ins->trueExpr()) == out, ++ "true expr input is reused for output"); ++ if (falseExpr->isRegister()) { ++ masm.moveIfZero(out, ToRegister(falseExpr), cond); ++ } else { ++ masm.cmp32Load32(Assembler::Zero, cond, cond, ToAddress(falseExpr), out); ++ } ++ return; ++ } ++ ++ FloatRegister out = ToFloatRegister(ins->output()); ++ MOZ_ASSERT(ToFloatRegister(ins->trueExpr()) == out, ++ "true expr input is reused for output"); ++ ++ if (falseExpr->isFloatReg()) { ++ if (mirType == MIRType::Float32) { ++ masm.ma_fmovz(SingleFloat, out, ToFloatRegister(falseExpr), cond); ++ } else if (mirType == MIRType::Double) { ++ masm.ma_fmovz(DoubleFloat, out, ToFloatRegister(falseExpr), cond); ++ } else { ++ MOZ_CRASH("unhandled type in visitWasmSelect!"); ++ } ++ } else { ++ Label done; ++ masm.ma_b(cond, cond, &done, Assembler::NonZero, ShortJump); ++ ++ if (mirType == MIRType::Float32) { ++ masm.loadFloat32(ToAddress(falseExpr), out); ++ } else if (mirType == MIRType::Double) { ++ masm.loadDouble(ToAddress(falseExpr), out); ++ } else { ++ MOZ_CRASH("unhandled type in visitWasmSelect!"); ++ } ++ ++ masm.bind(&done); ++ } ++} ++ ++// We expect to handle only the case where compare is {U,}Int32 and select is ++// {U,}Int32, and the "true" input is reused for the output. ++void CodeGenerator::visitWasmCompareAndSelect(LWasmCompareAndSelect* ins) { ++ bool cmpIs32bit = ins->compareType() == MCompare::Compare_Int32 || ++ ins->compareType() == MCompare::Compare_UInt32; ++ bool selIs32bit = ins->mir()->type() == MIRType::Int32; ++ ++ MOZ_RELEASE_ASSERT( ++ cmpIs32bit && selIs32bit, ++ "CodeGenerator::visitWasmCompareAndSelect: unexpected types"); ++ ++ Register trueExprAndDest = ToRegister(ins->output()); ++ MOZ_ASSERT(ToRegister(ins->ifTrueExpr()) == trueExprAndDest, ++ "true expr input is reused for output"); ++ ++ Assembler::Condition cond = Assembler::InvertCondition( ++ JSOpToCondition(ins->compareType(), ins->jsop())); ++ const LAllocation* rhs = ins->rightExpr(); ++ const LAllocation* falseExpr = ins->ifFalseExpr(); ++ Register lhs = ToRegister(ins->leftExpr()); ++ ++ masm.cmp32Move32(cond, lhs, ToRegister(rhs), ToRegister(falseExpr), ++ trueExprAndDest); ++} ++ ++void CodeGenerator::visitWasmReinterpret(LWasmReinterpret* lir) { ++ MOZ_ASSERT(gen->compilingWasm()); ++ MWasmReinterpret* ins = lir->mir(); ++ ++ MIRType to = ins->type(); ++ mozilla::DebugOnly from = ins->input()->type(); ++ ++ switch (to) { ++ case MIRType::Int32: ++ MOZ_ASSERT(from == MIRType::Float32); ++ masm.fmv_x_w(ToRegister(lir->output()), ToFloatRegister(lir->input())); ++ break; ++ case MIRType::Float32: ++ MOZ_ASSERT(from == MIRType::Int32); ++ masm.fmv_w_x(ToFloatRegister(lir->output()), ToRegister(lir->input())); ++ break; ++ case MIRType::Double: ++ case MIRType::Int64: ++ MOZ_CRASH("not handled by this LIR opcode"); ++ default: ++ MOZ_CRASH("unexpected WasmReinterpret"); ++ } ++} ++ ++void CodeGenerator::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()) { ++ if (ins->trapOnError()) { ++ Label nonZero; ++ masm.ma_b(rhs, rhs, &nonZero, Assembler::NonZero); ++ masm.wasmTrap(wasm::Trap::IntegerDivideByZero, ins->bytecodeOffset()); ++ masm.bind(&nonZero); ++ } else { ++ // Infinity|0 == 0 ++ Label notzero; ++ masm.ma_b(rhs, rhs, ¬zero, Assembler::NonZero, ShortJump); ++ masm.move32(Imm32(0), output); ++ masm.ma_branch(&done, ShortJump); ++ masm.bind(¬zero); ++ } ++ } else { ++ bailoutCmp32(Assembler::Equal, rhs, Imm32(0), ins->snapshot()); ++ } ++ } ++ ++ masm.ma_modu32(output, lhs, rhs); ++ ++ // 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.ma_divu32(output, lhs, rhs); ++ } ++ ++ if (!ins->mir()->isTruncated()) { ++ bailoutCmp32(Assembler::LessThan, output, Imm32(0), ins->snapshot()); ++ } ++ ++ masm.bind(&done); ++} ++ ++void CodeGenerator::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 CodeGenerator::visitNegI(LNegI* ins) { ++ Register input = ToRegister(ins->input()); ++ Register output = ToRegister(ins->output()); ++ ++ masm.ma_sub32(output, zero, input); ++} ++ ++void CodeGenerator::visitNegI64(LNegI64* ins) { ++ Register64 input = ToRegister64(ins->getInt64Operand(0)); ++ MOZ_ASSERT(input == ToOutRegister64(ins)); ++ masm.neg64(input); ++} ++ ++void CodeGenerator::visitNegD(LNegD* ins) { ++ FloatRegister input = ToFloatRegister(ins->input()); ++ FloatRegister output = ToFloatRegister(ins->output()); ++ ++ masm.fneg_d(output, input); ++} ++ ++void CodeGenerator::visitNegF(LNegF* ins) { ++ FloatRegister input = ToFloatRegister(ins->input()); ++ FloatRegister output = ToFloatRegister(ins->output()); ++ ++ masm.fneg_s(output, input); ++} ++ ++void CodeGenerator::visitWasmAddOffset(LWasmAddOffset* lir) { ++ MWasmAddOffset* mir = lir->mir(); ++ Register base = ToRegister(lir->base()); ++ Register out = ToRegister(lir->output()); ++ ++ Label ok; ++ masm.ma_add32TestCarry(Assembler::CarryClear, out, base, Imm32(mir->offset()), ++ &ok); ++ masm.wasmTrap(wasm::Trap::OutOfBounds, mir->bytecodeOffset()); ++ masm.bind(&ok); ++} ++ ++void CodeGenerator::visitWasmAddOffset64(LWasmAddOffset64* lir) { ++ MWasmAddOffset* mir = lir->mir(); ++ Register64 base = ToRegister64(lir->base()); ++ Register64 out = ToOutRegister64(lir); ++ ++ Label ok; ++ masm.ma_addPtrTestCarry(Assembler::CarryClear, out.reg, base.reg, ++ ImmWord(mir->offset()), &ok); ++ masm.wasmTrap(wasm::Trap::OutOfBounds, mir->bytecodeOffset()); ++ masm.bind(&ok); ++} ++ ++void CodeGenerator::visitAtomicTypedArrayElementBinop( ++ LAtomicTypedArrayElementBinop* lir) { ++ MOZ_ASSERT(!lir->mir()->isForEffect()); ++ ++ AnyRegister output = ToAnyRegister(lir->output()); ++ Register elements = ToRegister(lir->elements()); ++ Register outTemp = ToTempRegisterOrInvalid(lir->temp2()); ++ Register valueTemp = ToTempRegisterOrInvalid(lir->valueTemp()); ++ Register offsetTemp = ToTempRegisterOrInvalid(lir->offsetTemp()); ++ Register maskTemp = ToTempRegisterOrInvalid(lir->maskTemp()); ++ Register value = ToRegister(lir->value()); ++ Scalar::Type arrayType = lir->mir()->arrayType(); ++ ++ if (lir->index()->isConstant()) { ++ Address mem = ToAddress(elements, lir->index(), arrayType); ++ masm.atomicFetchOpJS(arrayType, Synchronization::Full(), ++ lir->mir()->operation(), value, mem, valueTemp, ++ offsetTemp, maskTemp, outTemp, output); ++ } else { ++ BaseIndex mem(elements, ToRegister(lir->index()), ++ ScaleFromScalarType(arrayType)); ++ masm.atomicFetchOpJS(arrayType, Synchronization::Full(), ++ lir->mir()->operation(), value, mem, valueTemp, ++ offsetTemp, maskTemp, outTemp, output); ++ } ++} ++ ++void CodeGenerator::visitAtomicTypedArrayElementBinopForEffect( ++ LAtomicTypedArrayElementBinopForEffect* lir) { ++ MOZ_ASSERT(lir->mir()->isForEffect()); ++ ++ Register elements = ToRegister(lir->elements()); ++ Register valueTemp = ToTempRegisterOrInvalid(lir->valueTemp()); ++ Register offsetTemp = ToTempRegisterOrInvalid(lir->offsetTemp()); ++ Register maskTemp = ToTempRegisterOrInvalid(lir->maskTemp()); ++ Register value = ToRegister(lir->value()); ++ Scalar::Type arrayType = lir->mir()->arrayType(); ++ ++ if (lir->index()->isConstant()) { ++ Address mem = ToAddress(elements, lir->index(), arrayType); ++ masm.atomicEffectOpJS(arrayType, Synchronization::Full(), ++ lir->mir()->operation(), value, mem, valueTemp, ++ offsetTemp, maskTemp); ++ } else { ++ BaseIndex mem(elements, ToRegister(lir->index()), ++ ScaleFromScalarType(arrayType)); ++ masm.atomicEffectOpJS(arrayType, Synchronization::Full(), ++ lir->mir()->operation(), value, mem, valueTemp, ++ offsetTemp, maskTemp); ++ } ++} ++ ++void CodeGenerator::visitCompareExchangeTypedArrayElement( ++ LCompareExchangeTypedArrayElement* lir) { ++ Register elements = ToRegister(lir->elements()); ++ AnyRegister output = ToAnyRegister(lir->output()); ++ Register outTemp = ToTempRegisterOrInvalid(lir->temp()); ++ ++ Register oldval = ToRegister(lir->oldval()); ++ Register newval = ToRegister(lir->newval()); ++ Register valueTemp = ToTempRegisterOrInvalid(lir->valueTemp()); ++ Register offsetTemp = ToTempRegisterOrInvalid(lir->offsetTemp()); ++ Register maskTemp = ToTempRegisterOrInvalid(lir->maskTemp()); ++ Scalar::Type arrayType = lir->mir()->arrayType(); ++ ++ if (lir->index()->isConstant()) { ++ Address dest = ToAddress(elements, lir->index(), arrayType); ++ masm.compareExchangeJS(arrayType, Synchronization::Full(), dest, oldval, ++ newval, valueTemp, offsetTemp, maskTemp, outTemp, ++ output); ++ } else { ++ BaseIndex dest(elements, ToRegister(lir->index()), ++ ScaleFromScalarType(arrayType)); ++ masm.compareExchangeJS(arrayType, Synchronization::Full(), dest, oldval, ++ newval, valueTemp, offsetTemp, maskTemp, outTemp, ++ output); ++ } ++} ++ ++void CodeGenerator::visitAtomicExchangeTypedArrayElement( ++ LAtomicExchangeTypedArrayElement* lir) { ++ Register elements = ToRegister(lir->elements()); ++ AnyRegister output = ToAnyRegister(lir->output()); ++ Register outTemp = ToTempRegisterOrInvalid(lir->temp()); ++ ++ Register value = ToRegister(lir->value()); ++ Register valueTemp = ToTempRegisterOrInvalid(lir->valueTemp()); ++ Register offsetTemp = ToTempRegisterOrInvalid(lir->offsetTemp()); ++ Register maskTemp = ToTempRegisterOrInvalid(lir->maskTemp()); ++ Scalar::Type arrayType = lir->mir()->arrayType(); ++ ++ if (lir->index()->isConstant()) { ++ Address dest = ToAddress(elements, lir->index(), arrayType); ++ masm.atomicExchangeJS(arrayType, Synchronization::Full(), dest, value, ++ valueTemp, offsetTemp, maskTemp, outTemp, output); ++ } else { ++ BaseIndex dest(elements, ToRegister(lir->index()), ++ ScaleFromScalarType(arrayType)); ++ masm.atomicExchangeJS(arrayType, Synchronization::Full(), dest, value, ++ valueTemp, offsetTemp, maskTemp, outTemp, output); ++ } ++} ++ ++void CodeGenerator::visitCompareExchangeTypedArrayElement64( ++ LCompareExchangeTypedArrayElement64* lir) { ++ Register elements = ToRegister(lir->elements()); ++ Register oldval = ToRegister(lir->oldval()); ++ Register newval = ToRegister(lir->newval()); ++ Register64 temp1 = ToRegister64(lir->temp1()); ++ Register64 temp2 = ToRegister64(lir->temp2()); ++ Register out = ToRegister(lir->output()); ++ Register64 tempOut(out); ++ Scalar::Type arrayType = lir->mir()->arrayType(); ++ ++ masm.loadBigInt64(oldval, temp1); ++ masm.loadBigInt64(newval, tempOut); ++ ++ if (lir->index()->isConstant()) { ++ Address dest = ToAddress(elements, lir->index(), arrayType); ++ masm.compareExchange64(Synchronization::Full(), dest, temp1, tempOut, ++ temp2); ++ } else { ++ BaseIndex dest(elements, ToRegister(lir->index()), ++ ScaleFromScalarType(arrayType)); ++ masm.compareExchange64(Synchronization::Full(), dest, temp1, tempOut, ++ temp2); ++ } ++ ++ emitCreateBigInt(lir, arrayType, temp2, out, temp1.scratchReg()); ++} ++ ++void CodeGenerator::visitAtomicExchangeTypedArrayElement64( ++ LAtomicExchangeTypedArrayElement64* lir) { ++ Register elements = ToRegister(lir->elements()); ++ Register value = ToRegister(lir->value()); ++ Register64 temp1 = ToRegister64(lir->temp1()); ++ Register64 temp2 = Register64(ToRegister(lir->temp2())); ++ Register out = ToRegister(lir->output()); ++ Scalar::Type arrayType = lir->mir()->arrayType(); ++ ++ masm.loadBigInt64(value, temp1); ++ ++ if (lir->index()->isConstant()) { ++ Address dest = ToAddress(elements, lir->index(), arrayType); ++ masm.atomicExchange64(Synchronization::Full(), dest, temp1, temp2); ++ } else { ++ BaseIndex dest(elements, ToRegister(lir->index()), ++ ScaleFromScalarType(arrayType)); ++ masm.atomicExchange64(Synchronization::Full(), dest, temp1, temp2); ++ } ++ ++ emitCreateBigInt(lir, arrayType, temp2, out, temp1.scratchReg()); ++} ++ ++void CodeGenerator::visitAtomicTypedArrayElementBinop64( ++ LAtomicTypedArrayElementBinop64* lir) { ++ MOZ_ASSERT(lir->mir()->hasUses()); ++ ++ Register elements = ToRegister(lir->elements()); ++ Register value = ToRegister(lir->value()); ++ Register64 temp1 = ToRegister64(lir->temp1()); ++ Register64 temp2 = ToRegister64(lir->temp2()); ++ Register out = ToRegister(lir->output()); ++ Register64 tempOut = Register64(out); ++ ++ Scalar::Type arrayType = lir->mir()->arrayType(); ++ AtomicOp atomicOp = lir->mir()->operation(); ++ ++ masm.loadBigInt64(value, temp1); ++ ++ if (lir->index()->isConstant()) { ++ Address dest = ToAddress(elements, lir->index(), arrayType); ++ masm.atomicFetchOp64(Synchronization::Full(), atomicOp, temp1, dest, ++ tempOut, temp2); ++ } else { ++ BaseIndex dest(elements, ToRegister(lir->index()), ++ ScaleFromScalarType(arrayType)); ++ masm.atomicFetchOp64(Synchronization::Full(), atomicOp, temp1, dest, ++ tempOut, temp2); ++ } ++ ++ emitCreateBigInt(lir, arrayType, temp2, out, temp1.scratchReg()); ++} ++ ++void CodeGenerator::visitAtomicTypedArrayElementBinopForEffect64( ++ LAtomicTypedArrayElementBinopForEffect64* lir) { ++ MOZ_ASSERT(!lir->mir()->hasUses()); ++ ++ Register elements = ToRegister(lir->elements()); ++ Register value = ToRegister(lir->value()); ++ Register64 temp1 = ToRegister64(lir->temp1()); ++ Register64 temp2 = ToRegister64(lir->temp2()); ++ ++ Scalar::Type arrayType = lir->mir()->arrayType(); ++ AtomicOp atomicOp = lir->mir()->operation(); ++ ++ masm.loadBigInt64(value, temp1); ++ ++ if (lir->index()->isConstant()) { ++ Address dest = ToAddress(elements, lir->index(), arrayType); ++ masm.atomicEffectOp64(Synchronization::Full(), atomicOp, temp1, dest, ++ temp2); ++ } else { ++ BaseIndex dest(elements, ToRegister(lir->index()), ++ ScaleFromScalarType(arrayType)); ++ masm.atomicEffectOp64(Synchronization::Full(), atomicOp, temp1, dest, ++ temp2); ++ } ++} ++ ++void CodeGenerator::visitAtomicLoad64(LAtomicLoad64* lir) { ++ Register elements = ToRegister(lir->elements()); ++ Register temp = ToRegister(lir->temp()); ++ Register64 temp64 = ToRegister64(lir->temp64()); ++ Register out = ToRegister(lir->output()); ++ const MLoadUnboxedScalar* mir = lir->mir(); ++ ++ Scalar::Type storageType = mir->storageType(); ++ ++ auto sync = Synchronization::Load(); ++ masm.memoryBarrierBefore(sync); ++ if (lir->index()->isConstant()) { ++ Address source = ++ ToAddress(elements, lir->index(), storageType, mir->offsetAdjustment()); ++ masm.load64(source, temp64); ++ } else { ++ BaseIndex source(elements, ToRegister(lir->index()), ++ ScaleFromScalarType(storageType), mir->offsetAdjustment()); ++ masm.load64(source, temp64); ++ } ++ masm.memoryBarrierAfter(sync); ++ emitCreateBigInt(lir, storageType, temp64, out, temp); ++} ++ ++void CodeGenerator::visitAtomicStore64(LAtomicStore64* lir) { ++ Register elements = ToRegister(lir->elements()); ++ Register value = ToRegister(lir->value()); ++ Register64 temp1 = ToRegister64(lir->temp1()); ++ ++ Scalar::Type writeType = lir->mir()->writeType(); ++ ++ masm.loadBigInt64(value, temp1); ++ auto sync = Synchronization::Store(); ++ masm.memoryBarrierBefore(sync); ++ if (lir->index()->isConstant()) { ++ Address dest = ToAddress(elements, lir->index(), writeType); ++ masm.store64(temp1, dest); ++ } else { ++ BaseIndex dest(elements, ToRegister(lir->index()), ++ ScaleFromScalarType(writeType)); ++ masm.store64(temp1, dest); ++ } ++ masm.memoryBarrierAfter(sync); ++} ++ ++void CodeGenerator::visitWasmCompareExchangeI64(LWasmCompareExchangeI64* lir) { ++ Register ptr = ToRegister(lir->ptr()); ++ Register64 oldValue = ToRegister64(lir->oldValue()); ++ Register64 newValue = ToRegister64(lir->newValue()); ++ Register64 output = ToOutRegister64(lir); ++ uint32_t offset = lir->mir()->access().offset(); ++ ++ BaseIndex addr(HeapReg, ptr, TimesOne, offset); ++ masm.wasmCompareExchange64(lir->mir()->access(), addr, oldValue, newValue, ++ output); ++} ++ ++void CodeGenerator::visitWasmAtomicExchangeI64(LWasmAtomicExchangeI64* lir) { ++ Register ptr = ToRegister(lir->ptr()); ++ Register64 value = ToRegister64(lir->value()); ++ Register64 output = ToOutRegister64(lir); ++ uint32_t offset = lir->mir()->access().offset(); ++ ++ BaseIndex addr(HeapReg, ptr, TimesOne, offset); ++ masm.wasmAtomicExchange64(lir->mir()->access(), addr, value, output); ++} ++ ++void CodeGenerator::visitWasmAtomicBinopI64(LWasmAtomicBinopI64* lir) { ++ Register ptr = ToRegister(lir->ptr()); ++ Register64 value = ToRegister64(lir->value()); ++ Register64 output = ToOutRegister64(lir); ++ Register64 temp(ToRegister(lir->getTemp(0))); ++ uint32_t offset = lir->mir()->access().offset(); ++ ++ BaseIndex addr(HeapReg, ptr, TimesOne, offset); ++ ++ masm.wasmAtomicFetchOp64(lir->mir()->access(), lir->mir()->operation(), value, ++ addr, temp, output); ++} ++ ++void CodeGenerator::visitNearbyInt(LNearbyInt*) { MOZ_CRASH("NYI"); } ++ ++void CodeGenerator::visitNearbyIntF(LNearbyIntF*) { MOZ_CRASH("NYI"); } ++ ++void CodeGenerator::visitSimd128(LSimd128* ins) { MOZ_CRASH("No SIMD"); } ++ ++void CodeGenerator::visitWasmTernarySimd128(LWasmTernarySimd128* ins) { ++ MOZ_CRASH("No SIMD"); ++} ++ ++void CodeGenerator::visitWasmBinarySimd128(LWasmBinarySimd128* ins) { ++ MOZ_CRASH("No SIMD"); ++} ++ ++void CodeGenerator::visitWasmBinarySimd128WithConstant( ++ LWasmBinarySimd128WithConstant* ins) { ++ MOZ_CRASH("No SIMD"); ++} ++ ++void CodeGenerator::visitWasmVariableShiftSimd128( ++ LWasmVariableShiftSimd128* ins) { ++ MOZ_CRASH("No SIMD"); ++} ++ ++void CodeGenerator::visitWasmConstantShiftSimd128( ++ LWasmConstantShiftSimd128* ins) { ++ MOZ_CRASH("No SIMD"); ++} ++ ++void CodeGenerator::visitWasmSignReplicationSimd128( ++ LWasmSignReplicationSimd128* ins) { ++ MOZ_CRASH("No SIMD"); ++} ++ ++void CodeGenerator::visitWasmShuffleSimd128(LWasmShuffleSimd128* ins) { ++ MOZ_CRASH("No SIMD"); ++} ++ ++void CodeGenerator::visitWasmPermuteSimd128(LWasmPermuteSimd128* ins) { ++ MOZ_CRASH("No SIMD"); ++} ++ ++void CodeGenerator::visitWasmReplaceLaneSimd128(LWasmReplaceLaneSimd128* ins) { ++ MOZ_CRASH("No SIMD"); ++} ++ ++void CodeGenerator::visitWasmReplaceInt64LaneSimd128( ++ LWasmReplaceInt64LaneSimd128* ins) { ++ MOZ_CRASH("No SIMD"); ++} ++ ++void CodeGenerator::visitWasmScalarToSimd128(LWasmScalarToSimd128* ins) { ++ MOZ_CRASH("No SIMD"); ++} ++ ++void CodeGenerator::visitWasmInt64ToSimd128(LWasmInt64ToSimd128* ins) { ++ MOZ_CRASH("No SIMD"); ++} ++ ++void CodeGenerator::visitWasmUnarySimd128(LWasmUnarySimd128* ins) { ++ MOZ_CRASH("No SIMD"); ++} ++ ++void CodeGenerator::visitWasmReduceSimd128(LWasmReduceSimd128* ins) { ++ MOZ_CRASH("No SIMD"); ++} ++ ++void CodeGenerator::visitWasmReduceAndBranchSimd128( ++ LWasmReduceAndBranchSimd128* ins) { ++ MOZ_CRASH("No SIMD"); ++} ++ ++void CodeGenerator::visitWasmReduceSimd128ToInt64( ++ LWasmReduceSimd128ToInt64* ins) { ++ MOZ_CRASH("No SIMD"); ++} ++ ++void CodeGenerator::visitWasmLoadLaneSimd128(LWasmLoadLaneSimd128* ins) { ++ MOZ_CRASH("No SIMD"); ++} ++ ++void CodeGenerator::visitWasmStoreLaneSimd128(LWasmStoreLaneSimd128* ins) { ++ MOZ_CRASH("No SIMD"); ++} +diff --git a/js/src/jit/riscv64/CodeGenerator-riscv64.h b/js/src/jit/riscv64/CodeGenerator-riscv64.h +new file mode 100644 +index 0000000000..793c834085 +--- /dev/null ++++ b/js/src/jit/riscv64/CodeGenerator-riscv64.h +@@ -0,0 +1,210 @@ ++/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- ++ * vim: set ts=8 sts=2 et sw=2 tw=80: ++ * 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_riscv64_CodeGenerator_riscv64_h ++#define jit_riscv64_CodeGenerator_riscv64_h ++ ++#include "jit/riscv64/Assembler-riscv64.h" ++#include "jit/riscv64/MacroAssembler-riscv64.h" ++#include "jit/shared/CodeGenerator-shared.h" ++ ++namespace js { ++namespace jit { ++ ++class CodeGeneratorRiscv64; ++class OutOfLineBailout; ++class OutOfLineTableSwitch; ++ ++using OutOfLineWasmTruncateCheck = ++ OutOfLineWasmTruncateCheckBase; ++ ++class CodeGeneratorRiscv64 : public CodeGeneratorShared { ++ friend class MoveResolverLA; ++ ++ protected: ++ CodeGeneratorRiscv64(MIRGenerator* gen, LIRGraph* graph, ++ MacroAssembler* masm); ++ ++ NonAssertingLabel deoptLabel_; ++ ++ Operand ToOperand(const LAllocation& a); ++ Operand ToOperand(const LAllocation* a); ++ Operand ToOperand(const LDefinition* def); ++ ++#ifdef JS_PUNBOX64 ++ Operand ToOperandOrRegister64(const LInt64Allocation input); ++#else ++ Register64 ToOperandOrRegister64(const LInt64Allocation input); ++#endif ++ ++ 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 bailoutTest32(Assembler::Condition c, T1 lhs, T2 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) { ++ // TODO(riscv64) Didn't use branchTestPtr due to '-Wundefined-inline'. ++ MOZ_ASSERT(c == Assembler::Zero || c == Assembler::NonZero || ++ c == Assembler::Signed || c == Assembler::NotSigned); ++ Label bail; ++ if (lhs == rhs) { ++ masm.ma_b(lhs, rhs, &bail, c); ++ } else { ++ ScratchRegisterScope scratch(masm); ++ masm.and_(scratch, lhs, rhs); ++ masm.ma_b(scratch, scratch, &bail, c); ++ } ++ bailoutFrom(&bail, snapshot); ++ } ++ void bailoutIfFalseBool(Register reg, LSnapshot* snapshot) { ++ Label bail; ++ ScratchRegisterScope scratch(masm); ++ masm.ma_and(scratch, reg, Imm32(0xFF)); ++ masm.ma_b(scratch, scratch, &bail, Assembler::Zero); ++ bailoutFrom(&bail, snapshot); ++ } ++ ++ void bailoutFrom(Label* label, LSnapshot* snapshot); ++ void bailout(LSnapshot* snapshot); ++ ++ bool generateOutOfLineCode(); ++ ++ template ++ void branchToBlock(Register lhs, T rhs, MBasicBlock* mir, ++ Assembler::Condition cond) { ++ masm.ma_b(lhs, rhs, skipTrivialBlocks(mir)->lir()->label(), cond); ++ } ++ void branchToBlock(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); ++ } ++ ++ void emitTableSwitchDispatch(MTableSwitch* mir, Register index, ++ Register base); ++ ++ template ++ void emitWasmLoad(T* ins); ++ template ++ void emitWasmStore(T* ins); ++ ++ void generateInvalidateEpilogue(); ++ ++ // Generating a result. ++ template ++ void atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type arrayType, ++ const S& value, const T& mem, ++ Register flagTemp, Register outTemp, ++ Register valueTemp, Register offsetTemp, ++ Register maskTemp, AnyRegister output); ++ ++ // Generating no result. ++ template ++ void atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type arrayType, ++ const S& value, const T& mem, ++ Register flagTemp, Register valueTemp, ++ Register offsetTemp, Register maskTemp); ++ ++ public: ++ // Out of line visitors. ++ void visitOutOfLineBailout(OutOfLineBailout* ool); ++ void visitOutOfLineTableSwitch(OutOfLineTableSwitch* ool); ++ void visitOutOfLineWasmTruncateCheck(OutOfLineWasmTruncateCheck* ool); ++ ++ protected: ++ void testNullEmitBranch(Assembler::Condition cond, const ValueOperand& value, ++ MBasicBlock* ifTrue, MBasicBlock* ifFalse) { ++ UseScratchRegisterScope temps(&masm); ++ Register scratch = temps.Acquire(); ++ masm.splitTag(value.valueReg(), scratch); ++ emitBranch(scratch, ImmTag(JSVAL_TAG_NULL), cond, ifTrue, ifFalse); ++ } ++ void testUndefinedEmitBranch(Assembler::Condition cond, ++ const ValueOperand& value, MBasicBlock* ifTrue, ++ MBasicBlock* ifFalse) { ++ UseScratchRegisterScope temps(&masm); ++ Register scratch = temps.Acquire(); ++ masm.splitTag(value.valueReg(), scratch); ++ emitBranch(scratch, ImmTag(JSVAL_TAG_UNDEFINED), cond, ifTrue, ifFalse); ++ } ++ void testObjectEmitBranch(Assembler::Condition cond, ++ const ValueOperand& value, MBasicBlock* ifTrue, ++ MBasicBlock* ifFalse) { ++ UseScratchRegisterScope temps(&masm); ++ Register scratch = temps.Acquire(); ++ masm.splitTag(value.valueReg(), scratch); ++ emitBranch(scratch, ImmTag(JSVAL_TAG_OBJECT), cond, ifTrue, ifFalse); ++ } ++ ++ void emitBigIntDiv(LBigIntDiv* ins, Register dividend, Register divisor, ++ Register output, Label* fail); ++ void emitBigIntMod(LBigIntMod* ins, Register dividend, Register divisor, ++ Register output, Label* fail); ++ ++ template ++ void emitWasmLoadI64(T* ins); ++ template ++ void emitWasmStoreI64(T* ins); ++ ++ ValueOperand ToValue(LInstruction* ins, size_t pos); ++ ValueOperand ToTempValue(LInstruction* ins, size_t pos); ++ ++ // Functions for LTestVAndBranch. ++ void splitTagForTest(const ValueOperand& value, ScratchTagScope& tag); ++}; ++ ++typedef CodeGeneratorRiscv64 CodeGeneratorSpecific; ++ ++// An out-of-line bailout thunk. ++class OutOfLineBailout : public OutOfLineCodeBase { ++ protected: ++ LSnapshot* snapshot_; ++ ++ public: ++ OutOfLineBailout(LSnapshot* snapshot) : snapshot_(snapshot) {} ++ ++ void accept(CodeGeneratorRiscv64* codegen) override; ++ ++ LSnapshot* snapshot() const { return snapshot_; } ++}; ++ ++} // namespace jit ++} // namespace js ++ ++#endif /* jit_riscv64_CodeGenerator_riscv64_h */ +diff --git a/js/src/jit/riscv64/LIR-riscv64.h b/js/src/jit/riscv64/LIR-riscv64.h +new file mode 100644 +index 0000000000..7143919608 +--- /dev/null ++++ b/js/src/jit/riscv64/LIR-riscv64.h +@@ -0,0 +1,399 @@ ++/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- ++ * vim: set ts=8 sts=2 et sw=2 tw=80: ++ * 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_riscv64_LIR_riscv64_h ++#define jit_riscv64_LIR_riscv64_h ++ ++namespace js { ++namespace jit { ++ ++class LUnbox : public LInstructionHelper<1, 1, 0> { ++ protected: ++ LUnbox(LNode::Opcode opcode, const LAllocation& input) ++ : LInstructionHelper(opcode) { ++ setOperand(0, input); ++ } ++ ++ public: ++ LIR_HEADER(Unbox); ++ ++ explicit LUnbox(const LAllocation& input) : LInstructionHelper(classOpcode) { ++ setOperand(0, input); ++ } ++ ++ static const size_t Input = 0; ++ ++ MUnbox* mir() const { return mir_->toUnbox(); } ++ const char* extraName() const { return StringFromMIRType(mir()->type()); } ++}; ++ ++class LUnboxFloatingPoint : public LUnbox { ++ MIRType type_; ++ ++ public: ++ LIR_HEADER(UnboxFloatingPoint); ++ ++ LUnboxFloatingPoint(const LAllocation& input, MIRType type) ++ : LUnbox(classOpcode, input), type_(type) {} ++ ++ MIRType type() const { return type_; } ++}; ++ ++// Convert a 32-bit unsigned integer to a double. ++class LWasmUint32ToDouble : public LInstructionHelper<1, 1, 0> { ++ public: ++ LIR_HEADER(WasmUint32ToDouble) ++ ++ explicit LWasmUint32ToDouble(const LAllocation& input) ++ : LInstructionHelper(classOpcode) { ++ setOperand(0, input); ++ } ++}; ++ ++// Convert a 32-bit unsigned integer to a float32. ++class LWasmUint32ToFloat32 : public LInstructionHelper<1, 1, 0> { ++ public: ++ LIR_HEADER(WasmUint32ToFloat32) ++ ++ explicit LWasmUint32ToFloat32(const LAllocation& input) ++ : LInstructionHelper(classOpcode) { ++ setOperand(0, input); ++ } ++}; ++ ++class LDivI : public LBinaryMath<1> { ++ public: ++ LIR_HEADER(DivI); ++ ++ LDivI(const LAllocation& lhs, const LAllocation& rhs, const LDefinition& temp) ++ : LBinaryMath(classOpcode) { ++ setOperand(0, lhs); ++ setOperand(1, rhs); ++ setTemp(0, temp); ++ } ++ ++ MDiv* mir() const { return mir_->toDiv(); } ++}; ++ ++class LDivPowTwoI : public LInstructionHelper<1, 1, 1> { ++ const int32_t shift_; ++ ++ public: ++ LIR_HEADER(DivPowTwoI) ++ ++ LDivPowTwoI(const LAllocation& lhs, int32_t shift, const LDefinition& temp) ++ : LInstructionHelper(classOpcode), shift_(shift) { ++ setOperand(0, lhs); ++ setTemp(0, temp); ++ } ++ ++ const LAllocation* numerator() { return getOperand(0); } ++ int32_t shift() const { return shift_; } ++ MDiv* mir() const { return mir_->toDiv(); } ++}; ++ ++class LModI : public LBinaryMath<1> { ++ public: ++ LIR_HEADER(ModI); ++ ++ LModI(const LAllocation& lhs, const LAllocation& rhs, ++ const LDefinition& callTemp) ++ : LBinaryMath(classOpcode) { ++ setOperand(0, lhs); ++ setOperand(1, rhs); ++ setTemp(0, callTemp); ++ } ++ ++ const LDefinition* callTemp() { return getTemp(0); } ++ MMod* mir() const { return mir_->toMod(); } ++}; ++ ++class LModPowTwoI : public LInstructionHelper<1, 1, 0> { ++ const int32_t shift_; ++ ++ public: ++ LIR_HEADER(ModPowTwoI); ++ ++ LModPowTwoI(const LAllocation& lhs, int32_t shift) ++ : LInstructionHelper(classOpcode), shift_(shift) { ++ setOperand(0, lhs); ++ } ++ ++ int32_t shift() const { return shift_; } ++ MMod* mir() const { return mir_->toMod(); } ++}; ++ ++class LModMaskI : public LInstructionHelper<1, 1, 2> { ++ const int32_t shift_; ++ ++ public: ++ LIR_HEADER(ModMaskI); ++ ++ LModMaskI(const LAllocation& lhs, const LDefinition& temp0, ++ const LDefinition& temp1, int32_t shift) ++ : LInstructionHelper(classOpcode), shift_(shift) { ++ setOperand(0, lhs); ++ setTemp(0, temp0); ++ setTemp(1, temp1); ++ } ++ ++ int32_t shift() const { return shift_; } ++ MMod* mir() const { return mir_->toMod(); } ++}; ++ ++// Takes a tableswitch with an integer to decide ++class LTableSwitch : public LInstructionHelper<0, 1, 2> { ++ public: ++ LIR_HEADER(TableSwitch); ++ ++ LTableSwitch(const LAllocation& in, const LDefinition& inputCopy, ++ const LDefinition& jumpTablePointer, MTableSwitch* ins) ++ : LInstructionHelper(classOpcode) { ++ setOperand(0, in); ++ setTemp(0, inputCopy); ++ setTemp(1, jumpTablePointer); ++ setMir(ins); ++ } ++ ++ MTableSwitch* mir() const { return mir_->toTableSwitch(); } ++ const LAllocation* index() { return getOperand(0); } ++ const LDefinition* tempInt() { return getTemp(0); } ++ // This is added to share the same CodeGenerator prefixes. ++ const LDefinition* tempPointer() { return getTemp(1); } ++}; ++ ++// Takes a tableswitch with an integer to decide ++class LTableSwitchV : public LInstructionHelper<0, BOX_PIECES, 3> { ++ public: ++ LIR_HEADER(TableSwitchV); ++ ++ LTableSwitchV(const LBoxAllocation& input, const LDefinition& inputCopy, ++ const LDefinition& floatCopy, ++ const LDefinition& jumpTablePointer, MTableSwitch* ins) ++ : LInstructionHelper(classOpcode) { ++ setBoxOperand(InputValue, input); ++ setTemp(0, inputCopy); ++ setTemp(1, floatCopy); ++ setTemp(2, jumpTablePointer); ++ setMir(ins); ++ } ++ ++ MTableSwitch* mir() const { return mir_->toTableSwitch(); } ++ ++ static const size_t InputValue = 0; ++ ++ const LDefinition* tempInt() { return getTemp(0); } ++ const LDefinition* tempFloat() { return getTemp(1); } ++ const LDefinition* tempPointer() { return getTemp(2); } ++}; ++ ++class LMulI : public LBinaryMath<0> { ++ public: ++ LIR_HEADER(MulI); ++ ++ LMulI() : LBinaryMath(classOpcode) {} ++ ++ MMul* mir() { return mir_->toMul(); } ++}; ++ ++class LUDivOrMod : public LBinaryMath<0> { ++ public: ++ LIR_HEADER(UDivOrMod); ++ ++ LUDivOrMod() : LBinaryMath(classOpcode) {} ++ ++ MBinaryArithInstruction* mir() const { ++ MOZ_ASSERT(mir_->isDiv() || mir_->isMod()); ++ return static_cast(mir_); ++ } ++ ++ bool canBeDivideByZero() const { ++ if (mir_->isMod()) { ++ return mir_->toMod()->canBeDivideByZero(); ++ } ++ return mir_->toDiv()->canBeDivideByZero(); ++ } ++ ++ bool trapOnError() const { ++ if (mir_->isMod()) { ++ return mir_->toMod()->trapOnError(); ++ } ++ return mir_->toDiv()->trapOnError(); ++ } ++ ++ wasm::BytecodeOffset bytecodeOffset() const { ++ MOZ_ASSERT(mir_->isDiv() || mir_->isMod()); ++ if (mir_->isMod()) { ++ return mir_->toMod()->bytecodeOffset(); ++ } ++ return mir_->toDiv()->bytecodeOffset(); ++ } ++}; ++ ++class LWasmCompareExchangeI64 ++ : public LInstructionHelper { ++ public: ++ LIR_HEADER(WasmCompareExchangeI64); ++ ++ LWasmCompareExchangeI64(const LAllocation& ptr, ++ const LInt64Allocation& oldValue, ++ const LInt64Allocation& newValue) ++ : LInstructionHelper(classOpcode) { ++ setOperand(0, ptr); ++ setInt64Operand(1, oldValue); ++ setInt64Operand(1 + INT64_PIECES, newValue); ++ } ++ ++ const LAllocation* ptr() { return getOperand(0); } ++ const LInt64Allocation oldValue() { return getInt64Operand(1); } ++ const LInt64Allocation newValue() { ++ return getInt64Operand(1 + INT64_PIECES); ++ } ++ const MWasmCompareExchangeHeap* mir() const { ++ return mir_->toWasmCompareExchangeHeap(); ++ } ++}; ++ ++class LWasmAtomicExchangeI64 ++ : public LInstructionHelper { ++ public: ++ LIR_HEADER(WasmAtomicExchangeI64); ++ ++ LWasmAtomicExchangeI64(const LAllocation& ptr, const LInt64Allocation& value) ++ : LInstructionHelper(classOpcode) { ++ setOperand(0, ptr); ++ setInt64Operand(1, value); ++ } ++ ++ const LAllocation* ptr() { return getOperand(0); } ++ const LInt64Allocation value() { return getInt64Operand(1); } ++ const MWasmAtomicExchangeHeap* mir() const { ++ return mir_->toWasmAtomicExchangeHeap(); ++ } ++}; ++ ++class LWasmAtomicBinopI64 ++ : public LInstructionHelper { ++ public: ++ LIR_HEADER(WasmAtomicBinopI64); ++ ++ LWasmAtomicBinopI64(const LAllocation& ptr, const LInt64Allocation& value) ++ : LInstructionHelper(classOpcode) { ++ setOperand(0, ptr); ++ setInt64Operand(1, value); ++ } ++ ++ const LAllocation* ptr() { return getOperand(0); } ++ const LInt64Allocation value() { return getInt64Operand(1); } ++ const MWasmAtomicBinopHeap* mir() const { ++ return mir_->toWasmAtomicBinopHeap(); ++ } ++}; ++ ++class LDivOrModI64 : public LBinaryMath<1> { ++ public: ++ LIR_HEADER(DivOrModI64) ++ ++ LDivOrModI64(const LAllocation& lhs, const LAllocation& rhs, ++ const LDefinition& temp) ++ : LBinaryMath(classOpcode) { ++ setOperand(0, lhs); ++ setOperand(1, rhs); ++ setTemp(0, temp); ++ } ++ ++ const LDefinition* remainder() { return getTemp(0); } ++ MBinaryArithInstruction* mir() const { ++ MOZ_ASSERT(mir_->isDiv() || mir_->isMod()); ++ return static_cast(mir_); ++ } ++ ++ bool canBeDivideByZero() const { ++ if (mir_->isMod()) { ++ return mir_->toMod()->canBeDivideByZero(); ++ } ++ return mir_->toDiv()->canBeDivideByZero(); ++ } ++ bool canBeNegativeOverflow() const { ++ if (mir_->isMod()) { ++ return mir_->toMod()->canBeNegativeDividend(); ++ } ++ return mir_->toDiv()->canBeNegativeOverflow(); ++ } ++ wasm::BytecodeOffset bytecodeOffset() const { ++ MOZ_ASSERT(mir_->isDiv() || mir_->isMod()); ++ if (mir_->isMod()) { ++ return mir_->toMod()->bytecodeOffset(); ++ } ++ return mir_->toDiv()->bytecodeOffset(); ++ } ++}; ++ ++class LUDivOrModI64 : public LBinaryMath<1> { ++ public: ++ LIR_HEADER(UDivOrModI64); ++ ++ LUDivOrModI64(const LAllocation& lhs, const LAllocation& rhs, ++ const LDefinition& temp) ++ : LBinaryMath(classOpcode) { ++ setOperand(0, lhs); ++ setOperand(1, rhs); ++ setTemp(0, temp); ++ } ++ ++ const LDefinition* remainder() { return getTemp(0); } ++ const char* extraName() const { ++ return mir()->isTruncated() ? "Truncated" : nullptr; ++ } ++ ++ MBinaryArithInstruction* mir() const { ++ MOZ_ASSERT(mir_->isDiv() || mir_->isMod()); ++ return static_cast(mir_); ++ } ++ bool canBeDivideByZero() const { ++ if (mir_->isMod()) { ++ return mir_->toMod()->canBeDivideByZero(); ++ } ++ return mir_->toDiv()->canBeDivideByZero(); ++ } ++ wasm::BytecodeOffset bytecodeOffset() const { ++ MOZ_ASSERT(mir_->isDiv() || mir_->isMod()); ++ if (mir_->isMod()) { ++ return mir_->toMod()->bytecodeOffset(); ++ } ++ return mir_->toDiv()->bytecodeOffset(); ++ } ++}; ++ ++class LWasmTruncateToInt64 : public LInstructionHelper<1, 1, 0> { ++ public: ++ LIR_HEADER(WasmTruncateToInt64); ++ ++ explicit LWasmTruncateToInt64(const LAllocation& in) ++ : LInstructionHelper(classOpcode) { ++ setOperand(0, in); ++ } ++ ++ MWasmTruncateToInt64* mir() const { return mir_->toWasmTruncateToInt64(); } ++}; ++ ++class LInt64ToFloatingPoint : public LInstructionHelper<1, 1, 0> { ++ public: ++ LIR_HEADER(Int64ToFloatingPoint); ++ ++ explicit LInt64ToFloatingPoint(const LInt64Allocation& in) ++ : LInstructionHelper(classOpcode) { ++ setInt64Operand(0, in); ++ } ++ ++ MInt64ToFloatingPoint* mir() const { return mir_->toInt64ToFloatingPoint(); } ++}; ++ ++} // namespace jit ++} // namespace js ++ ++#endif /* jit_riscv64_LIR_riscv64_h */ +diff --git a/js/src/jit/riscv64/Lowering-riscv64.cpp b/js/src/jit/riscv64/Lowering-riscv64.cpp +new file mode 100644 +index 0000000000..b32896694a +--- /dev/null ++++ b/js/src/jit/riscv64/Lowering-riscv64.cpp +@@ -0,0 +1,1087 @@ ++/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- ++ * vim: set ts=8 sts=2 et sw=2 tw=80: ++ * 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/riscv64/Lowering-riscv64.h" ++ ++#include "mozilla/MathAlgorithms.h" ++ ++#include "jit/Lowering.h" ++#include "jit/MIR.h" ++#include "jit/riscv64/Assembler-riscv64.h" ++ ++#include "jit/shared/Lowering-shared-inl.h" ++ ++using namespace js; ++using namespace js::jit; ++ ++using mozilla::FloorLog2; ++ ++LTableSwitch* LIRGeneratorRiscv64::newLTableSwitch(const LAllocation& in, ++ const LDefinition& inputCopy, ++ MTableSwitch* tableswitch) { ++ return new (alloc()) LTableSwitch(in, inputCopy, temp(), tableswitch); ++} ++ ++LTableSwitchV* LIRGeneratorRiscv64::newLTableSwitchV( ++ MTableSwitch* tableswitch) { ++ return new (alloc()) LTableSwitchV(useBox(tableswitch->getOperand(0)), temp(), ++ tempDouble(), temp(), tableswitch); ++} ++ ++void LIRGeneratorRiscv64::lowerForShift(LInstructionHelper<1, 2, 0>* ins, ++ MDefinition* mir, MDefinition* lhs, ++ MDefinition* rhs) { ++ ins->setOperand(0, useRegister(lhs)); ++ ins->setOperand(1, useRegisterOrConstant(rhs)); ++ define(ins, mir); ++} ++ ++template ++void LIRGeneratorRiscv64::lowerForShiftInt64( ++ LInstructionHelper* ins, ++ MDefinition* mir, MDefinition* lhs, MDefinition* rhs) { ++ ins->setInt64Operand(0, useInt64RegisterAtStart(lhs)); ++ ++ static_assert(LShiftI64::Rhs == INT64_PIECES, ++ "Assume Rhs is located at INT64_PIECES."); ++ static_assert(LRotateI64::Count == INT64_PIECES, ++ "Assume Count is located at INT64_PIECES."); ++ ++ ins->setOperand(INT64_PIECES, useRegisterOrConstant(rhs)); ++ ++ defineInt64ReuseInput(ins, mir, 0); ++} ++ ++template void LIRGeneratorRiscv64::lowerForShiftInt64( ++ LInstructionHelper* ins, ++ MDefinition* mir, MDefinition* lhs, MDefinition* rhs); ++template void LIRGeneratorRiscv64::lowerForShiftInt64( ++ LInstructionHelper* ins, ++ MDefinition* mir, MDefinition* lhs, MDefinition* rhs); ++ ++// x = !y ++void LIRGeneratorRiscv64::lowerForALU(LInstructionHelper<1, 1, 0>* ins, ++ MDefinition* mir, MDefinition* input) { ++ ins->setOperand(0, useRegister(input)); ++ define( ++ ins, mir, ++ LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER)); ++} ++ ++// z = x + y ++void LIRGeneratorRiscv64::lowerForALU(LInstructionHelper<1, 2, 0>* ins, ++ MDefinition* mir, MDefinition* lhs, ++ MDefinition* rhs) { ++ ins->setOperand(0, useRegister(lhs)); ++ ins->setOperand(1, useRegisterOrConstant(rhs)); ++ define( ++ ins, mir, ++ LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER)); ++} ++ ++void LIRGeneratorRiscv64::lowerForALUInt64( ++ LInstructionHelper* ins, MDefinition* mir, ++ MDefinition* input) { ++ ins->setInt64Operand(0, useInt64RegisterAtStart(input)); ++ defineInt64ReuseInput(ins, mir, 0); ++} ++ ++void LIRGeneratorRiscv64::lowerForALUInt64( ++ LInstructionHelper* ins, ++ MDefinition* mir, MDefinition* lhs, MDefinition* rhs) { ++ ins->setInt64Operand(0, useInt64RegisterAtStart(lhs)); ++ ins->setInt64Operand(INT64_PIECES, willHaveDifferentLIRNodes(lhs, rhs) ++ ? useInt64OrConstant(rhs) ++ : useInt64OrConstantAtStart(rhs)); ++ defineInt64ReuseInput(ins, mir, 0); ++} ++ ++void LIRGeneratorRiscv64::lowerForMulInt64(LMulI64* ins, MMul* mir, ++ MDefinition* lhs, MDefinition* rhs) { ++ bool needsTemp = false; ++ bool cannotAliasRhs = false; ++ bool reuseInput = true; ++ ++ ins->setInt64Operand(0, useInt64RegisterAtStart(lhs)); ++ ins->setInt64Operand(INT64_PIECES, ++ (willHaveDifferentLIRNodes(lhs, rhs) || cannotAliasRhs) ++ ? useInt64OrConstant(rhs) ++ : useInt64OrConstantAtStart(rhs)); ++ ++ if (needsTemp) { ++ ins->setTemp(0, temp()); ++ } ++ if (reuseInput) { ++ defineInt64ReuseInput(ins, mir, 0); ++ } else { ++ defineInt64(ins, mir); ++ } ++} ++ ++void LIRGeneratorRiscv64::lowerForFPU(LInstructionHelper<1, 1, 0>* ins, ++ MDefinition* mir, MDefinition* input) { ++ ins->setOperand(0, useRegister(input)); ++ define( ++ ins, mir, ++ LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER)); ++} ++ ++template ++void LIRGeneratorRiscv64::lowerForFPU(LInstructionHelper<1, 2, Temps>* ins, ++ MDefinition* mir, MDefinition* lhs, ++ MDefinition* rhs) { ++ ins->setOperand(0, useRegister(lhs)); ++ ins->setOperand(1, useRegister(rhs)); ++ define( ++ ins, mir, ++ LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER)); ++} ++ ++template void LIRGeneratorRiscv64::lowerForFPU(LInstructionHelper<1, 2, 0>* ins, ++ MDefinition* mir, ++ MDefinition* lhs, ++ MDefinition* rhs); ++template void LIRGeneratorRiscv64::lowerForFPU(LInstructionHelper<1, 2, 1>* ins, ++ MDefinition* mir, ++ MDefinition* lhs, ++ MDefinition* rhs); ++ ++void LIRGeneratorRiscv64::lowerForCompareI64AndBranch( ++ MTest* mir, MCompare* comp, JSOp op, MDefinition* left, MDefinition* right, ++ MBasicBlock* ifTrue, MBasicBlock* ifFalse) { ++ LCompareI64AndBranch* lir = new (alloc()) ++ LCompareI64AndBranch(comp, op, useInt64Register(left), ++ useInt64OrConstant(right), ifTrue, ifFalse); ++ add(lir, mir); ++} ++ ++void LIRGeneratorRiscv64::lowerForBitAndAndBranch(LBitAndAndBranch* baab, ++ MInstruction* mir, ++ MDefinition* lhs, ++ MDefinition* rhs) { ++ baab->setOperand(0, useRegisterAtStart(lhs)); ++ baab->setOperand(1, useRegisterOrConstantAtStart(rhs)); ++ add(baab, mir); ++} ++ ++LBoxAllocation LIRGeneratorRiscv64::useBoxFixed(MDefinition* mir, Register reg1, ++ Register reg2, ++ bool useAtStart) { ++ MOZ_ASSERT(mir->type() == MIRType::Value); ++ ++ ensureDefined(mir); ++ return LBoxAllocation(LUse(reg1, mir->virtualRegister(), useAtStart)); ++} ++ ++LAllocation LIRGeneratorRiscv64::useByteOpRegister(MDefinition* mir) { ++ return useRegister(mir); ++} ++ ++LAllocation LIRGeneratorRiscv64::useByteOpRegisterAtStart(MDefinition* mir) { ++ return useRegisterAtStart(mir); ++} ++ ++LAllocation LIRGeneratorRiscv64::useByteOpRegisterOrNonDoubleConstant( ++ MDefinition* mir) { ++ return useRegisterOrNonDoubleConstant(mir); ++} ++ ++LDefinition LIRGeneratorRiscv64::tempByteOpRegister() { return temp(); } ++LDefinition LIRGeneratorRiscv64::tempToUnbox() { return temp(); } ++ ++void LIRGeneratorRiscv64::lowerUntypedPhiInput(MPhi* phi, ++ uint32_t inputPosition, ++ LBlock* block, size_t lirIndex) { ++ lowerTypedPhiInput(phi, inputPosition, block, lirIndex); ++} ++void LIRGeneratorRiscv64::lowerInt64PhiInput(MPhi* phi, uint32_t inputPosition, ++ LBlock* block, size_t lirIndex) { ++ lowerTypedPhiInput(phi, inputPosition, block, lirIndex); ++} ++void LIRGeneratorRiscv64::defineInt64Phi(MPhi* phi, size_t lirIndex) { ++ defineTypedPhi(phi, lirIndex); ++} ++ ++void LIRGeneratorRiscv64::lowerNegI(MInstruction* ins, MDefinition* input) { ++ define(new (alloc()) LNegI(useRegisterAtStart(input)), ins); ++} ++void LIRGeneratorRiscv64::lowerNegI64(MInstruction* ins, MDefinition* input) { ++ defineInt64ReuseInput(new (alloc()) LNegI64(useInt64RegisterAtStart(input)), ++ ins, 0); ++} ++ ++void LIRGeneratorRiscv64::lowerMulI(MMul* mul, MDefinition* lhs, ++ MDefinition* rhs) { ++ LMulI* lir = new (alloc()) LMulI; ++ if (mul->fallible()) { ++ assignSnapshot(lir, mul->bailoutKind()); ++ } ++ ++ lowerForALU(lir, mul, lhs, rhs); ++} ++ ++void LIRGeneratorRiscv64::lowerDivI(MDiv* div) { ++ if (div->isUnsigned()) { ++ lowerUDiv(div); ++ return; ++ } ++ ++ // Division instructions are slow. Division by constant denominators can be ++ // rewritten to use other instructions. ++ if (div->rhs()->isConstant()) { ++ int32_t rhs = div->rhs()->toConstant()->toInt32(); ++ // Check for division by a positive power of two, which is an easy and ++ // important case to optimize. Note that other optimizations are also ++ // possible; division by negative powers of two can be optimized in a ++ // similar manner as positive powers of two, and division by other ++ // constants can be optimized by a reciprocal multiplication technique. ++ int32_t shift = FloorLog2(rhs); ++ if (rhs > 0 && 1 << shift == rhs) { ++ LDivPowTwoI* lir = ++ new (alloc()) LDivPowTwoI(useRegister(div->lhs()), shift, temp()); ++ if (div->fallible()) { ++ assignSnapshot(lir, div->bailoutKind()); ++ } ++ define(lir, div); ++ return; ++ } ++ } ++ ++ LDivI* lir = new (alloc()) ++ LDivI(useRegister(div->lhs()), useRegister(div->rhs()), temp()); ++ if (div->fallible()) { ++ assignSnapshot(lir, div->bailoutKind()); ++ } ++ define(lir, div); ++} ++ ++void LIRGeneratorRiscv64::lowerDivI64(MDiv* div) { ++ if (div->isUnsigned()) { ++ lowerUDivI64(div); ++ return; ++ } ++ ++ LDivOrModI64* lir = new (alloc()) ++ LDivOrModI64(useRegister(div->lhs()), useRegister(div->rhs()), temp()); ++ defineInt64(lir, div); ++} ++ ++void LIRGeneratorRiscv64::lowerModI(MMod* mod) { ++ if (mod->isUnsigned()) { ++ lowerUMod(mod); ++ return; ++ } ++ ++ if (mod->rhs()->isConstant()) { ++ int32_t rhs = mod->rhs()->toConstant()->toInt32(); ++ int32_t shift = FloorLog2(rhs); ++ if (rhs > 0 && 1 << shift == rhs) { ++ LModPowTwoI* lir = ++ new (alloc()) LModPowTwoI(useRegister(mod->lhs()), shift); ++ if (mod->fallible()) { ++ assignSnapshot(lir, mod->bailoutKind()); ++ } ++ define(lir, mod); ++ return; ++ } else if (shift < 31 && (1 << (shift + 1)) - 1 == rhs) { ++ LModMaskI* lir = new (alloc()) ++ LModMaskI(useRegister(mod->lhs()), temp(LDefinition::GENERAL), ++ temp(LDefinition::GENERAL), shift + 1); ++ if (mod->fallible()) { ++ assignSnapshot(lir, mod->bailoutKind()); ++ } ++ define(lir, mod); ++ return; ++ } ++ } ++ LModI* lir = ++ new (alloc()) LModI(useRegister(mod->lhs()), useRegister(mod->rhs()), ++ temp(LDefinition::GENERAL)); ++ ++ if (mod->fallible()) { ++ assignSnapshot(lir, mod->bailoutKind()); ++ } ++ define(lir, mod); ++} ++ ++void LIRGeneratorRiscv64::lowerModI64(MMod* mod) { ++ if (mod->isUnsigned()) { ++ lowerUModI64(mod); ++ return; ++ } ++ ++ LDivOrModI64* lir = new (alloc()) ++ LDivOrModI64(useRegister(mod->lhs()), useRegister(mod->rhs()), temp()); ++ defineInt64(lir, mod); ++} ++ ++void LIRGeneratorRiscv64::lowerUDiv(MDiv* div) { ++ MDefinition* lhs = div->getOperand(0); ++ MDefinition* rhs = div->getOperand(1); ++ ++ LUDivOrMod* lir = new (alloc()) LUDivOrMod; ++ lir->setOperand(0, useRegister(lhs)); ++ lir->setOperand(1, useRegister(rhs)); ++ if (div->fallible()) { ++ assignSnapshot(lir, div->bailoutKind()); ++ } ++ ++ define(lir, div); ++} ++ ++void LIRGeneratorRiscv64::lowerUDivI64(MDiv* div) { ++ LUDivOrModI64* lir = new (alloc()) ++ LUDivOrModI64(useRegister(div->lhs()), useRegister(div->rhs()), temp()); ++ defineInt64(lir, div); ++} ++ ++void LIRGeneratorRiscv64::lowerUMod(MMod* mod) { ++ MDefinition* lhs = mod->getOperand(0); ++ MDefinition* rhs = mod->getOperand(1); ++ ++ LUDivOrMod* lir = new (alloc()) LUDivOrMod; ++ lir->setOperand(0, useRegister(lhs)); ++ lir->setOperand(1, useRegister(rhs)); ++ if (mod->fallible()) { ++ assignSnapshot(lir, mod->bailoutKind()); ++ } ++ ++ define(lir, mod); ++} ++ ++void LIRGeneratorRiscv64::lowerUModI64(MMod* mod) { ++ LUDivOrModI64* lir = new (alloc()) ++ LUDivOrModI64(useRegister(mod->lhs()), useRegister(mod->rhs()), temp()); ++ defineInt64(lir, mod); ++} ++ ++void LIRGeneratorRiscv64::lowerUrshD(MUrsh* mir) { ++ MDefinition* lhs = mir->lhs(); ++ MDefinition* rhs = mir->rhs(); ++ ++ MOZ_ASSERT(lhs->type() == MIRType::Int32); ++ MOZ_ASSERT(rhs->type() == MIRType::Int32); ++ ++ LUrshD* lir = new (alloc()) ++ LUrshD(useRegister(lhs), useRegisterOrConstant(rhs), temp()); ++ define(lir, mir); ++} ++ ++void LIRGeneratorRiscv64::lowerPowOfTwoI(MPow* mir) { ++ int32_t base = mir->input()->toConstant()->toInt32(); ++ MDefinition* power = mir->power(); ++ ++ auto* lir = new (alloc()) LPowOfTwoI(useRegister(power), base); ++ assignSnapshot(lir, mir->bailoutKind()); ++ define(lir, mir); ++} ++ ++void LIRGeneratorRiscv64::lowerBigIntDiv(MBigIntDiv* ins) { ++ auto* lir = new (alloc()) LBigIntDiv(useRegister(ins->lhs()), ++ useRegister(ins->rhs()), temp(), temp()); ++ define(lir, ins); ++ assignSafepoint(lir, ins); ++} ++ ++void LIRGeneratorRiscv64::lowerBigIntMod(MBigIntMod* ins) { ++ auto* lir = new (alloc()) LBigIntMod(useRegister(ins->lhs()), ++ useRegister(ins->rhs()), temp(), temp()); ++ define(lir, ins); ++ assignSafepoint(lir, ins); ++} ++ ++void LIRGeneratorRiscv64::lowerBigIntLsh(MBigIntLsh* ins) { ++ auto* lir = new (alloc()) LBigIntLsh( ++ useRegister(ins->lhs()), useRegister(ins->rhs()), temp(), temp(), temp()); ++ define(lir, ins); ++ assignSafepoint(lir, ins); ++} ++ ++void LIRGeneratorRiscv64::lowerBigIntRsh(MBigIntRsh* ins) { ++ auto* lir = new (alloc()) LBigIntRsh( ++ useRegister(ins->lhs()), useRegister(ins->rhs()), temp(), temp(), temp()); ++ define(lir, ins); ++ assignSafepoint(lir, ins); ++} ++ ++void LIRGeneratorRiscv64::lowerTruncateDToInt32(MTruncateToInt32* ins) { ++ MDefinition* opd = ins->input(); ++ MOZ_ASSERT(opd->type() == MIRType::Double); ++ ++ define(new (alloc()) LTruncateDToInt32(useRegister(opd), tempDouble()), ins); ++} ++ ++void LIRGeneratorRiscv64::lowerTruncateFToInt32(MTruncateToInt32* ins) { ++ MDefinition* opd = ins->input(); ++ MOZ_ASSERT(opd->type() == MIRType::Float32); ++ ++ define(new (alloc()) LTruncateFToInt32(useRegister(opd), tempFloat32()), ins); ++} ++ ++void LIRGeneratorRiscv64::lowerBuiltinInt64ToFloatingPoint( ++ MBuiltinInt64ToFloatingPoint* ins) { ++ MOZ_CRASH("We don't use it for this architecture"); ++} ++ ++void LIRGeneratorRiscv64::lowerWasmSelectI(MWasmSelect* select) { ++ auto* lir = new (alloc()) ++ LWasmSelect(useRegisterAtStart(select->trueExpr()), ++ useAny(select->falseExpr()), useRegister(select->condExpr())); ++ defineReuseInput(lir, select, LWasmSelect::TrueExprIndex); ++} ++ ++void LIRGeneratorRiscv64::lowerWasmSelectI64(MWasmSelect* select) { ++ auto* lir = new (alloc()) LWasmSelectI64( ++ useInt64RegisterAtStart(select->trueExpr()), ++ useInt64(select->falseExpr()), useRegister(select->condExpr())); ++ defineInt64ReuseInput(lir, select, LWasmSelectI64::TrueExprIndex); ++} ++ ++// On loong64 we specialize the only cases where compare is {U,}Int32 and select ++// is {U,}Int32. ++bool LIRGeneratorShared::canSpecializeWasmCompareAndSelect( ++ MCompare::CompareType compTy, MIRType insTy) { ++ return insTy == MIRType::Int32 && (compTy == MCompare::Compare_Int32 || ++ compTy == MCompare::Compare_UInt32); ++} ++ ++void LIRGeneratorShared::lowerWasmCompareAndSelect(MWasmSelect* ins, ++ MDefinition* lhs, ++ MDefinition* rhs, ++ MCompare::CompareType compTy, ++ JSOp jsop) { ++ MOZ_ASSERT(canSpecializeWasmCompareAndSelect(compTy, ins->type())); ++ auto* lir = new (alloc()) LWasmCompareAndSelect( ++ useRegister(lhs), useRegister(rhs), compTy, jsop, ++ useRegisterAtStart(ins->trueExpr()), useRegister(ins->falseExpr())); ++ defineReuseInput(lir, ins, LWasmCompareAndSelect::IfTrueExprIndex); ++} ++ ++void LIRGeneratorRiscv64::lowerWasmBuiltinTruncateToInt32( ++ MWasmBuiltinTruncateToInt32* ins) { ++ MDefinition* opd = ins->input(); ++ MOZ_ASSERT(opd->type() == MIRType::Double || opd->type() == MIRType::Float32); ++ ++ if (opd->type() == MIRType::Double) { ++ define(new (alloc()) LWasmBuiltinTruncateDToInt32( ++ useRegister(opd), useFixed(ins->instance(), InstanceReg), ++ LDefinition::BogusTemp()), ++ ins); ++ return; ++ } ++ ++ define(new (alloc()) LWasmBuiltinTruncateFToInt32( ++ useRegister(opd), useFixed(ins->instance(), InstanceReg), ++ LDefinition::BogusTemp()), ++ ins); ++} ++ ++void LIRGeneratorRiscv64::lowerWasmBuiltinTruncateToInt64( ++ MWasmBuiltinTruncateToInt64* ins) { ++ MOZ_CRASH("We don't use it for this architecture"); ++} ++ ++void LIRGeneratorRiscv64::lowerWasmBuiltinDivI64(MWasmBuiltinDivI64* div) { ++ MOZ_CRASH("We don't use runtime div for this architecture"); ++} ++ ++void LIRGeneratorRiscv64::lowerWasmBuiltinModI64(MWasmBuiltinModI64* mod) { ++ MOZ_CRASH("We don't use runtime mod for this architecture"); ++} ++ ++void LIRGeneratorRiscv64::lowerAtomicLoad64(MLoadUnboxedScalar* ins) { ++ const LUse elements = useRegister(ins->elements()); ++ const LAllocation index = ++ useRegisterOrIndexConstant(ins->index(), ins->storageType()); ++ ++ auto* lir = new (alloc()) LAtomicLoad64(elements, index, temp(), tempInt64()); ++ define(lir, ins); ++ assignSafepoint(lir, ins); ++} ++ ++void LIRGeneratorRiscv64::lowerAtomicStore64(MStoreUnboxedScalar* ins) { ++ LUse elements = useRegister(ins->elements()); ++ LAllocation index = ++ useRegisterOrIndexConstant(ins->index(), ins->writeType()); ++ LAllocation value = useRegister(ins->value()); ++ ++ add(new (alloc()) LAtomicStore64(elements, index, value, tempInt64()), ins); ++} ++ ++void LIRGenerator::visitBox(MBox* box) { ++ MDefinition* opd = box->getOperand(0); ++ ++ // If the operand is a constant, emit near its uses. ++ if (opd->isConstant() && box->canEmitAtUses()) { ++ emitAtUses(box); ++ return; ++ } ++ ++ if (opd->isConstant()) { ++ define(new (alloc()) LValue(opd->toConstant()->toJSValue()), box, ++ LDefinition(LDefinition::BOX)); ++ } else { ++ LBox* ins = new (alloc()) LBox(useRegister(opd), opd->type()); ++ define(ins, box, LDefinition(LDefinition::BOX)); ++ } ++} ++ ++void LIRGenerator::visitUnbox(MUnbox* unbox) { ++ MDefinition* box = unbox->getOperand(0); ++ MOZ_ASSERT(box->type() == MIRType::Value); ++ ++ LUnbox* lir; ++ if (IsFloatingPointType(unbox->type())) { ++ lir = new (alloc()) ++ LUnboxFloatingPoint(useRegisterAtStart(box), unbox->type()); ++ } else if (unbox->fallible()) { ++ // If the unbox is fallible, load the Value in a register first to ++ // avoid multiple loads. ++ lir = new (alloc()) LUnbox(useRegisterAtStart(box)); ++ } else { ++ lir = new (alloc()) LUnbox(useAtStart(box)); ++ } ++ ++ if (unbox->fallible()) { ++ assignSnapshot(lir, unbox->bailoutKind()); ++ } ++ ++ define(lir, unbox); ++} ++ ++void LIRGenerator::visitAbs(MAbs* ins) { ++ define(allocateAbs(ins, useRegisterAtStart(ins->input())), ins); ++} ++ ++void LIRGenerator::visitCopySign(MCopySign* ins) { ++ MDefinition* lhs = ins->lhs(); ++ MDefinition* rhs = ins->rhs(); ++ ++ MOZ_ASSERT(IsFloatingPointType(lhs->type())); ++ MOZ_ASSERT(lhs->type() == rhs->type()); ++ MOZ_ASSERT(lhs->type() == ins->type()); ++ ++ LInstructionHelper<1, 2, 2>* lir; ++ if (lhs->type() == MIRType::Double) { ++ lir = new (alloc()) LCopySignD(); ++ } else { ++ lir = new (alloc()) LCopySignF(); ++ } ++ ++ lir->setTemp(0, temp()); ++ lir->setTemp(1, temp()); ++ ++ lir->setOperand(0, useRegisterAtStart(lhs)); ++ lir->setOperand(1, useRegister(rhs)); ++ defineReuseInput(lir, ins, 0); ++} ++ ++void LIRGenerator::visitPowHalf(MPowHalf* ins) { ++ MDefinition* input = ins->input(); ++ MOZ_ASSERT(input->type() == MIRType::Double); ++ LPowHalfD* lir = new (alloc()) LPowHalfD(useRegisterAtStart(input)); ++ defineReuseInput(lir, ins, 0); ++} ++ ++void LIRGenerator::visitExtendInt32ToInt64(MExtendInt32ToInt64* ins) { ++ defineInt64( ++ new (alloc()) LExtendInt32ToInt64(useRegisterAtStart(ins->input())), ins); ++} ++ ++void LIRGenerator::visitSignExtendInt64(MSignExtendInt64* ins) { ++ defineInt64(new (alloc()) ++ LSignExtendInt64(useInt64RegisterAtStart(ins->input())), ++ ins); ++} ++ ++void LIRGenerator::visitInt64ToFloatingPoint(MInt64ToFloatingPoint* ins) { ++ MDefinition* opd = ins->input(); ++ MOZ_ASSERT(opd->type() == MIRType::Int64); ++ MOZ_ASSERT(IsFloatingPointType(ins->type())); ++ ++ define(new (alloc()) LInt64ToFloatingPoint(useInt64Register(opd)), ins); ++} ++ ++void LIRGenerator::visitSubstr(MSubstr* ins) { ++ LSubstr* lir = new (alloc()) ++ LSubstr(useRegister(ins->string()), useRegister(ins->begin()), ++ useRegister(ins->length()), temp(), temp(), temp()); ++ define(lir, ins); ++ assignSafepoint(lir, ins); ++} ++ ++void LIRGenerator::visitCompareExchangeTypedArrayElement( ++ MCompareExchangeTypedArrayElement* ins) { ++ MOZ_ASSERT(ins->arrayType() != Scalar::Float32); ++ MOZ_ASSERT(ins->arrayType() != Scalar::Float64); ++ ++ MOZ_ASSERT(ins->elements()->type() == MIRType::Elements); ++ MOZ_ASSERT(ins->index()->type() == MIRType::IntPtr); ++ ++ const LUse elements = useRegister(ins->elements()); ++ const LAllocation index = ++ useRegisterOrIndexConstant(ins->index(), ins->arrayType()); ++ ++ const LAllocation newval = useRegister(ins->newval()); ++ const LAllocation oldval = useRegister(ins->oldval()); ++ ++ if (Scalar::isBigIntType(ins->arrayType())) { ++ LInt64Definition temp1 = tempInt64(); ++ LInt64Definition temp2 = tempInt64(); ++ ++ auto* lir = new (alloc()) LCompareExchangeTypedArrayElement64( ++ elements, index, oldval, newval, temp1, temp2); ++ define(lir, ins); ++ assignSafepoint(lir, ins); ++ return; ++ } ++ ++ // If the target is a floating register then we need a temp at the ++ // CodeGenerator level for creating the result. ++ ++ LDefinition outTemp = LDefinition::BogusTemp(); ++ LDefinition valueTemp = LDefinition::BogusTemp(); ++ LDefinition offsetTemp = LDefinition::BogusTemp(); ++ LDefinition maskTemp = LDefinition::BogusTemp(); ++ ++ if (ins->arrayType() == Scalar::Uint32 && IsFloatingPointType(ins->type())) { ++ outTemp = temp(); ++ } ++ ++ if (Scalar::byteSize(ins->arrayType()) < 4) { ++ valueTemp = temp(); ++ offsetTemp = temp(); ++ maskTemp = temp(); ++ } ++ ++ LCompareExchangeTypedArrayElement* lir = new (alloc()) ++ LCompareExchangeTypedArrayElement(elements, index, oldval, newval, ++ outTemp, valueTemp, offsetTemp, ++ maskTemp); ++ ++ define(lir, ins); ++} ++ ++void LIRGenerator::visitAtomicExchangeTypedArrayElement( ++ MAtomicExchangeTypedArrayElement* ins) { ++ MOZ_ASSERT(ins->elements()->type() == MIRType::Elements); ++ MOZ_ASSERT(ins->index()->type() == MIRType::IntPtr); ++ ++ const LUse elements = useRegister(ins->elements()); ++ const LAllocation index = ++ useRegisterOrIndexConstant(ins->index(), ins->arrayType()); ++ ++ const LAllocation value = useRegister(ins->value()); ++ ++ if (Scalar::isBigIntType(ins->arrayType())) { ++ LInt64Definition temp1 = tempInt64(); ++ LDefinition temp2 = temp(); ++ ++ auto* lir = new (alloc()) LAtomicExchangeTypedArrayElement64( ++ elements, index, value, temp1, temp2); ++ define(lir, ins); ++ assignSafepoint(lir, ins); ++ return; ++ } ++ ++ // If the target is a floating register then we need a temp at the ++ // CodeGenerator level for creating the result. ++ ++ MOZ_ASSERT(ins->arrayType() <= Scalar::Uint32); ++ ++ LDefinition outTemp = LDefinition::BogusTemp(); ++ LDefinition valueTemp = LDefinition::BogusTemp(); ++ LDefinition offsetTemp = LDefinition::BogusTemp(); ++ LDefinition maskTemp = LDefinition::BogusTemp(); ++ ++ if (ins->arrayType() == Scalar::Uint32) { ++ MOZ_ASSERT(ins->type() == MIRType::Double); ++ outTemp = temp(); ++ } ++ ++ if (Scalar::byteSize(ins->arrayType()) < 4) { ++ valueTemp = temp(); ++ offsetTemp = temp(); ++ maskTemp = temp(); ++ } ++ ++ LAtomicExchangeTypedArrayElement* lir = ++ new (alloc()) LAtomicExchangeTypedArrayElement( ++ elements, index, value, outTemp, valueTemp, offsetTemp, maskTemp); ++ ++ define(lir, ins); ++} ++ ++void LIRGenerator::visitAtomicTypedArrayElementBinop( ++ MAtomicTypedArrayElementBinop* ins) { ++ MOZ_ASSERT(ins->arrayType() != Scalar::Uint8Clamped); ++ MOZ_ASSERT(ins->arrayType() != Scalar::Float32); ++ MOZ_ASSERT(ins->arrayType() != Scalar::Float64); ++ ++ MOZ_ASSERT(ins->elements()->type() == MIRType::Elements); ++ MOZ_ASSERT(ins->index()->type() == MIRType::IntPtr); ++ ++ const LUse elements = useRegister(ins->elements()); ++ const LAllocation index = ++ useRegisterOrIndexConstant(ins->index(), ins->arrayType()); ++ const LAllocation value = useRegister(ins->value()); ++ ++ if (Scalar::isBigIntType(ins->arrayType())) { ++ LInt64Definition temp1 = tempInt64(); ++ LInt64Definition temp2 = tempInt64(); ++ ++ // Case 1: the result of the operation is not used. ++ // ++ // We can omit allocating the result BigInt. ++ ++ if (ins->isForEffect()) { ++ auto* lir = new (alloc()) LAtomicTypedArrayElementBinopForEffect64( ++ elements, index, value, temp1, temp2); ++ add(lir, ins); ++ return; ++ } ++ ++ // Case 2: the result of the operation is used. ++ ++ auto* lir = new (alloc()) ++ LAtomicTypedArrayElementBinop64(elements, index, value, temp1, temp2); ++ define(lir, ins); ++ assignSafepoint(lir, ins); ++ return; ++ } ++ ++ LDefinition valueTemp = LDefinition::BogusTemp(); ++ LDefinition offsetTemp = LDefinition::BogusTemp(); ++ LDefinition maskTemp = LDefinition::BogusTemp(); ++ ++ if (Scalar::byteSize(ins->arrayType()) < 4) { ++ valueTemp = temp(); ++ offsetTemp = temp(); ++ maskTemp = temp(); ++ } ++ ++ if (ins->isForEffect()) { ++ LAtomicTypedArrayElementBinopForEffect* lir = ++ new (alloc()) LAtomicTypedArrayElementBinopForEffect( ++ elements, index, value, valueTemp, offsetTemp, maskTemp); ++ add(lir, ins); ++ return; ++ } ++ ++ // For a Uint32Array with a known double result we need a temp for ++ // the intermediate output. ++ ++ LDefinition outTemp = LDefinition::BogusTemp(); ++ ++ if (ins->arrayType() == Scalar::Uint32 && IsFloatingPointType(ins->type())) { ++ outTemp = temp(); ++ } ++ ++ LAtomicTypedArrayElementBinop* lir = ++ new (alloc()) LAtomicTypedArrayElementBinop( ++ elements, index, value, outTemp, valueTemp, offsetTemp, maskTemp); ++ define(lir, ins); ++} ++ ++void LIRGenerator::visitReturnImpl(MDefinition* opd, bool isGenerator) { ++ MOZ_ASSERT(opd->type() == MIRType::Value); ++ ++ LReturn* ins = new (alloc()) LReturn(isGenerator); ++ ins->setOperand(0, useFixed(opd, JSReturnReg)); ++ add(ins); ++} ++ ++void LIRGenerator::visitAsmJSLoadHeap(MAsmJSLoadHeap* ins) { ++ MDefinition* base = ins->base(); ++ MOZ_ASSERT(base->type() == MIRType::Int32); ++ ++ MDefinition* boundsCheckLimit = ins->boundsCheckLimit(); ++ MOZ_ASSERT_IF(ins->needsBoundsCheck(), ++ boundsCheckLimit->type() == MIRType::Int32); ++ ++ LAllocation baseAlloc = useRegisterAtStart(base); ++ ++ LAllocation limitAlloc = ins->needsBoundsCheck() ++ ? useRegisterAtStart(boundsCheckLimit) ++ : LAllocation(); ++ ++ // We have no memory-base value, meaning that HeapReg is to be used as the ++ // memory base. This follows from the definition of ++ // FunctionCompiler::maybeLoadMemoryBase() in WasmIonCompile.cpp. ++ MOZ_ASSERT(!ins->hasMemoryBase()); ++ auto* lir = ++ new (alloc()) LAsmJSLoadHeap(baseAlloc, limitAlloc, LAllocation()); ++ define(lir, ins); ++} ++ ++void LIRGenerator::visitAsmJSStoreHeap(MAsmJSStoreHeap* ins) { ++ MDefinition* base = ins->base(); ++ MOZ_ASSERT(base->type() == MIRType::Int32); ++ ++ MDefinition* boundsCheckLimit = ins->boundsCheckLimit(); ++ MOZ_ASSERT_IF(ins->needsBoundsCheck(), ++ boundsCheckLimit->type() == MIRType::Int32); ++ ++ LAllocation baseAlloc = useRegisterAtStart(base); ++ ++ LAllocation limitAlloc = ins->needsBoundsCheck() ++ ? useRegisterAtStart(boundsCheckLimit) ++ : LAllocation(); ++ ++ // See comment in LIRGenerator::visitAsmJSStoreHeap just above. ++ MOZ_ASSERT(!ins->hasMemoryBase()); ++ add(new (alloc()) LAsmJSStoreHeap(baseAlloc, useRegisterAtStart(ins->value()), ++ limitAlloc, LAllocation()), ++ ins); ++} ++ ++void LIRGenerator::visitWasmHeapBase(MWasmHeapBase* ins) { ++ auto* lir = new (alloc()) LWasmHeapBase(LAllocation()); ++ define(lir, ins); ++} ++ ++void LIRGenerator::visitWasmLoad(MWasmLoad* ins) { ++ MDefinition* base = ins->base(); ++ // 'base' is a GPR but may be of either type. If it is 32-bit, it is ++ // sign-extended on loongarch64 platform and we should explicitly promote it ++ // to 64-bit when use it as an index register in memory accesses. ++ MOZ_ASSERT(base->type() == MIRType::Int32 || base->type() == MIRType::Int64); ++ ++ LAllocation ptr; ++ ptr = useRegisterAtStart(base); ++ ++ if (ins->type() == MIRType::Int64) { ++ auto* lir = new (alloc()) LWasmLoadI64(ptr); ++ if (ins->access().offset()) { ++ lir->setTemp(0, tempCopy(base, 0)); ++ } ++ ++ defineInt64(lir, ins); ++ return; ++ } ++ ++ auto* lir = new (alloc()) LWasmLoad(ptr); ++ if (ins->access().offset()) { ++ lir->setTemp(0, tempCopy(base, 0)); ++ } ++ ++ define(lir, ins); ++} ++ ++void LIRGenerator::visitWasmStore(MWasmStore* ins) { ++ MDefinition* base = ins->base(); ++ // See comment in visitWasmLoad re the type of 'base'. ++ MOZ_ASSERT(base->type() == MIRType::Int32 || base->type() == MIRType::Int64); ++ ++ MDefinition* value = ins->value(); ++ ++ if (ins->access().type() == Scalar::Int64) { ++ LAllocation baseAlloc = useRegisterAtStart(base); ++ LInt64Allocation valueAlloc = useInt64RegisterAtStart(value); ++ auto* lir = new (alloc()) LWasmStoreI64(baseAlloc, valueAlloc); ++ if (ins->access().offset()) { ++ lir->setTemp(0, tempCopy(base, 0)); ++ } ++ ++ add(lir, ins); ++ return; ++ } ++ ++ LAllocation baseAlloc = useRegisterAtStart(base); ++ LAllocation valueAlloc = useRegisterAtStart(value); ++ auto* lir = new (alloc()) LWasmStore(baseAlloc, valueAlloc); ++ if (ins->access().offset()) { ++ lir->setTemp(0, tempCopy(base, 0)); ++ } ++ ++ add(lir, ins); ++} ++ ++void LIRGenerator::visitWasmNeg(MWasmNeg* ins) { ++ if (ins->type() == MIRType::Int32) { ++ define(new (alloc()) LNegI(useRegisterAtStart(ins->input())), ins); ++ } else if (ins->type() == MIRType::Float32) { ++ define(new (alloc()) LNegF(useRegisterAtStart(ins->input())), ins); ++ } else { ++ MOZ_ASSERT(ins->type() == MIRType::Double); ++ define(new (alloc()) LNegD(useRegisterAtStart(ins->input())), ins); ++ } ++} ++ ++void LIRGenerator::visitWasmTruncateToInt64(MWasmTruncateToInt64* ins) { ++ MDefinition* opd = ins->input(); ++ MOZ_ASSERT(opd->type() == MIRType::Double || opd->type() == MIRType::Float32); ++ ++ defineInt64(new (alloc()) LWasmTruncateToInt64(useRegister(opd)), ins); ++} ++ ++void LIRGenerator::visitWasmUnsignedToDouble(MWasmUnsignedToDouble* ins) { ++ MOZ_ASSERT(ins->input()->type() == MIRType::Int32); ++ LWasmUint32ToDouble* lir = ++ new (alloc()) LWasmUint32ToDouble(useRegisterAtStart(ins->input())); ++ define(lir, ins); ++} ++ ++void LIRGenerator::visitWasmUnsignedToFloat32(MWasmUnsignedToFloat32* ins) { ++ MOZ_ASSERT(ins->input()->type() == MIRType::Int32); ++ LWasmUint32ToFloat32* lir = ++ new (alloc()) LWasmUint32ToFloat32(useRegisterAtStart(ins->input())); ++ define(lir, ins); ++} ++ ++void LIRGenerator::visitWasmCompareExchangeHeap(MWasmCompareExchangeHeap* ins) { ++ MDefinition* base = ins->base(); ++ // See comment in visitWasmLoad re the type of 'base'. ++ MOZ_ASSERT(base->type() == MIRType::Int32 || base->type() == MIRType::Int64); ++ ++ if (ins->access().type() == Scalar::Int64) { ++ auto* lir = new (alloc()) LWasmCompareExchangeI64( ++ useRegister(base), useInt64Register(ins->oldValue()), ++ useInt64Register(ins->newValue())); ++ defineInt64(lir, ins); ++ return; ++ } ++ ++ LDefinition valueTemp = LDefinition::BogusTemp(); ++ LDefinition offsetTemp = LDefinition::BogusTemp(); ++ LDefinition maskTemp = LDefinition::BogusTemp(); ++ ++ if (ins->access().byteSize() < 4) { ++ valueTemp = temp(); ++ offsetTemp = temp(); ++ maskTemp = temp(); ++ } ++ ++ LWasmCompareExchangeHeap* lir = new (alloc()) LWasmCompareExchangeHeap( ++ useRegister(base), useRegister(ins->oldValue()), ++ useRegister(ins->newValue()), valueTemp, offsetTemp, maskTemp); ++ ++ define(lir, ins); ++} ++ ++void LIRGenerator::visitWasmAtomicExchangeHeap(MWasmAtomicExchangeHeap* ins) { ++ MDefinition* base = ins->base(); ++ // See comment in visitWasmLoad re the type of 'base'. ++ MOZ_ASSERT(base->type() == MIRType::Int32 || base->type() == MIRType::Int64); ++ ++ if (ins->access().type() == Scalar::Int64) { ++ auto* lir = new (alloc()) LWasmAtomicExchangeI64( ++ useRegister(base), useInt64Register(ins->value())); ++ defineInt64(lir, ins); ++ return; ++ } ++ ++ LDefinition valueTemp = LDefinition::BogusTemp(); ++ LDefinition offsetTemp = LDefinition::BogusTemp(); ++ LDefinition maskTemp = LDefinition::BogusTemp(); ++ ++ if (ins->access().byteSize() < 4) { ++ valueTemp = temp(); ++ offsetTemp = temp(); ++ maskTemp = temp(); ++ } ++ ++ LWasmAtomicExchangeHeap* lir = new (alloc()) ++ LWasmAtomicExchangeHeap(useRegister(base), useRegister(ins->value()), ++ valueTemp, offsetTemp, maskTemp); ++ define(lir, ins); ++} ++ ++void LIRGenerator::visitWasmAtomicBinopHeap(MWasmAtomicBinopHeap* ins) { ++ MDefinition* base = ins->base(); ++ // See comment in visitWasmLoad re the type of 'base'. ++ MOZ_ASSERT(base->type() == MIRType::Int32 || base->type() == MIRType::Int64); ++ ++ if (ins->access().type() == Scalar::Int64) { ++ auto* lir = new (alloc()) ++ LWasmAtomicBinopI64(useRegister(base), useInt64Register(ins->value())); ++ lir->setTemp(0, temp()); ++ defineInt64(lir, ins); ++ return; ++ } ++ ++ LDefinition valueTemp = LDefinition::BogusTemp(); ++ LDefinition offsetTemp = LDefinition::BogusTemp(); ++ LDefinition maskTemp = LDefinition::BogusTemp(); ++ ++ if (ins->access().byteSize() < 4) { ++ valueTemp = temp(); ++ offsetTemp = temp(); ++ maskTemp = temp(); ++ } ++ ++ if (!ins->hasUses()) { ++ LWasmAtomicBinopHeapForEffect* lir = new (alloc()) ++ LWasmAtomicBinopHeapForEffect(useRegister(base), ++ useRegister(ins->value()), valueTemp, ++ offsetTemp, maskTemp); ++ add(lir, ins); ++ return; ++ } ++ ++ LWasmAtomicBinopHeap* lir = new (alloc()) ++ LWasmAtomicBinopHeap(useRegister(base), useRegister(ins->value()), ++ valueTemp, offsetTemp, maskTemp); ++ ++ define(lir, ins); ++} ++ ++void LIRGenerator::visitWasmTernarySimd128(MWasmTernarySimd128* ins) { ++ MOZ_CRASH("ternary SIMD NYI"); ++} ++ ++void LIRGenerator::visitWasmBinarySimd128(MWasmBinarySimd128* ins) { ++ MOZ_CRASH("binary SIMD NYI"); ++} ++ ++#ifdef ENABLE_WASM_SIMD ++bool MWasmTernarySimd128::specializeBitselectConstantMaskAsShuffle( ++ int8_t shuffle[16]) { ++ return false; ++} ++#endif ++ ++bool MWasmBinarySimd128::specializeForConstantRhs() { ++ // Probably many we want to do here ++ return false; ++} ++ ++void LIRGenerator::visitWasmBinarySimd128WithConstant( ++ MWasmBinarySimd128WithConstant* ins) { ++ MOZ_CRASH("binary SIMD with constant NYI"); ++} ++ ++void LIRGenerator::visitWasmShiftSimd128(MWasmShiftSimd128* ins) { ++ MOZ_CRASH("shift SIMD NYI"); ++} ++ ++void LIRGenerator::visitWasmShuffleSimd128(MWasmShuffleSimd128* ins) { ++ MOZ_CRASH("shuffle SIMD NYI"); ++} ++ ++void LIRGenerator::visitWasmReplaceLaneSimd128(MWasmReplaceLaneSimd128* ins) { ++ MOZ_CRASH("replace-lane SIMD NYI"); ++} ++ ++void LIRGenerator::visitWasmScalarToSimd128(MWasmScalarToSimd128* ins) { ++ MOZ_CRASH("scalar-to-SIMD NYI"); ++} ++ ++void LIRGenerator::visitWasmUnarySimd128(MWasmUnarySimd128* ins) { ++ MOZ_CRASH("unary SIMD NYI"); ++} ++ ++void LIRGenerator::visitWasmReduceSimd128(MWasmReduceSimd128* ins) { ++ MOZ_CRASH("reduce-SIMD NYI"); ++} ++ ++void LIRGenerator::visitWasmLoadLaneSimd128(MWasmLoadLaneSimd128* ins) { ++ MOZ_CRASH("load-lane SIMD NYI"); ++} ++ ++void LIRGenerator::visitWasmStoreLaneSimd128(MWasmStoreLaneSimd128* ins) { ++ MOZ_CRASH("store-lane SIMD NYI"); ++} +diff --git a/js/src/jit/riscv64/Lowering-riscv64.h b/js/src/jit/riscv64/Lowering-riscv64.h +new file mode 100644 +index 0000000000..03ccb3ac8f +--- /dev/null ++++ b/js/src/jit/riscv64/Lowering-riscv64.h +@@ -0,0 +1,110 @@ ++/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- ++ * vim: set ts=8 sts=2 et sw=2 tw=80: ++ * 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_riscv64_Lowering_riscv64_h ++#define jit_riscv64_Lowering_riscv64_h ++ ++#include "jit/shared/Lowering-shared.h" ++ ++namespace js { ++namespace jit { ++ ++class LIRGeneratorRiscv64 : public LIRGeneratorShared { ++ protected: ++ LIRGeneratorRiscv64(MIRGenerator* gen, MIRGraph& graph, LIRGraph& lirGraph) ++ : LIRGeneratorShared(gen, graph, lirGraph) {} ++ ++ LTableSwitch* newLTableSwitch(const LAllocation& in, ++ const LDefinition& inputCopy, ++ MTableSwitch* ins); ++ LTableSwitchV* newLTableSwitchV(MTableSwitch* ins); ++ ++ void lowerForShift(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir, ++ MDefinition* lhs, MDefinition* rhs); ++ template ++ void lowerForShiftInt64( ++ LInstructionHelper* ins, ++ MDefinition* mir, MDefinition* lhs, MDefinition* rhs); ++ ++ void lowerForALU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir, ++ MDefinition* input); ++ void lowerForALU(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir, ++ MDefinition* lhs, MDefinition* rhs); ++ void lowerForALUInt64(LInstructionHelper* ins, ++ MDefinition* mir, MDefinition* input); ++ void lowerForALUInt64( ++ LInstructionHelper* ins, ++ MDefinition* mir, MDefinition* lhs, MDefinition* rhs); ++ void lowerForMulInt64(LMulI64* ins, MMul* mir, MDefinition* lhs, ++ MDefinition* rhs); ++ ++ void lowerForFPU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir, ++ MDefinition* src); ++ template ++ void lowerForFPU(LInstructionHelper<1, 2, Temps>* ins, MDefinition* mir, ++ MDefinition* lhs, MDefinition* rhs); ++ ++ void lowerForCompareI64AndBranch(MTest* mir, MCompare* comp, JSOp op, ++ MDefinition* left, MDefinition* right, ++ MBasicBlock* ifTrue, MBasicBlock* ifFalse); ++ void lowerForBitAndAndBranch(LBitAndAndBranch* baab, MInstruction* mir, ++ MDefinition* lhs, MDefinition* rhs); ++ ++ // Returns a box allocation. reg2 is ignored on 64-bit platforms. ++ LBoxAllocation useBoxFixed(MDefinition* mir, Register reg1, Register reg2, ++ bool useAtStart = false); ++ ++ LAllocation useByteOpRegister(MDefinition* mir); ++ LAllocation useByteOpRegisterAtStart(MDefinition* mir); ++ LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition* mir); ++ ++ LDefinition tempByteOpRegister(); ++ LDefinition tempToUnbox(); ++ ++ bool needTempForPostBarrier() { return true; } ++ ++ void lowerUntypedPhiInput(MPhi* phi, uint32_t inputPosition, LBlock* block, ++ size_t lirIndex); ++ void lowerInt64PhiInput(MPhi*, uint32_t, LBlock*, size_t); ++ void defineInt64Phi(MPhi*, size_t); ++ ++ void lowerNegI(MInstruction* ins, MDefinition* input); ++ void lowerNegI64(MInstruction* ins, MDefinition* input); ++ void lowerMulI(MMul* mul, MDefinition* lhs, MDefinition* rhs); ++ void lowerDivI(MDiv* div); ++ void lowerDivI64(MDiv* div); ++ void lowerModI(MMod* mod); ++ void lowerModI64(MMod* mod); ++ void lowerUDiv(MDiv* div); ++ void lowerUDivI64(MDiv* div); ++ void lowerUMod(MMod* mod); ++ void lowerUModI64(MMod* mod); ++ void lowerUrshD(MUrsh* mir); ++ void lowerPowOfTwoI(MPow* mir); ++ void lowerBigIntDiv(MBigIntDiv* ins); ++ void lowerBigIntMod(MBigIntMod* ins); ++ void lowerBigIntLsh(MBigIntLsh* ins); ++ void lowerBigIntRsh(MBigIntRsh* ins); ++ void lowerTruncateDToInt32(MTruncateToInt32* ins); ++ void lowerTruncateFToInt32(MTruncateToInt32* ins); ++ void lowerBuiltinInt64ToFloatingPoint(MBuiltinInt64ToFloatingPoint* ins); ++ void lowerWasmSelectI(MWasmSelect* select); ++ void lowerWasmSelectI64(MWasmSelect* select); ++ void lowerWasmBuiltinTruncateToInt64(MWasmBuiltinTruncateToInt64* ins); ++ void lowerWasmBuiltinTruncateToInt32(MWasmBuiltinTruncateToInt32* ins); ++ void lowerWasmBuiltinDivI64(MWasmBuiltinDivI64* div); ++ void lowerWasmBuiltinModI64(MWasmBuiltinModI64* mod); ++ ++ void lowerAtomicLoad64(MLoadUnboxedScalar* ins); ++ void lowerAtomicStore64(MStoreUnboxedScalar* ins); ++}; ++ ++typedef LIRGeneratorRiscv64 LIRGeneratorSpecific; ++ ++} // namespace jit ++} // namespace js ++ ++#endif /* jit_riscv64_Lowering_riscv64_h */ +diff --git a/js/src/jit/riscv64/MacroAssembler-riscv64-inl.h b/js/src/jit/riscv64/MacroAssembler-riscv64-inl.h +new file mode 100644 +index 0000000000..65f04a33e8 +--- /dev/null ++++ b/js/src/jit/riscv64/MacroAssembler-riscv64-inl.h +@@ -0,0 +1,2025 @@ ++/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- ++ * vim: set ts=8 sts=2 et sw=2 tw=80: ++ * 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_riscv64_MacroAssembler_riscv64_inl_h ++#define jit_riscv64_MacroAssembler_riscv64_inl_h ++ ++#include "jit/riscv64/MacroAssembler-riscv64.h" ++ ++namespace js { ++namespace jit { ++ ++template <> ++inline void MacroAssembler::cmpPtrSet(Assembler::Condition cond, Address lhs, ++ ImmPtr rhs, Register dest) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ loadPtr(lhs, scratch2); ++ cmpPtrSet(cond, Register(scratch2), rhs, dest); ++} ++ ++template <> ++inline void MacroAssembler::cmpPtrSet(Assembler::Condition cond, Register lhs, ++ Address rhs, Register dest) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ MOZ_ASSERT(lhs != scratch); ++ loadPtr(rhs, scratch); ++ cmpPtrSet(cond, lhs, Register(scratch), dest); ++} ++ ++template <> ++inline void MacroAssembler::cmpPtrSet(Assembler::Condition cond, Address lhs, ++ Register rhs, Register dest) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ MOZ_ASSERT(rhs != scratch2); ++ loadPtr(lhs, scratch2); ++ cmpPtrSet(cond, Register(scratch2), rhs, dest); ++} ++ ++template <> ++inline void MacroAssembler::cmp32Set(Assembler::Condition cond, Register lhs, ++ Address rhs, Register dest) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ MOZ_ASSERT(lhs != scratch); ++ load32(rhs, scratch); ++ cmp32Set(cond, lhs, Register(scratch), dest); ++} ++ ++template <> ++inline void MacroAssembler::cmp32Set(Assembler::Condition cond, Address lhs, ++ Register rhs, Register dest) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ MOZ_ASSERT(rhs != scratch2); ++ load32(lhs, scratch2); ++ cmp32Set(cond, Register(scratch2), rhs, dest); ++} ++ ++//{{{ check_macroassembler_style ++CodeOffset MacroAssembler::sub32FromStackPtrWithPatch(Register dest) { ++ CodeOffset offset = CodeOffset(currentOffset()); ++ MacroAssemblerRiscv64::ma_liPatchable(dest, Imm32(0)); ++ sub(dest, StackPointer, dest); ++ return offset; ++} ++ ++template ++void MacroAssembler::branchTest32(Condition cond, Register lhs, Imm32 rhs, ++ L label) { ++ MOZ_ASSERT(cond == Zero || cond == NonZero || cond == Signed || ++ cond == NotSigned); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ ma_and(scratch, lhs, rhs); ++ ma_b(scratch, scratch, label, cond); ++} ++template ++void MacroAssembler::branchTest32(Condition cond, Register lhs, Register rhs, ++ L label) { ++ MOZ_ASSERT(cond == Zero || cond == NonZero || cond == Signed || ++ cond == NotSigned); ++ if (lhs == rhs) { ++ ma_b(lhs, rhs, label, cond); ++ } else { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ and_(scratch, lhs, rhs); ++ ma_b(scratch, scratch, label, cond); ++ } ++} ++template ++void MacroAssembler::branchTest64(Condition cond, Register64 lhs, ++ Register64 rhs, Register temp, L label) { ++ branchTestPtr(cond, lhs.reg, rhs.reg, label); ++} ++ ++template ++void MacroAssembler::branchAdd32(Condition cond, T src, Register dest, ++ Label* overflow) { ++ switch (cond) { ++ case Overflow: ++ ma_add32TestOverflow(dest, dest, src, overflow); ++ break; ++ case CarryClear: ++ case CarrySet: ++ ma_add32TestCarry(cond, dest, dest, src, overflow); ++ break; ++ default: ++ MOZ_CRASH("NYI"); ++ } ++} ++template ++void MacroAssembler::branchAddPtr(Condition cond, T src, Register dest, ++ Label* label) { ++ switch (cond) { ++ case Overflow: ++ ma_addPtrTestOverflow(dest, dest, src, label); ++ break; ++ case CarryClear: ++ case CarrySet: ++ ma_addPtrTestCarry(cond, dest, dest, src, label); ++ break; ++ default: ++ MOZ_CRASH("NYI"); ++ } ++} ++template ++void MacroAssembler::branchMul32(Condition cond, T src, Register dest, ++ Label* overflow) { ++ MOZ_ASSERT(cond == Assembler::Overflow); ++ ma_mul32TestOverflow(dest, dest, src, overflow); ++} ++template ++void MacroAssembler::branchRshift32(Condition cond, T src, Register dest, ++ Label* label) { ++ MOZ_ASSERT(cond == Zero || cond == NonZero); ++ rshift32(src, dest); ++ branch32(cond == Zero ? Equal : NotEqual, dest, Imm32(0), label); ++} ++// the type of 'T src' maybe a Register, maybe a Imm32,depends on who call it. ++template ++void MacroAssembler::branchSub32(Condition cond, T src, Register dest, ++ Label* label) { ++ switch (cond) { ++ case Overflow: ++ ma_sub32TestOverflow(dest, dest, src, label); ++ break; ++ case NonZero: ++ case Zero: ++ case Signed: ++ case NotSigned: ++ ma_sub32(dest, dest, src); ++ ma_b(dest, dest, label, cond); ++ break; ++ default: ++ MOZ_CRASH("NYI"); ++ } ++} ++template ++void MacroAssembler::branchSubPtr(Condition cond, T src, Register dest, ++ Label* label) { ++ switch (cond) { ++ case Overflow: ++ ma_subPtrTestOverflow(dest, dest, src, label); ++ break; ++ case NonZero: ++ case Zero: ++ case Signed: ++ case NotSigned: ++ subPtr(src, dest); ++ ma_b(dest, dest, label, cond); ++ break; ++ default: ++ MOZ_CRASH("NYI"); ++ } ++} ++template ++void MacroAssembler::branchTestGCThingImpl(Condition cond, const T& address, ++ Label* label) { ++ MOZ_ASSERT(cond == Equal || cond == NotEqual); ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ Register tag = extractTag(address, scratch2); ++ ma_b(tag, ImmTag(JS::detail::ValueLowerInclGCThingTag), label, ++ (cond == Equal) ? AboveOrEqual : Below); ++} ++template ++void MacroAssembler::testBigIntSet(Condition cond, const T& src, ++ Register dest) { ++ MOZ_ASSERT(cond == Equal || cond == NotEqual); ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ Register tag = extractTag(src, scratch2); ++ ma_cmp_set(dest, tag, ImmTag(JSVAL_TAG_BIGINT), cond); ++} ++ ++template ++void MacroAssembler::testBooleanSet(Condition cond, const T& src, ++ Register dest) { ++ MOZ_ASSERT(cond == Equal || cond == NotEqual); ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ Register tag = extractTag(src, scratch2); ++ ma_cmp_set(dest, tag, ImmTag(JSVAL_TAG_BOOLEAN), cond); ++} ++ ++template ++void MacroAssembler::testNumberSet(Condition cond, const T& src, ++ Register dest) { ++ MOZ_ASSERT(cond == Equal || cond == NotEqual); ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ Register tag = extractTag(src, scratch2); ++ ma_cmp_set(dest, tag, ImmTag(JS::detail::ValueUpperInclNumberTag), ++ cond == Equal ? BelowOrEqual : Above); ++} ++ ++template ++void MacroAssembler::testStringSet(Condition cond, const T& src, ++ Register dest) { ++ MOZ_ASSERT(cond == Equal || cond == NotEqual); ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ Register tag = extractTag(src, scratch2); ++ ma_cmp_set(dest, tag, ImmTag(JSVAL_TAG_STRING), cond); ++} ++ ++template ++void MacroAssembler::testSymbolSet(Condition cond, const T& src, ++ Register dest) { ++ MOZ_ASSERT(cond == Equal || cond == NotEqual); ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ Register tag = extractTag(src, scratch2); ++ ma_cmp_set(dest, tag, ImmTag(JSVAL_TAG_SYMBOL), cond); ++} ++ ++// Also see below for specializations of cmpPtrSet. ++template ++void MacroAssembler::cmp32Set(Condition cond, T1 lhs, T2 rhs, Register dest) { ++ ma_cmp_set(dest, lhs, rhs, cond); ++} ++template ++void MacroAssembler::cmpPtrSet(Condition cond, T1 lhs, T2 rhs, Register dest) { ++ ma_cmp_set(dest, lhs, rhs, cond); ++} ++void MacroAssembler::abs32(Register src, Register dest) { ++ ScratchRegisterScope scratch(asMasm()); ++ sraiw(scratch, src, 31); ++ xor_(dest, src, scratch); ++ subw(dest, dest, scratch); ++} ++void MacroAssembler::absFloat32(FloatRegister src, FloatRegister dest) { ++ fabs_s(dest, src); ++} ++ ++void MacroAssembler::absDouble(FloatRegister src, FloatRegister dest) { ++ fabs_d(dest, src); ++} ++void MacroAssembler::add32(Register src, Register dest) { ++ ma_add32(dest, dest, src); ++} ++ ++void MacroAssembler::add32(Imm32 imm, Register dest) { ++ ma_add32(dest, dest, imm); ++} ++ ++void MacroAssembler::add32(Imm32 imm, const Address& dest) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ load32(dest, scratch2); ++ ma_add32(scratch2, scratch2, imm); ++ store32(scratch2, dest); ++} ++void MacroAssembler::add64(Register64 src, Register64 dest) { ++ addPtr(src.reg, dest.reg); ++} ++ ++void MacroAssembler::add64(const Operand& src, Register64 dest) { ++ if (src.is_mem()) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ Register64 scratch64(scratch); ++ ++ load64(src.toAddress(), scratch64); ++ add64(scratch64, dest); ++ } else { ++ add64(Register64(src.toReg()), dest); ++ } ++} ++ ++void MacroAssembler::add64(Imm32 imm, Register64 dest) { ++ ma_add64(dest.reg, dest.reg, imm); ++} ++ ++void MacroAssembler::add64(Imm64 imm, Register64 dest) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ MOZ_ASSERT(dest.reg != scratch); ++ mov(ImmWord(imm.value), scratch); ++ add(dest.reg, dest.reg, scratch); ++} ++void MacroAssembler::addDouble(FloatRegister src, FloatRegister dest) { ++ fadd_d(dest, dest, src); ++} ++ ++void MacroAssembler::addFloat32(FloatRegister src, FloatRegister dest) { ++ fadd_s(dest, dest, src); ++} ++void MacroAssembler::addPtr(Register src, Register dest) { ++ ma_add64(dest, dest, Operand(src)); ++} ++ ++void MacroAssembler::addPtr(Imm32 imm, Register dest) { ++ ma_add64(dest, dest, imm); ++} ++ ++void MacroAssembler::addPtr(ImmWord imm, Register dest) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ movePtr(imm, scratch); ++ addPtr(scratch, dest); ++} ++void MacroAssembler::addPtr(Imm32 imm, const Address& dest) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ loadPtr(dest, scratch); ++ addPtr(imm, scratch); ++ storePtr(scratch, dest); ++} ++ ++void MacroAssembler::addPtr(const Address& src, Register dest) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ loadPtr(src, scratch); ++ addPtr(scratch, dest); ++} ++void MacroAssembler::and32(Register src, Register dest) { ++ ma_and(dest, dest, src); ++} ++ ++void MacroAssembler::and32(Imm32 imm, Register dest) { ++ ma_and(dest, dest, imm); ++} ++ ++void MacroAssembler::and32(Imm32 imm, const Address& dest) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ load32(dest, scratch2); ++ ma_and(scratch2, imm); ++ store32(scratch2, dest); ++} ++ ++void MacroAssembler::and32(const Address& src, Register dest) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ load32(src, scratch2); ++ ma_and(dest, dest, scratch2); ++} ++void MacroAssembler::and64(Imm64 imm, Register64 dest) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ ma_li(scratch, ImmWord(imm.value)); ++ ma_and(dest.reg, dest.reg, scratch); ++} ++ ++void MacroAssembler::and64(Register64 src, Register64 dest) { ++ ma_and(dest.reg, dest.reg, src.reg); ++} ++ ++void MacroAssembler::and64(const Operand& src, Register64 dest) { ++ if (src.is_mem()) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ Register64 scratch64(scratch); ++ ++ load64(src.toAddress(), scratch64); ++ ma_and(dest.scratchReg(), scratch64.scratchReg()); ++ } else { ++ ma_and(dest.scratchReg(), src.toReg()); ++ } ++} ++ ++void MacroAssembler::andPtr(Register src, Register dest) { ++ ma_and(dest, dest, src); ++} ++ ++void MacroAssembler::andPtr(Imm32 imm, Register dest) { ++ ma_and(dest, dest, imm); ++} ++ ++void MacroAssembler::branch8(Condition cond, const Address& lhs, Imm32 rhs, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ MOZ_ASSERT(scratch2 != lhs.base); ++ ++ switch (cond) { ++ case Assembler::Equal: ++ case Assembler::NotEqual: ++ case Assembler::Above: ++ case Assembler::AboveOrEqual: ++ case Assembler::Below: ++ case Assembler::BelowOrEqual: ++ load8ZeroExtend(lhs, scratch2); ++ branch32(cond, scratch2, Imm32(uint8_t(rhs.value)), label); ++ break; ++ ++ case Assembler::GreaterThan: ++ case Assembler::GreaterThanOrEqual: ++ case Assembler::LessThan: ++ case Assembler::LessThanOrEqual: ++ load8SignExtend(lhs, scratch2); ++ branch32(cond, scratch2, Imm32(int8_t(rhs.value)), label); ++ break; ++ ++ default: ++ MOZ_CRASH("unexpected condition"); ++ } ++} ++ ++void MacroAssembler::branch8(Condition cond, const BaseIndex& lhs, Register rhs, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ MOZ_ASSERT(scratch2 != lhs.base); ++ ++ computeScaledAddress(lhs, scratch2); ++ ++ switch (cond) { ++ case Assembler::Equal: ++ case Assembler::NotEqual: ++ case Assembler::Above: ++ case Assembler::AboveOrEqual: ++ case Assembler::Below: ++ case Assembler::BelowOrEqual: ++ load8ZeroExtend(Address(scratch2, lhs.offset), scratch2); ++ branch32(cond, scratch2, rhs, label); ++ break; ++ ++ case Assembler::GreaterThan: ++ case Assembler::GreaterThanOrEqual: ++ case Assembler::LessThan: ++ case Assembler::LessThanOrEqual: ++ load8SignExtend(Address(scratch2, lhs.offset), scratch2); ++ branch32(cond, scratch2, rhs, label); ++ break; ++ ++ default: ++ MOZ_CRASH("unexpected condition"); ++ } ++} ++ ++void MacroAssembler::branch16(Condition cond, const Address& lhs, Imm32 rhs, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ MOZ_ASSERT(scratch2 != lhs.base); ++ ++ switch (cond) { ++ case Assembler::Equal: ++ case Assembler::NotEqual: ++ case Assembler::Above: ++ case Assembler::AboveOrEqual: ++ case Assembler::Below: ++ case Assembler::BelowOrEqual: ++ load16ZeroExtend(lhs, scratch2); ++ branch32(cond, scratch2, Imm32(uint16_t(rhs.value)), label); ++ break; ++ ++ case Assembler::GreaterThan: ++ case Assembler::GreaterThanOrEqual: ++ case Assembler::LessThan: ++ case Assembler::LessThanOrEqual: ++ load16SignExtend(lhs, scratch2); ++ branch32(cond, scratch2, Imm32(int16_t(rhs.value)), label); ++ break; ++ ++ default: ++ MOZ_CRASH("unexpected condition"); ++ } ++} ++template ++void MacroAssembler::branch32(Condition cond, Register lhs, Register rhs, ++ L label) { ++ ma_b(lhs, rhs, label, cond); ++} ++ ++template ++void MacroAssembler::branch32(Condition cond, Register lhs, Imm32 imm, ++ L label) { ++ ma_b(lhs, imm, label, cond); ++} ++ ++void MacroAssembler::branch32(Condition cond, const Address& lhs, Register rhs, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ load32(lhs, scratch2); ++ ma_b(scratch2, rhs, label, cond); ++} ++ ++void MacroAssembler::branch32(Condition cond, const Address& lhs, Imm32 rhs, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ load32(lhs, scratch2); ++ ma_b(scratch2, rhs, label, cond); ++} ++ ++void MacroAssembler::branch32(Condition cond, const AbsoluteAddress& lhs, ++ Register rhs, Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ load32(lhs, scratch2); ++ ma_b(scratch2, rhs, label, cond); ++} ++ ++void MacroAssembler::branch32(Condition cond, const AbsoluteAddress& lhs, ++ Imm32 rhs, Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ load32(lhs, scratch2); ++ ma_b(scratch2, rhs, label, cond); ++} ++ ++void MacroAssembler::branch32(Condition cond, const BaseIndex& lhs, Imm32 rhs, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ load32(lhs, scratch2); ++ ma_b(scratch2, rhs, label, cond); ++} ++ ++void MacroAssembler::branch32(Condition cond, wasm::SymbolicAddress addr, ++ Imm32 imm, Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ load32(addr, scratch2); ++ ma_b(scratch2, imm, label, cond); ++} ++void MacroAssembler::branch64(Condition cond, Register64 lhs, Imm64 val, ++ Label* success, Label* fail) { ++ MOZ_ASSERT(cond == Assembler::NotEqual || cond == Assembler::Equal || ++ cond == Assembler::LessThan || ++ cond == Assembler::LessThanOrEqual || ++ cond == Assembler::GreaterThan || ++ cond == Assembler::GreaterThanOrEqual || ++ cond == Assembler::Below || cond == Assembler::BelowOrEqual || ++ cond == Assembler::Above || cond == Assembler::AboveOrEqual, ++ "other condition codes not supported"); ++ ++ branchPtr(cond, lhs.reg, ImmWord(val.value), success); ++ if (fail) { ++ jump(fail); ++ } ++} ++ ++void MacroAssembler::branch64(Condition cond, Register64 lhs, Register64 rhs, ++ Label* success, Label* fail) { ++ MOZ_ASSERT(cond == Assembler::NotEqual || cond == Assembler::Equal || ++ cond == Assembler::LessThan || ++ cond == Assembler::LessThanOrEqual || ++ cond == Assembler::GreaterThan || ++ cond == Assembler::GreaterThanOrEqual || ++ cond == Assembler::Below || cond == Assembler::BelowOrEqual || ++ cond == Assembler::Above || cond == Assembler::AboveOrEqual, ++ "other condition codes not supported"); ++ ++ branchPtr(cond, lhs.reg, rhs.reg, success); ++ if (fail) { ++ jump(fail); ++ } ++} ++ ++void MacroAssembler::branch64(Condition cond, const Address& lhs, Imm64 val, ++ Label* label) { ++ MOZ_ASSERT(cond == Assembler::NotEqual || cond == Assembler::Equal, ++ "other condition codes not supported"); ++ ++ branchPtr(cond, lhs, ImmWord(val.value), label); ++} ++ ++void MacroAssembler::branch64(Condition cond, const Address& lhs, ++ Register64 rhs, Label* label) { ++ MOZ_ASSERT(cond == Assembler::NotEqual || cond == Assembler::Equal, ++ "other condition codes not supported"); ++ ++ branchPtr(cond, lhs, rhs.reg, label); ++} ++ ++void MacroAssembler::branch64(Condition cond, const Address& lhs, ++ const Address& rhs, Register scratch, ++ Label* label) { ++ MOZ_ASSERT(cond == Assembler::NotEqual || cond == Assembler::Equal, ++ "other condition codes not supported"); ++ MOZ_ASSERT(lhs.base != scratch); ++ MOZ_ASSERT(rhs.base != scratch); ++ ++ loadPtr(rhs, scratch); ++ branchPtr(cond, lhs, scratch, label); ++} ++ ++void MacroAssembler::branchDouble(DoubleCondition cc, FloatRegister frs1, ++ FloatRegister frs2, Label* L) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ ma_compareF64(scratch, cc, frs1, frs2); ++ ma_b(scratch, Imm32(1), L, Equal); ++} ++void MacroAssembler::branchFloat(DoubleCondition cc, FloatRegister frs1, ++ FloatRegister frs2, Label* L) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ ma_compareF32(scratch, cc, frs1, frs2); ++ ma_b(scratch, Imm32(1), L, Equal); ++} ++void MacroAssembler::branchMulPtr(Condition cond, Register src, Register dest, ++ Label* label) { ++ MOZ_ASSERT(cond == Assembler::Overflow); ++ ma_mulPtrTestOverflow(dest, dest, src, label); ++} ++void MacroAssembler::branchNeg32(Condition cond, Register reg, Label* label) { ++ MOZ_ASSERT(cond == Overflow); ++ neg32(reg); ++ branch32(Assembler::Equal, reg, Imm32(INT32_MIN), label); ++} ++ ++void MacroAssembler::branchPrivatePtr(Condition cond, const Address& lhs, ++ Register rhs, Label* label) { ++ branchPtr(cond, lhs, rhs, label); ++} ++ ++template ++void MacroAssembler::branchPtr(Condition cond, Register lhs, Register rhs, ++ L label) { ++ ma_b(lhs, rhs, label, cond); ++} ++ ++void MacroAssembler::branchPtr(Condition cond, Register lhs, Imm32 rhs, ++ Label* label) { ++ ma_b(lhs, rhs, label, cond); ++} ++ ++void MacroAssembler::branchPtr(Condition cond, Register lhs, ImmPtr rhs, ++ Label* label) { ++ if (rhs.value == nullptr && (cond == Zero || cond == NonZero)) { ++ ma_b(lhs, lhs, label, cond); ++ } else { ++ ma_b(lhs, rhs, label, cond); ++ } ++} ++ ++void MacroAssembler::branchPtr(Condition cond, Register lhs, ImmGCPtr rhs, ++ Label* label) { ++ ma_b(lhs, rhs, label, cond); ++} ++ ++void MacroAssembler::branchPtr(Condition cond, Register lhs, ImmWord rhs, ++ Label* label) { ++ ma_b(lhs, rhs, label, cond); ++} ++ ++template ++void MacroAssembler::branchPtr(Condition cond, const Address& lhs, Register rhs, ++ L label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ loadPtr(lhs, scratch2); ++ branchPtr(cond, scratch2, rhs, label); ++} ++ ++void MacroAssembler::branchPtr(Condition cond, const Address& lhs, ImmPtr rhs, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ loadPtr(lhs, scratch2); ++ branchPtr(cond, scratch2, rhs, label); ++} ++ ++void MacroAssembler::branchPtr(Condition cond, const Address& lhs, ImmGCPtr rhs, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ loadPtr(lhs, scratch2); ++ branchPtr(cond, scratch2, rhs, label); ++} ++ ++void MacroAssembler::branchPtr(Condition cond, const Address& lhs, ImmWord rhs, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ loadPtr(lhs, scratch2); ++ branchPtr(cond, scratch2, rhs, label); ++} ++ ++void MacroAssembler::branchPtr(Condition cond, const AbsoluteAddress& lhs, ++ Register rhs, Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ loadPtr(lhs, scratch2); ++ branchPtr(cond, scratch2, rhs, label); ++} ++ ++void MacroAssembler::branchPtr(Condition cond, const AbsoluteAddress& lhs, ++ ImmWord rhs, Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ loadPtr(lhs, scratch2); ++ branchPtr(cond, scratch2, rhs, label); ++} ++ ++void MacroAssembler::branchPtr(Condition cond, wasm::SymbolicAddress lhs, ++ Register rhs, Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ loadPtr(lhs, scratch2); ++ branchPtr(cond, scratch2, rhs, label); ++} ++ ++void MacroAssembler::branchPtr(Condition cond, const BaseIndex& lhs, ++ Register rhs, Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ loadPtr(lhs, scratch2); ++ branchPtr(cond, scratch2, rhs, label); ++} ++ ++void MacroAssembler::branchPtr(Condition cond, const BaseIndex& lhs, ++ ImmWord rhs, Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ loadPtr(lhs, scratch2); ++ branchPtr(cond, scratch2, rhs, label); ++} ++ ++void MacroAssembler::branchTest32(Condition cond, const AbsoluteAddress& lhs, ++ Imm32 rhs, Label* label) { ++ MOZ_ASSERT(cond == Zero || cond == NonZero || cond == Signed || ++ cond == NotSigned); ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ load32(lhs, scratch2); ++ and32(rhs, scratch2); ++ ma_b(scratch2, scratch2, label, cond); ++} ++ ++void MacroAssembler::branchTest32(Condition cond, const Address& lhs, Imm32 rhs, ++ Label* label) { ++ MOZ_ASSERT(cond == Zero || cond == NonZero || cond == Signed || ++ cond == NotSigned); ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ load32(lhs, scratch2); ++ and32(rhs, scratch2); ++ ma_b(scratch2, scratch2, label, cond); ++} ++void MacroAssembler::branchTestBigInt(Condition cond, Register tag, ++ Label* label) { ++ MOZ_ASSERT(cond == Equal || cond == NotEqual); ++ ma_b(tag, ImmTag(JSVAL_TAG_BIGINT), label, cond); ++} ++ ++void MacroAssembler::branchTestBigInt(Condition cond, const ValueOperand& value, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ splitTag(value, scratch2); ++ branchTestBigInt(cond, scratch2, label); ++} ++ ++void MacroAssembler::branchTestBigInt(Condition cond, const Address& address, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ Register tag = extractTag(address, scratch2); ++ branchTestBigInt(cond, tag, label); ++} ++ ++void MacroAssembler::branchTestBigInt(Condition cond, const BaseIndex& address, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ computeEffectiveAddress(address, scratch2); ++ splitTag(scratch2, scratch2); ++ branchTestBigInt(cond, scratch2, label); ++} ++void MacroAssembler::branchTestBigIntTruthy(bool b, const ValueOperand& value, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ unboxBigInt(value, scratch2); ++ load32(Address(scratch2, BigInt::offsetOfDigitLength()), scratch2); ++ ma_b(scratch2, Imm32(0), label, b ? NotEqual : Equal); ++} ++void MacroAssembler::branchTestBoolean(Condition cond, Register tag, ++ Label* label) { ++ MOZ_ASSERT(cond == Equal || cond == NotEqual); ++ ma_b(tag, ImmTag(JSVAL_TAG_BOOLEAN), label, cond); ++} ++ ++void MacroAssembler::branchTestBoolean(Condition cond, ++ const ValueOperand& value, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ splitTag(value, scratch2); ++ branchTestBoolean(cond, scratch2, label); ++} ++ ++void MacroAssembler::branchTestBoolean(Condition cond, const Address& address, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ Register tag = extractTag(address, scratch2); ++ branchTestBoolean(cond, tag, label); ++} ++ ++void MacroAssembler::branchTestBoolean(Condition cond, const BaseIndex& address, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ Register tag = extractTag(address, scratch2); ++ branchTestBoolean(cond, tag, label); ++} ++void MacroAssembler::branchTestBooleanTruthy(bool b, const ValueOperand& value, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ unboxBoolean(value, scratch2); ++ ma_b(scratch2, scratch2, label, b ? NonZero : Zero); ++} ++void MacroAssembler::branchTestDouble(Condition cond, Register tag, ++ Label* label) { ++ MOZ_ASSERT(cond == Equal || cond == NotEqual); ++ Condition actual = (cond == Equal) ? BelowOrEqual : Above; ++ ma_b(tag, ImmTag(JSVAL_TAG_MAX_DOUBLE), label, actual); ++} ++ ++void MacroAssembler::branchTestDouble(Condition cond, const ValueOperand& value, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ splitTag(value, scratch2); ++ branchTestDouble(cond, scratch2, label); ++} ++ ++void MacroAssembler::branchTestDouble(Condition cond, const Address& address, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ Register tag = extractTag(address, scratch2); ++ branchTestDouble(cond, tag, label); ++} ++ ++void MacroAssembler::branchTestDouble(Condition cond, const BaseIndex& address, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ Register tag = extractTag(address, scratch2); ++ branchTestDouble(cond, tag, label); ++} ++ ++void MacroAssembler::branchTestDoubleTruthy(bool b, FloatRegister value, ++ Label* label) { ++ ScratchDoubleScope fpscratch(*this); ++ loadConstantDouble(0.0, fpscratch); ++ DoubleCondition cond = b ? DoubleNotEqual : DoubleEqualOrUnordered; ++ branchDouble(cond, value, fpscratch, label); ++} ++void MacroAssembler::branchTestGCThing(Condition cond, const Address& address, ++ Label* label) { ++ branchTestGCThingImpl(cond, address, label); ++} ++ ++void MacroAssembler::branchTestGCThing(Condition cond, const BaseIndex& address, ++ Label* label) { ++ branchTestGCThingImpl(cond, address, label); ++} ++ ++void MacroAssembler::branchTestGCThing(Condition cond, ++ const ValueOperand& address, ++ Label* label) { ++ branchTestGCThingImpl(cond, address, label); ++} ++void MacroAssembler::branchTestInt32(Condition cond, Register tag, ++ Label* label) { ++ MOZ_ASSERT(cond == Equal || cond == NotEqual); ++ ma_b(tag, ImmTag(JSVAL_TAG_INT32), label, cond); ++} ++ ++void MacroAssembler::branchTestInt32(Condition cond, const ValueOperand& value, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ splitTag(value, scratch2); ++ branchTestInt32(cond, scratch2, label); ++} ++ ++void MacroAssembler::branchTestInt32(Condition cond, const Address& address, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ Register tag = extractTag(address, scratch2); ++ branchTestInt32(cond, tag, label); ++} ++ ++void MacroAssembler::branchTestInt32(Condition cond, const BaseIndex& address, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ Register tag = extractTag(address, scratch2); ++ branchTestInt32(cond, tag, label); ++} ++void MacroAssembler::branchTestInt32Truthy(bool b, const ValueOperand& value, ++ Label* label) { ++ ScratchRegisterScope scratch(*this); ++ ExtractBits(scratch, value.valueReg(), 0, 32); ++ ma_b(scratch, scratch, label, b ? NonZero : Zero); ++} ++void MacroAssembler::branchTestMagic(Condition cond, Register tag, ++ Label* label) { ++ MOZ_ASSERT(cond == Equal || cond == NotEqual); ++ ma_b(tag, ImmTag(JSVAL_TAG_MAGIC), label, cond); ++} ++ ++void MacroAssembler::branchTestMagic(Condition cond, const Address& address, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ Register tag = extractTag(address, scratch2); ++ branchTestMagic(cond, tag, label); ++} ++ ++void MacroAssembler::branchTestMagic(Condition cond, const BaseIndex& address, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ Register tag = extractTag(address, scratch2); ++ branchTestMagic(cond, tag, label); ++} ++ ++template ++void MacroAssembler::branchTestMagic(Condition cond, const ValueOperand& value, ++ L label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ splitTag(value, scratch2); ++ ma_b(scratch2, ImmTag(JSVAL_TAG_MAGIC), label, cond); ++} ++ ++void MacroAssembler::branchTestMagic(Condition cond, const Address& valaddr, ++ JSWhyMagic why, Label* label) { ++ uint64_t magic = MagicValue(why).asRawBits(); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ loadPtr(valaddr, scratch); ++ ma_b(scratch, ImmWord(magic), label, cond); ++} ++void MacroAssembler::branchTestNull(Condition cond, Register tag, ++ Label* label) { ++ MOZ_ASSERT(cond == Equal || cond == NotEqual); ++ ma_b(tag, ImmTag(JSVAL_TAG_NULL), label, cond); ++} ++ ++void MacroAssembler::branchTestNull(Condition cond, const ValueOperand& value, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ splitTag(value, scratch2); ++ branchTestNull(cond, scratch2, label); ++} ++ ++void MacroAssembler::branchTestNull(Condition cond, const Address& address, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ Register tag = extractTag(address, scratch2); ++ branchTestNull(cond, tag, label); ++} ++ ++void MacroAssembler::branchTestNull(Condition cond, const BaseIndex& address, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ Register tag = extractTag(address, scratch2); ++ branchTestNull(cond, tag, label); ++} ++void MacroAssembler::branchTestNumber(Condition cond, Register tag, ++ Label* label) { ++ MOZ_ASSERT(cond == Equal || cond == NotEqual); ++ Condition actual = cond == Equal ? BelowOrEqual : Above; ++ ma_b(tag, ImmTag(JS::detail::ValueUpperInclNumberTag), label, actual); ++} ++ ++void MacroAssembler::branchTestNumber(Condition cond, const ValueOperand& value, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ splitTag(value, scratch2); ++ branchTestNumber(cond, scratch2, label); ++} ++void MacroAssembler::branchTestObject(Condition cond, Register tag, ++ Label* label) { ++ MOZ_ASSERT(cond == Equal || cond == NotEqual); ++ ma_b(tag, ImmTag(JSVAL_TAG_OBJECT), label, cond); ++} ++ ++void MacroAssembler::branchTestObject(Condition cond, const ValueOperand& value, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ splitTag(value, scratch2); ++ branchTestObject(cond, scratch2, label); ++} ++ ++void MacroAssembler::branchTestObject(Condition cond, const Address& address, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ Register tag = extractTag(address, scratch2); ++ branchTestObject(cond, tag, label); ++} ++ ++void MacroAssembler::branchTestObject(Condition cond, const BaseIndex& address, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ Register tag = extractTag(address, scratch2); ++ branchTestObject(cond, tag, label); ++} ++void MacroAssembler::branchTestPrimitive(Condition cond, ++ const ValueOperand& value, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ splitTag(value, scratch2); ++ branchTestPrimitive(cond, scratch2, label); ++} ++void MacroAssembler::branchTestPrimitive(Condition cond, Register tag, ++ Label* label) { ++ MOZ_ASSERT(cond == Equal || cond == NotEqual); ++ ma_b(tag, ImmTag(JS::detail::ValueUpperExclPrimitiveTag), label, ++ (cond == Equal) ? Below : AboveOrEqual); ++} ++template ++void MacroAssembler::branchTestPtr(Condition cond, Register lhs, Register rhs, ++ L label) { ++ MOZ_ASSERT(cond == Zero || cond == NonZero || cond == Signed || ++ cond == NotSigned); ++ if (lhs == rhs) { ++ ma_b(lhs, rhs, label, cond); ++ } else { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ ma_and(scratch, lhs, Operand(rhs)); ++ ma_b(scratch, scratch, label, cond); ++ } ++} ++ ++void MacroAssembler::branchTestPtr(Condition cond, Register lhs, Imm32 rhs, ++ Label* label) { ++ MOZ_ASSERT(cond == Zero || cond == NonZero || cond == Signed || ++ cond == NotSigned); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ ma_and(scratch, lhs, rhs); ++ ma_b(scratch, scratch, label, cond); ++} ++ ++void MacroAssembler::branchTestPtr(Condition cond, const Address& lhs, ++ Imm32 rhs, Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ loadPtr(lhs, scratch2); ++ branchTestPtr(cond, scratch2, rhs, label); ++} ++void MacroAssembler::branchTestString(Condition cond, Register tag, ++ Label* label) { ++ MOZ_ASSERT(cond == Equal || cond == NotEqual); ++ ma_b(tag, ImmTag(JSVAL_TAG_STRING), label, cond); ++} ++ ++void MacroAssembler::branchTestString(Condition cond, const ValueOperand& value, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ splitTag(value, scratch2); ++ branchTestString(cond, scratch2, label); ++} ++ ++void MacroAssembler::branchTestString(Condition cond, const Address& address, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ Register tag = extractTag(address, scratch2); ++ branchTestString(cond, tag, label); ++} ++ ++void MacroAssembler::branchTestString(Condition cond, const BaseIndex& address, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ Register tag = extractTag(address, scratch2); ++ branchTestString(cond, tag, label); ++} ++void MacroAssembler::branchTestStringTruthy(bool b, const ValueOperand& value, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ unboxString(value, scratch2); ++ load32(Address(scratch2, JSString::offsetOfLength()), scratch2); ++ ma_b(scratch2, Imm32(0), label, b ? NotEqual : Equal); ++} ++void MacroAssembler::branchTestSymbol(Condition cond, Register tag, ++ Label* label) { ++ MOZ_ASSERT(cond == Equal || cond == NotEqual); ++ ma_b(tag, ImmTag(JSVAL_TAG_SYMBOL), label, cond); ++} ++ ++void MacroAssembler::branchTestSymbol(Condition cond, const ValueOperand& value, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ splitTag(value, scratch2); ++ branchTestSymbol(cond, scratch2, label); ++} ++ ++void MacroAssembler::branchTestSymbol(Condition cond, const BaseIndex& address, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ Register tag = extractTag(address, scratch2); ++ branchTestSymbol(cond, tag, label); ++} ++ ++void MacroAssembler::branchTestSymbol(Condition cond, const Address& address, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ Register tag = extractTag(address, scratch2); ++ branchTestSymbol(cond, tag, label); ++} ++void MacroAssembler::branchTestUndefined(Condition cond, ++ const ValueOperand& value, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ splitTag(value, scratch2); ++ branchTestUndefined(cond, scratch2, label); ++} ++ ++void MacroAssembler::branchTestUndefined(Condition cond, const Address& address, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ Register tag = extractTag(address, scratch2); ++ branchTestUndefined(cond, tag, label); ++} ++ ++void MacroAssembler::branchTestUndefined(Condition cond, ++ const BaseIndex& address, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ Register tag = extractTag(address, scratch2); ++ branchTestUndefined(cond, tag, label); ++} ++void MacroAssembler::branchTestUndefined(Condition cond, Register tag, ++ Label* label) { ++ MOZ_ASSERT(cond == Equal || cond == NotEqual); ++ ma_b(tag, ImmTag(JSVAL_TAG_UNDEFINED), label, cond); ++} ++void MacroAssembler::branchTestValue(Condition cond, const BaseIndex& lhs, ++ const ValueOperand& rhs, Label* label) { ++ MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); ++ branchPtr(cond, lhs, rhs.valueReg(), label); ++} ++void MacroAssembler::branchToComputedAddress(const BaseIndex& addr) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ loadPtr(addr, scratch2); ++ branch(scratch2); ++} ++void MacroAssembler::branchTruncateDoubleMaybeModUint32(FloatRegister src, ++ Register dest, ++ Label* fail) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ Trunc_w_d(dest, src, scratch); ++ ma_b(scratch, Imm32(0), fail, Assembler::Equal); ++} ++ ++void MacroAssembler::branchTruncateDoubleToInt32(FloatRegister src, ++ Register dest, Label* fail) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ Trunc_w_d(dest, src, scratch); ++ ma_b(scratch, Imm32(0), fail, Assembler::Equal); ++} ++void MacroAssembler::branchTruncateFloat32MaybeModUint32(FloatRegister src, ++ Register dest, ++ Label* fail) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ Trunc_w_s(dest, src, scratch); ++ ma_b(scratch, Imm32(0), fail, Assembler::Equal); ++} ++ ++void MacroAssembler::branchTruncateFloat32ToInt32(FloatRegister src, ++ Register dest, Label* fail) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ Trunc_w_s(dest, src, scratch); ++ ma_b(scratch, Imm32(0), fail, Assembler::Equal); ++} ++ ++void MacroAssembler::byteSwap16SignExtend(Register src) { ++ JitSpew(JitSpew_Codegen, "[ %s\n", __FUNCTION__); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ Register scratch2 = temps.Acquire(); ++ // src 0xFFFFFFFFFFFF8000 ++ andi(scratch, src, 0xFF); // ++ slli(scratch, scratch, 8); // scratch 0x00 ++ ma_li(scratch2, 0xFF00); // scratch2 0xFF00 ++ and_(src, src, scratch2); // src 0x8000 ++ srli(src, src, 8); // src 0x0080 ++ or_(src, src, scratch); // src 0x0080 ++ slliw(src, src, 16); ++ sraiw(src, src, 16); ++ JitSpew(JitSpew_Codegen, "]"); ++} ++ ++void MacroAssembler::byteSwap16ZeroExtend(Register src) { ++ JitSpew(JitSpew_Codegen, "[ %s\n", __FUNCTION__); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ Register scratch2 = temps.Acquire(); ++ andi(scratch, src, 0xFF); ++ slli(scratch, scratch, 8); ++ ma_li(scratch2, 0xFF00); ++ and_(src, src, scratch2); ++ srli(src, src, 8); ++ or_(src, src, scratch); ++ slliw(src, src, 16); ++ srliw(src, src, 16); ++ JitSpew(JitSpew_Codegen, "]"); ++} ++ ++void MacroAssembler::byteSwap32(Register src) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ ByteSwap(src, src, 4, scratch); ++} ++void MacroAssembler::byteSwap64(Register64 src) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ ByteSwap(src.reg, src.reg, 8, scratch); ++} ++void MacroAssembler::clampIntToUint8(Register reg) { ++ // If reg is < 0, then we want to clamp to 0. ++ Label skip, skip2; ++ slti(ScratchRegister, reg, 0); ++ ma_branch(&skip, NotEqual, ScratchRegister, Operand(1)); ++ ma_li(reg, Imm32(0)); ++ jump(&skip2); ++ bind(&skip); ++ // If reg is >= 255, then we want to clamp to 255. ++ ma_branch(&skip2, LessThanOrEqual, reg, Operand(255)); ++ ma_li(reg, Imm32(255)); ++ bind(&skip2); ++} ++ ++void MacroAssembler::clz32(Register src, Register dest, bool knownNotZero) { ++ Clz32(dest, src); ++} ++void MacroAssembler::clz64(Register64 src, Register dest) { ++ Clz64(dest, src.reg); ++} ++ ++void MacroAssembler::ctz64(Register64 src, Register dest) { ++ Ctz64(dest, src.reg); ++} ++ ++void MacroAssembler::cmp16Set(Condition cond, Address lhs, Imm32 rhs, ++ Register dest) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ MOZ_ASSERT(scratch2 != lhs.base); ++ ++ switch (cond) { ++ case Assembler::Equal: ++ case Assembler::NotEqual: ++ case Assembler::Above: ++ case Assembler::AboveOrEqual: ++ case Assembler::Below: ++ case Assembler::BelowOrEqual: ++ load16ZeroExtend(lhs, scratch2); ++ ma_cmp_set(dest, scratch2, Imm32(uint16_t(rhs.value)), cond); ++ break; ++ ++ case Assembler::GreaterThan: ++ case Assembler::GreaterThanOrEqual: ++ case Assembler::LessThan: ++ case Assembler::LessThanOrEqual: ++ load16SignExtend(lhs, scratch2); ++ ma_cmp_set(dest, scratch2, Imm32(int16_t(rhs.value)), cond); ++ break; ++ ++ default: ++ MOZ_CRASH("unexpected condition"); ++ } ++} ++ ++void MacroAssembler::cmp32Load32(Condition cond, Register lhs, ++ const Address& rhs, const Address& src, ++ Register dest) { ++ ScratchRegisterScope scratch(*this); ++ MOZ_ASSERT(lhs != scratch && dest != scratch); ++ load32(rhs, scratch); ++ cmp32Load32(cond, lhs, scratch, src, dest); ++} ++ ++void MacroAssembler::cmp32Load32(Condition cond, Register lhs, Register rhs, ++ const Address& src, Register dest) { ++ Label skip; ++ branch32(Assembler::InvertCondition(cond), lhs, rhs, &skip); ++ load32(src, dest); ++ bind(&skip); ++} ++ ++void MacroAssembler::cmp32LoadPtr(Condition cond, const Address& lhs, Imm32 rhs, ++ const Address& src, Register dest) { ++ Label skip; ++ branch32(Assembler::InvertCondition(cond), lhs, rhs, &skip); ++ loadPtr(src, dest); ++ bind(&skip); ++} ++ ++void MacroAssembler::cmp32Move32(Condition cond, Register lhs, Register rhs, ++ Register src, Register dest) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ cmp32Set(cond, lhs, rhs, scratch2); ++ moveIfNotZero(dest, src, scratch2); ++} ++ ++void MacroAssembler::cmp32Move32(Condition cond, Register lhs, ++ const Address& rhs, Register src, ++ Register dest) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ MOZ_ASSERT(lhs != scratch2 && src != scratch2 && dest != scratch2); ++ load32(rhs, scratch2); ++ cmp32Move32(cond, lhs, scratch2, src, dest); ++} ++void MacroAssembler::cmp32MovePtr(Condition cond, Register lhs, Imm32 rhs, ++ Register src, Register dest) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ cmp32Set(cond, lhs, rhs, scratch2); ++ moveIfNotZero(dest, src, scratch2); ++} ++void MacroAssembler::cmp64Set(Condition cond, Address lhs, Imm64 rhs, ++ Register dest) { ++ ma_cmp_set(dest, lhs, ImmWord(uint64_t(rhs.value)), cond); ++} ++void MacroAssembler::cmp8Set(Condition cond, Address lhs, Imm32 rhs, ++ Register dest) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ MOZ_ASSERT(scratch2 != lhs.base); ++ ++ switch (cond) { ++ case Assembler::Equal: ++ case Assembler::NotEqual: ++ case Assembler::Above: ++ case Assembler::AboveOrEqual: ++ case Assembler::Below: ++ case Assembler::BelowOrEqual: ++ load8ZeroExtend(lhs, scratch2); ++ ma_cmp_set(dest, scratch2, Imm32(uint8_t(rhs.value)), cond); ++ break; ++ ++ case Assembler::GreaterThan: ++ case Assembler::GreaterThanOrEqual: ++ case Assembler::LessThan: ++ case Assembler::LessThanOrEqual: ++ load8SignExtend(lhs, scratch2); ++ ma_cmp_set(dest, scratch2, Imm32(int8_t(rhs.value)), cond); ++ break; ++ ++ default: ++ MOZ_CRASH("unexpected condition"); ++ } ++} ++void MacroAssembler::cmpPtrMovePtr(Condition cond, Register lhs, Register rhs, ++ Register src, Register dest) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ cmpPtrSet(cond, lhs, rhs, scratch2); ++ moveIfNotZero(dest, src, scratch2); ++} ++ ++void MacroAssembler::cmpPtrMovePtr(Condition cond, Register lhs, ++ const Address& rhs, Register src, ++ Register dest) { ++ MOZ_CRASH("NYI"); ++} ++void MacroAssembler::ctz32(Register, Register, bool) { MOZ_CRASH(); } ++void MacroAssembler::decBranchPtr(Condition cond, Register lhs, Imm32 rhs, ++ Label* label) { ++ subPtr(rhs, lhs); ++ branchPtr(cond, lhs, Imm32(0), label); ++} ++void MacroAssembler::divFloat32(FloatRegister src, FloatRegister dest) { ++ fdiv_s(dest, dest, src); ++} ++ ++void MacroAssembler::divDouble(FloatRegister src, FloatRegister dest) { ++ fdiv_d(dest, dest, src); ++} ++void MacroAssembler::fallibleUnboxPtr(const ValueOperand& src, Register dest, ++ JSValueType type, Label* fail) { ++ MOZ_ASSERT(type == JSVAL_TYPE_OBJECT || type == JSVAL_TYPE_STRING || ++ type == JSVAL_TYPE_SYMBOL || type == JSVAL_TYPE_BIGINT); ++ // dest := src XOR mask ++ // scratch := dest >> JSVAL_TAG_SHIFT ++ // fail if scratch != 0 ++ // ++ // Note: src and dest can be the same register ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ MOZ_ASSERT(src.valueReg() != scratch); ++ mov(ImmWord(JSVAL_TYPE_TO_SHIFTED_TAG(type)), scratch); ++ xor_(dest, src.valueReg(), scratch); ++ srli(scratch, dest, JSVAL_TAG_SHIFT); ++ ma_b(scratch, Imm32(0), fail, Assembler::NotEqual); ++} ++ ++void MacroAssembler::fallibleUnboxPtr(const Address& src, Register dest, ++ JSValueType type, Label* fail) { ++ loadValue(src, ValueOperand(dest)); ++ fallibleUnboxPtr(ValueOperand(dest), dest, type, fail); ++} ++ ++void MacroAssembler::fallibleUnboxPtr(const BaseIndex& src, Register dest, ++ JSValueType type, Label* fail) { ++ loadValue(src, ValueOperand(dest)); ++ fallibleUnboxPtr(ValueOperand(dest), dest, type, fail); ++} ++void MacroAssembler::flexibleLshift32(Register src, Register dest) { ++ lshift32(src, dest); ++} ++void MacroAssembler::flexibleRshift32Arithmetic(Register src, Register dest) { ++ rshift32Arithmetic(src, dest); ++} ++void MacroAssembler::flexibleRshift32(Register src, Register dest) { ++ rshift32(src, dest); ++} ++void MacroAssembler::inc64(AbsoluteAddress dest) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ Register scratch2 = temps.Acquire(); ++ ma_li(scratch, ImmWord(uintptr_t(dest.addr))); ++ ld(scratch2, scratch, 0); ++ addi(scratch2, scratch2, 1); ++ sd(scratch2, scratch, 0); ++} ++ ++void MacroAssembler::load32SignExtendToPtr(const Address& src, Register dest) { ++ load32(src, dest); ++} ++void MacroAssembler::loadAbiReturnAddress(Register dest) { movePtr(ra, dest); } ++ ++void MacroAssembler::lshift32(Register src, Register dest) { ++ sllw(dest, dest, src); ++} ++ ++void MacroAssembler::lshift32(Imm32 imm, Register dest) { ++ slliw(dest, dest, imm.value % 32); ++} ++void MacroAssembler::lshift64(Register shift, Register64 dest) { ++ sll(dest.reg, dest.reg, shift); ++} ++ ++void MacroAssembler::lshift64(Imm32 imm, Register64 dest) { ++ MOZ_ASSERT(0 <= imm.value && imm.value < 64); ++ slli(dest.reg, dest.reg, imm.value); ++} ++void MacroAssembler::lshiftPtr(Register shift, Register dest) { ++ sll(dest, dest, shift); ++} ++ ++void MacroAssembler::lshiftPtr(Imm32 imm, Register dest) { ++ MOZ_ASSERT(0 <= imm.value && imm.value < 64); ++ slli(dest, dest, imm.value); ++} ++void MacroAssembler::maxDouble(FloatRegister other, FloatRegister srcDest, ++ bool handleNaN) { ++ Float64Max(srcDest, srcDest, other); ++} ++void MacroAssembler::maxFloat32(FloatRegister other, FloatRegister srcDest, ++ bool handleNaN) { ++ Float32Max(srcDest, srcDest, other); ++} ++void MacroAssembler::memoryBarrier(MemoryBarrierBits barrier) { ++ if (barrier) { ++ sync(); ++ } ++} ++void MacroAssembler::minDouble(FloatRegister other, FloatRegister srcDest, ++ bool handleNaN) { ++ Float64Min(srcDest, srcDest, other); ++} ++void MacroAssembler::minFloat32(FloatRegister other, FloatRegister srcDest, ++ bool handleNaN) { ++ Float32Min(srcDest, srcDest, other); ++} ++void MacroAssembler::move16SignExtend(Register src, Register dest) { ++ slli(dest, src, xlen - 16); ++ srai(dest, dest, xlen - 16); ++} ++void MacroAssembler::move16To64SignExtend(Register src, Register64 dest) { ++ move32To64SignExtend(src, dest); ++ move16SignExtend(dest.reg, dest.reg); ++} ++void MacroAssembler::move32SignExtendToPtr(Register src, Register dest) { ++ slliw(dest, src, 0); ++} ++void MacroAssembler::move32To64SignExtend(Register src, Register64 dest) { ++ slliw(dest.reg, src, 0); ++} ++void MacroAssembler::move32To64ZeroExtend(Register src, Register64 dest) { ++ slli(dest.reg, src, 32); ++ srli(dest.reg, dest.reg, 32); ++} ++void MacroAssembler::move32ZeroExtendToPtr(Register src, Register dest) { ++ slli(dest, src, 32); ++ srli(dest, dest, 32); ++} ++void MacroAssembler::move64(Register64 src, Register64 dest) { ++ movePtr(src.reg, dest.reg); ++} ++ ++void MacroAssembler::move64(Imm64 imm, Register64 dest) { ++ movePtr(ImmWord(imm.value), dest.reg); ++} ++ ++void MacroAssembler::move64To32(Register64 src, Register dest) { ++ slliw(dest, src.reg, 0); ++} ++ ++void MacroAssembler::move8SignExtend(Register src, Register dest) { ++ slli(dest, src, xlen - 8); ++ srai(dest, dest, xlen - 8); ++} ++void MacroAssembler::move8To64SignExtend(Register src, Register64 dest) { ++ move32To64SignExtend(src, dest); ++ move8SignExtend(dest.reg, dest.reg); ++} ++void MacroAssembler::moveDoubleToGPR64(FloatRegister src, Register64 dest) { ++ fmv_x_d(dest.reg, src); ++} ++ ++void MacroAssembler::moveGPR64ToDouble(Register64 src, FloatRegister dest) { ++ fmv_d_x(dest, src.reg); ++} ++void MacroAssembler::moveFloat32ToGPR(FloatRegister src, Register dest) { ++ fmv_x_w(dest, src); ++} ++void MacroAssembler::moveGPRToFloat32(Register src, FloatRegister dest) { ++ fmv_w_x(dest, src); ++} ++void MacroAssembler::mul32(Register rhs, Register srcDest) { ++ mulw(srcDest, srcDest, rhs); ++} ++ ++void MacroAssembler::mul32(Imm32 imm, Register srcDest) { ++ ScratchRegisterScope scratch(asMasm()); ++ move32(imm, scratch); ++ mul32(scratch, srcDest); ++} ++ ++void MacroAssembler::mulHighUnsigned32(Imm32 imm, Register src, Register dest) { ++ ScratchRegisterScope scratch(asMasm()); ++ ma_li(scratch, uint32_t(imm.value)); ++ mul(dest, src, scratch); ++ srli(dest, dest, 32); ++} ++ ++void MacroAssembler::mul64(Imm64 imm, const Register64& dest) { ++ ScratchRegisterScope scratch(asMasm()); ++ MOZ_ASSERT(dest.reg != scratch); ++ mov(ImmWord(imm.value), scratch); ++ mul(dest.reg, dest.reg, scratch); ++} ++ ++void MacroAssembler::mul64(Imm64 imm, const Register64& dest, ++ const Register temp) { ++ MOZ_ASSERT(temp == Register::Invalid()); ++ mul64(imm, dest); ++} ++ ++void MacroAssembler::mul64(const Register64& src, const Register64& dest, ++ const Register temp) { ++ MOZ_ASSERT(temp == Register::Invalid()); ++ mul(dest.reg, dest.reg, src.reg); ++} ++ ++void MacroAssembler::mul64(const Operand& src, const Register64& dest, ++ const Register temp) { ++ if (src.is_mem()) { ++ ScratchRegisterScope scratch(asMasm()); ++ Register64 scratch64(scratch); ++ ++ load64(src.toAddress(), scratch64); ++ mul64(scratch64, dest, temp); ++ } else { ++ mul64(Register64(src.toReg()), dest, temp); ++ } ++} ++void MacroAssembler::mulBy3(Register src, Register dest) { ++ ScratchRegisterScope scratch(asMasm()); ++ MOZ_ASSERT(src != scratch); ++ add(scratch, src, src); ++ add(dest, scratch, src); ++} ++void MacroAssembler::mulDouble(FloatRegister src, FloatRegister dest) { ++ fmul_d(dest, dest, src); ++} ++void MacroAssembler::mulDoublePtr(ImmPtr imm, Register temp, ++ FloatRegister dest) { ++ ScratchRegisterScope scratch(asMasm()); ++ ScratchDoubleScope fpscratch(asMasm()); ++ movePtr(imm, scratch); ++ loadDouble(Address(scratch, 0), fpscratch); ++ mulDouble(fpscratch, dest); ++} ++void MacroAssembler::mulFloat32(FloatRegister src, FloatRegister dest) { ++ fmul_s(dest, dest, src); ++} ++void MacroAssembler::mulPtr(Register rhs, Register srcDest) { ++ mul(srcDest, srcDest, rhs); ++} ++ ++void MacroAssembler::negateDouble(FloatRegister reg) { fneg_d(reg, reg); } ++ ++void MacroAssembler::negateFloat(FloatRegister reg) { fneg_s(reg, reg); } ++ ++void MacroAssembler::neg64(Register64 reg) { sub(reg.reg, zero, reg.reg); } ++ ++void MacroAssembler::negPtr(Register reg) { sub(reg, zero, reg); } ++ ++void MacroAssembler::neg32(Register reg) { subw(reg, zero, reg); } ++void MacroAssembler::not32(Register reg) { nor(reg, reg, zero); } ++ ++void MacroAssembler::notPtr(Register reg) { nor(reg, reg, zero); } ++ ++void MacroAssembler::or32(Register src, Register dest) { ++ ma_or(dest, dest, src); ++} ++ ++void MacroAssembler::or32(Imm32 imm, Register dest) { ma_or(dest, dest, imm); } ++ ++void MacroAssembler::or32(Imm32 imm, const Address& dest) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ load32(dest, scratch2); ++ ma_or(scratch2, imm); ++ store32(scratch2, dest); ++} ++ ++void MacroAssembler::or64(Register64 src, Register64 dest) { ++ ma_or(dest.reg, dest.reg, src.reg); ++} ++ ++void MacroAssembler::or64(const Operand& src, Register64 dest) { ++ if (src.is_mem()) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ Register64 scratch64(scratch); ++ ++ load64(src.toAddress(), scratch64); ++ or64(scratch64, dest); ++ } else { ++ or64(Register64(src.toReg()), dest); ++ } ++} ++void MacroAssembler::or64(Imm64 imm, Register64 dest) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ ma_li(scratch, ImmWord(imm.value)); ++ ma_or(dest.reg, dest.reg, scratch); ++} ++ ++void MacroAssembler::orPtr(Register src, Register dest) { ++ ma_or(dest, dest, src); ++} ++ ++void MacroAssembler::orPtr(Imm32 imm, Register dest) { ma_or(dest, dest, imm); } ++ ++void MacroAssembler::patchSub32FromStackPtr(CodeOffset, Imm32) { MOZ_CRASH(); } ++void MacroAssembler::popcnt32(Register input, Register output, Register tmp) { ++ Popcnt32(output, input, tmp); ++} ++void MacroAssembler::popcnt64(Register64 input, Register64 output, ++ Register tmp) { ++ Popcnt64(output.reg, input.reg, tmp); ++} ++void MacroAssembler::quotient32(Register rhs, Register srcDest, ++ bool isUnsigned) { ++ if (isUnsigned) { ++ ma_divu32(srcDest, srcDest, rhs); ++ } else { ++ ma_div32(srcDest, srcDest, rhs); ++ } ++} ++ ++void MacroAssembler::remainder32(Register rhs, Register srcDest, ++ bool isUnsigned) { ++ if (isUnsigned) { ++ ma_modu32(srcDest, srcDest, rhs); ++ } else { ++ ma_mod32(srcDest, srcDest, rhs); ++ } ++} ++void MacroAssembler::rotateLeft64(Imm32 count, Register64 src, Register64 dest, ++ Register temp) { ++ Dror(dest.reg, src.reg, Operand(64 - (count.value % 64))); ++} ++void MacroAssembler::rotateLeft64(Register count, Register64 src, ++ Register64 dest, Register temp) { ++ ScratchRegisterScope scratch(asMasm()); ++ ma_mod32(scratch, count, Operand(64)); ++ negw(scratch, scratch); ++ addi(scratch, scratch, 64); ++ Dror(dest.reg, src.reg, Operand(scratch)); ++} ++ ++void MacroAssembler::rotateLeft(Imm32 count, Register input, Register dest) { ++ JitSpew(JitSpew_Codegen, "[ rotateLeft\n"); ++ Ror(dest, input, Operand(32 - (count.value % 32))); ++ JitSpew(JitSpew_Codegen, "]\n"); ++} ++void MacroAssembler::rotateLeft(Register count, Register input, Register dest) { ++ JitSpew(JitSpew_Codegen, "[ rotateLeft\n"); ++ ScratchRegisterScope scratch(asMasm()); ++ ma_mod32(scratch, count, Operand(32)); ++ negw(scratch, scratch); ++ addi(scratch, scratch, 32); ++ Ror(dest, input, Operand(scratch)); ++ JitSpew(JitSpew_Codegen, "]\n"); ++} ++void MacroAssembler::rotateRight64(Register count, Register64 src, ++ Register64 dest, Register temp) { ++ Dror(dest.reg, src.reg, Operand(count)); ++} ++void MacroAssembler::rotateRight64(Imm32 count, Register64 src, Register64 dest, ++ Register temp) { ++ Dror(dest.reg, src.reg, Operand(count.value)); ++} ++void MacroAssembler::rotateRight(Imm32 count, Register input, Register dest) { ++ Ror(dest, input, Operand(count.value)); ++} ++void MacroAssembler::rotateRight(Register count, Register input, ++ Register dest) { ++ Ror(dest, input, Operand(count)); ++} ++void MacroAssembler::rshift32Arithmetic(Register src, Register dest) { ++ sraw(dest, dest, src); ++} ++ ++void MacroAssembler::rshift32Arithmetic(Imm32 imm, Register dest) { ++ sraiw(dest, dest, imm.value % 32); ++} ++void MacroAssembler::rshift32(Register src, Register dest) { ++ srlw(dest, dest, src); ++} ++ ++void MacroAssembler::rshift32(Imm32 imm, Register dest) { ++ srliw(dest, dest, imm.value % 32); ++} ++ ++void MacroAssembler::rshift64Arithmetic(Imm32 imm, Register64 dest) { ++ MOZ_ASSERT(0 <= imm.value && imm.value < 64); ++ srai(dest.reg, dest.reg, imm.value); ++} ++ ++void MacroAssembler::rshift64Arithmetic(Register shift, Register64 dest) { ++ sra(dest.reg, dest.reg, shift); ++} ++ ++void MacroAssembler::rshift64(Register shift, Register64 dest) { ++ srl(dest.reg, dest.reg, shift); ++} ++ ++void MacroAssembler::rshift64(Imm32 imm, Register64 dest) { ++ MOZ_ASSERT(0 <= imm.value && imm.value < 64); ++ srli(dest.reg, dest.reg, imm.value); ++} ++ ++void MacroAssembler::rshiftPtrArithmetic(Imm32 imm, Register dest) { ++ MOZ_ASSERT(0 <= imm.value && imm.value < 64); ++ srai(dest, dest, imm.value); ++} ++void MacroAssembler::rshiftPtr(Register shift, Register dest) { ++ srl(dest, dest, shift); ++} ++ ++void MacroAssembler::rshiftPtr(Imm32 imm, Register dest) { ++ MOZ_ASSERT(0 <= imm.value && imm.value < 64); ++ srli(dest, dest, imm.value); ++} ++void MacroAssembler::spectreBoundsCheck32(Register index, Register length, ++ Register maybeScratch, ++ Label* failure) { ++ MOZ_RELEASE_ASSERT(!JitOptions.spectreIndexMasking); ++ branch32(Assembler::BelowOrEqual, length, index, failure); ++} ++ ++void MacroAssembler::spectreBoundsCheck32(Register index, const Address& length, ++ Register maybeScratch, ++ Label* failure) { ++ MOZ_RELEASE_ASSERT(!JitOptions.spectreIndexMasking); ++ branch32(Assembler::BelowOrEqual, length, index, failure); ++} ++void MacroAssembler::spectreBoundsCheckPtr(Register index, Register length, ++ Register maybeScratch, ++ Label* failure) { ++ MOZ_RELEASE_ASSERT(!JitOptions.spectreIndexMasking); ++ branchPtr(Assembler::BelowOrEqual, length, index, failure); ++} ++ ++void MacroAssembler::spectreBoundsCheckPtr(Register index, ++ const Address& length, ++ Register maybeScratch, ++ Label* failure) { ++ MOZ_RELEASE_ASSERT(!JitOptions.spectreIndexMasking); ++ branchPtr(Assembler::BelowOrEqual, length, index, failure); ++} ++void MacroAssembler::spectreMovePtr(Condition, Register, Register) { ++ MOZ_CRASH("spectreMovePtr"); ++} ++void MacroAssembler::spectreZeroRegister(Condition cond, Register scratch, ++ Register dest) { ++ MOZ_CRASH("spectreZeroRegister"); ++} ++void MacroAssembler::sqrtDouble(FloatRegister src, FloatRegister dest) { ++ fsqrt_d(dest, src); ++} ++void MacroAssembler::sqrtFloat32(FloatRegister src, FloatRegister dest) { ++ fsqrt_s(dest, src); ++} ++void MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, ++ const Address& addr) { ++ ma_fst_s(src, addr); ++} ++void MacroAssembler::storeUncanonicalizedFloat32(FloatRegister src, ++ const BaseIndex& addr) { ++ ma_fst_s(src, addr); ++} ++ ++void MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, ++ const Address& addr) { ++ ma_fst_d(src, addr); ++} ++void MacroAssembler::storeUncanonicalizedDouble(FloatRegister src, ++ const BaseIndex& addr) { ++ ma_fst_d(src, addr); ++} ++void MacroAssembler::sub32(Register src, Register dest) { ++ subw(dest, dest, src); ++} ++ ++void MacroAssembler::sub32(Imm32 imm, Register dest) { ++ ma_sub32(dest, dest, imm); ++} ++ ++void MacroAssembler::sub32(const Address& src, Register dest) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ load32(src, scratch); ++ subw(dest, dest, scratch); ++} ++ ++void MacroAssembler::sub64(Register64 src, Register64 dest) { ++ sub(dest.reg, dest.reg, src.reg); ++} ++ ++void MacroAssembler::sub64(const Operand& src, Register64 dest) { ++ if (src.is_mem()) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ Register64 scratch64(scratch); ++ ++ load64(src.toAddress(), scratch64); ++ sub64(scratch64, dest); ++ } else { ++ sub64(Register64(src.toReg()), dest); ++ } ++} ++ ++void MacroAssembler::sub64(Imm64 imm, Register64 dest) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ MOZ_ASSERT(dest.reg != scratch); ++ ma_li(scratch, ImmWord(imm.value)); ++ sub(dest.reg, dest.reg, scratch); ++} ++ ++void MacroAssembler::subDouble(FloatRegister src, FloatRegister dest) { ++ fsub_d(dest, dest, src); ++} ++ ++void MacroAssembler::subFloat32(FloatRegister src, FloatRegister dest) { ++ fsub_s(dest, dest, src); ++} ++ ++void MacroAssembler::subPtr(Register src, const Address& dest) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ loadPtr(dest, scratch); ++ subPtr(src, scratch); ++ storePtr(scratch, dest); ++} ++ ++void MacroAssembler::subPtr(const Address& addr, Register dest) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ loadPtr(addr, scratch); ++ subPtr(scratch, dest); ++} ++void MacroAssembler::subPtr(Imm32 imm, Register dest) { ++ ma_sub64(dest, dest, imm); ++} ++void MacroAssembler::subPtr(Register src, Register dest) { ++ sub(dest, dest, src); ++} ++void MacroAssembler::test32LoadPtr(Condition cond, const Address& addr, ++ Imm32 mask, const Address& src, ++ Register dest) { ++ MOZ_RELEASE_ASSERT(!JitOptions.spectreStringMitigations); ++ Label skip; ++ branchTest32(Assembler::InvertCondition(cond), addr, mask, &skip); ++ loadPtr(src, dest); ++ bind(&skip); ++} ++void MacroAssembler::test32MovePtr(Condition, const Address&, Imm32, Register, ++ Register) { ++ MOZ_CRASH(); ++} ++void MacroAssembler::xor32(Register src, Register dest) { ++ ma_xor(dest, dest, src); ++} ++ ++void MacroAssembler::xor32(Imm32 imm, Register dest) { ++ ma_xor(dest, dest, imm); ++} ++ ++void MacroAssembler::xor32(Imm32 imm, const Address& dest) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ load32(dest, scratch2); ++ xor32(imm, scratch2); ++ store32(scratch2, dest); ++} ++ ++void MacroAssembler::xor32(const Address& src, Register dest) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ load32(src, scratch2); ++ xor32(scratch2, dest); ++} ++void MacroAssembler::xor64(Register64 src, Register64 dest) { ++ ma_xor(dest.reg, dest.reg, src.reg); ++} ++ ++void MacroAssembler::xor64(const Operand& src, Register64 dest) { ++ if (src.is_mem()) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ Register64 scratch64(scratch); ++ ++ load64(src.toAddress(), scratch64); ++ xor64(scratch64, dest); ++ } else { ++ xor64(Register64(src.toReg()), dest); ++ } ++} ++void MacroAssembler::xor64(Imm64 imm, Register64 dest) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ ma_li(scratch, ImmWord(imm.value)); ++ ma_xor(dest.reg, dest.reg, scratch); ++} ++void MacroAssembler::xorPtr(Register src, Register dest) { ++ ma_xor(dest, dest, src); ++} ++ ++void MacroAssembler::xorPtr(Imm32 imm, Register dest) { ++ ma_xor(dest, dest, imm); ++} ++//}}} check_macroassembler_style ++ ++void MacroAssemblerRiscv64Compat::incrementInt32Value(const Address& addr) { ++ asMasm().add32(Imm32(1), addr); ++} ++ ++void MacroAssemblerRiscv64Compat::retn(Imm32 n) { ++ // pc <- [sp]; sp += n ++ loadPtr(Address(StackPointer, 0), ra); ++ asMasm().addPtr(n, StackPointer); ++ jr(ra, 0); ++} ++} // namespace jit ++} // namespace js ++ ++#endif /* jit_riscv64_MacroAssembler_riscv64_inl_h */ +diff --git a/js/src/jit/riscv64/MacroAssembler-riscv64.cpp b/js/src/jit/riscv64/MacroAssembler-riscv64.cpp +new file mode 100644 +index 0000000000..40f546398a +--- /dev/null ++++ b/js/src/jit/riscv64/MacroAssembler-riscv64.cpp +@@ -0,0 +1,6515 @@ ++/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- ++ * vim: set ts=8 sts=2 et sw=2 tw=80: ++ * 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/. */ ++ ++// Copyright 2021 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++#include "jit/riscv64/MacroAssembler-riscv64.h" ++ ++#include "jsmath.h" ++#include "jit/Bailouts.h" ++#include "jit/BaselineFrame.h" ++#include "jit/JitFrames.h" ++#include "jit/JitRuntime.h" ++#include "jit/MacroAssembler.h" ++#include "jit/MoveEmitter.h" ++#include "jit/riscv64/SharedICRegisters-riscv64.h" ++#include "util/Memory.h" ++#include "vm/JitActivation.h" // jit::JitActivation ++#include "vm/JSContext.h" ++ ++#include "jit/MacroAssembler-inl.h" ++ ++namespace js { ++namespace jit { ++ ++MacroAssembler& MacroAssemblerRiscv64::asMasm() { ++ return *static_cast(this); ++} ++ ++const MacroAssembler& MacroAssemblerRiscv64::asMasm() const { ++ return *static_cast(this); ++} ++ ++void MacroAssemblerRiscv64::ma_cmp_set(Register rd, Register rj, ImmWord imm, ++ Condition c) { ++ if (imm.value <= INT32_MAX) { ++ ma_cmp_set(rd, rj, Imm32(uint32_t(imm.value)), c); ++ } else { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ ma_li(scratch, imm); ++ ma_cmp_set(rd, rj, scratch, c); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_cmp_set(Register rd, Register rj, ImmPtr imm, ++ Condition c) { ++ ma_cmp_set(rd, rj, ImmWord(uintptr_t(imm.value)), c); ++} ++ ++void MacroAssemblerRiscv64::ma_cmp_set(Register rd, Address address, Imm32 imm, ++ Condition c) { ++ // TODO(loong64): 32-bit ma_cmp_set? ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ ma_load(scratch2, address, SizeWord); ++ ma_cmp_set(rd, Register(scratch2), imm, c); ++} ++ ++void MacroAssemblerRiscv64::ma_cmp_set(Register rd, Address address, ++ ImmWord imm, Condition c) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ ma_load(scratch2, address, SizeDouble); ++ ma_cmp_set(rd, Register(scratch2), imm, c); ++} ++ ++void MacroAssemblerRiscv64::ma_cmp_set(Register rd, Register rj, Imm32 imm, ++ Condition c) { ++ if (imm.value == 0) { ++ switch (c) { ++ case Equal: ++ case BelowOrEqual: ++ ma_sltu(rd, rj, Operand(1)); ++ break; ++ case NotEqual: ++ case Above: ++ sltu(rd, zero, rj); ++ break; ++ case AboveOrEqual: ++ case Below: ++ ori(rd, zero, c == AboveOrEqual ? 1 : 0); ++ break; ++ case GreaterThan: ++ case LessThanOrEqual: ++ slt(rd, zero, rj); ++ if (c == LessThanOrEqual) { ++ xori(rd, rd, 1); ++ } ++ break; ++ case LessThan: ++ case GreaterThanOrEqual: ++ slt(rd, rj, zero); ++ if (c == GreaterThanOrEqual) { ++ xori(rd, rd, 1); ++ } ++ break; ++ case Zero: ++ ma_sltu(rd, rj, Operand(1)); ++ break; ++ case NonZero: ++ sltu(rd, zero, rj); ++ break; ++ case Signed: ++ slt(rd, rj, zero); ++ break; ++ case NotSigned: ++ slt(rd, rj, zero); ++ xori(rd, rd, 1); ++ break; ++ default: ++ MOZ_CRASH("Invalid condition."); ++ } ++ return; ++ } ++ ++ switch (c) { ++ case Equal: ++ case NotEqual: ++ ma_xor(rd, rj, imm); ++ if (c == Equal) { ++ ma_sltu(rd, rd, Operand(1)); ++ } else { ++ sltu(rd, zero, rd); ++ } ++ break; ++ case Zero: ++ case NonZero: ++ case Signed: ++ case NotSigned: ++ MOZ_CRASH("Invalid condition."); ++ default: ++ Condition cond = ma_cmp(rd, rj, imm, c); ++ MOZ_ASSERT(cond == Equal || cond == NotEqual); ++ ++ if (cond == Equal) xori(rd, rd, 1); ++ } ++} ++ ++Assembler::Condition MacroAssemblerRiscv64::ma_cmp(Register dest, Register lhs, ++ Register rhs, Condition c) { ++ switch (c) { ++ case Above: ++ // bgtu s,t,label => ++ // sltu at,t,s ++ // bne at,$zero,offs ++ sltu(dest, rhs, lhs); ++ return NotEqual; ++ case AboveOrEqual: ++ // bgeu s,t,label => ++ // sltu at,s,t ++ // beq at,$zero,offs ++ sltu(dest, lhs, rhs); ++ return Equal; ++ case Below: ++ // bltu s,t,label => ++ // sltu at,s,t ++ // bne at,$zero,offs ++ sltu(dest, lhs, rhs); ++ return NotEqual; ++ case BelowOrEqual: ++ // bleu s,t,label => ++ // sltu at,t,s ++ // beq at,$zero,offs ++ sltu(dest, rhs, lhs); ++ return Equal; ++ case GreaterThan: ++ // bgt s,t,label => ++ // slt at,t,s ++ // bne at,$zero,offs ++ slt(dest, rhs, lhs); ++ return NotEqual; ++ case GreaterThanOrEqual: ++ // bge s,t,label => ++ // slt at,s,t ++ // beq at,$zero,offs ++ slt(dest, lhs, rhs); ++ return Equal; ++ case LessThan: ++ // blt s,t,label => ++ // slt at,s,t ++ // bne at,$zero,offs ++ slt(dest, lhs, rhs); ++ return NotEqual; ++ case LessThanOrEqual: ++ // ble s,t,label => ++ // slt at,t,s ++ // beq at,$zero,offs ++ slt(dest, rhs, lhs); ++ return Equal; ++ default: ++ MOZ_CRASH("Invalid condition."); ++ } ++ return Always; ++} ++ ++Assembler::Condition MacroAssemblerRiscv64::ma_cmp(Register dest, Register lhs, ++ Imm32 imm, Condition c) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ MOZ_RELEASE_ASSERT(lhs != scratch); ++ ++ switch (c) { ++ case Above: ++ case BelowOrEqual: ++ if (imm.value != 0x7fffffff && is_intn(imm.value + 1, 12) && ++ imm.value != -1) { ++ // lhs <= rhs via lhs < rhs + 1 if rhs + 1 does not overflow ++ ma_sltu(dest, lhs, Operand(imm.value + 1)); ++ ++ return (c == BelowOrEqual ? NotEqual : Equal); ++ } else { ++ ma_li(scratch, imm); ++ sltu(dest, scratch, lhs); ++ return (c == BelowOrEqual ? Equal : NotEqual); ++ } ++ case AboveOrEqual: ++ case Below: ++ if (is_intn(imm.value, 12)) { ++ ma_sltu(dest, lhs, Operand(imm.value)); ++ } else { ++ ma_li(scratch, imm); ++ sltu(dest, lhs, scratch); ++ } ++ return (c == AboveOrEqual ? Equal : NotEqual); ++ case GreaterThan: ++ case LessThanOrEqual: ++ if (imm.value != 0x7fffffff && is_intn(imm.value + 1, 12)) { ++ // lhs <= rhs via lhs < rhs + 1. ++ ma_slt(dest, lhs, Operand(imm.value + 1)); ++ return (c == LessThanOrEqual ? NotEqual : Equal); ++ } else { ++ ma_li(scratch, imm); ++ slt(dest, scratch, lhs); ++ return (c == LessThanOrEqual ? Equal : NotEqual); ++ } ++ case GreaterThanOrEqual: ++ case LessThan: ++ if (is_intn(imm.value, 12)) { ++ ma_slt(dest, lhs, imm); ++ } else { ++ ma_li(scratch, imm); ++ slt(dest, lhs, scratch); ++ } ++ return (c == GreaterThanOrEqual ? Equal : NotEqual); ++ default: ++ MOZ_CRASH("Invalid condition."); ++ } ++ return Always; ++} ++ ++void MacroAssemblerRiscv64::ma_cmp_set(Register rd, Register rj, Register rk, ++ Condition c) { ++ switch (c) { ++ case Equal: ++ // seq d,s,t => ++ // xor d,s,t ++ // sltiu d,d,1 ++ xor_(rd, rj, rk); ++ ma_sltu(rd, rd, Operand(1)); ++ break; ++ case NotEqual: ++ // sne d,s,t => ++ // xor d,s,t ++ // sltu d,$zero,d ++ xor_(rd, rj, rk); ++ sltu(rd, zero, rd); ++ break; ++ case Above: ++ // sgtu d,s,t => ++ // sltu d,t,s ++ sltu(rd, rk, rj); ++ break; ++ case AboveOrEqual: ++ // sgeu d,s,t => ++ // sltu d,s,t ++ // xori d,d,1 ++ sltu(rd, rj, rk); ++ xori(rd, rd, 1); ++ break; ++ case Below: ++ // sltu d,s,t ++ sltu(rd, rj, rk); ++ break; ++ case BelowOrEqual: ++ // sleu d,s,t => ++ // sltu d,t,s ++ // xori d,d,1 ++ sltu(rd, rk, rj); ++ xori(rd, rd, 1); ++ break; ++ case GreaterThan: ++ // sgt d,s,t => ++ // slt d,t,s ++ slt(rd, rk, rj); ++ break; ++ case GreaterThanOrEqual: ++ // sge d,s,t => ++ // slt d,s,t ++ // xori d,d,1 ++ slt(rd, rj, rk); ++ xori(rd, rd, 1); ++ break; ++ case LessThan: ++ // slt d,s,t ++ slt(rd, rj, rk); ++ break; ++ case LessThanOrEqual: ++ // sle d,s,t => ++ // slt d,t,s ++ // xori d,d,1 ++ slt(rd, rk, rj); ++ xori(rd, rd, 1); ++ break; ++ case Zero: ++ MOZ_ASSERT(rj == rk); ++ // seq d,s,$zero => ++ // sltiu d,s,1 ++ ma_sltu(rd, rj, Operand(1)); ++ break; ++ case NonZero: ++ MOZ_ASSERT(rj == rk); ++ // sne d,s,$zero => ++ // sltu d,$zero,s ++ sltu(rd, zero, rj); ++ break; ++ case Signed: ++ MOZ_ASSERT(rj == rk); ++ slt(rd, rj, zero); ++ break; ++ case NotSigned: ++ MOZ_ASSERT(rj == rk); ++ // sge d,s,$zero => ++ // slt d,s,$zero ++ // xori d,d,1 ++ slt(rd, rj, zero); ++ xori(rd, rd, 1); ++ break; ++ default: ++ MOZ_CRASH("Invalid condition."); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_compareF32(Register rd, DoubleCondition cc, ++ FloatRegister cmp1, ++ FloatRegister cmp2) { ++ switch (cc) { ++ case DoubleEqualOrUnordered: ++ case DoubleEqual: ++ feq_s(rd, cmp1, cmp2); ++ break; ++ case DoubleNotEqualOrUnordered: ++ case DoubleNotEqual: { ++ Label done; ++ CompareIsNanF32(rd, cmp1, cmp2); ++ ma_branch(&done, Equal, rd, Operand(1)); ++ feq_s(rd, cmp1, cmp2); ++ bind(&done); ++ NegateBool(rd, rd); ++ break; ++ } ++ case DoubleLessThanOrUnordered: ++ case DoubleLessThan: ++ flt_s(rd, cmp1, cmp2); ++ break; ++ case DoubleGreaterThanOrEqualOrUnordered: ++ case DoubleGreaterThanOrEqual: ++ fle_s(rd, cmp2, cmp1); ++ break; ++ case DoubleLessThanOrEqualOrUnordered: ++ case DoubleLessThanOrEqual: ++ fle_s(rd, cmp1, cmp2); ++ break; ++ case DoubleGreaterThanOrUnordered: ++ case DoubleGreaterThan: ++ flt_s(rd, cmp2, cmp1); ++ break; ++ case DoubleOrdered: ++ CompareIsNotNanF32(rd, cmp1, cmp2); ++ return; ++ case DoubleUnordered: ++ CompareIsNanF32(rd, cmp1, cmp2); ++ return; ++ } ++ if (cc >= FIRST_UNORDERED && cc <= LAST_UNORDERED) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ CompareIsNanF32(scratch, cmp1, cmp2); ++ or_(rd, rd, scratch); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_compareF64(Register rd, DoubleCondition cc, ++ FloatRegister cmp1, ++ FloatRegister cmp2) { ++ switch (cc) { ++ case DoubleEqualOrUnordered: ++ case DoubleEqual: ++ feq_d(rd, cmp1, cmp2); ++ break; ++ case DoubleNotEqualOrUnordered: ++ case DoubleNotEqual: { ++ Label done; ++ CompareIsNanF64(rd, cmp1, cmp2); ++ ma_branch(&done, Equal, rd, Operand(1)); ++ feq_d(rd, cmp1, cmp2); ++ bind(&done); ++ NegateBool(rd, rd); ++ } break; ++ case DoubleLessThanOrUnordered: ++ case DoubleLessThan: ++ flt_d(rd, cmp1, cmp2); ++ break; ++ case DoubleGreaterThanOrEqualOrUnordered: ++ case DoubleGreaterThanOrEqual: ++ fle_d(rd, cmp2, cmp1); ++ break; ++ case DoubleLessThanOrEqualOrUnordered: ++ case DoubleLessThanOrEqual: ++ fle_d(rd, cmp1, cmp2); ++ break; ++ case DoubleGreaterThanOrUnordered: ++ case DoubleGreaterThan: ++ flt_d(rd, cmp2, cmp1); ++ break; ++ case DoubleOrdered: ++ CompareIsNotNanF64(rd, cmp1, cmp2); ++ return; ++ case DoubleUnordered: ++ CompareIsNanF64(rd, cmp1, cmp2); ++ return; ++ } ++ ++ if (cc >= FIRST_UNORDERED && cc <= LAST_UNORDERED) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ CompareIsNanF64(scratch, cmp1, cmp2); ++ or_(rd, rd, scratch); ++ } ++} ++ ++void MacroAssemblerRiscv64Compat::movePtr(Register src, Register dest) { ++ mv(dest, src); ++} ++void MacroAssemblerRiscv64Compat::movePtr(ImmWord imm, Register dest) { ++ ma_li(dest, imm); ++} ++ ++void MacroAssemblerRiscv64Compat::movePtr(ImmGCPtr imm, Register dest) { ++ ma_li(dest, imm); ++} ++ ++void MacroAssemblerRiscv64Compat::movePtr(ImmPtr imm, Register dest) { ++ movePtr(ImmWord(uintptr_t(imm.value)), dest); ++} ++void MacroAssemblerRiscv64Compat::movePtr(wasm::SymbolicAddress imm, ++ Register dest) { ++ DEBUG_PRINTF("[ %s\n", __FUNCTION__); ++ BlockTrampolinePoolScope block_trampoline_pool(this, 8); ++ append(wasm::SymbolicAccess(CodeOffset(nextOffset().getOffset()), imm)); ++ ma_liPatchable(dest, ImmWord(-1), Li64); ++ DEBUG_PRINTF("]\n"); ++} ++ ++bool MacroAssemblerRiscv64Compat::buildOOLFakeExitFrame(void* fakeReturnAddr) { ++ asMasm().PushFrameDescriptor(FrameType::IonJS); // descriptor_ ++ asMasm().Push(ImmPtr(fakeReturnAddr)); ++ asMasm().Push(FramePointer); ++ return true; ++} ++ ++void MacroAssemblerRiscv64Compat::convertUInt32ToDouble(Register src, ++ FloatRegister dest) { ++ fcvt_d_wu(dest, src); ++} ++ ++void MacroAssemblerRiscv64Compat::convertUInt64ToDouble(Register src, ++ FloatRegister dest) { ++ fcvt_d_lu(dest, src); ++} ++ ++void MacroAssemblerRiscv64Compat::convertUInt32ToFloat32(Register src, ++ FloatRegister dest) { ++ fcvt_s_wu(dest, src); ++} ++ ++void MacroAssemblerRiscv64Compat::convertDoubleToFloat32(FloatRegister src, ++ FloatRegister dest) { ++ fcvt_s_d(dest, src); ++} ++ ++template ++void MacroAssemblerRiscv64::RoundHelper(FPURegister dst, FPURegister src, ++ FPURegister fpu_scratch, ++ FPURoundingMode frm) { ++ BlockTrampolinePoolScope block_trampoline_pool(this, 20); ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ ++ MOZ_ASSERT((std::is_same::value) || ++ (std::is_same::value)); ++ // Need at least two FPRs, so check against dst == src == fpu_scratch ++ MOZ_ASSERT(!(dst == src && dst == fpu_scratch)); ++ ++ const int kFloatMantissaBits = ++ sizeof(F) == 4 ? kFloat32MantissaBits : kFloat64MantissaBits; ++ const int kFloatExponentBits = ++ sizeof(F) == 4 ? kFloat32ExponentBits : kFloat64ExponentBits; ++ const int kFloatExponentBias = ++ sizeof(F) == 4 ? kFloat32ExponentBias : kFloat64ExponentBias; ++ Label done; ++ ++ { ++ UseScratchRegisterScope temps2(this); ++ Register scratch = temps2.Acquire(); ++ // extract exponent value of the source floating-point to scratch ++ if (std::is_same::value) { ++ fmv_x_d(scratch, src); ++ } else { ++ fmv_x_w(scratch, src); ++ } ++ ExtractBits(scratch2, scratch, kFloatMantissaBits, kFloatExponentBits); ++ } ++ ++ // if src is NaN/+-Infinity/+-Zero or if the exponent is larger than # of bits ++ // in mantissa, the result is the same as src, so move src to dest (to avoid ++ // generating another branch) ++ if (dst != src) { ++ if (std::is_same::value) { ++ fmv_d(dst, src); ++ } else { ++ fmv_s(dst, src); ++ } ++ } ++ { ++ Label not_NaN; ++ UseScratchRegisterScope temps2(this); ++ Register scratch = temps2.Acquire(); ++ // According to the wasm spec ++ // (https://webassembly.github.io/spec/core/exec/numerics.html#aux-nans) ++ // if input is canonical NaN, then output is canonical NaN, and if input is ++ // any other NaN, then output is any NaN with most significant bit of ++ // payload is 1. In RISC-V, feq_d will set scratch to 0 if src is a NaN. If ++ // src is not a NaN, branch to the label and do nothing, but if it is, ++ // fmin_d will set dst to the canonical NaN. ++ if (std::is_same::value) { ++ feq_d(scratch, src, src); ++ bnez(scratch, ¬_NaN); ++ fmin_d(dst, src, src); ++ } else { ++ feq_s(scratch, src, src); ++ bnez(scratch, ¬_NaN); ++ fmin_s(dst, src, src); ++ } ++ bind(¬_NaN); ++ } ++ ++ // If real exponent (i.e., scratch2 - kFloatExponentBias) is greater than ++ // kFloat32MantissaBits, it means the floating-point value has no fractional ++ // part, thus the input is already rounded, jump to done. Note that, NaN and ++ // Infinity in floating-point representation sets maximal exponent value, so ++ // they also satisfy (scratch2 - kFloatExponentBias >= kFloatMantissaBits), ++ // and JS round semantics specify that rounding of NaN (Infinity) returns NaN ++ // (Infinity), so NaN and Infinity are considered rounded value too. ++ ma_branch(&done, GreaterThanOrEqual, scratch2, ++ Operand(kFloatExponentBias + kFloatMantissaBits)); ++ ++ // Actual rounding is needed along this path ++ ++ // old_src holds the original input, needed for the case of src == dst ++ FPURegister old_src = src; ++ if (src == dst) { ++ MOZ_ASSERT(fpu_scratch != dst); ++ fmv_d(fpu_scratch, src); ++ old_src = fpu_scratch; ++ } ++ ++ // Since only input whose real exponent value is less than kMantissaBits ++ // (i.e., 23 or 52-bits) falls into this path, the value range of the input ++ // falls into that of 23- or 53-bit integers. So we round the input to integer ++ // values, then convert them back to floating-point. ++ { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ if (std::is_same::value) { ++ fcvt_l_d(scratch, src, frm); ++ fcvt_d_l(dst, scratch, frm); ++ } else { ++ fcvt_w_s(scratch, src, frm); ++ fcvt_s_w(dst, scratch, frm); ++ } ++ } ++ // A special handling is needed if the input is a very small positive/negative ++ // number that rounds to zero. JS semantics requires that the rounded result ++ // retains the sign of the input, so a very small positive (negative) ++ // floating-point number should be rounded to positive (negative) 0. ++ // Therefore, we use sign-bit injection to produce +/-0 correctly. Instead of ++ // testing for zero w/ a branch, we just insert sign-bit for everyone on this ++ // path (this is where old_src is needed) ++ if (std::is_same::value) { ++ fsgnj_d(dst, dst, old_src); ++ } else { ++ fsgnj_s(dst, dst, old_src); ++ } ++ ++ bind(&done); ++} ++ ++template ++void MacroAssemblerRiscv64::RoundFloatingPointToInteger(Register rd, ++ FPURegister fs, ++ Register result, ++ CvtFunc fcvt_generator, ++ bool Inexact) { ++ // Save csr_fflags to scratch & clear exception flags ++ if (result != Register::Invalid()) { ++ BlockTrampolinePoolScope block_trampoline_pool(this, 6); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ ++ int exception_flags = kInvalidOperation; ++ if (Inexact) exception_flags |= kInexact; ++ csrrci(scratch, csr_fflags, exception_flags); ++ ++ // actual conversion instruction ++ fcvt_generator(this, rd, fs); ++ ++ // check kInvalidOperation flag (out-of-range, NaN) ++ // set result to 1 if normal, otherwise set result to 0 for abnormal ++ frflags(result); ++ andi(result, result, exception_flags); ++ seqz(result, result); // result <-- 1 (normal), result <-- 0 (abnormal) ++ ++ // restore csr_fflags ++ csrw(csr_fflags, scratch); ++ } else { ++ // actual conversion instruction ++ fcvt_generator(this, rd, fs); ++ } ++} ++ ++void MacroAssemblerRiscv64::Trunc_uw_d(Register rd, FPURegister fs, ++ Register result, bool Inexact) { ++ RoundFloatingPointToInteger( ++ rd, fs, result, ++ [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { ++ masm->fcvt_wu_d(dst, src, RTZ); ++ }, ++ Inexact); ++} ++ ++void MacroAssemblerRiscv64::Trunc_w_d(Register rd, FPURegister fs, ++ Register result, bool Inexact) { ++ RoundFloatingPointToInteger( ++ rd, fs, result, ++ [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { ++ masm->fcvt_w_d(dst, src, RTZ); ++ }, ++ Inexact); ++} ++ ++void MacroAssemblerRiscv64::Trunc_uw_s(Register rd, FPURegister fs, ++ Register result, bool Inexact) { ++ RoundFloatingPointToInteger( ++ rd, fs, result, ++ [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { ++ masm->fcvt_wu_s(dst, src, RTZ); ++ }, ++ Inexact); ++} ++ ++void MacroAssemblerRiscv64::Trunc_w_s(Register rd, FPURegister fs, ++ Register result, bool Inexact) { ++ RoundFloatingPointToInteger( ++ rd, fs, result, ++ [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { ++ masm->fcvt_w_s(dst, src, RTZ); ++ }, ++ Inexact); ++} ++void MacroAssemblerRiscv64::Trunc_ul_d(Register rd, FPURegister fs, ++ Register result, bool Inexact) { ++ RoundFloatingPointToInteger( ++ rd, fs, result, ++ [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { ++ masm->fcvt_lu_d(dst, src, RTZ); ++ }, ++ Inexact); ++} ++ ++void MacroAssemblerRiscv64::Trunc_l_d(Register rd, FPURegister fs, ++ Register result, bool Inexact) { ++ RoundFloatingPointToInteger( ++ rd, fs, result, ++ [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { ++ masm->fcvt_l_d(dst, src, RTZ); ++ }, ++ Inexact); ++} ++ ++void MacroAssemblerRiscv64::Trunc_ul_s(Register rd, FPURegister fs, ++ Register result, bool Inexact) { ++ RoundFloatingPointToInteger( ++ rd, fs, result, ++ [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { ++ masm->fcvt_lu_s(dst, src, RTZ); ++ }, ++ Inexact); ++} ++ ++void MacroAssemblerRiscv64::Trunc_l_s(Register rd, FPURegister fs, ++ Register result, bool Inexact) { ++ RoundFloatingPointToInteger( ++ rd, fs, result, ++ [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { ++ masm->fcvt_l_s(dst, src, RTZ); ++ }, ++ Inexact); ++} ++ ++void MacroAssemblerRiscv64::Floor_d_d(FPURegister dst, FPURegister src, ++ FPURegister fpu_scratch) { ++ RoundHelper(dst, src, fpu_scratch, RDN); ++} ++ ++void MacroAssemblerRiscv64::Ceil_d_d(FPURegister dst, FPURegister src, ++ FPURegister fpu_scratch) { ++ RoundHelper(dst, src, fpu_scratch, RUP); ++} ++ ++void MacroAssemblerRiscv64::Trunc_d_d(FPURegister dst, FPURegister src, ++ FPURegister fpu_scratch) { ++ RoundHelper(dst, src, fpu_scratch, RTZ); ++} ++ ++void MacroAssemblerRiscv64::Round_d_d(FPURegister dst, FPURegister src, ++ FPURegister fpu_scratch) { ++ RoundHelper(dst, src, fpu_scratch, RNE); ++} ++ ++void MacroAssemblerRiscv64::Floor_s_s(FPURegister dst, FPURegister src, ++ FPURegister fpu_scratch) { ++ RoundHelper(dst, src, fpu_scratch, RDN); ++} ++ ++void MacroAssemblerRiscv64::Ceil_s_s(FPURegister dst, FPURegister src, ++ FPURegister fpu_scratch) { ++ RoundHelper(dst, src, fpu_scratch, RUP); ++} ++ ++void MacroAssemblerRiscv64::Trunc_s_s(FPURegister dst, FPURegister src, ++ FPURegister fpu_scratch) { ++ RoundHelper(dst, src, fpu_scratch, RTZ); ++} ++ ++void MacroAssemblerRiscv64::Round_s_s(FPURegister dst, FPURegister src, ++ FPURegister fpu_scratch) { ++ RoundHelper(dst, src, fpu_scratch, RNE); ++} ++ ++void MacroAssemblerRiscv64::Round_w_s(Register rd, FPURegister fs, ++ Register result, bool Inexact) { ++ RoundFloatingPointToInteger( ++ rd, fs, result, ++ [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { ++ masm->fcvt_w_s(dst, src, RNE); ++ }, ++ Inexact); ++} ++ ++void MacroAssemblerRiscv64::Round_w_d(Register rd, FPURegister fs, ++ Register result, bool Inexact) { ++ RoundFloatingPointToInteger( ++ rd, fs, result, ++ [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { ++ masm->fcvt_w_d(dst, src, RNE); ++ }, ++ Inexact); ++} ++ ++void MacroAssemblerRiscv64::Ceil_w_s(Register rd, FPURegister fs, ++ Register result, bool Inexact) { ++ RoundFloatingPointToInteger( ++ rd, fs, result, ++ [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { ++ masm->fcvt_w_s(dst, src, RUP); ++ }, ++ Inexact); ++} ++ ++void MacroAssemblerRiscv64::Ceil_w_d(Register rd, FPURegister fs, ++ Register result, bool Inexact) { ++ RoundFloatingPointToInteger( ++ rd, fs, result, ++ [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { ++ masm->fcvt_w_d(dst, src, RUP); ++ }, ++ Inexact); ++} ++ ++void MacroAssemblerRiscv64::Floor_w_s(Register rd, FPURegister fs, ++ Register result, bool Inexact) { ++ RoundFloatingPointToInteger( ++ rd, fs, result, ++ [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { ++ masm->fcvt_w_s(dst, src, RDN); ++ }, ++ Inexact); ++} ++ ++void MacroAssemblerRiscv64::Floor_w_d(Register rd, FPURegister fs, ++ Register result, bool Inexact) { ++ RoundFloatingPointToInteger( ++ rd, fs, result, ++ [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { ++ masm->fcvt_w_d(dst, src, RDN); ++ }, ++ Inexact); ++} ++ ++// Checks whether a double is representable as a 32-bit integer. If so, the ++// integer is written to the output register. Otherwise, a bailout is taken to ++// the given snapshot. This function overwrites the scratch float register. ++void MacroAssemblerRiscv64Compat::convertDoubleToInt32(FloatRegister src, ++ Register dest, ++ Label* fail, ++ bool negativeZeroCheck) { ++ if (negativeZeroCheck) { ++ fclass_d(dest, src); ++ ma_b(dest, Imm32(kNegativeZero), fail, Equal); ++ } ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ Trunc_w_d(dest, src, scratch, true); ++ ma_b(scratch, Imm32(0), fail, Equal); ++} ++ ++void MacroAssemblerRiscv64Compat::convertDoubleToPtr(FloatRegister src, ++ Register dest, Label* fail, ++ bool negativeZeroCheck) { ++ if (negativeZeroCheck) { ++ fclass_d(dest, src); ++ ma_b(dest, Imm32(kNegativeZero), fail, Equal); ++ } ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ Trunc_l_d(dest, src, scratch, true); ++ ma_b(scratch, Imm32(0), fail, Equal); ++} ++ ++// Checks whether a float32 is representable as a 32-bit integer. If so, the ++// integer is written to the output register. Otherwise, a bailout is taken to ++// the given snapshot. This function overwrites the scratch float register. ++void MacroAssemblerRiscv64Compat::convertFloat32ToInt32( ++ FloatRegister src, Register dest, Label* fail, bool negativeZeroCheck) { ++ if (negativeZeroCheck) { ++ fclass_d(dest, src); ++ ma_b(dest, Imm32(kNegativeZero), fail, Equal); ++ } ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ Trunc_w_s(dest, src, scratch, true); ++ ma_b(scratch, Imm32(0), fail, Equal); ++} ++ ++void MacroAssemblerRiscv64Compat::convertFloat32ToDouble(FloatRegister src, ++ FloatRegister dest) { ++ fcvt_d_s(dest, src); ++} ++ ++void MacroAssemblerRiscv64Compat::convertInt32ToFloat32(Register src, ++ FloatRegister dest) { ++ fcvt_s_w(dest, src); ++} ++ ++void MacroAssemblerRiscv64Compat::convertInt32ToFloat32(const Address& src, ++ FloatRegister dest) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ load32(src, scratch); ++ fcvt_s_w(dest, scratch); ++} ++ ++void MacroAssemblerRiscv64Compat::movq(Register rj, Register rd) { mv(rd, rj); } ++ ++// Memory. ++void MacroAssemblerRiscv64::ma_loadDouble(FloatRegister dest, Address address) { ++ int16_t encodedOffset; ++ Register base; ++ ++ if (!is_int12(address.offset)) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ ma_li(ScratchRegister, Imm32(address.offset)); ++ add(ScratchRegister, address.base, ScratchRegister); ++ base = ScratchRegister; ++ encodedOffset = 0; ++ } else { ++ encodedOffset = address.offset; ++ base = address.base; ++ } ++ fld(dest, base, encodedOffset); ++} ++ ++void MacroAssemblerRiscv64::ma_loadFloat(FloatRegister dest, Address address) { ++ int16_t encodedOffset; ++ Register base; ++ ++ if (!is_int12(address.offset)) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ ma_li(ScratchRegister, Imm32(address.offset)); ++ add(ScratchRegister, address.base, ScratchRegister); ++ base = ScratchRegister; ++ encodedOffset = 0; ++ } else { ++ encodedOffset = address.offset; ++ base = address.base; ++ } ++ flw(dest, base, encodedOffset); ++} ++ ++void MacroAssemblerRiscv64::ma_load(Register dest, Address address, ++ LoadStoreSize size, ++ LoadStoreExtension extension) { ++ int16_t encodedOffset; ++ Register base; ++ ++ if (!is_int12(address.offset)) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ ma_li(ScratchRegister, Imm32(address.offset)); ++ add(ScratchRegister, address.base, ScratchRegister); ++ base = ScratchRegister; ++ encodedOffset = 0; ++ } else { ++ encodedOffset = address.offset; ++ base = address.base; ++ } ++ ++ switch (size) { ++ case SizeByte: ++ if (ZeroExtend == extension) { ++ lbu(dest, base, encodedOffset); ++ } else { ++ lb(dest, base, encodedOffset); ++ } ++ break; ++ case SizeHalfWord: ++ if (ZeroExtend == extension) { ++ lhu(dest, base, encodedOffset); ++ } else { ++ lh(dest, base, encodedOffset); ++ } ++ break; ++ case SizeWord: ++ if (ZeroExtend == extension) { ++ lwu(dest, base, encodedOffset); ++ } else { ++ lw(dest, base, encodedOffset); ++ } ++ break; ++ case SizeDouble: ++ ld(dest, base, encodedOffset); ++ break; ++ default: ++ MOZ_CRASH("Invalid argument for ma_load"); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_store(Register data, const BaseIndex& dest, ++ LoadStoreSize size, ++ LoadStoreExtension extension) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ asMasm().computeScaledAddress(dest, scratch2); ++ asMasm().ma_store(data, Address(scratch2, dest.offset), size, extension); ++} ++ ++void MacroAssemblerRiscv64::ma_store(Imm32 imm, const BaseIndex& dest, ++ LoadStoreSize size, ++ LoadStoreExtension extension) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ Register address = temps.Acquire(); ++ // Make sure that scratch contains absolute address so that ++ // offset is 0. ++ computeScaledAddress(dest, address); ++ ++ // Scrach register is free now, use it for loading imm value ++ ma_li(scratch, imm); ++ ++ // with offset=0 ScratchRegister will not be used in ma_store() ++ // so we can use it as a parameter here ++ ma_store(scratch, Address(address, 0), size, extension); ++} ++ ++void MacroAssemblerRiscv64::ma_store(Imm32 imm, Address address, ++ LoadStoreSize size, ++ LoadStoreExtension extension) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ ma_li(scratch, imm); ++ ma_store(scratch, address, size, extension); ++} ++ ++void MacroAssemblerRiscv64::ma_store(Register data, Address address, ++ LoadStoreSize size, ++ LoadStoreExtension extension) { ++ int16_t encodedOffset; ++ Register base; ++ ++ if (!is_int12(address.offset)) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ ma_li(ScratchRegister, Imm32(address.offset)); ++ add(ScratchRegister, address.base, ScratchRegister); ++ base = ScratchRegister; ++ encodedOffset = 0; ++ } else { ++ encodedOffset = address.offset; ++ base = address.base; ++ } ++ ++ switch (size) { ++ case SizeByte: ++ sb(data, base, encodedOffset); ++ break; ++ case SizeHalfWord: ++ sh(data, base, encodedOffset); ++ break; ++ case SizeWord: ++ sw(data, base, encodedOffset); ++ break; ++ case SizeDouble: ++ sd(data, base, encodedOffset); ++ break; ++ default: ++ MOZ_CRASH("Invalid argument for ma_store"); ++ } ++} ++ ++// Memory. ++void MacroAssemblerRiscv64::ma_storeDouble(FloatRegister dest, ++ Address address) { ++ int16_t encodedOffset; ++ Register base; ++ ++ if (!is_int12(address.offset)) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ ma_li(ScratchRegister, Imm32(address.offset)); ++ add(ScratchRegister, address.base, ScratchRegister); ++ base = ScratchRegister; ++ encodedOffset = 0; ++ } else { ++ encodedOffset = address.offset; ++ base = address.base; ++ } ++ fsd(dest, base, encodedOffset); ++} ++ ++void MacroAssemblerRiscv64::ma_storeFloat(FloatRegister dest, Address address) { ++ int16_t encodedOffset; ++ Register base; ++ ++ if (!is_int12(address.offset)) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ ma_li(ScratchRegister, Imm32(address.offset)); ++ add(ScratchRegister, address.base, ScratchRegister); ++ base = ScratchRegister; ++ encodedOffset = 0; ++ } else { ++ encodedOffset = address.offset; ++ base = address.base; ++ } ++ fsw(dest, base, encodedOffset); ++} ++ ++void MacroAssemblerRiscv64::computeScaledAddress(const BaseIndex& address, ++ Register dest) { ++ Register base = address.base; ++ Register index = address.index; ++ int32_t shift = Imm32::ShiftOf(address.scale).value; ++ UseScratchRegisterScope temps(this); ++ Register tmp = dest == base ? temps.Acquire() : dest; ++ if (shift) { ++ MOZ_ASSERT(shift <= 4); ++ slli(tmp, index, shift); ++ add(dest, base, tmp); ++ } else { ++ add(dest, base, index); ++ } ++} ++ ++void MacroAssemblerRiscv64Compat::wasmLoadI64Impl( ++ const wasm::MemoryAccessDesc& access, Register memoryBase, Register ptr, ++ Register ptrScratch, Register64 output, Register tmp) { ++ uint32_t offset = access.offset(); ++ MOZ_ASSERT(offset < asMasm().wasmMaxOffsetGuardLimit()); ++ MOZ_ASSERT_IF(offset, ptrScratch != InvalidReg); ++ ++ // Maybe add the offset. ++ if (offset) { ++ asMasm().addPtr(ImmWord(offset), ptrScratch); ++ ptr = ptrScratch; ++ } ++ ++ asMasm().memoryBarrierBefore(access.sync()); ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ switch (access.type()) { ++ case Scalar::Int8: ++ add(ScratchRegister, memoryBase, ptr); ++ lb(output.reg, ScratchRegister, 0); ++ break; ++ case Scalar::Uint8: ++ add(ScratchRegister, memoryBase, ptr); ++ lbu(output.reg, ScratchRegister, 0); ++ break; ++ case Scalar::Int16: ++ add(ScratchRegister, memoryBase, ptr); ++ lh(output.reg, ScratchRegister, 0); ++ break; ++ case Scalar::Uint16: ++ add(ScratchRegister, memoryBase, ptr); ++ lhu(output.reg, ScratchRegister, 0); ++ break; ++ case Scalar::Int32: ++ add(ScratchRegister, memoryBase, ptr); ++ lw(output.reg, ScratchRegister, 0); ++ break; ++ case Scalar::Uint32: ++ // TODO(loong64): Why need zero-extension here? ++ add(ScratchRegister, memoryBase, ptr); ++ lwu(output.reg, ScratchRegister, 0); ++ break; ++ case Scalar::Int64: ++ add(ScratchRegister, memoryBase, ptr); ++ ld(output.reg, ScratchRegister, 0); ++ break; ++ default: ++ MOZ_CRASH("unexpected array type"); ++ } ++ ++ asMasm().append(access, asMasm().size() - 4); ++ asMasm().memoryBarrierAfter(access.sync()); ++} ++ ++void MacroAssemblerRiscv64Compat::wasmStoreI64Impl( ++ const wasm::MemoryAccessDesc& access, Register64 value, Register memoryBase, ++ Register ptr, Register ptrScratch, Register tmp) { ++ uint32_t offset = access.offset(); ++ MOZ_ASSERT(offset < asMasm().wasmMaxOffsetGuardLimit()); ++ MOZ_ASSERT_IF(offset, ptrScratch != InvalidReg); ++ ++ // Maybe add the offset. ++ if (offset) { ++ asMasm().addPtr(ImmWord(offset), ptrScratch); ++ ptr = ptrScratch; ++ } ++ ++ asMasm().memoryBarrierBefore(access.sync()); ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ switch (access.type()) { ++ case Scalar::Int8: ++ case Scalar::Uint8: ++ add(ScratchRegister, memoryBase, ptr); ++ sb(value.reg, ScratchRegister, 0); ++ break; ++ case Scalar::Int16: ++ case Scalar::Uint16: ++ add(ScratchRegister, memoryBase, ptr); ++ sh(value.reg, ScratchRegister, 0); ++ break; ++ case Scalar::Int32: ++ case Scalar::Uint32: ++ add(ScratchRegister, memoryBase, ptr); ++ sw(value.reg, ScratchRegister, 0); ++ break; ++ case Scalar::Int64: ++ add(ScratchRegister, memoryBase, ptr); ++ sd(value.reg, ScratchRegister, 0); ++ break; ++ default: ++ MOZ_CRASH("unexpected array type"); ++ } ++ ++ asMasm().append(access, asMasm().size() - 4); ++ asMasm().memoryBarrierAfter(access.sync()); ++} ++ ++void MacroAssemblerRiscv64Compat::profilerEnterFrame(Register framePtr, ++ Register scratch) { ++ asMasm().loadJSContext(scratch); ++ loadPtr(Address(scratch, offsetof(JSContext, profilingActivation_)), scratch); ++ storePtr(framePtr, ++ Address(scratch, JitActivation::offsetOfLastProfilingFrame())); ++ storePtr(ImmPtr(nullptr), ++ Address(scratch, JitActivation::offsetOfLastProfilingCallSite())); ++} ++ ++void MacroAssemblerRiscv64Compat::profilerExitFrame() { ++ jump(asMasm().runtime()->jitRuntime()->getProfilerExitFrameTail()); ++} ++ ++void MacroAssemblerRiscv64Compat::move32(Imm32 imm, Register dest) { ++ ma_li(dest, imm); ++} ++ ++void MacroAssemblerRiscv64Compat::move32(Register src, Register dest) { ++ slliw(dest, src, 0); ++} ++ ++void MacroAssemblerRiscv64Compat::load8ZeroExtend(const Address& address, ++ Register dest) { ++ ma_load(dest, address, SizeByte, ZeroExtend); ++} ++ ++void MacroAssemblerRiscv64Compat::load8ZeroExtend(const BaseIndex& src, ++ Register dest) { ++ ma_load(dest, src, SizeByte, ZeroExtend); ++} ++ ++void MacroAssemblerRiscv64Compat::load8SignExtend(const Address& address, ++ Register dest) { ++ ma_load(dest, address, SizeByte, SignExtend); ++} ++ ++void MacroAssemblerRiscv64Compat::load8SignExtend(const BaseIndex& src, ++ Register dest) { ++ ma_load(dest, src, SizeByte, SignExtend); ++} ++ ++void MacroAssemblerRiscv64Compat::load16ZeroExtend(const Address& address, ++ Register dest) { ++ ma_load(dest, address, SizeHalfWord, ZeroExtend); ++} ++ ++void MacroAssemblerRiscv64Compat::load16ZeroExtend(const BaseIndex& src, ++ Register dest) { ++ ma_load(dest, src, SizeHalfWord, ZeroExtend); ++} ++ ++void MacroAssemblerRiscv64Compat::load16SignExtend(const Address& address, ++ Register dest) { ++ ma_load(dest, address, SizeHalfWord, SignExtend); ++} ++ ++void MacroAssemblerRiscv64Compat::load16SignExtend(const BaseIndex& src, ++ Register dest) { ++ ma_load(dest, src, SizeHalfWord, SignExtend); ++} ++ ++void MacroAssemblerRiscv64Compat::load32(const Address& address, ++ Register dest) { ++ ma_load(dest, address, SizeWord); ++} ++ ++void MacroAssemblerRiscv64Compat::load32(const BaseIndex& address, ++ Register dest) { ++ ma_load(dest, address, SizeWord); ++} ++ ++void MacroAssemblerRiscv64Compat::load32(AbsoluteAddress address, ++ Register dest) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ movePtr(ImmPtr(address.addr), ScratchRegister); ++ load32(Address(ScratchRegister, 0), dest); ++} ++ ++void MacroAssemblerRiscv64Compat::load32(wasm::SymbolicAddress address, ++ Register dest) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ movePtr(address, ScratchRegister); ++ load32(Address(ScratchRegister, 0), dest); ++} ++ ++void MacroAssemblerRiscv64Compat::loadPtr(const Address& address, ++ Register dest) { ++ ma_load(dest, address, SizeDouble); ++} ++ ++void MacroAssemblerRiscv64Compat::loadPtr(const BaseIndex& src, Register dest) { ++ ma_load(dest, src, SizeDouble); ++} ++ ++void MacroAssemblerRiscv64Compat::loadPtr(AbsoluteAddress address, ++ Register dest) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ movePtr(ImmPtr(address.addr), ScratchRegister); ++ loadPtr(Address(ScratchRegister, 0), dest); ++} ++ ++void MacroAssemblerRiscv64Compat::loadPtr(wasm::SymbolicAddress address, ++ Register dest) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ movePtr(address, ScratchRegister); ++ loadPtr(Address(ScratchRegister, 0), dest); ++} ++ ++void MacroAssemblerRiscv64Compat::loadPrivate(const Address& address, ++ Register dest) { ++ loadPtr(address, dest); ++} ++ ++void MacroAssemblerRiscv64Compat::store8(Imm32 imm, const Address& address) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ ma_li(ScratchRegister, imm); ++ ma_store(ScratchRegister, address, SizeByte); ++} ++ ++void MacroAssemblerRiscv64Compat::store8(Register src, const Address& address) { ++ ma_store(src, address, SizeByte); ++} ++ ++void MacroAssemblerRiscv64Compat::store8(Imm32 imm, const BaseIndex& dest) { ++ ma_store(imm, dest, SizeByte); ++} ++ ++void MacroAssemblerRiscv64Compat::store8(Register src, const BaseIndex& dest) { ++ ma_store(src, dest, SizeByte); ++} ++ ++void MacroAssemblerRiscv64Compat::store16(Imm32 imm, const Address& address) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ ma_li(ScratchRegister, imm); ++ ma_store(ScratchRegister, address, SizeHalfWord); ++} ++ ++void MacroAssemblerRiscv64Compat::store16(Register src, ++ const Address& address) { ++ ma_store(src, address, SizeHalfWord); ++} ++ ++void MacroAssemblerRiscv64Compat::store16(Imm32 imm, const BaseIndex& dest) { ++ ma_store(imm, dest, SizeHalfWord); ++} ++ ++void MacroAssemblerRiscv64Compat::store16(Register src, ++ const BaseIndex& address) { ++ ma_store(src, address, SizeHalfWord); ++} ++ ++void MacroAssemblerRiscv64Compat::store32(Register src, ++ AbsoluteAddress address) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ movePtr(ImmPtr(address.addr), ScratchRegister); ++ store32(src, Address(ScratchRegister, 0)); ++} ++ ++void MacroAssemblerRiscv64Compat::store32(Register src, ++ const Address& address) { ++ ma_store(src, address, SizeWord); ++} ++ ++void MacroAssemblerRiscv64Compat::store32(Imm32 src, const Address& address) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ move32(src, ScratchRegister); ++ ma_store(ScratchRegister, address, SizeWord); ++} ++ ++void MacroAssemblerRiscv64Compat::store32(Imm32 imm, const BaseIndex& dest) { ++ ma_store(imm, dest, SizeWord); ++} ++ ++void MacroAssemblerRiscv64Compat::store32(Register src, const BaseIndex& dest) { ++ ma_store(src, dest, SizeWord); ++} ++ ++template ++void MacroAssemblerRiscv64Compat::storePtr(ImmWord imm, T address) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ ma_li(ScratchRegister, imm); ++ ma_store(ScratchRegister, address, SizeDouble); ++} ++ ++template void MacroAssemblerRiscv64Compat::storePtr
(ImmWord imm, ++ Address address); ++template void MacroAssemblerRiscv64Compat::storePtr( ++ ImmWord imm, BaseIndex address); ++ ++template ++void MacroAssemblerRiscv64Compat::storePtr(ImmPtr imm, T address) { ++ storePtr(ImmWord(uintptr_t(imm.value)), address); ++} ++ ++template void MacroAssemblerRiscv64Compat::storePtr
(ImmPtr imm, ++ Address address); ++template void MacroAssemblerRiscv64Compat::storePtr( ++ ImmPtr imm, BaseIndex address); ++ ++template ++void MacroAssemblerRiscv64Compat::storePtr(ImmGCPtr imm, T address) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ movePtr(imm, ScratchRegister); ++ storePtr(ScratchRegister, address); ++} ++ ++template void MacroAssemblerRiscv64Compat::storePtr
(ImmGCPtr imm, ++ Address address); ++template void MacroAssemblerRiscv64Compat::storePtr( ++ ImmGCPtr imm, BaseIndex address); ++ ++void MacroAssemblerRiscv64Compat::storePtr(Register src, ++ const Address& address) { ++ ma_store(src, address, SizeDouble); ++} ++ ++void MacroAssemblerRiscv64Compat::storePtr(Register src, ++ const BaseIndex& address) { ++ ma_store(src, address, SizeDouble); ++} ++ ++void MacroAssemblerRiscv64Compat::storePtr(Register src, AbsoluteAddress dest) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ movePtr(ImmPtr(dest.addr), ScratchRegister); ++ storePtr(src, Address(ScratchRegister, 0)); ++} ++ ++void MacroAssemblerRiscv64Compat::testNullSet(Condition cond, ++ const ValueOperand& value, ++ Register dest) { ++ MOZ_ASSERT(cond == Equal || cond == NotEqual); ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ splitTag(value, ScratchRegister); ++ ma_cmp_set(dest, ScratchRegister, ImmTag(JSVAL_TAG_NULL), cond); ++} ++ ++void MacroAssemblerRiscv64Compat::testObjectSet(Condition cond, ++ const ValueOperand& value, ++ Register dest) { ++ MOZ_ASSERT(cond == Equal || cond == NotEqual); ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ splitTag(value, ScratchRegister); ++ ma_cmp_set(dest, ScratchRegister, ImmTag(JSVAL_TAG_OBJECT), cond); ++} ++ ++void MacroAssemblerRiscv64Compat::testUndefinedSet(Condition cond, ++ const ValueOperand& value, ++ Register dest) { ++ MOZ_ASSERT(cond == Equal || cond == NotEqual); ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ splitTag(value, ScratchRegister); ++ ma_cmp_set(dest, ScratchRegister, ImmTag(JSVAL_TAG_UNDEFINED), cond); ++} ++ ++void MacroAssemblerRiscv64Compat::unboxInt32(const ValueOperand& operand, ++ Register dest) { ++ slliw(dest, operand.valueReg(), 0); ++} ++ ++void MacroAssemblerRiscv64Compat::unboxInt32(Register src, Register dest) { ++ slliw(dest, src, 0); ++} ++ ++void MacroAssemblerRiscv64Compat::unboxInt32(const Address& src, ++ Register dest) { ++ load32(Address(src.base, src.offset), dest); ++} ++ ++void MacroAssemblerRiscv64Compat::unboxInt32(const BaseIndex& src, ++ Register dest) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ computeScaledAddress(src, ScratchRegister); ++ load32(Address(ScratchRegister, src.offset), dest); ++} ++ ++void MacroAssemblerRiscv64Compat::unboxBoolean(const ValueOperand& operand, ++ Register dest) { ++ ExtractBits(dest, operand.valueReg(), 0, 32); ++} ++ ++void MacroAssemblerRiscv64Compat::unboxBoolean(Register src, Register dest) { ++ ExtractBits(dest, src, 0, 32); ++} ++ ++void MacroAssemblerRiscv64Compat::unboxBoolean(const Address& src, ++ Register dest) { ++ ma_load(dest, Address(src.base, src.offset), SizeWord, ZeroExtend); ++} ++ ++void MacroAssemblerRiscv64Compat::unboxBoolean(const BaseIndex& src, ++ Register dest) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ computeScaledAddress(src, ScratchRegister); ++ ma_load(dest, Address(ScratchRegister, src.offset), SizeWord, ZeroExtend); ++} ++ ++void MacroAssemblerRiscv64Compat::unboxDouble(const ValueOperand& operand, ++ FloatRegister dest) { ++ fmv_d_x(dest, operand.valueReg()); ++} ++ ++void MacroAssemblerRiscv64Compat::unboxDouble(const Address& src, ++ FloatRegister dest) { ++ ma_loadDouble(dest, Address(src.base, src.offset)); ++} ++ ++void MacroAssemblerRiscv64Compat::unboxDouble(const BaseIndex& src, ++ FloatRegister dest) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ loadPtr(src, scratch); ++ unboxDouble(ValueOperand(scratch), dest); ++} ++ ++void MacroAssemblerRiscv64Compat::unboxString(const ValueOperand& operand, ++ Register dest) { ++ unboxNonDouble(operand, dest, JSVAL_TYPE_STRING); ++} ++ ++void MacroAssemblerRiscv64Compat::unboxString(Register src, Register dest) { ++ unboxNonDouble(src, dest, JSVAL_TYPE_STRING); ++} ++ ++void MacroAssemblerRiscv64Compat::unboxString(const Address& src, ++ Register dest) { ++ unboxNonDouble(src, dest, JSVAL_TYPE_STRING); ++} ++ ++void MacroAssemblerRiscv64Compat::unboxSymbol(const ValueOperand& operand, ++ Register dest) { ++ unboxNonDouble(operand, dest, JSVAL_TYPE_SYMBOL); ++} ++ ++void MacroAssemblerRiscv64Compat::unboxSymbol(Register src, Register dest) { ++ unboxNonDouble(src, dest, JSVAL_TYPE_SYMBOL); ++} ++ ++void MacroAssemblerRiscv64Compat::unboxSymbol(const Address& src, ++ Register dest) { ++ unboxNonDouble(src, dest, JSVAL_TYPE_SYMBOL); ++} ++ ++void MacroAssemblerRiscv64Compat::unboxBigInt(const ValueOperand& operand, ++ Register dest) { ++ unboxNonDouble(operand, dest, JSVAL_TYPE_BIGINT); ++} ++ ++void MacroAssemblerRiscv64Compat::unboxBigInt(Register src, Register dest) { ++ unboxNonDouble(src, dest, JSVAL_TYPE_BIGINT); ++} ++ ++void MacroAssemblerRiscv64Compat::unboxBigInt(const Address& src, ++ Register dest) { ++ unboxNonDouble(src, dest, JSVAL_TYPE_BIGINT); ++} ++ ++void MacroAssemblerRiscv64Compat::unboxObject(const ValueOperand& src, ++ Register dest) { ++ unboxNonDouble(src, dest, JSVAL_TYPE_OBJECT); ++} ++ ++void MacroAssemblerRiscv64Compat::unboxObject(Register src, Register dest) { ++ unboxNonDouble(src, dest, JSVAL_TYPE_OBJECT); ++} ++ ++void MacroAssemblerRiscv64Compat::unboxObject(const Address& src, ++ Register dest) { ++ unboxNonDouble(src, dest, JSVAL_TYPE_OBJECT); ++} ++ ++void MacroAssemblerRiscv64Compat::unboxValue(const ValueOperand& src, ++ AnyRegister dest, ++ JSValueType type) { ++ if (dest.isFloat()) { ++ Label notInt32, end; ++ asMasm().branchTestInt32(Assembler::NotEqual, src, ¬Int32); ++ convertInt32ToDouble(src.valueReg(), dest.fpu()); ++ ma_branch(&end); ++ bind(¬Int32); ++ unboxDouble(src, dest.fpu()); ++ bind(&end); ++ } else { ++ unboxNonDouble(src, dest.gpr(), type); ++ } ++} ++ ++void MacroAssemblerRiscv64Compat::boxDouble(FloatRegister src, ++ const ValueOperand& dest, ++ FloatRegister) { ++ fmv_x_d(dest.valueReg(), src); ++} ++ ++void MacroAssemblerRiscv64Compat::boxNonDouble(JSValueType type, Register src, ++ const ValueOperand& dest) { ++ MOZ_ASSERT(src != dest.valueReg()); ++ boxValue(type, src, dest.valueReg()); ++} ++ ++void MacroAssemblerRiscv64Compat::boolValueToDouble(const ValueOperand& operand, ++ FloatRegister dest) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ convertBoolToInt32(operand.valueReg(), ScratchRegister); ++ convertInt32ToDouble(ScratchRegister, dest); ++} ++ ++void MacroAssemblerRiscv64Compat::int32ValueToDouble( ++ const ValueOperand& operand, FloatRegister dest) { ++ convertInt32ToDouble(operand.valueReg(), dest); ++} ++ ++void MacroAssemblerRiscv64Compat::boolValueToFloat32( ++ const ValueOperand& operand, FloatRegister dest) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ convertBoolToInt32(operand.valueReg(), ScratchRegister); ++ convertInt32ToFloat32(ScratchRegister, dest); ++} ++ ++void MacroAssemblerRiscv64Compat::int32ValueToFloat32( ++ const ValueOperand& operand, FloatRegister dest) { ++ convertInt32ToFloat32(operand.valueReg(), dest); ++} ++ ++void MacroAssemblerRiscv64Compat::loadConstantFloat32(float f, ++ FloatRegister dest) { ++ ma_lis(dest, f); ++} ++ ++void MacroAssemblerRiscv64Compat::loadInt32OrDouble(const Address& src, ++ FloatRegister dest) { ++ Label notInt32, end; ++ // If it's an int, convert it to double. ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ Register SecondScratchReg = temps.Acquire(); ++ loadPtr(Address(src.base, src.offset), ScratchRegister); ++ srli(SecondScratchReg, ScratchRegister, JSVAL_TAG_SHIFT); ++ asMasm().branchTestInt32(Assembler::NotEqual, SecondScratchReg, ¬Int32); ++ loadPtr(Address(src.base, src.offset), SecondScratchReg); ++ convertInt32ToDouble(SecondScratchReg, dest); ++ ma_branch(&end); ++ ++ // Not an int, just load as double. ++ bind(¬Int32); ++ unboxDouble(src, dest); ++ bind(&end); ++} ++ ++void MacroAssemblerRiscv64Compat::loadInt32OrDouble(const BaseIndex& addr, ++ FloatRegister dest) { ++ Label notInt32, end; ++ ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ Register SecondScratchReg = temps.Acquire(); ++ // If it's an int, convert it to double. ++ computeScaledAddress(addr, SecondScratchReg); ++ // Since we only have one scratch, we need to stomp over it with the tag. ++ loadPtr(Address(SecondScratchReg, 0), ScratchRegister); ++ srli(SecondScratchReg, ScratchRegister, JSVAL_TAG_SHIFT); ++ asMasm().branchTestInt32(Assembler::NotEqual, SecondScratchReg, ¬Int32); ++ ++ computeScaledAddress(addr, SecondScratchReg); ++ loadPtr(Address(SecondScratchReg, 0), SecondScratchReg); ++ convertInt32ToDouble(SecondScratchReg, dest); ++ ma_branch(&end); ++ ++ // Not an int, just load as double. ++ bind(¬Int32); ++ // First, recompute the offset that had been stored in the scratch register ++ // since the scratch register was overwritten loading in the type. ++ computeScaledAddress(addr, SecondScratchReg); ++ unboxDouble(Address(SecondScratchReg, 0), dest); ++ bind(&end); ++} ++ ++void MacroAssemblerRiscv64Compat::loadConstantDouble(double dp, ++ FloatRegister dest) { ++ ma_lid(dest, dp); ++} ++ ++Register MacroAssemblerRiscv64Compat::extractObject(const Address& address, ++ Register scratch) { ++ loadPtr(Address(address.base, address.offset), scratch); ++ ExtractBits(scratch, scratch, 0, JSVAL_TAG_SHIFT); ++ return scratch; ++} ++ ++Register MacroAssemblerRiscv64Compat::extractTag(const Address& address, ++ Register scratch) { ++ loadPtr(Address(address.base, address.offset), scratch); ++ ExtractBits(scratch, scratch, JSVAL_TAG_SHIFT, 64 - JSVAL_TAG_SHIFT); ++ return scratch; ++} ++ ++Register MacroAssemblerRiscv64Compat::extractTag(const BaseIndex& address, ++ Register scratch) { ++ computeScaledAddress(address, scratch); ++ return extractTag(Address(scratch, address.offset), scratch); ++} ++ ++///////////////////////////////////////////////////////////////// ++// X86/X64-common/ARM/LoongArch interface. ++///////////////////////////////////////////////////////////////// ++///////////////////////////////////////////////////////////////// ++// X86/X64-common/ARM/MIPS interface. ++///////////////////////////////////////////////////////////////// ++void MacroAssemblerRiscv64Compat::storeValue(ValueOperand val, ++ const BaseIndex& dest) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ computeScaledAddress(dest, ScratchRegister); ++ storeValue(val, Address(ScratchRegister, dest.offset)); ++} ++ ++void MacroAssemblerRiscv64Compat::storeValue(JSValueType type, Register reg, ++ BaseIndex dest) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ ++ computeScaledAddress(dest, ScratchRegister); ++ ++ int32_t offset = dest.offset; ++ if (!is_int12(offset)) { ++ UseScratchRegisterScope temps(this); ++ Register SecondScratchReg = temps.Acquire(); ++ ma_li(SecondScratchReg, Imm32(offset)); ++ add(ScratchRegister, ScratchRegister, SecondScratchReg); ++ offset = 0; ++ } ++ ++ storeValue(type, reg, Address(ScratchRegister, offset)); ++} ++ ++void MacroAssemblerRiscv64Compat::storeValue(ValueOperand val, ++ const Address& dest) { ++ storePtr(val.valueReg(), Address(dest.base, dest.offset)); ++} ++ ++void MacroAssemblerRiscv64Compat::storeValue(JSValueType type, Register reg, ++ Address dest) { ++ if (type == JSVAL_TYPE_INT32 || type == JSVAL_TYPE_BOOLEAN) { ++ store32(reg, dest); ++ JSValueShiftedTag tag = (JSValueShiftedTag)JSVAL_TYPE_TO_SHIFTED_TAG(type); ++ store32(((Imm64(tag)).secondHalf()), Address(dest.base, dest.offset + 4)); ++ } else { ++ ScratchRegisterScope SecondScratchReg(asMasm()); ++ MOZ_ASSERT(dest.base != SecondScratchReg); ++ ma_li(SecondScratchReg, ImmTag(JSVAL_TYPE_TO_TAG(type))); ++ slli(SecondScratchReg, SecondScratchReg, JSVAL_TAG_SHIFT); ++ InsertBits(SecondScratchReg, reg, 0, JSVAL_TAG_SHIFT); ++ storePtr(SecondScratchReg, Address(dest.base, dest.offset)); ++ } ++} ++ ++void MacroAssemblerRiscv64Compat::storeValue(const Value& val, Address dest) { ++ UseScratchRegisterScope temps(this); ++ Register SecondScratchReg = temps.Acquire(); ++ if (val.isGCThing()) { ++ writeDataRelocation(val); ++ movWithPatch(ImmWord(val.asRawBits()), SecondScratchReg); ++ } else { ++ ma_li(SecondScratchReg, ImmWord(val.asRawBits())); ++ } ++ storePtr(SecondScratchReg, Address(dest.base, dest.offset)); ++} ++ ++void MacroAssemblerRiscv64Compat::storeValue(const Value& val, BaseIndex dest) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ Register SecondScratchReg = temps.Acquire(); ++ computeScaledAddress(dest, ScratchRegister); ++ ++ int32_t offset = dest.offset; ++ if (!is_int12(offset)) { ++ ma_li(SecondScratchReg, Imm32(offset)); ++ add(ScratchRegister, ScratchRegister, SecondScratchReg); ++ offset = 0; ++ } ++ storeValue(val, Address(ScratchRegister, offset)); ++} ++ ++void MacroAssemblerRiscv64Compat::loadValue(const BaseIndex& addr, ++ ValueOperand val) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ computeScaledAddress(addr, ScratchRegister); ++ loadValue(Address(ScratchRegister, addr.offset), val); ++} ++ ++void MacroAssemblerRiscv64Compat::loadValue(Address src, ValueOperand val) { ++ loadPtr(Address(src.base, src.offset), val.valueReg()); ++} ++ ++void MacroAssemblerRiscv64Compat::tagValue(JSValueType type, Register payload, ++ ValueOperand dest) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ MOZ_ASSERT(dest.valueReg() != ScratchRegister); ++ JitSpew(JitSpew_Codegen, "[ tagValue"); ++ if (payload != dest.valueReg()) { ++ mv(dest.valueReg(), payload); ++ } ++ ma_li(ScratchRegister, ImmTag(JSVAL_TYPE_TO_TAG(type))); ++ InsertBits(dest.valueReg(), ScratchRegister, JSVAL_TAG_SHIFT, ++ 64 - JSVAL_TAG_SHIFT); ++ if (type == JSVAL_TYPE_INT32 || type == JSVAL_TYPE_BOOLEAN) { ++ InsertBits(dest.valueReg(), zero, 32, JSVAL_TAG_SHIFT - 32); ++ } ++ JitSpew(JitSpew_Codegen, "]"); ++} ++ ++void MacroAssemblerRiscv64Compat::pushValue(ValueOperand val) { ++ // Allocate stack slots for Value. One for each. ++ asMasm().subPtr(Imm32(sizeof(Value)), StackPointer); ++ // Store Value ++ storeValue(val, Address(StackPointer, 0)); ++} ++ ++void MacroAssemblerRiscv64Compat::pushValue(const Address& addr) { ++ // Load value before allocate stack, addr.base may be is sp. ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ loadPtr(Address(addr.base, addr.offset), ScratchRegister); ++ ma_sub64(StackPointer, StackPointer, Imm32(sizeof(Value))); ++ storePtr(ScratchRegister, Address(StackPointer, 0)); ++} ++ ++void MacroAssemblerRiscv64Compat::popValue(ValueOperand val) { ++ ld(val.valueReg(), StackPointer, 0); ++ ma_add64(StackPointer, StackPointer, Imm32(sizeof(Value))); ++} ++ ++void MacroAssemblerRiscv64Compat::breakpoint(uint32_t value) { break_(value); } ++ ++void MacroAssemblerRiscv64Compat::ensureDouble(const ValueOperand& source, ++ FloatRegister dest, ++ Label* failure) { ++ Label isDouble, done; ++ { ++ ScratchTagScope tag(asMasm(), source); ++ splitTagForTest(source, tag); ++ asMasm().branchTestDouble(Assembler::Equal, tag, &isDouble); ++ asMasm().branchTestInt32(Assembler::NotEqual, tag, failure); ++ } ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ unboxInt32(source, ScratchRegister); ++ convertInt32ToDouble(ScratchRegister, dest); ++ jump(&done); ++ ++ bind(&isDouble); ++ unboxDouble(source, dest); ++ ++ bind(&done); ++} ++ ++void MacroAssemblerRiscv64Compat::handleFailureWithHandlerTail( ++ Label* profilerExitTail, Label* bailoutTail) { ++ // Reserve space for exception information. ++ int size = (sizeof(ResumeFromException) + ABIStackAlignment) & ++ ~(ABIStackAlignment - 1); ++ asMasm().subPtr(Imm32(size), StackPointer); ++ mv(a0, StackPointer); // Use a0 since it is a first function argument ++ ++ // Call the handler. ++ using Fn = void (*)(ResumeFromException * rfe); ++ asMasm().setupUnalignedABICall(a1); ++ asMasm().passABIArg(a0); ++ asMasm().callWithABI( ++ MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckHasExitFrame); ++ ++ Label entryFrame; ++ Label catch_; ++ Label finally; ++ Label returnBaseline; ++ Label returnIon; ++ Label bailout; ++ Label wasm; ++ Label wasmCatch; ++ ++ // Already clobbered a0, so use it... ++ load32(Address(StackPointer, ResumeFromException::offsetOfKind()), a0); ++ asMasm().branch32(Assembler::Equal, a0, ++ Imm32(ExceptionResumeKind::EntryFrame), &entryFrame); ++ asMasm().branch32(Assembler::Equal, a0, Imm32(ExceptionResumeKind::Catch), ++ &catch_); ++ asMasm().branch32(Assembler::Equal, a0, Imm32(ExceptionResumeKind::Finally), ++ &finally); ++ asMasm().branch32(Assembler::Equal, a0, ++ Imm32(ExceptionResumeKind::ForcedReturnBaseline), ++ &returnBaseline); ++ asMasm().branch32(Assembler::Equal, a0, ++ Imm32(ExceptionResumeKind::ForcedReturnIon), &returnIon); ++ asMasm().branch32(Assembler::Equal, a0, Imm32(ExceptionResumeKind::Bailout), ++ &bailout); ++ asMasm().branch32(Assembler::Equal, a0, Imm32(ExceptionResumeKind::Wasm), ++ &wasm); ++ asMasm().branch32(Assembler::Equal, a0, Imm32(ExceptionResumeKind::WasmCatch), ++ &wasmCatch); ++ ++ breakpoint(); // Invalid kind. ++ ++ // No exception handler. Load the error value, restore state and return from ++ // the entry frame. ++ bind(&entryFrame); ++ asMasm().moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand); ++ loadPtr(Address(StackPointer, ResumeFromException::offsetOfFramePointer()), ++ FramePointer); ++ loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()), ++ StackPointer); ++ ++ // We're going to be returning by the ion calling convention ++ ma_pop(ra); ++ jump(ra); ++ nop(); ++ ++ // If we found a catch handler, this must be a baseline frame. Restore ++ // state and jump to the catch block. ++ bind(&catch_); ++ loadPtr(Address(StackPointer, ResumeFromException::offsetOfTarget()), a0); ++ loadPtr(Address(StackPointer, ResumeFromException::offsetOfFramePointer()), ++ FramePointer); ++ loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()), ++ StackPointer); ++ jump(a0); ++ ++ // If we found a finally block, this must be a baseline frame. Push two ++ // values expected by the finally block: the exception and BooleanValue(true). ++ bind(&finally); ++ ValueOperand exception = ValueOperand(a1); ++ loadValue(Address(sp, ResumeFromException::offsetOfException()), exception); ++ ++ loadPtr(Address(sp, ResumeFromException::offsetOfTarget()), a0); ++ loadPtr(Address(sp, ResumeFromException::offsetOfFramePointer()), ++ FramePointer); ++ loadPtr(Address(sp, ResumeFromException::offsetOfStackPointer()), sp); ++ ++ pushValue(exception); ++ pushValue(BooleanValue(true)); ++ jump(a0); ++ ++ // Return BaselineFrame->returnValue() to the caller. ++ // Used in debug mode and for GeneratorReturn. ++ Label profilingInstrumentation; ++ bind(&returnBaseline); ++ loadPtr(Address(StackPointer, ResumeFromException::offsetOfFramePointer()), ++ FramePointer); ++ loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()), ++ StackPointer); ++ loadValue(Address(FramePointer, BaselineFrame::reverseOffsetOfReturnValue()), ++ JSReturnOperand); ++ jump(&profilingInstrumentation); ++ ++ // Return the given value to the caller. ++ bind(&returnIon); ++ loadValue(Address(StackPointer, ResumeFromException::offsetOfException()), ++ JSReturnOperand); ++ loadPtr(Address(StackPointer, ResumeFromException::offsetOfFramePointer()), ++ FramePointer); ++ loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()), ++ StackPointer); ++ ++ // If profiling is enabled, then update the lastProfilingFrame to refer to ++ // caller frame before returning. This code is shared by ForcedReturnIon ++ // and ForcedReturnBaseline. ++ bind(&profilingInstrumentation); ++ { ++ Label skipProfilingInstrumentation; ++ // Test if profiler enabled. ++ AbsoluteAddress addressOfEnabled( ++ asMasm().runtime()->geckoProfiler().addressOfEnabled()); ++ asMasm().branch32(Assembler::Equal, addressOfEnabled, Imm32(0), ++ &skipProfilingInstrumentation); ++ jump(profilerExitTail); ++ bind(&skipProfilingInstrumentation); ++ } ++ ++ mv(StackPointer, FramePointer); ++ pop(FramePointer); ++ ret(); ++ ++ // If we are bailing out to baseline to handle an exception, jump to ++ // the bailout tail stub. Load 1 (true) in ReturnReg to indicate success. ++ bind(&bailout); ++ loadPtr(Address(sp, ResumeFromException::offsetOfBailoutInfo()), a2); ++ loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()), ++ StackPointer); ++ ma_li(ReturnReg, Imm32(1)); ++ jump(bailoutTail); ++ ++ // If we are throwing and the innermost frame was a wasm frame, reset SP and ++ // FP; SP is pointing to the unwound return address to the wasm entry, so ++ // we can just ret(). ++ bind(&wasm); ++ loadPtr(Address(StackPointer, ResumeFromException::offsetOfFramePointer()), ++ FramePointer); ++ loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()), ++ StackPointer); ++ ret(); ++ ++ // Found a wasm catch handler, restore state and jump to it. ++ bind(&wasmCatch); ++ loadPtr(Address(sp, ResumeFromException::offsetOfTarget()), a1); ++ loadPtr(Address(StackPointer, ResumeFromException::offsetOfFramePointer()), ++ FramePointer); ++ loadPtr(Address(StackPointer, ResumeFromException::offsetOfStackPointer()), ++ StackPointer); ++ jump(a1); ++} ++ ++CodeOffset MacroAssemblerRiscv64Compat::toggledJump(Label* label) { ++ CodeOffset ret(nextOffset().getOffset()); ++ BranchShort(label); ++ return ret; ++} ++ ++CodeOffset MacroAssemblerRiscv64Compat::toggledCall(JitCode* target, ++ bool enabled) { ++ DEBUG_PRINTF("\ttoggledCall\n"); ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ BlockTrampolinePoolScope block_trampoline_pool(this, 8); ++ BufferOffset bo = nextOffset(); ++ CodeOffset offset(bo.getOffset()); ++ addPendingJump(bo, ImmPtr(target->raw()), RelocationKind::JITCODE); ++ ma_liPatchable(ScratchRegister, ImmPtr(target->raw())); ++ if (enabled) { ++ jalr(ScratchRegister); ++ } else { ++ nop(); ++ } ++ MOZ_ASSERT_IF(!oom(), nextOffset().getOffset() - offset.offset() == ++ ToggledCallSize(nullptr)); ++ return offset; ++} ++ ++void MacroAssembler::subFromStackPtr(Imm32 imm32) { ++ if (imm32.value) { ++ asMasm().subPtr(imm32, StackPointer); ++ } ++} ++ ++void MacroAssembler::clampDoubleToUint8(FloatRegister input, Register output) { ++ JitSpew(JitSpew_Codegen, "[ clampDoubleToUint8"); ++ Label nan, done; ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ feq_d(scratch, input, input); ++ beqz(scratch, &nan); ++ addi(zero, scratch, 0x11); ++ Round_w_d(output, input); ++ clampIntToUint8(output); ++ ma_branch(&done); ++ // Input is nan ++ bind(&nan); ++ mv(output, zero_reg); ++ bind(&done); ++ JitSpew(JitSpew_Codegen, "]"); ++} ++ ++//{{{ check_macroassembler_style ++// =============================================================== ++// MacroAssembler high-level usage. ++bool MacroAssembler::convertUInt64ToDoubleNeedsTemp() { return false; } ++CodeOffset MacroAssembler::call(Label* label) { ++ BranchAndLink(label); ++ return CodeOffset(currentOffset()); ++} ++CodeOffset MacroAssembler::call(Register reg) { ++ jalr(reg, 0); ++ return CodeOffset(currentOffset()); ++} ++CodeOffset MacroAssembler::call(wasm::SymbolicAddress target) { ++ UseScratchRegisterScope temps(this); ++ temps.Exclude(GeneralRegisterSet(1 << CallReg.code())); ++ movePtr(target, CallReg); ++ return call(CallReg); ++} ++CodeOffset MacroAssembler::farJumpWithPatch() { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ Register scratch2 = temps.Acquire(); ++ // Allocate space which will be patched by patchFarJump(). ++ CodeOffset farJump(nextInstrOffset(5).getOffset()); ++ auipc(scratch, 0); ++ lw(scratch2, scratch, 4 * sizeof(Instr)); ++ add(scratch, scratch, scratch2); ++ jr(scratch, 0); ++ spew(".space 32bit initValue 0xffff ffff"); ++ emit(UINT32_MAX); ++ return farJump; ++} ++CodeOffset MacroAssembler::moveNearAddressWithPatch(Register dest) { ++ return movWithPatch(ImmPtr(nullptr), dest); ++} ++CodeOffset MacroAssembler::nopPatchableToCall() { ++ BlockTrampolinePoolScope block_trampoline_pool(this, 7); ++ // riscv64 ++ nop(); // lui(rd, (int32_t)high_20); ++ nop(); // addi(rd, rd, low_12); // 31 bits in rd. ++ nop(); // slli(rd, rd, 11); // Space for next 11 bis ++ nop(); // ori(rd, rd, b11); // 11 bits are put in. 42 bit in rd ++ nop(); // slli(rd, rd, 6); // Space for next 6 bits ++ nop(); // ori(rd, rd, a6); // 6 bits are put in. 48 bis in rd ++ nop(); // jirl ++ return CodeOffset(currentOffset()); ++} ++CodeOffset MacroAssembler::wasmTrapInstruction() { ++ CodeOffset offset(currentOffset()); ++ BlockTrampolinePoolScope block_trampoline_pool(this, 2); ++ break_(kWasmTrapCode); // TODO: teq(zero, zero, WASM_TRAP) ++ return offset; ++} ++size_t MacroAssembler::PushRegsInMaskSizeInBytes(LiveRegisterSet set) { ++ return set.gprs().size() * sizeof(intptr_t) + set.fpus().getPushSizeInBytes(); ++} ++template ++void MacroAssembler::branchValueIsNurseryCellImpl(Condition cond, ++ const T& value, Register temp, ++ Label* label) { ++ MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); ++ MOZ_ASSERT(temp != InvalidReg); ++ Label done; ++ branchTestGCThing(Assembler::NotEqual, value, ++ cond == Assembler::Equal ? &done : label); ++ ++ unboxGCThingForGCBarrier(value, temp); ++ orPtr(Imm32(gc::ChunkMask), temp); ++ loadPtr(Address(temp, gc::ChunkStoreBufferOffsetFromLastByte), temp); ++ branchPtr(InvertCondition(cond), temp, zero, label); ++ ++ bind(&done); ++} ++ ++template ++void MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, ++ MIRType valueType, const T& dest) { ++ MOZ_ASSERT(valueType < MIRType::Value); ++ ++ if (valueType == MIRType::Double) { ++ boxDouble(value.reg().typedReg().fpu(), dest); ++ return; ++ } ++ ++ if (value.constant()) { ++ storeValue(value.value(), dest); ++ } else { ++ storeValue(ValueTypeFromMIRType(valueType), value.reg().typedReg().gpr(), ++ dest); ++ } ++} ++ ++template void MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, ++ MIRType valueType, ++ const Address& dest); ++template void MacroAssembler::storeUnboxedValue( ++ const ConstantOrRegister& value, MIRType valueType, ++ const BaseObjectElementIndex& dest); ++ ++// =============================================================== ++// Jit Frames. ++ ++uint32_t MacroAssembler::pushFakeReturnAddress(Register scratch) { ++ CodeLabel cl; ++ ++ ma_li(scratch, &cl); ++ Push(scratch); ++ bind(&cl); ++ uint32_t retAddr = currentOffset(); ++ ++ addCodeLabel(cl); ++ return retAddr; ++} ++ ++//=============================== ++// AtomicOp ++ ++template ++static void AtomicExchange(MacroAssembler& masm, ++ const wasm::MemoryAccessDesc* access, ++ Scalar::Type type, const Synchronization& sync, ++ const T& mem, Register value, Register valueTemp, ++ Register offsetTemp, Register maskTemp, ++ Register output) { ++ ScratchRegisterScope scratch(masm); ++ UseScratchRegisterScope temps(&masm); ++ Register scratch2 = temps.Acquire(); ++ bool signExtend = Scalar::isSignedIntType(type); ++ unsigned nbytes = Scalar::byteSize(type); ++ ++ switch (nbytes) { ++ case 1: ++ case 2: ++ break; ++ case 4: ++ MOZ_ASSERT(valueTemp == InvalidReg); ++ MOZ_ASSERT(offsetTemp == InvalidReg); ++ MOZ_ASSERT(maskTemp == InvalidReg); ++ break; ++ default: ++ MOZ_CRASH(); ++ } ++ ++ Label again; ++ ++ masm.computeEffectiveAddress(mem, scratch); ++ ++ if (nbytes == 4) { ++ masm.memoryBarrierBefore(sync); ++ masm.bind(&again); ++ ++ if (access) { ++ masm.append(*access, masm.size()); ++ } ++ ++ masm.lr_w(true, true, output, scratch); ++ masm.or_(scratch2, value, zero); ++ masm.sc_w(true, true, scratch2, scratch, scratch2); ++ masm.ma_b(scratch2, Register(scratch2), &again, Assembler::NonZero, ++ ShortJump); ++ ++ masm.memoryBarrierAfter(sync); ++ ++ return; ++ } ++ ++ masm.andi(offsetTemp, scratch, 3); ++ masm.subPtr(offsetTemp, scratch); ++ masm.slliw(offsetTemp, offsetTemp, 3); ++ masm.ma_li(maskTemp, Imm32(UINT32_MAX >> ((4 - nbytes) * 8))); ++ masm.sllw(maskTemp, maskTemp, offsetTemp); ++ masm.nor(maskTemp, zero, maskTemp); ++ switch (nbytes) { ++ case 1: ++ masm.andi(valueTemp, value, 0xff); ++ break; ++ case 2: ++ masm.ma_and(valueTemp, value, Imm32(0xffff)); ++ break; ++ } ++ masm.sllw(valueTemp, valueTemp, offsetTemp); ++ ++ masm.memoryBarrierBefore(sync); ++ ++ masm.bind(&again); ++ ++ if (access) { ++ masm.append(*access, masm.size()); ++ } ++ ++ masm.lr_w(true, true, output, scratch); ++ masm.and_(scratch2, output, maskTemp); ++ masm.or_(scratch2, scratch2, valueTemp); ++ ++ masm.sc_w(true, true, scratch2, scratch, scratch2); ++ ++ masm.ma_b(scratch2, Register(scratch2), &again, Assembler::NonZero, ++ ShortJump); ++ ++ masm.srlw(output, output, offsetTemp); ++ ++ switch (nbytes) { ++ case 1: ++ if (signExtend) { ++ masm.slliw(output, output, 32 - 8); ++ masm.sraiw(output, output, 32 - 8); ++ } else { ++ masm.andi(valueTemp, value, 0xff); ++ } ++ break; ++ case 2: ++ if (signExtend) { ++ masm.slliw(output, output, 32 - 16); ++ masm.sraiw(output, output, 32 - 16); ++ } else { ++ masm.ma_and(valueTemp, value, Imm32(0xffff)); ++ } ++ break; ++ } ++ ++ masm.memoryBarrierAfter(sync); ++} ++ ++template ++static void AtomicExchange64(MacroAssembler& masm, ++ const wasm::MemoryAccessDesc* access, ++ const Synchronization& sync, const T& mem, ++ Register64 value, Register64 output) { ++ MOZ_ASSERT(value != output); ++ UseScratchRegisterScope temps(&masm); ++ Register SecondScratchReg = temps.Acquire(); ++ masm.computeEffectiveAddress(mem, SecondScratchReg); ++ ++ Label tryAgain; ++ ++ masm.memoryBarrierBefore(sync); ++ ++ masm.bind(&tryAgain); ++ ++ if (access) { ++ masm.append(*access, masm.size()); ++ } ++ ++ masm.lr_d(true, true, output.reg, SecondScratchReg); ++ masm.movePtr(value.reg, ScratchRegister); ++ masm.sc_d(true, true, ScratchRegister, SecondScratchReg, ScratchRegister); ++ masm.ma_b(ScratchRegister, ScratchRegister, &tryAgain, Assembler::NonZero, ++ ShortJump); ++ ++ masm.memoryBarrierAfter(sync); ++} ++ ++template ++static void AtomicFetchOp64(MacroAssembler& masm, ++ const wasm::MemoryAccessDesc* access, ++ const Synchronization& sync, AtomicOp op, ++ Register64 value, const T& mem, Register64 temp, ++ Register64 output) { ++ MOZ_ASSERT(value != output); ++ MOZ_ASSERT(value != temp); ++ UseScratchRegisterScope temps(&masm); ++ Register SecondScratchReg = temps.Acquire(); ++ masm.computeEffectiveAddress(mem, SecondScratchReg); ++ ++ Label tryAgain; ++ ++ masm.memoryBarrierBefore(sync); ++ ++ masm.bind(&tryAgain); ++ if (access) { ++ masm.append(*access, masm.size()); ++ } ++ ++ masm.lr_d(true, true, output.reg, SecondScratchReg); ++ ++ switch (op) { ++ case AtomicFetchAddOp: ++ masm.add(temp.reg, output.reg, value.reg); ++ break; ++ case AtomicFetchSubOp: ++ masm.sub(temp.reg, output.reg, value.reg); ++ break; ++ case AtomicFetchAndOp: ++ masm.and_(temp.reg, output.reg, value.reg); ++ break; ++ case AtomicFetchOrOp: ++ masm.or_(temp.reg, output.reg, value.reg); ++ break; ++ case AtomicFetchXorOp: ++ masm.xor_(temp.reg, output.reg, value.reg); ++ break; ++ default: ++ MOZ_CRASH(); ++ } ++ ++ masm.sc_d(true, true, temp.reg, SecondScratchReg, temp.reg); ++ masm.ma_b(temp.reg, temp.reg, &tryAgain, Assembler::NonZero, ShortJump); ++ ++ masm.memoryBarrierAfter(sync); ++} ++ ++template ++static void AtomicEffectOp(MacroAssembler& masm, ++ const wasm::MemoryAccessDesc* access, ++ Scalar::Type type, const Synchronization& sync, ++ AtomicOp op, const T& mem, Register value, ++ Register valueTemp, Register offsetTemp, ++ Register maskTemp) { ++ ScratchRegisterScope scratch(masm); ++ UseScratchRegisterScope temps(&masm); ++ Register scratch2 = temps.Acquire(); ++ unsigned nbytes = Scalar::byteSize(type); ++ ++ switch (nbytes) { ++ case 1: ++ case 2: ++ break; ++ case 4: ++ MOZ_ASSERT(valueTemp == InvalidReg); ++ MOZ_ASSERT(offsetTemp == InvalidReg); ++ MOZ_ASSERT(maskTemp == InvalidReg); ++ break; ++ default: ++ MOZ_CRASH(); ++ } ++ ++ Label again; ++ ++ masm.computeEffectiveAddress(mem, scratch); ++ ++ if (nbytes == 4) { ++ masm.memoryBarrierBefore(sync); ++ masm.bind(&again); ++ ++ if (access) { ++ masm.append(*access, masm.size()); ++ } ++ ++ masm.lr_w(true, true, scratch2, scratch); ++ ++ switch (op) { ++ case AtomicFetchAddOp: ++ masm.addw(scratch2, scratch2, value); ++ break; ++ case AtomicFetchSubOp: ++ masm.subw(scratch2, scratch2, value); ++ break; ++ case AtomicFetchAndOp: ++ masm.and_(scratch2, scratch2, value); ++ break; ++ case AtomicFetchOrOp: ++ masm.or_(scratch2, scratch2, value); ++ break; ++ case AtomicFetchXorOp: ++ masm.xor_(scratch2, scratch2, value); ++ break; ++ default: ++ MOZ_CRASH(); ++ } ++ ++ masm.sc_w(true, true, scratch2, scratch, scratch2); ++ masm.ma_b(scratch2, Register(scratch2), &again, Assembler::NonZero, ++ ShortJump); ++ ++ masm.memoryBarrierAfter(sync); ++ ++ return; ++ } ++ ++ masm.andi(offsetTemp, scratch, 3); ++ masm.subPtr(offsetTemp, scratch); ++ masm.slliw(offsetTemp, offsetTemp, 3); ++ masm.ma_li(maskTemp, Imm32(UINT32_MAX >> ((4 - nbytes) * 8))); ++ masm.sllw(maskTemp, maskTemp, offsetTemp); ++ masm.nor(maskTemp, zero, maskTemp); ++ ++ masm.memoryBarrierBefore(sync); ++ ++ masm.bind(&again); ++ ++ if (access) { ++ masm.append(*access, masm.size()); ++ } ++ ++ masm.lr_w(true, true, scratch2, scratch); ++ masm.srlw(valueTemp, scratch2, offsetTemp); ++ ++ switch (op) { ++ case AtomicFetchAddOp: ++ masm.addw(valueTemp, valueTemp, value); ++ break; ++ case AtomicFetchSubOp: ++ masm.subw(valueTemp, valueTemp, value); ++ break; ++ case AtomicFetchAndOp: ++ masm.and_(valueTemp, valueTemp, value); ++ break; ++ case AtomicFetchOrOp: ++ masm.or_(valueTemp, valueTemp, value); ++ break; ++ case AtomicFetchXorOp: ++ masm.xor_(valueTemp, valueTemp, value); ++ break; ++ default: ++ MOZ_CRASH(); ++ } ++ ++ switch (nbytes) { ++ case 1: ++ masm.andi(valueTemp, valueTemp, 0xff); ++ break; ++ case 2: ++ masm.ma_and(valueTemp, valueTemp, Imm32(0xffff)); ++ break; ++ } ++ ++ masm.sllw(valueTemp, valueTemp, offsetTemp); ++ ++ masm.and_(scratch2, scratch2, maskTemp); ++ masm.or_(scratch2, scratch2, valueTemp); ++ ++ masm.sc_w(true, true, scratch2, scratch, scratch2); ++ ++ masm.ma_b(scratch2, Register(scratch2), &again, Assembler::NonZero, ++ ShortJump); ++ ++ masm.memoryBarrierAfter(sync); ++} ++ ++template ++static void AtomicFetchOp(MacroAssembler& masm, ++ const wasm::MemoryAccessDesc* access, ++ Scalar::Type type, const Synchronization& sync, ++ AtomicOp op, const T& mem, Register value, ++ Register valueTemp, Register offsetTemp, ++ Register maskTemp, Register output) { ++ ScratchRegisterScope scratch(masm); ++ UseScratchRegisterScope temps(&masm); ++ Register scratch2 = temps.Acquire(); ++ bool signExtend = Scalar::isSignedIntType(type); ++ unsigned nbytes = Scalar::byteSize(type); ++ ++ switch (nbytes) { ++ case 1: ++ case 2: ++ break; ++ case 4: ++ MOZ_ASSERT(valueTemp == InvalidReg); ++ MOZ_ASSERT(offsetTemp == InvalidReg); ++ MOZ_ASSERT(maskTemp == InvalidReg); ++ break; ++ default: ++ MOZ_CRASH(); ++ } ++ ++ Label again; ++ ++ masm.computeEffectiveAddress(mem, scratch); ++ ++ if (nbytes == 4) { ++ masm.memoryBarrierBefore(sync); ++ masm.bind(&again); ++ ++ if (access) { ++ masm.append(*access, masm.size()); ++ } ++ ++ masm.lr_w(true, true, output, scratch); ++ ++ switch (op) { ++ case AtomicFetchAddOp: ++ masm.addw(scratch2, output, value); ++ break; ++ case AtomicFetchSubOp: ++ masm.subw(scratch2, output, value); ++ break; ++ case AtomicFetchAndOp: ++ masm.and_(scratch2, output, value); ++ break; ++ case AtomicFetchOrOp: ++ masm.or_(scratch2, output, value); ++ break; ++ case AtomicFetchXorOp: ++ masm.xor_(scratch2, output, value); ++ break; ++ default: ++ MOZ_CRASH(); ++ } ++ ++ masm.sc_w(true, true, scratch2, scratch, scratch2); ++ masm.ma_b(scratch2, Register(scratch2), &again, Assembler::NonZero, ++ ShortJump); ++ ++ masm.memoryBarrierAfter(sync); ++ ++ return; ++ } ++ ++ masm.andi(offsetTemp, scratch, 3); ++ masm.subPtr(offsetTemp, scratch); ++ masm.slliw(offsetTemp, offsetTemp, 3); ++ masm.ma_li(maskTemp, Imm32(UINT32_MAX >> ((4 - nbytes) * 8))); ++ masm.sllw(maskTemp, maskTemp, offsetTemp); ++ masm.nor(maskTemp, zero, maskTemp); ++ ++ masm.memoryBarrierBefore(sync); ++ ++ masm.bind(&again); ++ ++ if (access) { ++ masm.append(*access, masm.size()); ++ } ++ ++ masm.lr_w(true, true, scratch2, scratch); ++ masm.srlw(output, scratch2, offsetTemp); ++ ++ switch (op) { ++ case AtomicFetchAddOp: ++ masm.addw(valueTemp, output, value); ++ break; ++ case AtomicFetchSubOp: ++ masm.subw(valueTemp, output, value); ++ break; ++ case AtomicFetchAndOp: ++ masm.and_(valueTemp, output, value); ++ break; ++ case AtomicFetchOrOp: ++ masm.or_(valueTemp, output, value); ++ break; ++ case AtomicFetchXorOp: ++ masm.xor_(valueTemp, output, value); ++ break; ++ default: ++ MOZ_CRASH(); ++ } ++ ++ switch (nbytes) { ++ case 1: ++ masm.andi(valueTemp, valueTemp, 0xff); ++ break; ++ case 2: ++ masm.andi(valueTemp, valueTemp, 0xffff); ++ break; ++ } ++ ++ masm.sllw(valueTemp, valueTemp, offsetTemp); ++ ++ masm.and_(scratch2, scratch2, maskTemp); ++ masm.or_(scratch2, scratch2, valueTemp); ++ ++ masm.sc_w(true, true, scratch2, scratch, scratch2); ++ ++ masm.ma_b(scratch2, Register(scratch2), &again, Assembler::NonZero, ++ ShortJump); ++ ++ switch (nbytes) { ++ case 1: ++ if (signExtend) { ++ masm.slliw(output, output, 32 - 8); ++ masm.sraiw(output, output, 32 - 8); ++ } else { ++ masm.andi(output, output, 0xff); ++ } ++ break; ++ case 2: ++ if (signExtend) { ++ masm.slliw(output, output, 32 - 16); ++ masm.sraiw(output, output, 32 - 16); ++ } else { ++ masm.andi(output, output, 0xffff); ++ } ++ break; ++ } ++ ++ masm.memoryBarrierAfter(sync); ++} ++ ++// ======================================================================== ++// JS atomic operations. ++ ++template ++static void CompareExchangeJS(MacroAssembler& masm, Scalar::Type arrayType, ++ const Synchronization& sync, const T& mem, ++ Register oldval, Register newval, ++ Register valueTemp, Register offsetTemp, ++ Register maskTemp, Register temp, ++ AnyRegister output) { ++ if (arrayType == Scalar::Uint32) { ++ masm.compareExchange(arrayType, sync, mem, oldval, newval, valueTemp, ++ offsetTemp, maskTemp, temp); ++ masm.convertUInt32ToDouble(temp, output.fpu()); ++ } else { ++ masm.compareExchange(arrayType, sync, mem, oldval, newval, valueTemp, ++ offsetTemp, maskTemp, output.gpr()); ++ } ++} ++ ++template ++static void AtomicExchangeJS(MacroAssembler& masm, Scalar::Type arrayType, ++ const Synchronization& sync, const T& mem, ++ Register value, Register valueTemp, ++ Register offsetTemp, Register maskTemp, ++ Register temp, AnyRegister output) { ++ if (arrayType == Scalar::Uint32) { ++ masm.atomicExchange(arrayType, sync, mem, value, valueTemp, offsetTemp, ++ maskTemp, temp); ++ masm.convertUInt32ToDouble(temp, output.fpu()); ++ } else { ++ masm.atomicExchange(arrayType, sync, mem, value, valueTemp, offsetTemp, ++ maskTemp, output.gpr()); ++ } ++} ++ ++template ++static void AtomicFetchOpJS(MacroAssembler& masm, Scalar::Type arrayType, ++ const Synchronization& sync, AtomicOp op, ++ Register value, const T& mem, Register valueTemp, ++ Register offsetTemp, Register maskTemp, ++ Register temp, AnyRegister output) { ++ if (arrayType == Scalar::Uint32) { ++ masm.atomicFetchOp(arrayType, sync, op, value, mem, valueTemp, offsetTemp, ++ maskTemp, temp); ++ masm.convertUInt32ToDouble(temp, output.fpu()); ++ } else { ++ masm.atomicFetchOp(arrayType, sync, op, value, mem, valueTemp, offsetTemp, ++ maskTemp, output.gpr()); ++ } ++} ++ ++void MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType, ++ const Synchronization& sync, AtomicOp op, ++ Register value, const BaseIndex& mem, ++ Register valueTemp, Register offsetTemp, ++ Register maskTemp) { ++ AtomicEffectOp(*this, nullptr, arrayType, sync, op, mem, value, valueTemp, ++ offsetTemp, maskTemp); ++} ++ ++void MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType, ++ const Synchronization& sync, AtomicOp op, ++ Register value, const Address& mem, ++ Register valueTemp, Register offsetTemp, ++ Register maskTemp) { ++ AtomicEffectOp(*this, nullptr, arrayType, sync, op, mem, value, valueTemp, ++ offsetTemp, maskTemp); ++} ++void MacroAssembler::atomicExchange64(const Synchronization& sync, ++ const Address& mem, Register64 value, ++ Register64 output) { ++ AtomicExchange64(*this, nullptr, sync, mem, value, output); ++} ++ ++void MacroAssembler::atomicExchange64(const Synchronization& sync, ++ const BaseIndex& mem, Register64 value, ++ Register64 output) { ++ AtomicExchange64(*this, nullptr, sync, mem, value, output); ++} ++ ++void MacroAssembler::atomicExchangeJS(Scalar::Type arrayType, ++ const Synchronization& sync, ++ const Address& mem, Register value, ++ Register valueTemp, Register offsetTemp, ++ Register maskTemp, Register temp, ++ AnyRegister output) { ++ AtomicExchangeJS(*this, arrayType, sync, mem, value, valueTemp, offsetTemp, ++ maskTemp, temp, output); ++} ++ ++void MacroAssembler::atomicExchangeJS(Scalar::Type arrayType, ++ const Synchronization& sync, ++ const BaseIndex& mem, Register value, ++ Register valueTemp, Register offsetTemp, ++ Register maskTemp, Register temp, ++ AnyRegister output) { ++ AtomicExchangeJS(*this, arrayType, sync, mem, value, valueTemp, offsetTemp, ++ maskTemp, temp, output); ++} ++ ++void MacroAssembler::atomicExchange(Scalar::Type type, ++ const Synchronization& sync, ++ const Address& mem, Register value, ++ Register valueTemp, Register offsetTemp, ++ Register maskTemp, Register output) { ++ AtomicExchange(*this, nullptr, type, sync, mem, value, valueTemp, offsetTemp, ++ maskTemp, output); ++} ++ ++void MacroAssembler::atomicExchange(Scalar::Type type, ++ const Synchronization& sync, ++ const BaseIndex& mem, Register value, ++ Register valueTemp, Register offsetTemp, ++ Register maskTemp, Register output) { ++ AtomicExchange(*this, nullptr, type, sync, mem, value, valueTemp, offsetTemp, ++ maskTemp, output); ++} ++ ++void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType, ++ const Synchronization& sync, AtomicOp op, ++ Register value, const Address& mem, ++ Register valueTemp, Register offsetTemp, ++ Register maskTemp, Register temp, ++ AnyRegister output) { ++ AtomicFetchOpJS(*this, arrayType, sync, op, value, mem, valueTemp, offsetTemp, ++ maskTemp, temp, output); ++} ++ ++void MacroAssembler::atomicFetchOpJS(Scalar::Type arrayType, ++ const Synchronization& sync, AtomicOp op, ++ Register value, const BaseIndex& mem, ++ Register valueTemp, Register offsetTemp, ++ Register maskTemp, Register temp, ++ AnyRegister output) { ++ AtomicFetchOpJS(*this, arrayType, sync, op, value, mem, valueTemp, offsetTemp, ++ maskTemp, temp, output); ++} ++ ++void MacroAssembler::atomicFetchOp(Scalar::Type type, ++ const Synchronization& sync, AtomicOp op, ++ Register value, const Address& mem, ++ Register valueTemp, Register offsetTemp, ++ Register maskTemp, Register output) { ++ AtomicFetchOp(*this, nullptr, type, sync, op, mem, value, valueTemp, ++ offsetTemp, maskTemp, output); ++} ++ ++void MacroAssembler::atomicFetchOp(Scalar::Type type, ++ const Synchronization& sync, AtomicOp op, ++ Register value, const BaseIndex& mem, ++ Register valueTemp, Register offsetTemp, ++ Register maskTemp, Register output) { ++ AtomicFetchOp(*this, nullptr, type, sync, op, mem, value, valueTemp, ++ offsetTemp, maskTemp, output); ++} ++void MacroAssembler::branchPtrInNurseryChunk(Condition cond, Register ptr, ++ Register temp, Label* label) { ++ MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); ++ MOZ_ASSERT(ptr != temp); ++ MOZ_ASSERT(ptr != ScratchRegister); // Both may be used internally. ++ MOZ_ASSERT(temp != ScratchRegister); ++ MOZ_ASSERT(temp != InvalidReg); ++ ++ movePtr(ptr, temp); ++ orPtr(Imm32(gc::ChunkMask), temp); ++ branchPtr(InvertCondition(cond), ++ Address(temp, gc::ChunkStoreBufferOffsetFromLastByte), zero, label); ++} ++void MacroAssembler::branchTestValue(Condition cond, const ValueOperand& lhs, ++ const Value& rhs, Label* label) { ++ MOZ_ASSERT(cond == Equal || cond == NotEqual); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ MOZ_ASSERT(lhs.valueReg() != scratch); ++ moveValue(rhs, ValueOperand(scratch)); ++ ma_b(lhs.valueReg(), scratch, label, cond); ++} ++void MacroAssembler::branchValueIsNurseryCell(Condition cond, ++ const Address& address, ++ Register temp, Label* label) { ++ branchValueIsNurseryCellImpl(cond, address, temp, label); ++} ++ ++void MacroAssembler::branchValueIsNurseryCell(Condition cond, ++ ValueOperand value, Register temp, ++ Label* label) { ++ branchValueIsNurseryCellImpl(cond, value, temp, label); ++} ++void MacroAssembler::call(const Address& addr) { ++ UseScratchRegisterScope temps(this); ++ temps.Exclude(GeneralRegisterSet(1 << CallReg.code())); ++ loadPtr(addr, CallReg); ++ call(CallReg); ++} ++void MacroAssembler::call(ImmPtr target) { ++ BufferOffset bo = m_buffer.nextOffset(); ++ addPendingJump(bo, target, RelocationKind::HARDCODED); ++ ma_call(target); ++} ++void MacroAssembler::call(ImmWord target) { call(ImmPtr((void*)target.value)); } ++ ++void MacroAssembler::call(JitCode* c) { ++ DEBUG_PRINTF("[ %s\n", __FUNCTION__); ++ BlockTrampolinePoolScope block_trampoline_pool(this, 8); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ BufferOffset bo = m_buffer.nextOffset(); ++ addPendingJump(bo, ImmPtr(c->raw()), RelocationKind::JITCODE); ++ ma_liPatchable(scratch, ImmPtr(c->raw())); ++ callJitNoProfiler(scratch); ++ DEBUG_PRINTF("]\n"); ++} ++ ++void MacroAssembler::callWithABIPre(uint32_t* stackAdjust, bool callFromWasm) { ++ MOZ_ASSERT(inCall_); ++ uint32_t stackForCall = abiArgs_.stackBytesConsumedSoFar(); ++ ++ // Reserve place for $ra. ++ stackForCall += sizeof(intptr_t); ++ ++ if (dynamicAlignment_) { ++ stackForCall += ComputeByteAlignment(stackForCall, ABIStackAlignment); ++ } else { ++ uint32_t alignmentAtPrologue = callFromWasm ? sizeof(wasm::Frame) : 0; ++ stackForCall += ComputeByteAlignment( ++ stackForCall + framePushed() + alignmentAtPrologue, ABIStackAlignment); ++ } ++ ++ *stackAdjust = stackForCall; ++ reserveStack(stackForCall); ++ ++ // Save $ra because call is going to clobber it. Restore it in ++ // callWithABIPost. NOTE: This is needed for calls from SharedIC. ++ // Maybe we can do this differently. ++ storePtr(ra, Address(StackPointer, stackForCall - sizeof(intptr_t))); ++ ++ // Position all arguments. ++ { ++ enoughMemory_ &= moveResolver_.resolve(); ++ if (!enoughMemory_) { ++ return; ++ } ++ ++ MoveEmitter emitter(asMasm()); ++ emitter.emit(moveResolver_); ++ emitter.finish(); ++ } ++ ++ assertStackAlignment(ABIStackAlignment); ++} ++ ++void MacroAssembler::callWithABIPost(uint32_t stackAdjust, MoveOp::Type result, ++ bool callFromWasm) { ++ // Restore ra value (as stored in callWithABIPre()). ++ loadPtr(Address(StackPointer, stackAdjust - sizeof(intptr_t)), ra); ++ ++ if (dynamicAlignment_) { ++ // Restore sp value from stack (as stored in setupUnalignedABICall()). ++ loadPtr(Address(StackPointer, stackAdjust), StackPointer); ++ // Use adjustFrame instead of freeStack because we already restored sp. ++ adjustFrame(-stackAdjust); ++ } else { ++ freeStack(stackAdjust); ++ } ++ ++#ifdef DEBUG ++ MOZ_ASSERT(inCall_); ++ inCall_ = false; ++#endif ++} ++ ++void MacroAssembler::callWithABINoProfiler(Register fun, MoveOp::Type result) { ++ // Load the callee in scratch2, no instruction between the movePtr and ++ // call should clobber it. Note that we can't use fun because it may be ++ // one of the IntArg registers clobbered before the call. ++ UseScratchRegisterScope temps(this); ++ temps.Exclude(GeneralRegisterSet(1 << CallReg.code())); ++ movePtr(fun, CallReg); ++ ++ uint32_t stackAdjust; ++ callWithABIPre(&stackAdjust); ++ call(CallReg); ++ callWithABIPost(stackAdjust, result); ++} ++ ++void MacroAssembler::callWithABINoProfiler(const Address& fun, ++ MoveOp::Type result) { ++ // Load the callee in scratch2, as above. ++ UseScratchRegisterScope temps(this); ++ temps.Exclude(GeneralRegisterSet(1 << CallReg.code())); ++ loadPtr(fun, CallReg); ++ ++ uint32_t stackAdjust; ++ callWithABIPre(&stackAdjust); ++ call(CallReg); ++ callWithABIPost(stackAdjust, result); ++} ++ ++void MacroAssembler::ceilDoubleToInt32(FloatRegister src, Register dest, ++ Label* fail) { ++ UseScratchRegisterScope temps(this); ++ ScratchDoubleScope fscratch(*this); ++ Label performCeil, done; ++ // If x < -1 or x > 0 then perform ceil. ++ loadConstantDouble(0, fscratch); ++ branchDouble(Assembler::DoubleGreaterThan, src, fscratch, &performCeil); ++ loadConstantDouble(-1.0, fscratch); ++ branchDouble(Assembler::DoubleLessThanOrEqual, src, fscratch, &performCeil); ++ ++ Register scratch = temps.Acquire(); ++ // If binary value is not zero, the input was not 0, so we bail. ++ { ++ moveFromDoubleHi(src, scratch); ++ branch32(Assembler::NotEqual, scratch, zero, fail); ++ } ++ ++ bind(&performCeil); ++ Ceil_w_d(dest, src, scratch); ++ ma_b(scratch, Imm32(1), fail, NotEqual); ++ bind(&done); ++} ++ ++void MacroAssembler::ceilFloat32ToInt32(FloatRegister src, Register dest, ++ Label* fail) { ++ UseScratchRegisterScope temps(this); ++ ScratchDoubleScope fscratch(*this); ++ Label performCeil, done; ++ // If x < -1 or x > 0 then perform ceil. ++ loadConstantFloat32(0, fscratch); ++ branchFloat(Assembler::DoubleGreaterThan, src, fscratch, &performCeil); ++ loadConstantFloat32(-1.0, fscratch); ++ branchFloat(Assembler::DoubleLessThanOrEqual, src, fscratch, &performCeil); ++ ++ Register scratch = temps.Acquire(); ++ // If binary value is not zero, the input was not 0, so we bail. ++ { ++ fmv_x_w(scratch, src); ++ branch32(Assembler::NotEqual, scratch, zero, fail); ++ } ++ bind(&performCeil); ++ Ceil_w_s(dest, src, scratch); ++ ma_b(scratch, Imm32(1), fail, NotEqual); ++ bind(&done); ++} ++void MacroAssembler::comment(const char* msg) { Assembler::comment(msg); } ++ ++template ++static void CompareExchange64(MacroAssembler& masm, ++ const wasm::MemoryAccessDesc* access, ++ const Synchronization& sync, const T& mem, ++ Register64 expect, Register64 replace, ++ Register64 output) { ++ MOZ_ASSERT(expect != output && replace != output); ++ ScratchRegisterScope scratch(masm); ++ UseScratchRegisterScope temps(&masm); ++ Register scratch2 = temps.Acquire(); ++ masm.computeEffectiveAddress(mem, scratch); ++ ++ Label tryAgain; ++ Label exit; ++ ++ masm.memoryBarrierBefore(sync); ++ ++ masm.bind(&tryAgain); ++ ++ if (access) { ++ masm.append(*access, masm.size()); ++ } ++ ++ masm.lr_d(true, true, output.reg, scratch); ++ ++ masm.ma_b(output.reg, expect.reg, &exit, Assembler::NotEqual, ShortJump); ++ masm.movePtr(replace.reg, scratch2); ++ masm.sc_d(true, true, scratch2, scratch, scratch2); ++ masm.ma_b(scratch2, Register(scratch2), &tryAgain, Assembler::NonZero, ++ ShortJump); ++ ++ masm.memoryBarrierAfter(sync); ++ ++ masm.bind(&exit); ++} ++ ++void MacroAssembler::compareExchange64(const Synchronization& sync, ++ const Address& mem, Register64 expect, ++ Register64 replace, Register64 output) { ++ CompareExchange64(*this, nullptr, sync, mem, expect, replace, output); ++} ++ ++void MacroAssembler::compareExchange64(const Synchronization& sync, ++ const BaseIndex& mem, Register64 expect, ++ Register64 replace, Register64 output) { ++ CompareExchange64(*this, nullptr, sync, mem, expect, replace, output); ++} ++ ++void MacroAssembler::compareExchangeJS(Scalar::Type arrayType, ++ const Synchronization& sync, ++ const Address& mem, Register oldval, ++ Register newval, Register valueTemp, ++ Register offsetTemp, Register maskTemp, ++ Register temp, AnyRegister output) { ++ CompareExchangeJS(*this, arrayType, sync, mem, oldval, newval, valueTemp, ++ offsetTemp, maskTemp, temp, output); ++} ++ ++void MacroAssembler::compareExchangeJS(Scalar::Type arrayType, ++ const Synchronization& sync, ++ const BaseIndex& mem, Register oldval, ++ Register newval, Register valueTemp, ++ Register offsetTemp, Register maskTemp, ++ Register temp, AnyRegister output) { ++ CompareExchangeJS(*this, arrayType, sync, mem, oldval, newval, valueTemp, ++ offsetTemp, maskTemp, temp, output); ++} ++ ++void MacroAssembler::convertInt64ToDouble(Register64 src, FloatRegister dest) { ++ fcvt_d_l(dest, src.scratchReg()); ++} ++void MacroAssembler::convertInt64ToFloat32(Register64 src, FloatRegister dest) { ++ fcvt_s_l(dest, src.scratchReg()); ++} ++void MacroAssembler::convertIntPtrToDouble(Register src, FloatRegister dest) { ++ fcvt_d_l(dest, src); ++} ++void MacroAssembler::convertUInt64ToDouble(Register64 src, FloatRegister dest, ++ Register tmp) { ++ fcvt_d_lu(dest, src.scratchReg()); ++} ++void MacroAssembler::convertUInt64ToFloat32(Register64 src, FloatRegister dest, ++ Register tmp) { ++ fcvt_s_lu(dest, src.scratchReg()); ++} ++void MacroAssembler::copySignDouble(FloatRegister lhs, FloatRegister rhs, ++ FloatRegister output) { ++ fsgnj_d(output, lhs, rhs); ++} ++void MacroAssembler::enterFakeExitFrameForWasm(Register cxreg, Register scratch, ++ ExitFrameType type) { ++ enterFakeExitFrame(cxreg, scratch, type); ++} ++void MacroAssembler::flexibleDivMod32(Register rhs, Register srcDest, ++ Register remOutput, bool isUnsigned, ++ const LiveRegisterSet&) { ++ if (isUnsigned) { ++ ma_modu32(remOutput, srcDest, rhs); ++ ma_divu32(srcDest, srcDest, rhs); ++ } else { ++ ma_mod32(remOutput, srcDest, rhs); ++ ma_div32(srcDest, srcDest, rhs); ++ } ++} ++void MacroAssembler::flexibleQuotient32(Register rhs, Register srcDest, ++ bool isUnsigned, ++ const LiveRegisterSet&) { ++ quotient32(rhs, srcDest, isUnsigned); ++} ++ ++void MacroAssembler::flexibleRemainder32(Register rhs, Register srcDest, ++ bool isUnsigned, ++ const LiveRegisterSet&) { ++ remainder32(rhs, srcDest, isUnsigned); ++} ++ ++void MacroAssembler::floorDoubleToInt32(FloatRegister src, Register dest, ++ Label* fail) { ++ JitSpew(JitSpew_Codegen, "[ %s", __FUNCTION__); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ Floor_w_d(dest, src, scratch); ++ ma_b(scratch, Imm32(1), fail, NotEqual); ++ fmv_x_d(scratch, src); ++ ma_branch(fail, Equal, scratch, Operand(0x8000000000000000)); ++ JitSpew(JitSpew_Codegen, "]"); ++} ++void MacroAssembler::floorFloat32ToInt32(FloatRegister src, Register dest, ++ Label* fail) { ++ JitSpew(JitSpew_Codegen, "[ %s", __FUNCTION__); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ Floor_w_s(dest, src, scratch); ++ ma_b(scratch, Imm32(1), fail, NotEqual); ++ fmv_x_w(scratch, src); ++ ma_branch(fail, Equal, scratch, Operand(int32_t(0x80000000))); ++ JitSpew(JitSpew_Codegen, "]"); ++} ++void MacroAssembler::flush() {} ++void MacroAssembler::loadStoreBuffer(Register ptr, Register buffer) { ++ if (ptr != buffer) { ++ movePtr(ptr, buffer); ++ } ++ orPtr(Imm32(gc::ChunkMask), buffer); ++ loadPtr(Address(buffer, gc::ChunkStoreBufferOffsetFromLastByte), buffer); ++} ++ ++void MacroAssembler::moveValue(const TypedOrValueRegister& src, ++ const ValueOperand& dest) { ++ if (src.hasValue()) { ++ moveValue(src.valueReg(), dest); ++ return; ++ } ++ ++ MIRType type = src.type(); ++ AnyRegister reg = src.typedReg(); ++ ++ if (!IsFloatingPointType(type)) { ++ boxNonDouble(ValueTypeFromMIRType(type), reg.gpr(), dest); ++ return; ++ } ++ ++ ScratchDoubleScope fpscratch(asMasm()); ++ FloatRegister scratch = fpscratch; ++ FloatRegister freg = reg.fpu(); ++ if (type == MIRType::Float32) { ++ convertFloat32ToDouble(freg, scratch); ++ freg = scratch; ++ } ++ boxDouble(freg, dest, scratch); ++} ++void MacroAssembler::moveValue(const ValueOperand& src, ++ const ValueOperand& dest) { ++ if (src == dest) { ++ return; ++ } ++ movePtr(src.valueReg(), dest.valueReg()); ++} ++ ++void MacroAssembler::moveValue(const Value& src, const ValueOperand& dest) { ++ if (!src.isGCThing()) { ++ ma_li(dest.valueReg(), ImmWord(src.asRawBits())); ++ return; ++ } ++ ++ writeDataRelocation(src); ++ movWithPatch(ImmWord(src.asRawBits()), dest.valueReg()); ++} ++void MacroAssembler::nearbyIntDouble(RoundingMode, FloatRegister, ++ FloatRegister) { ++ MOZ_CRASH("not supported on this platform"); ++} ++void MacroAssembler::nearbyIntFloat32(RoundingMode, FloatRegister, ++ FloatRegister) { ++ MOZ_CRASH("not supported on this platform"); ++} ++ ++void MacroAssembler::oolWasmTruncateCheckF32ToI32(FloatRegister input, ++ Register output, ++ TruncFlags flags, ++ wasm::BytecodeOffset off, ++ Label* rejoin) { ++ Label notNaN; ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ CompareIsNotNanF32(scratch, input, input); ++ ma_branch(¬NaN, Equal, scratch, Operand(1)); ++ wasmTrap(wasm::Trap::InvalidConversionToInteger, off); ++ bind(¬NaN); ++ ++ Label isOverflow; ++ const float two_31 = -float(INT32_MIN); ++ ScratchFloat32Scope fpscratch(*this); ++ if (flags & TRUNC_UNSIGNED) { ++ loadConstantFloat32(two_31 * 2, fpscratch); ++ ma_compareF32(scratch, Assembler::DoubleGreaterThanOrEqual, input, ++ fpscratch); ++ ma_branch(&isOverflow, Equal, scratch, Operand(1)); ++ loadConstantFloat32(-1.0f, fpscratch); ++ ma_compareF32(scratch, Assembler::DoubleGreaterThan, input, fpscratch); ++ ma_b(scratch, Imm32(1), rejoin, Equal); ++ } else { ++ loadConstantFloat32(two_31, fpscratch); ++ ma_compareF32(scratch, Assembler::DoubleGreaterThanOrEqual, input, ++ fpscratch); ++ ma_branch(&isOverflow, Equal, scratch, Operand(1)); ++ loadConstantFloat32(-two_31, fpscratch); ++ ma_compareF32(scratch, Assembler::DoubleGreaterThanOrEqual, input, ++ fpscratch); ++ ma_b(scratch, Imm32(1), rejoin, Equal); ++ } ++ bind(&isOverflow); ++ wasmTrap(wasm::Trap::IntegerOverflow, off); ++} ++ ++void MacroAssembler::oolWasmTruncateCheckF64ToI32(FloatRegister input, ++ Register output, ++ TruncFlags flags, ++ wasm::BytecodeOffset off, ++ Label* rejoin) { ++ Label notNaN; ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ CompareIsNotNanF64(scratch, input, input); ++ ma_branch(¬NaN, Equal, scratch, Operand(1)); ++ wasmTrap(wasm::Trap::InvalidConversionToInteger, off); ++ bind(¬NaN); ++ ++ Label isOverflow; ++ const double two_31 = -double(INT32_MIN); ++ ScratchDoubleScope fpscratch(*this); ++ if (flags & TRUNC_UNSIGNED) { ++ loadConstantDouble(two_31 * 2, fpscratch); ++ ma_compareF64(scratch, Assembler::DoubleGreaterThanOrEqual, input, ++ fpscratch); ++ ma_branch(&isOverflow, Equal, scratch, Operand(1)); ++ loadConstantDouble(-1.0, fpscratch); ++ ma_compareF64(scratch, Assembler::DoubleGreaterThan, input, fpscratch); ++ ma_b(scratch, Imm32(1), rejoin, Equal); ++ } else { ++ loadConstantDouble(two_31, fpscratch); ++ ma_compareF64(scratch, Assembler::DoubleGreaterThanOrEqual, input, ++ fpscratch); ++ ma_branch(&isOverflow, Equal, scratch, Operand(1)); ++ loadConstantDouble(-two_31 - 1, fpscratch); ++ ma_compareF64(scratch, Assembler::DoubleGreaterThan, input, fpscratch); ++ ma_b(scratch, Imm32(1), rejoin, Equal); ++ } ++ bind(&isOverflow); ++ wasmTrap(wasm::Trap::IntegerOverflow, off); ++} ++ ++void MacroAssembler::oolWasmTruncateCheckF32ToI64(FloatRegister input, ++ Register64 output, ++ TruncFlags flags, ++ wasm::BytecodeOffset off, ++ Label* rejoin) { ++ Label notNaN; ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ CompareIsNotNanF32(scratch, input, input); ++ ma_branch(¬NaN, Equal, scratch, Operand(1)); ++ wasmTrap(wasm::Trap::InvalidConversionToInteger, off); ++ bind(¬NaN); ++ ++ Label isOverflow; ++ const float two_63 = -float(INT64_MIN); ++ ScratchFloat32Scope fpscratch(*this); ++ if (flags & TRUNC_UNSIGNED) { ++ loadConstantFloat32(two_63 * 2, fpscratch); ++ ma_compareF32(scratch, Assembler::DoubleGreaterThanOrEqual, input, ++ fpscratch); ++ ma_branch(&isOverflow, Equal, scratch, Operand(1)); ++ loadConstantFloat32(-1.0f, fpscratch); ++ ma_compareF32(scratch, Assembler::DoubleGreaterThan, input, fpscratch); ++ ma_b(scratch, Imm32(1), rejoin, Equal); ++ } else { ++ loadConstantFloat32(two_63, fpscratch); ++ ma_compareF32(scratch, Assembler::DoubleGreaterThanOrEqual, input, ++ fpscratch); ++ ma_branch(&isOverflow, Equal, scratch, Operand(1)); ++ loadConstantFloat32(-two_63, fpscratch); ++ ma_compareF32(scratch, Assembler::DoubleGreaterThanOrEqual, input, ++ fpscratch); ++ ma_b(scratch, Imm32(1), rejoin, Equal); ++ } ++ bind(&isOverflow); ++ wasmTrap(wasm::Trap::IntegerOverflow, off); ++} ++ ++void MacroAssembler::oolWasmTruncateCheckF64ToI64(FloatRegister input, ++ Register64 output, ++ TruncFlags flags, ++ wasm::BytecodeOffset off, ++ Label* rejoin) { ++ Label notNaN; ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ CompareIsNotNanF64(scratch, input, input); ++ ma_branch(¬NaN, Equal, scratch, Operand(1)); ++ wasmTrap(wasm::Trap::InvalidConversionToInteger, off); ++ bind(¬NaN); ++ ++ Label isOverflow; ++ const double two_63 = -double(INT64_MIN); ++ ScratchDoubleScope fpscratch(*this); ++ if (flags & TRUNC_UNSIGNED) { ++ loadConstantDouble(two_63 * 2, fpscratch); ++ ma_compareF64(scratch, Assembler::DoubleGreaterThanOrEqual, input, ++ fpscratch); ++ ma_branch(&isOverflow, Equal, scratch, Operand(1)); ++ loadConstantDouble(-1.0, fpscratch); ++ ma_compareF64(scratch, Assembler::DoubleGreaterThan, input, fpscratch); ++ ma_b(scratch, Imm32(1), rejoin, Equal); ++ } else { ++ loadConstantDouble(two_63, fpscratch); ++ ma_compareF64(scratch, Assembler::DoubleGreaterThanOrEqual, input, ++ fpscratch); ++ ma_branch(&isOverflow, Equal, scratch, Operand(1)); ++ loadConstantDouble(-two_63, fpscratch); ++ ma_compareF64(scratch, Assembler::DoubleGreaterThan, input, fpscratch); ++ ma_b(scratch, Imm32(1), rejoin, Equal); ++ } ++ bind(&isOverflow); ++ wasmTrap(wasm::Trap::IntegerOverflow, off); ++} ++void MacroAssembler::patchCallToNop(uint8_t* call) { ++ uint32_t* p = reinterpret_cast(call) - 7; ++ *reinterpret_cast(p) = kNopByte; ++ *reinterpret_cast(p + 1) = kNopByte; ++ *reinterpret_cast(p + 2) = kNopByte; ++ *reinterpret_cast(p + 3) = kNopByte; ++ *reinterpret_cast(p + 4) = kNopByte; ++ *reinterpret_cast(p + 5) = kNopByte; ++ *reinterpret_cast(p + 6) = kNopByte; ++} ++ ++CodeOffset MacroAssembler::callWithPatch() { ++ BlockTrampolinePoolScope block_trampoline_pool(this, 2); ++ DEBUG_PRINTF("\tcallWithPatch\n"); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ int32_t imm32 = 1 * sizeof(uint32_t); ++ int32_t Hi20 = ((imm32 + 0x800) >> 12); ++ int32_t Lo12 = imm32 << 20 >> 20; ++ auipc(scratch, Hi20); // Read PC + Hi20 into scratch. ++ jalr(scratch, Lo12); // jump PC + Hi20 + Lo12 ++ DEBUG_PRINTF("\tret %d\n", currentOffset()); ++ return CodeOffset(currentOffset()); ++} ++ ++void MacroAssembler::patchCall(uint32_t callerOffset, uint32_t calleeOffset) { ++ DEBUG_PRINTF("\tpatchCall\n"); ++ BufferOffset call(callerOffset - 2 * sizeof(uint32_t)); ++ DEBUG_PRINTF("\tcallerOffset %d\n", callerOffset); ++ int32_t offset = BufferOffset(calleeOffset).getOffset() - call.getOffset(); ++ if (is_int32(offset)) { ++ Instruction* auipc_ = (Instruction*)editSrc(call); ++ Instruction* jalr_ = (Instruction*)editSrc( ++ BufferOffset(callerOffset - 1 * sizeof(uint32_t))); ++ DEBUG_PRINTF("\t%p %lu\n\t", auipc_, callerOffset - 2 * sizeof(uint32_t)); ++ disassembleInstr(auipc_->InstructionBits()); ++ DEBUG_PRINTF("\t%p %lu\n\t", jalr_, callerOffset - 1 * sizeof(uint32_t)); ++ disassembleInstr(jalr_->InstructionBits()); ++ DEBUG_PRINTF("\t\n"); ++ MOZ_ASSERT(IsJalr(jalr_->InstructionBits()) && ++ IsAuipc(auipc_->InstructionBits())); ++ MOZ_ASSERT(auipc_->RdValue() == jalr_->Rs1Value()); ++ int32_t Hi20 = (((int32_t)offset + 0x800) >> 12); ++ int32_t Lo12 = (int32_t)offset << 20 >> 20; ++ instr_at_put(call, SetAuipcOffset(Hi20, auipc_->InstructionBits())); ++ instr_at_put(BufferOffset(callerOffset - 1 * sizeof(uint32_t)), ++ SetJalrOffset(Lo12, jalr_->InstructionBits())); ++ } else { ++ MOZ_CRASH(); ++ } ++} ++ ++void MacroAssembler::patchFarJump(CodeOffset farJump, uint32_t targetOffset) { ++ uint32_t* u32 = reinterpret_cast( ++ editSrc(BufferOffset(farJump.offset() + 4 * kInstrSize))); ++ MOZ_ASSERT(*u32 == UINT32_MAX); ++ *u32 = targetOffset - farJump.offset(); ++} ++ ++void MacroAssembler::patchNearAddressMove(CodeLocationLabel loc, ++ CodeLocationLabel target) { ++ PatchDataWithValueCheck(loc, ImmPtr(target.raw()), ImmPtr(nullptr)); ++} ++ ++void MacroAssembler::patchNopToCall(uint8_t* call, uint8_t* target) { ++ uint32_t* p = reinterpret_cast(call) - 7; ++ Assembler::WriteLoad64Instructions((Instruction*)p, ScratchRegister, ++ (uint64_t)target); ++ DEBUG_PRINTF("\tpatchNopToCall %lu %lu\n", (uint64_t)target, ++ ExtractLoad64Value((Instruction*)p)); ++ MOZ_ASSERT(ExtractLoad64Value((Instruction*)p) == (uint64_t)target); ++ Instr jalr_ = JALR | (ra.code() << kRdShift) | (0x0 << kFunct3Shift) | ++ (ScratchRegister.code() << kRs1Shift) | (0x0 << kImm12Shift); ++ *reinterpret_cast(p + 6) = jalr_; ++} ++void MacroAssembler::Pop(Register reg) { ++ ma_pop(reg); ++ adjustFrame(-int32_t(sizeof(intptr_t))); ++} ++ ++void MacroAssembler::Pop(FloatRegister f) { ++ ma_pop(f); ++ adjustFrame(-int32_t(sizeof(double))); ++} ++ ++void MacroAssembler::Pop(const ValueOperand& val) { ++ popValue(val); ++ adjustFrame(-int32_t(sizeof(Value))); ++} ++ ++void MacroAssembler::PopRegsInMaskIgnore(LiveRegisterSet set, ++ LiveRegisterSet ignore) { ++ int32_t diff = ++ set.gprs().size() * sizeof(intptr_t) + set.fpus().getPushSizeInBytes(); ++ const int32_t reserved = diff; ++ ++ for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); ++iter) { ++ diff -= sizeof(intptr_t); ++ if (!ignore.has(*iter)) { ++ loadPtr(Address(StackPointer, diff), *iter); ++ } ++ } ++ ++#ifdef ENABLE_WASM_SIMD ++# error "Needs more careful logic if SIMD is enabled" ++#endif ++ ++ for (FloatRegisterBackwardIterator iter(set.fpus().reduceSetForPush()); ++ iter.more(); ++iter) { ++ diff -= sizeof(double); ++ if (!ignore.has(*iter)) { ++ loadDouble(Address(StackPointer, diff), *iter); ++ } ++ } ++ MOZ_ASSERT(diff == 0); ++ freeStack(reserved); ++} ++ ++void MacroAssembler::pushReturnAddress() { push(ra); } ++ ++void MacroAssembler::popReturnAddress() { pop(ra); } ++void MacroAssembler::PopStackPtr() { ++ loadPtr(Address(StackPointer, 0), StackPointer); ++ adjustFrame(-int32_t(sizeof(intptr_t))); ++} ++void MacroAssembler::PushBoxed(FloatRegister reg) { ++ subFromStackPtr(Imm32(sizeof(double))); ++ boxDouble(reg, Address(getStackPointer(), 0)); ++ adjustFrame(sizeof(double)); ++} ++ ++void MacroAssembler::Push(Register reg) { ++ ma_push(reg); ++ adjustFrame(int32_t(sizeof(intptr_t))); ++} ++ ++void MacroAssembler::Push(const Imm32 imm) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ ma_li(scratch, imm); ++ ma_push(scratch); ++ adjustFrame(int32_t(sizeof(intptr_t))); ++} ++ ++void MacroAssembler::Push(const ImmWord imm) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ ma_li(scratch, imm); ++ ma_push(scratch); ++ adjustFrame(int32_t(sizeof(intptr_t))); ++} ++ ++void MacroAssembler::Push(const ImmPtr imm) { ++ Push(ImmWord(uintptr_t(imm.value))); ++} ++ ++void MacroAssembler::Push(const ImmGCPtr ptr) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ ma_li(scratch, ptr); ++ ma_push(scratch); ++ adjustFrame(int32_t(sizeof(intptr_t))); ++} ++ ++void MacroAssembler::Push(FloatRegister f) { ++ ma_push(f); ++ adjustFrame(int32_t(sizeof(double))); ++} ++ ++void MacroAssembler::PushRegsInMask(LiveRegisterSet set) { ++ int32_t diff = ++ set.gprs().size() * sizeof(intptr_t) + set.fpus().getPushSizeInBytes(); ++ const int32_t reserved = diff; ++ ++ reserveStack(reserved); ++ for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); ++iter) { ++ diff -= sizeof(intptr_t); ++ storePtr(*iter, Address(StackPointer, diff)); ++ } ++ ++#ifdef ENABLE_WASM_SIMD ++# error "Needs more careful logic if SIMD is enabled" ++#endif ++ ++ for (FloatRegisterBackwardIterator iter(set.fpus().reduceSetForPush()); ++ iter.more(); ++iter) { ++ diff -= sizeof(double); ++ storeDouble(*iter, Address(StackPointer, diff)); ++ } ++ MOZ_ASSERT(diff == 0); ++} ++ ++void MacroAssembler::roundFloat32ToInt32(FloatRegister src, Register dest, ++ FloatRegister temp, Label* fail) { ++ JitSpew(JitSpew_Codegen, "[ %s", __FUNCTION__); ++ ScratchDoubleScope fscratch(*this); ++ Label negative, done; ++ // Branch to a slow path if input < 0.0 due to complicated rounding rules. ++ // Note that Fcmp with NaN unsets the negative flag. ++ { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ fmv_x_w(scratch, src); ++ ma_branch(fail, Equal, scratch, Operand(int32_t(0x80000000))); ++ fmv_w_x(temp, zero); ++ ma_compareF32(scratch, DoubleLessThan, src, temp); ++ ma_branch(&negative, Equal, scratch, Operand(1)); ++ } ++ // Handle the simple case of a positive input, and also -0 and NaN. ++ // Rounding proceeds with consideration of the fractional part of the input: ++ // 1. If > 0.5, round to integer with higher absolute value (so, up). ++ // 2. If < 0.5, round to integer with lower absolute value (so, down). ++ // 3. If = 0.5, round to +Infinity (so, up). ++ { ++ // Convert to signed 32-bit integer, rounding halfway cases away from zero. ++ // In the case of overflow, the output is saturated. ++ // In the case of NaN and -0, the output is zero. ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ RoundFloatingPointToInteger( ++ dest, src, scratch, ++ [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { ++ masm->fcvt_w_s(dst, src, RMM); ++ }, ++ false); ++ ma_b(scratch, Imm32(1), fail, NotEqual); ++ jump(&done); ++ } ++ ++ // Handle the complicated case of a negative input. ++ // Rounding proceeds with consideration of the fractional part of the input: ++ // 1. If > 0.5, round to integer with higher absolute value (so, down). ++ // 2. If < 0.5, round to integer with lower absolute value (so, up). ++ // 3. If = 0.5, round to +Infinity (so, up). ++ bind(&negative); ++ { ++ // Inputs in [-0.5, 0) need 0.5 added; other negative inputs need ++ // the biggest double less than 0.5. ++ Label join; ++ loadConstantFloat32(GetBiggestNumberLessThan(0.5), temp); ++ loadConstantFloat32(-0.5, fscratch); ++ branchFloat(Assembler::DoubleLessThan, src, fscratch, &join); ++ loadConstantFloat32(0.5, temp); ++ bind(&join); ++ addFloat32(src, temp); ++ // Round all values toward -Infinity. ++ // In the case of overflow, the output is saturated. ++ // NaN and -0 are already handled by the "positive number" path above. ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ RoundFloatingPointToInteger( ++ dest, temp, scratch, ++ [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { ++ masm->fcvt_w_s(dst, src, RDN); ++ }, ++ false); ++ ma_b(scratch, Imm32(1), fail, NotEqual); ++ // If output is zero, then the actual result is -0. Fail. ++ branchTest32(Assembler::Zero, dest, dest, fail); ++ } ++ bind(&done); ++ JitSpew(JitSpew_Codegen, "]"); ++} ++ ++void MacroAssembler::roundDoubleToInt32(FloatRegister src, Register dest, ++ FloatRegister temp, Label* fail) { ++ JitSpew(JitSpew_Codegen, "[ %s", __FUNCTION__); ++ ++ ScratchDoubleScope fscratch(*this); ++ Label negative, done; ++ // Branch to a slow path if input < 0.0 due to complicated rounding rules. ++ // Note that Fcmp with NaN unsets the negative flag. ++ { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ fmv_x_d(scratch, src); ++ ma_branch(fail, Equal, scratch, Operand(0x8000000000000000)); ++ fmv_d_x(temp, zero); ++ ma_compareF64(scratch, DoubleLessThan, src, temp); ++ ma_branch(&negative, Equal, scratch, Operand(1)); ++ } ++ // Handle the simple case of a positive input, and also -0 and NaN. ++ // Rounding proceeds with consideration of the fractional part of the input: ++ // 1. If > 0.5, round to integer with higher absolute value (so, up). ++ // 2. If < 0.5, round to integer with lower absolute value (so, down). ++ // 3. If = 0.5, round to +Infinity (so, up). ++ { ++ // Convert to signed 32-bit integer, rounding halfway cases away from zero. ++ // In the case of overflow, the output is saturated. ++ // In the case of NaN and -0, the output is zero. ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ RoundFloatingPointToInteger( ++ dest, src, scratch, ++ [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { ++ masm->fcvt_w_d(dst, src, RMM); ++ }, ++ false); ++ ma_b(scratch, Imm32(1), fail, NotEqual); ++ jump(&done); ++ } ++ ++ // Handle the complicated case of a negative input. ++ // Rounding proceeds with consideration of the fractional part of the input: ++ // 1. If > 0.5, round to integer with higher absolute value (so, down). ++ // 2. If < 0.5, round to integer with lower absolute value (so, up). ++ // 3. If = 0.5, round to +Infinity (so, up). ++ bind(&negative); ++ { ++ // Inputs in [-0.5, 0) need 0.5 added; other negative inputs need ++ // the biggest double less than 0.5. ++ Label join; ++ loadConstantDouble(GetBiggestNumberLessThan(0.5), temp); ++ loadConstantDouble(-0.5, fscratch); ++ branchDouble(Assembler::DoubleLessThan, src, fscratch, &join); ++ loadConstantDouble(0.5, temp); ++ bind(&join); ++ addDouble(src, temp); ++ // Round all values toward -Infinity. ++ // In the case of overflow, the output is saturated. ++ // NaN and -0 are already handled by the "positive number" path above. ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ RoundFloatingPointToInteger( ++ dest, temp, scratch, ++ [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { ++ masm->fcvt_w_d(dst, src, RDN); ++ }, ++ false); ++ ma_b(scratch, Imm32(1), fail, NotEqual); ++ // If output is zero, then the actual result is -0. Fail. ++ branchTest32(Assembler::Zero, dest, dest, fail); ++ } ++ bind(&done); ++ JitSpew(JitSpew_Codegen, "]"); ++} ++ ++void MacroAssembler::setupUnalignedABICall(Register scratch) { ++ MOZ_ASSERT(!IsCompilingWasm(), "wasm should only use aligned ABI calls"); ++ setupNativeABICall(); ++ dynamicAlignment_ = true; ++ ++ or_(scratch, StackPointer, zero); ++ ++ // Force sp to be aligned ++ asMasm().subPtr(Imm32(sizeof(uintptr_t)), StackPointer); ++ ma_and(StackPointer, StackPointer, Imm32(~(ABIStackAlignment - 1))); ++ storePtr(scratch, Address(StackPointer, 0)); ++} ++void MacroAssembler::shiftIndex32AndAdd(Register indexTemp32, int shift, ++ Register pointer) { ++ if (IsShiftInScaleRange(shift)) { ++ computeEffectiveAddress( ++ BaseIndex(pointer, indexTemp32, ShiftToScale(shift)), pointer); ++ return; ++ } ++ lshift32(Imm32(shift), indexTemp32); ++ addPtr(indexTemp32, pointer); ++} ++void MacroAssembler::speculationBarrier() { MOZ_CRASH(); } ++void MacroAssembler::storeRegsInMask(LiveRegisterSet set, Address dest, ++ Register) { ++ FloatRegisterSet fpuSet(set.fpus().reduceSetForPush()); ++ unsigned numFpu = fpuSet.size(); ++ int32_t diffF = fpuSet.getPushSizeInBytes(); ++ int32_t diffG = set.gprs().size() * sizeof(intptr_t); ++ ++ MOZ_ASSERT(dest.offset >= diffG + diffF); ++ ++ for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); ++iter) { ++ diffG -= sizeof(intptr_t); ++ dest.offset -= sizeof(intptr_t); ++ storePtr(*iter, dest); ++ } ++ MOZ_ASSERT(diffG == 0); ++ ++#ifdef ENABLE_WASM_SIMD ++# error "Needs more careful logic if SIMD is enabled" ++#endif ++ ++ for (FloatRegisterBackwardIterator iter(fpuSet); iter.more(); ++iter) { ++ FloatRegister reg = *iter; ++ diffF -= reg.size(); ++ numFpu -= 1; ++ dest.offset -= reg.size(); ++ if (reg.isDouble()) { ++ storeDouble(reg, dest); ++ } else if (reg.isSingle()) { ++ storeFloat32(reg, dest); ++ } else { ++ MOZ_CRASH("Unknown register type."); ++ } ++ } ++ MOZ_ASSERT(numFpu == 0); ++ diffF -= diffF % sizeof(uintptr_t); ++ MOZ_ASSERT(diffF == 0); ++} ++void MacroAssembler::truncDoubleToInt32(FloatRegister src, Register dest, ++ Label* fail) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ Label zeroCase, done; ++ // Convert scalar to signed 32-bit fixed-point, rounding toward zero. ++ // In the case of overflow, the output is saturated. ++ // In the case of NaN and -0, the output is zero. ++ RoundFloatingPointToInteger( ++ dest, src, scratch, ++ [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { ++ masm->fcvt_w_d(dst, src, RTZ); ++ }, ++ false); ++ ma_b(scratch, Imm32(1), fail, NotEqual); ++ // If the output was zero, worry about special cases. ++ branch32(Assembler::Equal, dest, Imm32(0), &zeroCase); ++ jump(&done); ++ // Handle the case of a zero output: ++ // 1. The input may have been NaN, requiring a failure. ++ // 2. The input may have been in (-1,-0], requiring a failure. ++ // 3. +0, return 0. ++ { ++ bind(&zeroCase); ++ ++ // If input is a negative number that truncated to zero, the real ++ // output should be the non-integer -0. ++ // The use of "lt" instead of "lo" also catches unordered NaN input. ++ ScratchDoubleScope fscratch(*this); ++ fmv_d_x(fscratch, zero); ++ ma_compareF64(scratch, DoubleLessThan, src, fscratch); ++ ma_b(scratch, Imm32(1), fail, Equal); ++ ++ // Check explicitly for -0, bitwise. ++ fmv_x_d(dest, src); ++ branchTestPtr(Assembler::Signed, dest, dest, fail); ++ movePtr(ImmPtr(0), dest); ++ } ++ ++ bind(&done); ++} ++void MacroAssembler::truncFloat32ToInt32(FloatRegister src, Register dest, ++ Label* fail) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ Label zeroCase, done; ++ // Convert scalar to signed 32-bit fixed-point, rounding toward zero. ++ // In the case of overflow, the output is saturated. ++ // In the case of NaN and -0, the output is zero. ++ RoundFloatingPointToInteger( ++ dest, src, scratch, ++ [](MacroAssemblerRiscv64* masm, Register dst, FPURegister src) { ++ masm->fcvt_w_s(dst, src, RTZ); ++ }, ++ false); ++ ma_b(scratch, Imm32(1), fail, NotEqual); ++ // If the output was zero, worry about special cases. ++ branch32(Assembler::Equal, dest, Imm32(0), &zeroCase); ++ jump(&done); ++ // Handle the case of a zero output: ++ // 1. The input may have been NaN, requiring a failure. ++ // 2. The input may have been in (-1,-0], requiring a failure. ++ // 3. +0, return 0. ++ { ++ bind(&zeroCase); ++ ++ // If input is a negative number that truncated to zero, the real ++ // output should be the non-integer -0. ++ // The use of "lt" instead of "lo" also catches unordered NaN input. ++ ScratchDoubleScope fscratch(*this); ++ fmv_w_x(fscratch, zero); ++ ma_compareF32(scratch, DoubleLessThan, src, fscratch); ++ ma_b(scratch, Imm32(1), fail, Equal); ++ ++ // Check explicitly for -0, bitwise. ++ fmv_x_w(dest, src); ++ branchTestPtr(Assembler::Signed, dest, dest, fail); ++ movePtr(ImmPtr(0), dest); ++ } ++ ++ bind(&done); ++} ++void MacroAssembler::wasmAtomicEffectOp(const wasm::MemoryAccessDesc& access, ++ AtomicOp op, Register value, ++ const Address& mem, Register valueTemp, ++ Register offsetTemp, ++ Register maskTemp) { ++ AtomicEffectOp(*this, &access, access.type(), access.sync(), op, mem, value, ++ valueTemp, offsetTemp, maskTemp); ++} ++ ++void MacroAssembler::wasmAtomicEffectOp(const wasm::MemoryAccessDesc& access, ++ AtomicOp op, Register value, ++ const BaseIndex& mem, ++ Register valueTemp, Register offsetTemp, ++ Register maskTemp) { ++ AtomicEffectOp(*this, &access, access.type(), access.sync(), op, mem, value, ++ valueTemp, offsetTemp, maskTemp); ++} ++template ++static void WasmAtomicExchange64(MacroAssembler& masm, ++ const wasm::MemoryAccessDesc& access, ++ const T& mem, Register64 value, ++ Register64 output) { ++ AtomicExchange64(masm, &access, access.sync(), mem, value, output); ++} ++ ++void MacroAssembler::wasmAtomicExchange64(const wasm::MemoryAccessDesc& access, ++ const Address& mem, Register64 src, ++ Register64 output) { ++ WasmAtomicExchange64(*this, access, mem, src, output); ++} ++ ++void MacroAssembler::wasmAtomicExchange64(const wasm::MemoryAccessDesc& access, ++ const BaseIndex& mem, Register64 src, ++ Register64 output) { ++ WasmAtomicExchange64(*this, access, mem, src, output); ++} ++void MacroAssembler::wasmAtomicExchange(const wasm::MemoryAccessDesc& access, ++ const Address& mem, Register value, ++ Register valueTemp, Register offsetTemp, ++ Register maskTemp, Register output) { ++ AtomicExchange(*this, &access, access.type(), access.sync(), mem, value, ++ valueTemp, offsetTemp, maskTemp, output); ++} ++ ++void MacroAssembler::wasmAtomicExchange(const wasm::MemoryAccessDesc& access, ++ const BaseIndex& mem, Register value, ++ Register valueTemp, Register offsetTemp, ++ Register maskTemp, Register output) { ++ AtomicExchange(*this, &access, access.type(), access.sync(), mem, value, ++ valueTemp, offsetTemp, maskTemp, output); ++} ++void MacroAssembler::wasmAtomicFetchOp64(const wasm::MemoryAccessDesc& access, ++ AtomicOp op, Register64 value, ++ const Address& mem, Register64 temp, ++ Register64 output) { ++ AtomicFetchOp64(*this, &access, access.sync(), op, value, mem, temp, output); ++} ++void MacroAssembler::wasmAtomicFetchOp64(const wasm::MemoryAccessDesc& access, ++ AtomicOp op, Register64 value, ++ const BaseIndex& mem, Register64 temp, ++ Register64 output) { ++ AtomicFetchOp64(*this, &access, access.sync(), op, value, mem, temp, output); ++} ++ ++void MacroAssembler::atomicFetchOp64(const Synchronization& sync, AtomicOp op, ++ Register64 value, const Address& mem, ++ Register64 temp, Register64 output) { ++ AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, output); ++} ++ ++void MacroAssembler::atomicFetchOp64(const Synchronization& sync, AtomicOp op, ++ Register64 value, const BaseIndex& mem, ++ Register64 temp, Register64 output) { ++ AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, output); ++} ++ ++void MacroAssembler::atomicEffectOp64(const Synchronization& sync, AtomicOp op, ++ Register64 value, const Address& mem, ++ Register64 temp) { ++ AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, temp); ++} ++ ++void MacroAssembler::atomicEffectOp64(const Synchronization& sync, AtomicOp op, ++ Register64 value, const BaseIndex& mem, ++ Register64 temp) { ++ AtomicFetchOp64(*this, nullptr, sync, op, value, mem, temp, temp); ++} ++void MacroAssembler::wasmAtomicFetchOp(const wasm::MemoryAccessDesc& access, ++ AtomicOp op, Register value, ++ const Address& mem, Register valueTemp, ++ Register offsetTemp, Register maskTemp, ++ Register output) { ++ AtomicFetchOp(*this, &access, access.type(), access.sync(), op, mem, value, ++ valueTemp, offsetTemp, maskTemp, output); ++} ++ ++void MacroAssembler::wasmAtomicFetchOp(const wasm::MemoryAccessDesc& access, ++ AtomicOp op, Register value, ++ const BaseIndex& mem, Register valueTemp, ++ Register offsetTemp, Register maskTemp, ++ Register output) { ++ AtomicFetchOp(*this, &access, access.type(), access.sync(), op, mem, value, ++ valueTemp, offsetTemp, maskTemp, output); ++} ++void MacroAssembler::wasmBoundsCheck32(Condition cond, Register index, ++ Register boundsCheckLimit, Label* ok) { ++ ma_b(index, boundsCheckLimit, ok, cond); ++} ++ ++void MacroAssembler::wasmBoundsCheck32(Condition cond, Register index, ++ Address boundsCheckLimit, Label* ok) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ load32(boundsCheckLimit, scratch2); ++ ma_b(index, Register(scratch2), ok, cond); ++} ++ ++void MacroAssembler::wasmBoundsCheck64(Condition cond, Register64 index, ++ Register64 boundsCheckLimit, Label* ok) { ++ ma_b(index.reg, boundsCheckLimit.reg, ok, cond); ++} ++ ++void MacroAssembler::wasmBoundsCheck64(Condition cond, Register64 index, ++ Address boundsCheckLimit, Label* ok) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ loadPtr(boundsCheckLimit, scratch2); ++ ma_b(index.reg, scratch2, ok, cond); ++} ++ ++void MacroAssembler::wasmCompareExchange64(const wasm::MemoryAccessDesc& access, ++ const Address& mem, ++ Register64 expect, ++ Register64 replace, ++ Register64 output) { ++ CompareExchange64(*this, &access, access.sync(), mem, expect, replace, ++ output); ++} ++ ++void MacroAssembler::wasmCompareExchange64(const wasm::MemoryAccessDesc& access, ++ const BaseIndex& mem, ++ Register64 expect, ++ Register64 replace, ++ Register64 output) { ++ CompareExchange64(*this, &access, access.sync(), mem, expect, replace, ++ output); ++} ++ ++template ++static void CompareExchange(MacroAssembler& masm, ++ const wasm::MemoryAccessDesc* access, ++ Scalar::Type type, const Synchronization& sync, ++ const T& mem, Register oldval, Register newval, ++ Register valueTemp, Register offsetTemp, ++ Register maskTemp, Register output) { ++ bool signExtend = Scalar::isSignedIntType(type); ++ unsigned nbytes = Scalar::byteSize(type); ++ ++ switch (nbytes) { ++ case 1: ++ case 2: ++ break; ++ case 4: ++ MOZ_ASSERT(valueTemp == InvalidReg); ++ MOZ_ASSERT(offsetTemp == InvalidReg); ++ MOZ_ASSERT(maskTemp == InvalidReg); ++ break; ++ default: ++ MOZ_CRASH(); ++ } ++ ++ Label again, end; ++ UseScratchRegisterScope temps(&masm); ++ Register SecondScratchReg = temps.Acquire(); ++ masm.computeEffectiveAddress(mem, SecondScratchReg); ++ ++ if (nbytes == 4) { ++ masm.memoryBarrierBefore(sync); ++ masm.bind(&again); ++ ++ if (access) { ++ masm.append(*access, masm.size()); ++ } ++ ++ masm.lr_w(true, true, output, SecondScratchReg); ++ masm.ma_b(output, oldval, &end, Assembler::NotEqual, ShortJump); ++ masm.mv(ScratchRegister, newval); ++ masm.sc_w(true, true, ScratchRegister, SecondScratchReg, ScratchRegister); ++ masm.ma_b(ScratchRegister, ScratchRegister, &again, Assembler::NonZero, ++ ShortJump); ++ ++ masm.memoryBarrierAfter(sync); ++ masm.bind(&end); ++ ++ return; ++ } ++ ++ masm.andi(offsetTemp, SecondScratchReg, 3); ++ masm.subPtr(offsetTemp, SecondScratchReg); ++#if !MOZ_LITTLE_ENDIAN() ++ masm.as_xori(offsetTemp, offsetTemp, 3); ++#endif ++ masm.slli(offsetTemp, offsetTemp, 3); ++ masm.ma_li(maskTemp, Imm32(UINT32_MAX >> ((4 - nbytes) * 8))); ++ masm.sll(maskTemp, maskTemp, offsetTemp); ++ masm.nor(maskTemp, zero, maskTemp); ++ ++ masm.memoryBarrierBefore(sync); ++ ++ masm.bind(&again); ++ ++ if (access) { ++ masm.append(*access, masm.size()); ++ } ++ ++ masm.lr_w(true, true, ScratchRegister, SecondScratchReg); ++ ++ masm.srl(output, ScratchRegister, offsetTemp); ++ ++ switch (nbytes) { ++ case 1: ++ if (signExtend) { ++ masm.SignExtendByte(valueTemp, oldval); ++ masm.SignExtendByte(output, output); ++ } else { ++ masm.andi(valueTemp, oldval, 0xff); ++ masm.andi(output, output, 0xff); ++ } ++ break; ++ case 2: ++ if (signExtend) { ++ masm.SignExtendShort(valueTemp, oldval); ++ masm.SignExtendShort(output, output); ++ } else { ++ masm.andi(valueTemp, oldval, 0xffff); ++ masm.andi(output, output, 0xffff); ++ } ++ break; ++ } ++ ++ masm.ma_b(output, valueTemp, &end, Assembler::NotEqual, ShortJump); ++ ++ masm.sll(valueTemp, newval, offsetTemp); ++ masm.and_(ScratchRegister, ScratchRegister, maskTemp); ++ masm.or_(ScratchRegister, ScratchRegister, valueTemp); ++ masm.sc_w(true, true, ScratchRegister, SecondScratchReg, ScratchRegister); ++ ++ masm.ma_b(ScratchRegister, ScratchRegister, &again, Assembler::NonZero, ++ ShortJump); ++ ++ masm.memoryBarrierAfter(sync); ++ ++ masm.bind(&end); ++} ++ ++void MacroAssembler::compareExchange(Scalar::Type type, ++ const Synchronization& sync, ++ const Address& mem, Register oldval, ++ Register newval, Register valueTemp, ++ Register offsetTemp, Register maskTemp, ++ Register output) { ++ CompareExchange(*this, nullptr, type, sync, mem, oldval, newval, valueTemp, ++ offsetTemp, maskTemp, output); ++} ++ ++void MacroAssembler::compareExchange(Scalar::Type type, ++ const Synchronization& sync, ++ const BaseIndex& mem, Register oldval, ++ Register newval, Register valueTemp, ++ Register offsetTemp, Register maskTemp, ++ Register output) { ++ CompareExchange(*this, nullptr, type, sync, mem, oldval, newval, valueTemp, ++ offsetTemp, maskTemp, output); ++} ++ ++void MacroAssembler::wasmCompareExchange(const wasm::MemoryAccessDesc& access, ++ const Address& mem, Register oldval, ++ Register newval, Register valueTemp, ++ Register offsetTemp, Register maskTemp, ++ Register output) { ++ CompareExchange(*this, &access, access.type(), access.sync(), mem, oldval, ++ newval, valueTemp, offsetTemp, maskTemp, output); ++} ++ ++void MacroAssembler::wasmCompareExchange(const wasm::MemoryAccessDesc& access, ++ const BaseIndex& mem, Register oldval, ++ Register newval, Register valueTemp, ++ Register offsetTemp, Register maskTemp, ++ Register output) { ++ CompareExchange(*this, &access, access.type(), access.sync(), mem, oldval, ++ newval, valueTemp, offsetTemp, maskTemp, output); ++} ++ ++void MacroAssembler::wasmLoad(const wasm::MemoryAccessDesc& access, ++ Register memoryBase, Register ptr, ++ Register ptrScratch, AnyRegister output) { ++ wasmLoadImpl(access, memoryBase, ptr, ptrScratch, output, InvalidReg); ++} ++ ++void MacroAssembler::wasmLoadI64(const wasm::MemoryAccessDesc& access, ++ Register memoryBase, Register ptr, ++ Register ptrScratch, Register64 output) { ++ wasmLoadI64Impl(access, memoryBase, ptr, ptrScratch, output, InvalidReg); ++} ++ ++void MacroAssembler::wasmStore(const wasm::MemoryAccessDesc& access, ++ AnyRegister value, Register memoryBase, ++ Register ptr, Register ptrScratch) { ++ wasmStoreImpl(access, value, memoryBase, ptr, ptrScratch, InvalidReg); ++} ++ ++void MacroAssembler::wasmStoreI64(const wasm::MemoryAccessDesc& access, ++ Register64 value, Register memoryBase, ++ Register ptr, Register ptrScratch) { ++ wasmStoreI64Impl(access, value, memoryBase, ptr, ptrScratch, InvalidReg); ++} ++ ++void MacroAssemblerRiscv64::Clear_if_nan_d(Register rd, FPURegister fs) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ Label no_nan; ++ feq_d(ScratchRegister, fs, fs); ++ bnez(ScratchRegister, &no_nan); ++ mv(rd, zero_reg); ++ bind(&no_nan); ++} ++ ++void MacroAssemblerRiscv64::Clear_if_nan_s(Register rd, FPURegister fs) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ Label no_nan; ++ feq_s(ScratchRegister, fs, fs); ++ bnez(ScratchRegister, &no_nan); ++ mv(rd, zero_reg); ++ bind(&no_nan); ++} ++ ++void MacroAssembler::wasmTruncateDoubleToInt32(FloatRegister input, ++ Register output, ++ bool isSaturating, ++ Label* oolEntry) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ Trunc_w_d(output, input, ScratchRegister); ++ if (isSaturating) { ++ Clear_if_nan_d(output, input); ++ } else { ++ ma_b(ScratchRegister, Imm32(1), oolEntry, Assembler::NotEqual); ++ } ++} ++ ++void MacroAssembler::wasmTruncateDoubleToInt64( ++ FloatRegister input, Register64 output, bool isSaturating, Label* oolEntry, ++ Label* oolRejoin, FloatRegister tempDouble) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ Trunc_l_d(output.reg, input, ScratchRegister); ++ if (isSaturating) { ++ bind(oolRejoin); ++ Clear_if_nan_d(output.reg, input); ++ } else { ++ ma_b(ScratchRegister, Imm32(1), oolEntry, Assembler::NotEqual); ++ } ++} ++ ++void MacroAssembler::wasmTruncateDoubleToUInt32(FloatRegister input, ++ Register output, ++ bool isSaturating, ++ Label* oolEntry) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ Trunc_uw_d(output, input, ScratchRegister); ++ if (isSaturating) { ++ Clear_if_nan_d(output, input); ++ } else { ++ ma_b(ScratchRegister, Imm32(1), oolEntry, Assembler::NotEqual); ++ } ++} ++ ++void MacroAssembler::wasmTruncateDoubleToUInt64( ++ FloatRegister input, Register64 output, bool isSaturating, Label* oolEntry, ++ Label* oolRejoin, FloatRegister tempDouble) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ Trunc_ul_d(output.reg, input, ScratchRegister); ++ if (isSaturating) { ++ bind(oolRejoin); ++ Clear_if_nan_d(output.reg, input); ++ } else { ++ ma_b(ScratchRegister, Imm32(1), oolEntry, Assembler::NotEqual); ++ } ++} ++ ++void MacroAssembler::wasmTruncateFloat32ToInt32(FloatRegister input, ++ Register output, ++ bool isSaturating, ++ Label* oolEntry) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ Trunc_w_s(output, input, ScratchRegister); ++ if (isSaturating) { ++ Clear_if_nan_s(output, input); ++ } else { ++ ma_b(ScratchRegister, Imm32(1), oolEntry, Assembler::NotEqual); ++ } ++} ++ ++void MacroAssembler::wasmTruncateFloat32ToInt64( ++ FloatRegister input, Register64 output, bool isSaturating, Label* oolEntry, ++ Label* oolRejoin, FloatRegister tempFloat) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ Trunc_l_s(output.reg, input, ScratchRegister); ++ ++ if (isSaturating) { ++ bind(oolRejoin); ++ Clear_if_nan_s(output.reg, input); ++ } else { ++ ma_b(ScratchRegister, Imm32(1), oolEntry, Assembler::NotEqual); ++ } ++} ++ ++void MacroAssembler::wasmTruncateFloat32ToUInt32(FloatRegister input, ++ Register output, ++ bool isSaturating, ++ Label* oolEntry) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ Trunc_uw_s(output, input, ScratchRegister); ++ if (isSaturating) { ++ Clear_if_nan_s(output, input); ++ } else { ++ ma_b(ScratchRegister, Imm32(1), oolEntry, Assembler::NotEqual); ++ } ++} ++ ++void MacroAssembler::wasmTruncateFloat32ToUInt64( ++ FloatRegister input, Register64 output, bool isSaturating, Label* oolEntry, ++ Label* oolRejoin, FloatRegister tempFloat) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ Trunc_ul_s(output.reg, input, ScratchRegister); ++ ++ if (isSaturating) { ++ bind(oolRejoin); ++ Clear_if_nan_s(output.reg, input); ++ } else { ++ ma_b(ScratchRegister, Imm32(1), oolEntry, Assembler::NotEqual); ++ } ++} ++ ++// TODO(riscv64): widenInt32 should be nop? ++void MacroAssembler::widenInt32(Register r) { ++ move32To64SignExtend(r, Register64(r)); ++} ++ ++//}}} check_macroassembler_style ++ ++// This method generates lui, dsll and ori instruction block that can be ++// modified by UpdateLoad64Value, either during compilation (eg. ++// Assembler::bind), or during execution (eg. jit::PatchJump). ++void MacroAssemblerRiscv64::ma_liPatchable(Register dest, Imm32 imm) { ++ return ma_liPatchable(dest, ImmWord(uintptr_t(imm.value))); ++} ++ ++void MacroAssemblerRiscv64::ma_liPatchable(Register dest, ImmPtr imm) { ++ return ma_liPatchable(dest, ImmWord(uintptr_t(imm.value))); ++} ++ ++void MacroAssemblerRiscv64::ma_liPatchable(Register dest, ImmWord imm, ++ LiFlags flags) { ++ DEBUG_PRINTF("\tma_liPatchable\n"); ++ if (Li64 == flags) { ++ li_constant(dest, imm.value); ++ } else { ++ li_ptr(dest, imm.value); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_li(Register dest, ImmGCPtr ptr) { ++ BlockTrampolinePoolScope block_trampoline_pool(this, 6); ++ writeDataRelocation(ptr); ++ ma_liPatchable(dest, ImmPtr(ptr.value)); ++} ++void MacroAssemblerRiscv64::ma_li(Register dest, Imm32 imm) { ++ RV_li(dest, imm.value); ++} ++void MacroAssemblerRiscv64::ma_li(Register dest, Imm64 imm) { ++ RV_li(dest, imm.value); ++} ++void MacroAssemblerRiscv64::ma_li(Register dest, CodeLabel* label) { ++ DEBUG_PRINTF("[ %s\n", __FUNCTION__); ++ BlockTrampolinePoolScope block_trampoline_pool(this, 7); ++ BufferOffset bo = m_buffer.nextOffset(); ++ JitSpew(JitSpew_Codegen, ".load CodeLabel %p", label); ++ ma_liPatchable(dest, ImmWord(/* placeholder */ 0)); ++ label->patchAt()->bind(bo.getOffset()); ++ label->setLinkMode(CodeLabel::MoveImmediate); ++ DEBUG_PRINTF("]\n"); ++} ++void MacroAssemblerRiscv64::ma_li(Register dest, ImmWord imm) { ++ RV_li(dest, imm.value); ++} ++ ++// Shortcut for when we know we're transferring 32 bits of data. ++void MacroAssemblerRiscv64::ma_pop(Register r) { ++ ld(r, StackPointer, 0); ++ addi(StackPointer, StackPointer, sizeof(intptr_t)); ++} ++ ++void MacroAssemblerRiscv64::ma_push(Register r) { ++ if (r == sp) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ // Pushing sp requires one more instruction. ++ mv(ScratchRegister, sp); ++ r = ScratchRegister; ++ } ++ ++ addi(StackPointer, StackPointer, (int32_t) - sizeof(intptr_t)); ++ sd(r, StackPointer, 0); ++} ++ ++// multiplies. For now, there are only few that we care about. ++void MacroAssemblerRiscv64::ma_mul32TestOverflow(Register rd, Register rj, ++ Register rk, Label* overflow) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ MulOverflow32(rd, rj, rk, ScratchRegister); ++ ma_b(ScratchRegister, Register(zero), overflow, Assembler::NotEqual); ++} ++void MacroAssemblerRiscv64::ma_mul32TestOverflow(Register rd, Register rj, ++ Imm32 imm, Label* overflow) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ MulOverflow32(rd, rj, Operand(imm.value), ScratchRegister); ++ ma_b(ScratchRegister, Register(zero), overflow, Assembler::NotEqual); ++} ++ ++void MacroAssemblerRiscv64::ma_mulPtrTestOverflow(Register rd, Register rj, ++ Register rk, ++ Label* overflow) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ Register scratch2 = temps.Acquire(); ++ MOZ_ASSERT(rd != scratch); ++ ++ if (rd == rj) { ++ or_(scratch, rj, zero); ++ rj = scratch; ++ rk = (rd == rk) ? rj : rk; ++ } else if (rd == rk) { ++ or_(scratch, rk, zero); ++ rk = scratch; ++ } ++ ++ mul(rd, rj, rk); ++ mulh(scratch, rj, rk); ++ srai(scratch2, rd, 63); ++ ma_b(scratch, Register(scratch2), overflow, Assembler::NotEqual); ++} ++ ++// MulOverflow32 sets overflow register to zero if no overflow occured ++void MacroAssemblerRiscv64::MulOverflow32(Register dst, Register left, ++ const Operand& right, ++ Register overflow) { ++ UseScratchRegisterScope temps(this); ++ BlockTrampolinePoolScope block_trampoline_pool(this, 11); ++ Register right_reg; ++ Register scratch = temps.Acquire(); ++ Register scratch2 = temps.Acquire(); ++ if (right.is_imm()) { ++ ma_li(scratch, right.immediate()); ++ right_reg = scratch; ++ } else { ++ MOZ_ASSERT(right.is_reg()); ++ right_reg = right.rm(); ++ } ++ ++ MOZ_ASSERT(left != scratch2 && right_reg != scratch2 && dst != scratch2 && ++ overflow != scratch2); ++ MOZ_ASSERT(overflow != left && overflow != right_reg); ++ sext_w(overflow, left); ++ sext_w(scratch2, right_reg); ++ ++ mul(overflow, overflow, scratch2); ++ sext_w(dst, overflow); ++ xor_(overflow, overflow, dst); ++} ++ ++int32_t MacroAssemblerRiscv64::GetOffset(int32_t offset, Label* L, ++ OffsetSize bits) { ++ if (L) { ++ offset = branch_offset_helper(L, bits); ++ } else { ++ MOZ_ASSERT(is_intn(offset, bits)); ++ } ++ return offset; ++} ++ ++bool MacroAssemblerRiscv64::CalculateOffset(Label* L, int32_t* offset, ++ OffsetSize bits) { ++ if (!is_near(L, bits)) return false; ++ *offset = GetOffset(*offset, L, bits); ++ return true; ++} ++ ++void MacroAssemblerRiscv64::BranchShortHelper(int32_t offset, Label* L) { ++ MOZ_ASSERT(L == nullptr || offset == 0); ++ offset = GetOffset(offset, L, OffsetSize::kOffset21); ++ Assembler::j(offset); ++} ++ ++bool MacroAssemblerRiscv64::BranchShortHelper(int32_t offset, Label* L, ++ Condition cond, Register rs, ++ const Operand& rt) { ++ MOZ_ASSERT(L == nullptr || offset == 0); ++ MOZ_ASSERT(rt.is_reg() || rt.is_imm()); ++ UseScratchRegisterScope temps(this); ++ Register scratch = Register(); ++ if (rt.is_imm()) { ++ scratch = temps.Acquire(); ++ ma_li(scratch, Imm64(rt.immediate())); ++ } else { ++ MOZ_ASSERT(rt.is_reg()); ++ scratch = rt.rm(); ++ } ++ { ++ switch (cond) { ++ case Always: ++ if (!CalculateOffset(L, &offset, OffsetSize::kOffset21)) return false; ++ Assembler::j(offset); ++ EmitConstPoolWithJumpIfNeeded(); ++ break; ++ case Equal: ++ // rs == rt ++ if (rt.is_reg() && rs == rt.rm()) { ++ if (!CalculateOffset(L, &offset, OffsetSize::kOffset21)) return false; ++ Assembler::j(offset); ++ } else { ++ if (!CalculateOffset(L, &offset, OffsetSize::kOffset13)) return false; ++ Assembler::beq(rs, scratch, offset); ++ } ++ break; ++ case NotEqual: ++ // rs != rt ++ if (rt.is_reg() && rs == rt.rm()) { ++ break; // No code needs to be emitted ++ } else { ++ if (!CalculateOffset(L, &offset, OffsetSize::kOffset13)) return false; ++ Assembler::bne(rs, scratch, offset); ++ } ++ break; ++ ++ // Signed comparison. ++ case GreaterThan: ++ // rs > rt ++ if (rt.is_reg() && rs == rt.rm()) { ++ break; // No code needs to be emitted. ++ } else { ++ if (!CalculateOffset(L, &offset, OffsetSize::kOffset13)) return false; ++ Assembler::bgt(rs, scratch, offset); ++ } ++ break; ++ case GreaterThanOrEqual: ++ // rs >= rt ++ if (rt.is_reg() && rs == rt.rm()) { ++ if (!CalculateOffset(L, &offset, OffsetSize::kOffset21)) return false; ++ Assembler::j(offset); ++ } else { ++ if (!CalculateOffset(L, &offset, OffsetSize::kOffset13)) return false; ++ Assembler::bge(rs, scratch, offset); ++ } ++ break; ++ case LessThan: ++ // rs < rt ++ if (rt.is_reg() && rs == rt.rm()) { ++ break; // No code needs to be emitted. ++ } else { ++ if (!CalculateOffset(L, &offset, OffsetSize::kOffset13)) return false; ++ Assembler::blt(rs, scratch, offset); ++ } ++ break; ++ case LessThanOrEqual: ++ // rs <= rt ++ if (rt.is_reg() && rs == rt.rm()) { ++ if (!CalculateOffset(L, &offset, OffsetSize::kOffset21)) return false; ++ Assembler::j(offset); ++ } else { ++ if (!CalculateOffset(L, &offset, OffsetSize::kOffset13)) return false; ++ Assembler::ble(rs, scratch, offset); ++ } ++ break; ++ ++ // Unsigned comparison. ++ case Above: ++ // rs > rt ++ if (rt.is_reg() && rs == rt.rm()) { ++ break; // No code needs to be emitted. ++ } else { ++ if (!CalculateOffset(L, &offset, OffsetSize::kOffset13)) return false; ++ Assembler::bgtu(rs, scratch, offset); ++ } ++ break; ++ case AboveOrEqual: ++ // rs >= rt ++ if (rt.is_reg() && rs == rt.rm()) { ++ if (!CalculateOffset(L, &offset, OffsetSize::kOffset21)) return false; ++ Assembler::j(offset); ++ } else { ++ if (!CalculateOffset(L, &offset, OffsetSize::kOffset13)) return false; ++ Assembler::bgeu(rs, scratch, offset); ++ } ++ break; ++ case Below: ++ // rs < rt ++ if (rt.is_reg() && rs == rt.rm()) { ++ break; // No code needs to be emitted. ++ } else { ++ if (!CalculateOffset(L, &offset, OffsetSize::kOffset13)) return false; ++ bltu(rs, scratch, offset); ++ } ++ break; ++ case BelowOrEqual: ++ // rs <= rt ++ if (rt.is_reg() && rs == rt.rm()) { ++ if (!CalculateOffset(L, &offset, OffsetSize::kOffset21)) return false; ++ Assembler::j(offset); ++ } else { ++ if (!CalculateOffset(L, &offset, OffsetSize::kOffset13)) return false; ++ Assembler::bleu(rs, scratch, offset); ++ } ++ break; ++ default: ++ MOZ_CRASH("UNREACHABLE"); ++ } ++ } ++ return true; ++} ++ ++// BRANCH_ARGS_CHECK checks that conditional jump arguments are correct. ++#define BRANCH_ARGS_CHECK(cond, rs, rt) \ ++ MOZ_ASSERT((cond == Always && rs == zero && rt.rm() == zero) || \ ++ (cond != Always && (rs != zero || rt.rm() != zero))) ++ ++bool MacroAssemblerRiscv64::BranchShortCheck(int32_t offset, Label* L, ++ Condition cond, Register rs, ++ const Operand& rt) { ++ BRANCH_ARGS_CHECK(cond, rs, rt); ++ ++ if (!L) { ++ MOZ_ASSERT(is_int13(offset)); ++ return BranchShortHelper(offset, nullptr, cond, rs, rt); ++ } else { ++ MOZ_ASSERT(offset == 0); ++ return BranchShortHelper(0, L, cond, rs, rt); ++ } ++} ++ ++void MacroAssemblerRiscv64::BranchShort(Label* L) { BranchShortHelper(0, L); } ++ ++void MacroAssemblerRiscv64::BranchShort(int32_t offset, Condition cond, ++ Register rs, const Operand& rt) { ++ BranchShortCheck(offset, nullptr, cond, rs, rt); ++} ++ ++void MacroAssemblerRiscv64::BranchShort(Label* L, Condition cond, Register rs, ++ const Operand& rt) { ++ BranchShortCheck(0, L, cond, rs, rt); ++} ++ ++void MacroAssemblerRiscv64::BranchLong(Label* L) { ++ // Generate position independent long branch. ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ int32_t imm; ++ imm = branch_long_offset(L); ++ GenPCRelativeJump(scratch, imm); ++} ++ ++void MacroAssemblerRiscv64::BranchAndLinkLong(Label* L) { ++ // Generate position independent long branch and link. ++ int32_t imm; ++ imm = branch_long_offset(L); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ GenPCRelativeJumpAndLink(scratch, imm); ++} ++ ++void MacroAssemblerRiscv64::ma_branch(Label* L, Condition cond, Register rs, ++ const Operand& rt, JumpKind jumpKind) { ++ if (L->used()) { ++ if (jumpKind == ShortJump && BranchShortCheck(0, L, cond, rs, rt)) { ++ return; ++ } ++ if (cond != Always) { ++ Label skip; ++ Condition neg_cond = InvertCondition(cond); ++ BranchShort(&skip, neg_cond, rs, rt); ++ BranchLong(L); ++ bind(&skip); ++ } else { ++ BranchLong(L); ++ EmitConstPoolWithJumpIfNeeded(); ++ } ++ } else { ++ if (jumpKind == LongJump) { ++ if (cond != Always) { ++ Label skip; ++ Condition neg_cond = InvertCondition(cond); ++ BranchShort(&skip, neg_cond, rs, rt); ++ BranchLong(L); ++ bind(&skip); ++ } else { ++ BranchLong(L); ++ EmitConstPoolWithJumpIfNeeded(); ++ } ++ } else { ++ BranchShort(L, cond, rs, rt); ++ } ++ } ++} ++ ++// Branches when done from within riscv code. ++void MacroAssemblerRiscv64::ma_b(Register lhs, Address addr, Label* label, ++ Condition c, JumpKind jumpKind) { ++ ScratchRegisterScope scratch(asMasm()); ++ MOZ_ASSERT(lhs != scratch); ++ ma_load(scratch, addr, SizeDouble); ++ ma_b(lhs, Register(scratch), label, c, jumpKind); ++} ++ ++void MacroAssemblerRiscv64::ma_b(Register lhs, ImmPtr imm, Label* l, ++ Condition c, JumpKind jumpKind) { ++ asMasm().ma_b(lhs, ImmWord(uintptr_t(imm.value)), l, c, jumpKind); ++} ++ ++// Branches when done from within loongarch-specific code. ++void MacroAssemblerRiscv64::ma_b(Register lhs, ImmWord imm, Label* label, ++ Condition c, JumpKind jumpKind) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ MOZ_ASSERT(lhs != scratch); ++ ma_li(scratch, imm); ++ ma_b(lhs, Register(scratch), label, c, jumpKind); ++} ++ ++void MacroAssemblerRiscv64::ma_b(Register lhs, Imm32 imm, Label* label, ++ Condition c, JumpKind jumpKind) { ++ if ((c == NonZero || c == Zero || c == Signed || c == NotSigned) && ++ imm.value == 0) { ++ ma_b(lhs, lhs, label, c, jumpKind); ++ } else { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ MOZ_ASSERT(lhs != scratch); ++ ma_li(scratch, imm); ++ ma_b(lhs, Register(scratch), label, c, jumpKind); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_b(Address addr, Imm32 imm, Label* label, ++ Condition c, JumpKind jumpKind) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ ma_load(scratch2, addr); ++ ma_b(Register(scratch2), imm, label, c, jumpKind); ++} ++ ++void MacroAssemblerRiscv64::ma_b(Register lhs, Register rhs, Label* label, ++ Condition c, JumpKind jumpKind) { ++ switch (c) { ++ case Equal: ++ case NotEqual: ++ ma_branch(label, c, lhs, rhs, jumpKind); ++ break; ++ case Always: ++ ma_branch(label, c, zero, Operand(zero), jumpKind); ++ break; ++ case Zero: ++ MOZ_ASSERT(lhs == rhs); ++ ma_branch(label, Equal, lhs, Operand(zero), jumpKind); ++ break; ++ case NonZero: ++ MOZ_ASSERT(lhs == rhs); ++ ma_branch(label, NotEqual, lhs, Operand(zero), jumpKind); ++ break; ++ case Signed: ++ MOZ_ASSERT(lhs == rhs); ++ ma_branch(label, LessThan, lhs, Operand(zero), jumpKind); ++ break; ++ case NotSigned: ++ MOZ_ASSERT(lhs == rhs); ++ ma_branch(label, GreaterThanOrEqual, lhs, Operand(zero), jumpKind); ++ break; ++ default: { ++ ma_branch(label, c, lhs, rhs, jumpKind); ++ break; ++ } ++ } ++} ++ ++void MacroAssemblerRiscv64::ExtractBits(Register rt, Register rs, uint16_t pos, ++ uint16_t size, bool sign_extend) { ++#if JS_CODEGEN_RISCV64 ++ MOZ_ASSERT(pos < 64 && 0 < size && size <= 64 && 0 < pos + size && ++ pos + size <= 64); ++ slli(rt, rs, 64 - (pos + size)); ++ if (sign_extend) { ++ srai(rt, rt, 64 - size); ++ } else { ++ srli(rt, rt, 64 - size); ++ } ++#elif JS_CODEGEN_RISCV32 ++ MOZ_ASSERT(pos < 32); ++ MOZ_ASSERT(size > 0); ++ MOZ_ASSERT(size <= 32); ++ MOZ_ASSERT((pos + size) > 0); ++ MOZ_ASSERT((pos + size) <= 32); ++ slli(rt, rs, 32 - (pos + size)); ++ if (sign_extend) { ++ srai(rt, rt, 32 - size); ++ } else { ++ srli(rt, rt, 32 - size); ++ } ++#endif ++} ++ ++void MacroAssemblerRiscv64::InsertBits(Register dest, Register source, int pos, ++ int size) { ++#if JS_CODEGEN_RISCV64 ++ MOZ_ASSERT(size < 64); ++#elif JS_CODEGEN_RISCV32 ++ MOZ_ASSERT(size < 32); ++#endif ++ UseScratchRegisterScope temps(this); ++ Register mask = temps.Acquire(); ++ BlockTrampolinePoolScope block_trampoline_pool(this, 9); ++ Register source_ = temps.Acquire(); ++ // Create a mask of the length=size. ++ ma_li(mask, Imm32(1)); ++ slli(mask, mask, size); ++ addi(mask, mask, -1); ++ and_(source_, mask, source); ++ slli(source_, source_, pos); ++ // Make a mask containing 0's. 0's start at "pos" with length=size. ++ slli(mask, mask, pos); ++ not_(mask, mask); ++ // cut area for insertion of source. ++ and_(dest, mask, dest); ++ // insert source ++ or_(dest, dest, source_); ++} ++ ++void MacroAssemblerRiscv64::InsertBits(Register dest, Register source, ++ Register pos, int size) { ++#if JS_CODEGEN_RISCV64 ++ MOZ_ASSERT(size < 64); ++#elif JS_CODEGEN_RISCV32 ++ MOZ_ASSERT(size < 32); ++#endif ++ UseScratchRegisterScope temps(this); ++ Register mask = temps.Acquire(); ++ BlockTrampolinePoolScope block_trampoline_pool(this, 9); ++ Register source_ = temps.Acquire(); ++ // Create a mask of the length=size. ++ ma_li(mask, Imm32(1)); ++ slli(mask, mask, size); ++ addi(mask, mask, -1); ++ and_(source_, mask, source); ++ sll(source_, source_, pos); ++ // Make a mask containing 0's. 0's start at "pos" with length=size. ++ sll(mask, mask, pos); ++ not_(mask, mask); ++ // cut area for insertion of source. ++ and_(dest, mask, dest); ++ // insert source ++ or_(dest, dest, source_); ++} ++ ++void MacroAssemblerRiscv64::ma_add32(Register rd, Register rs, Operand rt) { ++ if (rt.is_imm()) { ++ if (is_int12(rt.immediate())) { ++ addiw(rd, rs, static_cast(rt.immediate())); ++ } else if ((-4096 <= rt.immediate() && rt.immediate() <= -2049) || ++ (2048 <= rt.immediate() && rt.immediate() <= 4094)) { ++ addiw(rd, rs, rt.immediate() / 2); ++ addiw(rd, rd, rt.immediate() - (rt.immediate() / 2)); ++ } else { ++ // li handles the relocation. ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ BlockTrampolinePoolScope block_trampoline_pool(this, 9); ++ ma_li(scratch, rt.immediate()); ++ addw(rd, rs, scratch); ++ } ++ } else { ++ MOZ_ASSERT(rt.is_reg()); ++ addw(rd, rs, rt.rm()); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_add64(Register rd, Register rs, Operand rt) { ++ if (rt.is_imm()) { ++ if (is_int12(rt.immediate())) { ++ addi(rd, rs, static_cast(rt.immediate())); ++ } else if ((-4096 <= rt.immediate() && rt.immediate() <= -2049) || ++ (2048 <= rt.immediate() && rt.immediate() <= 4094)) { ++ addi(rd, rs, rt.immediate() / 2); ++ addi(rd, rd, rt.immediate() - (rt.immediate() / 2)); ++ } else { ++ // li handles the relocation. ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ BlockTrampolinePoolScope block_trampoline_pool(this, 9); ++ ma_li(scratch, rt.immediate()); ++ add(rd, rs, scratch); ++ } ++ } else { ++ MOZ_ASSERT(rt.is_reg()); ++ add(rd, rs, rt.rm()); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_sub32(Register rd, Register rs, Operand rt) { ++ if (rt.is_imm()) { ++ if (is_int12(-rt.immediate())) { ++ addiw(rd, rs, ++ static_cast( ++ -rt.immediate())); // No subi instr, use addi(x, y, -imm). ++ } else if ((-4096 <= -rt.immediate() && -rt.immediate() <= -2049) || ++ (2048 <= -rt.immediate() && -rt.immediate() <= 4094)) { ++ addiw(rd, rs, -rt.immediate() / 2); ++ addiw(rd, rd, -rt.immediate() - (-rt.immediate() / 2)); ++ } else { ++ // li handles the relocation. ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ ma_li(scratch, rt.immediate()); ++ subw(rd, rs, scratch); ++ } ++ } else { ++ MOZ_ASSERT(rt.is_reg()); ++ subw(rd, rs, rt.rm()); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_sub64(Register rd, Register rs, Operand rt) { ++ if (rt.is_imm()) { ++ if (is_int12(-rt.immediate())) { ++ addi(rd, rs, ++ static_cast( ++ -rt.immediate())); // No subi instr, use addi(x, y, -imm). ++ } else if ((-4096 <= -rt.immediate() && -rt.immediate() <= -2049) || ++ (2048 <= -rt.immediate() && -rt.immediate() <= 4094)) { ++ addi(rd, rs, -rt.immediate() / 2); ++ addi(rd, rd, -rt.immediate() - (-rt.immediate() / 2)); ++ } else { ++ // li handles the relocation. ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ ma_li(scratch, rt.immediate()); ++ sub(rd, rs, scratch); ++ } ++ } else { ++ MOZ_ASSERT(rt.is_reg()); ++ sub(rd, rs, rt.rm()); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_and(Register rd, Register rs, Operand rt) { ++ if (rt.is_imm()) { ++ if (is_int12(rt.immediate())) { ++ andi(rd, rs, rt.immediate()); ++ } else { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ ma_li(ScratchRegister, rt.immediate()); ++ and_(rd, rs, ScratchRegister); ++ } ++ } else { ++ MOZ_ASSERT(rt.is_reg()); ++ and_(rd, rs, rt.rm()); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_or(Register rd, Register rs, Operand rt) { ++ if (rt.is_imm()) { ++ if (is_int12(rt.immediate())) { ++ ori(rd, rs, rt.immediate()); ++ } else { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ ma_li(ScratchRegister, rt.immediate()); ++ or_(rd, rs, ScratchRegister); ++ } ++ } else { ++ MOZ_ASSERT(rt.is_reg()); ++ or_(rd, rs, rt.rm()); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_xor(Register rd, Register rs, Operand rt) { ++ if (rt.is_imm()) { ++ if (is_int12(rt.immediate())) { ++ xori(rd, rs, rt.immediate()); ++ } else { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ ma_li(ScratchRegister, rt.immediate()); ++ xor_(rd, rs, ScratchRegister); ++ } ++ } else { ++ MOZ_ASSERT(rt.is_reg()); ++ xor_(rd, rs, rt.rm()); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_nor(Register rd, Register rs, Operand rt) { ++ if (rt.is_imm()) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ ma_li(ScratchRegister, rt.immediate()); ++ nor(rd, rs, ScratchRegister); ++ } else { ++ MOZ_ASSERT(rt.is_reg()); ++ nor(rd, rs, rt.rm()); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_div32(Register rd, Register rs, Operand rt) { ++ if (rt.is_imm()) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ ma_li(ScratchRegister, rt.immediate()); ++ divw(rd, rs, ScratchRegister); ++ } else { ++ MOZ_ASSERT(rt.is_reg()); ++ divw(rd, rs, rt.rm()); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_divu32(Register rd, Register rs, Operand rt) { ++ if (rt.is_imm()) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ ma_li(ScratchRegister, rt.immediate()); ++ divuw(rd, rs, ScratchRegister); ++ } else { ++ MOZ_ASSERT(rt.is_reg()); ++ divuw(rd, rs, rt.rm()); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_div64(Register rd, Register rs, Operand rt) { ++ if (rt.is_imm()) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ ma_li(ScratchRegister, rt.immediate()); ++ div(rd, rs, ScratchRegister); ++ } else { ++ MOZ_ASSERT(rt.is_reg()); ++ div(rd, rs, rt.rm()); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_divu64(Register rd, Register rs, Operand rt) { ++ if (rt.is_imm()) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ ma_li(ScratchRegister, rt.immediate()); ++ divu(rd, rs, ScratchRegister); ++ } else { ++ MOZ_ASSERT(rt.is_reg()); ++ divu(rd, rs, rt.rm()); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_mod32(Register rd, Register rs, Operand rt) { ++ if (rt.is_imm()) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ ma_li(ScratchRegister, rt.immediate()); ++ remw(rd, rs, ScratchRegister); ++ } else { ++ MOZ_ASSERT(rt.is_reg()); ++ remw(rd, rs, rt.rm()); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_modu32(Register rd, Register rs, Operand rt) { ++ if (rt.is_imm()) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ ma_li(ScratchRegister, rt.immediate()); ++ remuw(rd, rs, ScratchRegister); ++ } else { ++ MOZ_ASSERT(rt.is_reg()); ++ remuw(rd, rs, rt.rm()); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_mod64(Register rd, Register rs, Operand rt) { ++ if (rt.is_imm()) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ ma_li(ScratchRegister, rt.immediate()); ++ rem(rd, rs, ScratchRegister); ++ } else { ++ MOZ_ASSERT(rt.is_reg()); ++ rem(rd, rs, rt.rm()); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_modu64(Register rd, Register rs, Operand rt) { ++ if (rt.is_imm()) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ ma_li(ScratchRegister, rt.immediate()); ++ remu(rd, rs, ScratchRegister); ++ } else { ++ MOZ_ASSERT(rt.is_reg()); ++ remu(rd, rs, rt.rm()); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_mul32(Register rd, Register rs, Operand rt) { ++ if (rt.is_imm()) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ ma_li(ScratchRegister, rt.immediate()); ++ mulw(rd, rs, ScratchRegister); ++ } else { ++ MOZ_ASSERT(rt.is_reg()); ++ mulw(rd, rs, rt.rm()); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_mulh32(Register rd, Register rs, Operand rt) { ++ if (rt.is_imm()) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ ma_li(ScratchRegister, rt.immediate()); ++ mul(rd, rs, ScratchRegister); ++ } else { ++ MOZ_ASSERT(rt.is_reg()); ++ mul(rd, rs, rt.rm()); ++ } ++ srai(rd, rd, 32); ++} ++ ++void MacroAssemblerRiscv64::ma_mul64(Register rd, Register rs, Operand rt) { ++ if (rt.is_imm()) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ ma_li(ScratchRegister, rt.immediate()); ++ mul(rd, rs, ScratchRegister); ++ } else { ++ MOZ_ASSERT(rt.is_reg()); ++ mul(rd, rs, rt.rm()); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_mulh64(Register rd, Register rs, Operand rt) { ++ if (rt.is_imm()) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ ma_li(ScratchRegister, rt.immediate()); ++ mulh(rd, rs, ScratchRegister); ++ } else { ++ MOZ_ASSERT(rt.is_reg()); ++ mulh(rd, rs, rt.rm()); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_sll64(Register rd, Register rs, Operand rt) { ++ if (rt.is_reg()) { ++ sll(rd, rs, rt.rm()); ++ } else { ++ MOZ_ASSERT(rt.is_imm()); ++ uint8_t shamt = static_cast(rt.immediate()); ++ slli(rd, rs, shamt); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_sll32(Register rd, Register rs, Operand rt) { ++ if (rt.is_reg()) { ++ sllw(rd, rs, rt.rm()); ++ } else { ++ MOZ_ASSERT(rt.is_imm()); ++ uint8_t shamt = static_cast(rt.immediate()); ++ slliw(rd, rs, shamt); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_sra64(Register rd, Register rs, Operand rt) { ++ if (rt.is_reg()) { ++ sra(rd, rs, rt.rm()); ++ } else { ++ MOZ_ASSERT(rt.is_imm()); ++ uint8_t shamt = static_cast(rt.immediate()); ++ srai(rd, rs, shamt); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_sra32(Register rd, Register rs, Operand rt) { ++ if (rt.is_reg()) { ++ sraw(rd, rs, rt.rm()); ++ } else { ++ MOZ_ASSERT(rt.is_imm()); ++ uint8_t shamt = static_cast(rt.immediate()); ++ sraiw(rd, rs, shamt); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_srl64(Register rd, Register rs, Operand rt) { ++ if (rt.is_reg()) { ++ srl(rd, rs, rt.rm()); ++ } else { ++ MOZ_ASSERT(rt.is_imm()); ++ uint8_t shamt = static_cast(rt.immediate()); ++ srli(rd, rs, shamt); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_srl32(Register rd, Register rs, Operand rt) { ++ if (rt.is_reg()) { ++ srlw(rd, rs, rt.rm()); ++ } else { ++ MOZ_ASSERT(rt.is_imm()); ++ uint8_t shamt = static_cast(rt.immediate()); ++ srliw(rd, rs, shamt); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_slt(Register rd, Register rs, Operand rt) { ++ if (rt.is_reg()) { ++ slt(rd, rs, rt.rm()); ++ } else { ++ MOZ_ASSERT(rt.is_imm()); ++ if (is_int12(rt.immediate())) { ++ slti(rd, rs, static_cast(rt.immediate())); ++ } else { ++ // li handles the relocation. ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ BlockTrampolinePoolScope block_trampoline_pool(this, 9); ++ ma_li(scratch, rt.immediate()); ++ slt(rd, rs, scratch); ++ } ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_sltu(Register rd, Register rs, Operand rt) { ++ if (rt.is_reg()) { ++ sltu(rd, rs, rt.rm()); ++ } else { ++ MOZ_ASSERT(rt.is_imm()); ++ if (is_int12(rt.immediate())) { ++ sltiu(rd, rs, static_cast(rt.immediate())); ++ } else { ++ // li handles the relocation. ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ BlockTrampolinePoolScope block_trampoline_pool(this, 9); ++ ma_li(scratch, rt.immediate()); ++ sltu(rd, rs, scratch); ++ } ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_sle(Register rd, Register rs, Operand rt) { ++ if (rt.is_reg()) { ++ slt(rd, rt.rm(), rs); ++ } else { ++ MOZ_ASSERT(rt.is_imm()); ++ // li handles the relocation. ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ BlockTrampolinePoolScope block_trampoline_pool(this, 9); ++ ma_li(scratch, rt.immediate()); ++ slt(rd, scratch, rs); ++ } ++ xori(rd, rd, 1); ++} ++ ++void MacroAssemblerRiscv64::ma_sleu(Register rd, Register rs, Operand rt) { ++ if (rt.is_reg()) { ++ sltu(rd, rt.rm(), rs); ++ } else { ++ MOZ_ASSERT(rt.is_imm()); ++ // li handles the relocation. ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ BlockTrampolinePoolScope block_trampoline_pool(this, 9); ++ ma_li(scratch, rt.immediate()); ++ sltu(rd, scratch, rs); ++ } ++ xori(rd, rd, 1); ++} ++ ++void MacroAssemblerRiscv64::ma_sgt(Register rd, Register rs, Operand rt) { ++ if (rt.is_reg()) { ++ slt(rd, rt.rm(), rs); ++ } else { ++ MOZ_ASSERT(rt.is_imm()); ++ // li handles the relocation. ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ BlockTrampolinePoolScope block_trampoline_pool(this, 9); ++ ma_li(scratch, rt.immediate()); ++ slt(rd, scratch, rs); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_sgtu(Register rd, Register rs, Operand rt) { ++ if (rt.is_reg()) { ++ sltu(rd, rt.rm(), rs); ++ } else { ++ MOZ_ASSERT(rt.is_imm()); ++ // li handles the relocation. ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ BlockTrampolinePoolScope block_trampoline_pool(this, 9); ++ ma_li(scratch, rt.immediate()); ++ sltu(rd, scratch, rs); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_sge(Register rd, Register rs, Operand rt) { ++ ma_slt(rd, rs, rt); ++ xori(rd, rd, 1); ++} ++ ++void MacroAssemblerRiscv64::ma_sgeu(Register rd, Register rs, Operand rt) { ++ ma_sltu(rd, rs, rt); ++ xori(rd, rd, 1); ++} ++ ++static inline bool IsZero(const Operand& rt) { ++ if (rt.is_reg()) { ++ return rt.rm() == zero_reg; ++ } else { ++ MOZ_ASSERT(rt.is_imm()); ++ return rt.immediate() == 0; ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_seq(Register rd, Register rs, Operand rt) { ++ if (rs == zero_reg) { ++ ma_seqz(rd, rt); ++ } else if (IsZero(rt)) { ++ seqz(rd, rs); ++ } else { ++ ma_sub64(rd, rs, rt); ++ seqz(rd, rd); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_sne(Register rd, Register rs, Operand rt) { ++ if (rs == zero_reg) { ++ ma_snez(rd, rt); ++ } else if (IsZero(rt)) { ++ snez(rd, rs); ++ } else { ++ ma_sub64(rd, rs, rt); ++ snez(rd, rd); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_seqz(Register rd, const Operand& rt) { ++ if (rt.is_reg()) { ++ seqz(rd, rt.rm()); ++ } else { ++ ma_li(rd, rt.immediate() == 0); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_snez(Register rd, const Operand& rt) { ++ if (rt.is_reg()) { ++ snez(rd, rt.rm()); ++ } else { ++ ma_li(rd, rt.immediate() != 0); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_neg(Register rd, const Operand& rt) { ++ MOZ_ASSERT(rt.is_reg()); ++ neg(rd, rt.rm()); ++} ++ ++void MacroAssemblerRiscv64::ma_jump(ImmPtr dest) { ++ DEBUG_PRINTF("[ %s\n", __FUNCTION__); ++ BlockTrampolinePoolScope block_trampoline_pool(this, 8); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ asMasm().ma_liPatchable(scratch, dest); ++ jr(scratch, 0); ++ DEBUG_PRINTF("]\n"); ++} ++// fp instructions ++void MacroAssemblerRiscv64::ma_lid(FloatRegister dest, double value) { ++ ImmWord imm(mozilla::BitwiseCast(value)); ++ ++ if (imm.value != 0) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ ma_li(scratch, imm); ++ fmv_d_x(dest, scratch); ++ } else { ++ fmv_d_x(dest, zero); ++ } ++} ++// fp instructions ++void MacroAssemblerRiscv64::ma_lis(FloatRegister dest, float value) { ++ Imm32 imm(mozilla::BitwiseCast(value)); ++ ++ if (imm.value != 0) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ ma_li(scratch, imm); ++ fmv_w_x(dest, scratch); ++ } else { ++ fmv_w_x(dest, zero); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_sub32TestOverflow(Register rd, Register rj, ++ Register rk, Label* overflow) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ sub(scratch, rj, rk); ++ subw(rd, rj, rk); ++ ma_b(rd, Register(scratch), overflow, Assembler::NotEqual); ++} ++ ++void MacroAssemblerRiscv64::ma_sub32TestOverflow(Register rd, Register rj, ++ Imm32 imm, Label* overflow) { ++ if (imm.value != INT32_MIN) { ++ asMasm().ma_add32TestOverflow(rd, rj, Imm32(-imm.value), overflow); ++ } else { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ MOZ_ASSERT(rj != scratch); ++ ma_li(scratch, Imm32(imm.value)); ++ asMasm().ma_sub32TestOverflow(rd, rj, scratch, overflow); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_add32TestOverflow(Register rd, Register rj, ++ Register rk, Label* overflow) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ add(scratch, rj, rk); ++ addw(rd, rj, rk); ++ ma_b(rd, Register(scratch), overflow, Assembler::NotEqual); ++} ++ ++void MacroAssemblerRiscv64::ma_add32TestOverflow(Register rd, Register rj, ++ Imm32 imm, Label* overflow) { ++ // Check for signed range because of addi ++ if (is_intn(imm.value, 12)) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ addi(scratch, rj, imm.value); ++ addiw(rd, rj, imm.value); ++ ma_b(rd, scratch, overflow, Assembler::NotEqual); ++ } else { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ ma_li(scratch2, imm); ++ ma_add32TestOverflow(rd, rj, scratch2, overflow); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_subPtrTestOverflow(Register rd, Register rj, ++ Register rk, ++ Label* overflow) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ MOZ_ASSERT_IF(rj == rd, rj != rk); ++ MOZ_ASSERT(rj != scratch2); ++ MOZ_ASSERT(rk != scratch2); ++ MOZ_ASSERT(rd != scratch2); ++ ++ Register rj_copy = rj; ++ ++ if (rj == rd) { ++ ma_or(scratch2, rj, zero); ++ rj_copy = scratch2; ++ } ++ ++ { ++ Register scratch = temps.Acquire(); ++ MOZ_ASSERT(rd != scratch); ++ ++ sub(rd, rj, rk); ++ // If the sign of rj and rk are the same, no overflow ++ ma_xor(scratch, rj_copy, rk); ++ // Check if the sign of rd and rj are the same ++ ma_xor(scratch2, rd, rj_copy); ++ ma_and(scratch2, scratch2, scratch); ++ } ++ ++ ma_b(scratch2, zero, overflow, Assembler::LessThan); ++} ++ ++void MacroAssemblerRiscv64::ma_addPtrTestOverflow(Register rd, Register rj, ++ Register rk, ++ Label* overflow) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ MOZ_ASSERT(rd != scratch); ++ ++ if (rj == rk) { ++ if (rj == rd) { ++ ma_or(scratch, rj, zero); ++ rj = scratch; ++ } ++ ++ add(rd, rj, rj); ++ ma_xor(scratch, rj, rd); ++ ma_b(scratch, zero, overflow, Assembler::LessThan); ++ } else { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ MOZ_ASSERT(rj != scratch); ++ MOZ_ASSERT(rd != scratch2); ++ ++ if (rj == rd) { ++ ma_or(scratch2, rj, zero); ++ rj = scratch2; ++ } ++ ++ add(rd, rj, rk); ++ slti(scratch, rj, 0); ++ slt(scratch2, rd, rj); ++ ma_b(scratch, Register(scratch2), overflow, Assembler::NotEqual); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_addPtrTestOverflow(Register rd, Register rj, ++ Imm32 imm, Label* overflow) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ ++ if (imm.value == 0) { ++ ori(rd, rj, 0); ++ return; ++ } ++ ++ if (rj == rd) { ++ ori(scratch2, rj, 0); ++ rj = scratch2; ++ } ++ ++ ma_add64(rd, rj, imm); ++ ++ if (imm.value > 0) { ++ ma_b(rd, rj, overflow, Assembler::LessThan); ++ } else { ++ MOZ_ASSERT(imm.value < 0); ++ ma_b(rd, rj, overflow, Assembler::GreaterThan); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_addPtrTestOverflow(Register rd, Register rj, ++ ImmWord imm, ++ Label* overflow) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ ++ if (imm.value == 0) { ++ ori(rd, rj, 0); ++ return; ++ } ++ ++ if (rj == rd) { ++ MOZ_ASSERT(rj != scratch2); ++ ori(scratch2, rj, 0); ++ rj = scratch2; ++ } ++ ++ ma_li(rd, imm); ++ add(rd, rj, rd); ++ ++ if (imm.value > 0) { ++ ma_b(rd, rj, overflow, Assembler::LessThan); ++ } else { ++ MOZ_ASSERT(imm.value < 0); ++ ma_b(rd, rj, overflow, Assembler::GreaterThan); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_add32TestCarry(Condition cond, Register rd, ++ Register rj, Register rk, ++ Label* overflow) { ++ MOZ_ASSERT(cond == Assembler::CarrySet || cond == Assembler::CarryClear); ++ MOZ_ASSERT_IF(rd == rj, rk != rd); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ addw(rd, rj, rk); ++ sltu(scratch, rd, rd == rj ? rk : rj); ++ ma_b(Register(scratch), Register(scratch), overflow, ++ cond == Assembler::CarrySet ? Assembler::NonZero : Assembler::Zero); ++} ++ ++void MacroAssemblerRiscv64::ma_add32TestCarry(Condition cond, Register rd, ++ Register rj, Imm32 imm, ++ Label* overflow) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ MOZ_ASSERT(rj != scratch2); ++ ma_li(scratch2, imm); ++ ma_add32TestCarry(cond, rd, rj, scratch2, overflow); ++} ++ ++void MacroAssemblerRiscv64::ma_subPtrTestOverflow(Register rd, Register rj, ++ Imm32 imm, Label* overflow) { ++ // TODO(loong64): Check subPtrTestOverflow ++ MOZ_ASSERT(imm.value != INT32_MIN); ++ ma_addPtrTestOverflow(rd, rj, Imm32(-imm.value), overflow); ++} ++ ++void MacroAssemblerRiscv64::ma_addPtrTestCarry(Condition cond, Register rd, ++ Register rj, Register rk, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ MOZ_ASSERT(rd != rk); ++ MOZ_ASSERT(rd != scratch); ++ add(rd, rj, rk); ++ sltu(scratch, rd, rk); ++ ma_b(scratch, Register(scratch), label, ++ cond == Assembler::CarrySet ? Assembler::NonZero : Assembler::Zero); ++} ++ ++void MacroAssemblerRiscv64::ma_addPtrTestCarry(Condition cond, Register rd, ++ Register rj, Imm32 imm, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ ++ // Check for signed range because of addi ++ if (is_intn(imm.value, 12)) { ++ addi(rd, rj, imm.value); ++ sltiu(scratch2, rd, imm.value); ++ ma_b(scratch2, scratch2, label, ++ cond == Assembler::CarrySet ? Assembler::NonZero : Assembler::Zero); ++ } else { ++ ma_li(scratch2, imm); ++ ma_addPtrTestCarry(cond, rd, rj, scratch2, label); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_addPtrTestCarry(Condition cond, Register rd, ++ Register rj, ImmWord imm, ++ Label* label) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ ++ // Check for signed range because of addi_d ++ if (is_intn(imm.value, 12)) { ++ uint32_t value = imm.value; ++ addi(rd, rj, value); ++ ma_sltu(scratch2, rd, Operand(value)); ++ ma_b(scratch2, scratch2, label, ++ cond == Assembler::CarrySet ? Assembler::NonZero : Assembler::Zero); ++ } else { ++ ma_li(scratch2, imm); ++ ma_addPtrTestCarry(cond, rd, rj, scratch2, label); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_load(Register dest, const BaseIndex& src, ++ LoadStoreSize size, ++ LoadStoreExtension extension) { ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ asMasm().computeScaledAddress(src, scratch2); ++ asMasm().ma_load(dest, Address(scratch2, src.offset), size, extension); ++} ++void MacroAssemblerRiscv64::ma_pop(FloatRegister f) { ++ fld(f, StackPointer, 0); ++ addi(StackPointer, StackPointer, sizeof(double)); ++} ++ ++void MacroAssemblerRiscv64::ma_push(FloatRegister f) { ++ addi(StackPointer, StackPointer, (int32_t) - sizeof(double)); ++ fsd(f, StackPointer, 0); ++} ++ ++void MacroAssemblerRiscv64::ma_fld_s(FloatRegister ft, Address address) { ++ int32_t offset = address.offset; ++ Register base = address.base; ++ ++ if (is_intn(offset, 12)) { ++ flw(ft, base, offset); ++ } else { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ MOZ_ASSERT(base != scratch); ++ ma_li(scratch, Imm32(offset)); ++ ma_add64(scratch, base, scratch); ++ flw(ft, scratch, 0); ++ } ++} ++void MacroAssemblerRiscv64::ma_fld_d(FloatRegister ft, Address address) { ++ int32_t offset = address.offset; ++ Register base = address.base; ++ ++ if (is_intn(offset, 12)) { ++ fld(ft, base, offset); ++ } else { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ MOZ_ASSERT(base != scratch); ++ ma_li(scratch, Imm32(offset)); ++ ma_add64(scratch, base, scratch); ++ fld(ft, scratch, 0); ++ } ++} ++void MacroAssemblerRiscv64::ma_fst_d(FloatRegister ft, Address address) { ++ int32_t offset = address.offset; ++ Register base = address.base; ++ ++ if (is_intn(offset, 12)) { ++ fsd(ft, base, offset); ++ } else { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ MOZ_ASSERT(base != scratch); ++ ma_li(scratch, Imm32(offset)); ++ ma_add64(scratch, base, scratch); ++ fsd(ft, scratch, 0); ++ } ++} ++void MacroAssemblerRiscv64::ma_fst_s(FloatRegister ft, Address address) { ++ int32_t offset = address.offset; ++ Register base = address.base; ++ ++ if (is_intn(offset, 12)) { ++ fsw(ft, base, offset); ++ } else { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ MOZ_ASSERT(base != scratch); ++ ma_li(scratch, Imm32(offset)); ++ ma_add64(scratch, base, scratch); ++ fsw(ft, scratch, 0); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_fst_d(FloatRegister ft, BaseIndex address) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ asMasm().computeScaledAddress(address, scratch); ++ asMasm().ma_fst_d(ft, Address(scratch, address.offset)); ++} ++ ++void MacroAssemblerRiscv64::ma_fst_s(FloatRegister ft, BaseIndex address) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ asMasm().computeScaledAddress(address, scratch); ++ asMasm().ma_fst_s(ft, Address(scratch, address.offset)); ++} ++ ++void MacroAssemblerRiscv64::ma_fld_d(FloatRegister ft, const BaseIndex& src) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ asMasm().computeScaledAddress(src, scratch); ++ asMasm().ma_fld_d(ft, Address(scratch, src.offset)); ++} ++ ++void MacroAssemblerRiscv64::ma_fld_s(FloatRegister ft, const BaseIndex& src) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ asMasm().computeScaledAddress(src, scratch); ++ asMasm().ma_fld_s(ft, Address(scratch, src.offset)); ++} ++ ++void MacroAssemblerRiscv64::ma_call(ImmPtr dest) { ++ DEBUG_PRINTF("[ %s\n", __FUNCTION__); ++ BlockTrampolinePoolScope block_trampoline_pool(this, 8); ++ UseScratchRegisterScope temps(this); ++ temps.Exclude(GeneralRegisterSet(1 << CallReg.code())); ++ asMasm().ma_liPatchable(CallReg, dest); ++ jalr(CallReg, 0); ++ DEBUG_PRINTF("]\n"); ++} ++ ++void MacroAssemblerRiscv64::CompareIsNotNanF32(Register rd, FPURegister cmp1, ++ FPURegister cmp2) { ++ UseScratchRegisterScope temps(this); ++ BlockTrampolinePoolScope block_trampoline_pool(this, 3); ++ Register scratch = temps.Acquire(); ++ ++ feq_s(rd, cmp1, cmp1); // rd <- !isNan(cmp1) ++ feq_s(scratch, cmp2, cmp2); // scratch <- !isNaN(cmp2) ++ ma_and(rd, rd, scratch); // rd <- !isNan(cmp1) && !isNan(cmp2) ++} ++ ++void MacroAssemblerRiscv64::CompareIsNotNanF64(Register rd, FPURegister cmp1, ++ FPURegister cmp2) { ++ UseScratchRegisterScope temps(this); ++ BlockTrampolinePoolScope block_trampoline_pool(this, 3); ++ Register scratch = temps.Acquire(); ++ ++ feq_d(rd, cmp1, cmp1); // rd <- !isNan(cmp1) ++ feq_d(scratch, cmp2, cmp2); // scratch <- !isNaN(cmp2) ++ ma_and(rd, rd, scratch); // rd <- !isNan(cmp1) && !isNan(cmp2) ++} ++ ++void MacroAssemblerRiscv64::CompareIsNanF32(Register rd, FPURegister cmp1, ++ FPURegister cmp2) { ++ CompareIsNotNanF32(rd, cmp1, cmp2); // rd <- !isNan(cmp1) && !isNan(cmp2) ++ ma_xor(rd, rd, Operand(1)); // rd <- isNan(cmp1) || isNan(cmp2) ++} ++ ++void MacroAssemblerRiscv64::CompareIsNanF64(Register rd, FPURegister cmp1, ++ FPURegister cmp2) { ++ CompareIsNotNanF64(rd, cmp1, cmp2); // rd <- !isNan(cmp1) && !isNan(cmp2) ++ ma_xor(rd, rd, Operand(1)); // rd <- isNan(cmp1) || isNan(cmp2) ++} ++ ++void MacroAssemblerRiscv64::Clz32(Register rd, Register xx) { ++ // 32 bit unsigned in lower word: count number of leading zeros. ++ // int n = 32; ++ // unsigned y; ++ ++ // y = x >>16; if (y != 0) { n = n -16; x = y; } ++ // y = x >> 8; if (y != 0) { n = n - 8; x = y; } ++ // y = x >> 4; if (y != 0) { n = n - 4; x = y; } ++ // y = x >> 2; if (y != 0) { n = n - 2; x = y; } ++ // y = x >> 1; if (y != 0) {rd = n - 2; return;} ++ // rd = n - x; ++ ++ Label L0, L1, L2, L3, L4; ++ UseScratchRegisterScope temps(this); ++ Register x = rd; ++ Register y = temps.Acquire(); ++ Register n = temps.Acquire(); ++ MOZ_ASSERT(xx != y && xx != n); ++ mv(x, xx); ++ ma_li(n, Imm32(32)); ++#if JS_CODEGEN_RISCV64 ++ srliw(y, x, 16); ++ ma_branch(&L0, Equal, y, Operand(zero_reg)); ++ mv(x, y); ++ addiw(n, n, -16); ++ bind(&L0); ++ srliw(y, x, 8); ++ ma_branch(&L1, Equal, y, Operand(zero_reg)); ++ addiw(n, n, -8); ++ mv(x, y); ++ bind(&L1); ++ srliw(y, x, 4); ++ ma_branch(&L2, Equal, y, Operand(zero_reg)); ++ addiw(n, n, -4); ++ mv(x, y); ++ bind(&L2); ++ srliw(y, x, 2); ++ ma_branch(&L3, Equal, y, Operand(zero_reg)); ++ addiw(n, n, -2); ++ mv(x, y); ++ bind(&L3); ++ srliw(y, x, 1); ++ subw(rd, n, x); ++ ma_branch(&L4, Equal, y, Operand(zero_reg)); ++ addiw(rd, n, -2); ++ bind(&L4); ++#elif JS_CODEGEN_RISCV32 ++ srli(y, x, 16); ++ ma_branch(&L0, Equal, y, Operand(zero_reg)); ++ mv(x, y); ++ addi(n, n, -16); ++ bind(&L0); ++ srli(y, x, 8); ++ ma_branch(&L1, Equal, y, Operand(zero_reg)); ++ addi(n, n, -8); ++ mv(x, y); ++ bind(&L1); ++ srli(y, x, 4); ++ ma_branch(&L2, Equal, y, Operand(zero_reg)); ++ addi(n, n, -4); ++ mv(x, y); ++ bind(&L2); ++ srli(y, x, 2); ++ ma_branch(&L3, Equal, y, Operand(zero_reg)); ++ addi(n, n, -2); ++ mv(x, y); ++ bind(&L3); ++ srli(y, x, 1); ++ sub(rd, n, x); ++ ma_branch(&L4, Equal, y, Operand(zero_reg)); ++ addi(rd, n, -2); ++ bind(&L4); ++#endif ++} ++ ++#if JS_CODEGEN_RISCV64 ++void MacroAssemblerRiscv64::Clz64(Register rd, Register xx) { ++ // 64 bit: count number of leading zeros. ++ // int n = 64; ++ // unsigned y; ++ ++ // y = x >>32; if (y != 0) { n = n - 32; x = y; } ++ // y = x >>16; if (y != 0) { n = n - 16; x = y; } ++ // y = x >> 8; if (y != 0) { n = n - 8; x = y; } ++ // y = x >> 4; if (y != 0) { n = n - 4; x = y; } ++ // y = x >> 2; if (y != 0) { n = n - 2; x = y; } ++ // y = x >> 1; if (y != 0) {rd = n - 2; return;} ++ // rd = n - x; ++ ++ Label L0, L1, L2, L3, L4, L5; ++ UseScratchRegisterScope temps(this); ++ Register x = rd; ++ Register y = temps.Acquire(); ++ Register n = temps.Acquire(); ++ MOZ_ASSERT(xx != y && xx != n); ++ mv(x, xx); ++ ma_li(n, Imm32(64)); ++ srli(y, x, 32); ++ ma_branch(&L0, Equal, y, Operand(zero_reg)); ++ addiw(n, n, -32); ++ mv(x, y); ++ bind(&L0); ++ srli(y, x, 16); ++ ma_branch(&L1, Equal, y, Operand(zero_reg)); ++ addiw(n, n, -16); ++ mv(x, y); ++ bind(&L1); ++ srli(y, x, 8); ++ ma_branch(&L2, Equal, y, Operand(zero_reg)); ++ addiw(n, n, -8); ++ mv(x, y); ++ bind(&L2); ++ srli(y, x, 4); ++ ma_branch(&L3, Equal, y, Operand(zero_reg)); ++ addiw(n, n, -4); ++ mv(x, y); ++ bind(&L3); ++ srli(y, x, 2); ++ ma_branch(&L4, Equal, y, Operand(zero_reg)); ++ addiw(n, n, -2); ++ mv(x, y); ++ bind(&L4); ++ srli(y, x, 1); ++ subw(rd, n, x); ++ ma_branch(&L5, Equal, y, Operand(zero_reg)); ++ addiw(rd, n, -2); ++ bind(&L5); ++} ++#endif ++void MacroAssemblerRiscv64::Ctz32(Register rd, Register rs) { ++ // Convert trailing zeroes to trailing ones, and bits to their left ++ // to zeroes. ++ ++ { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ ma_add64(scratch, rs, Operand(-1)); ++ ma_xor(rd, scratch, rs); ++ ma_and(rd, rd, scratch); ++ // Count number of leading zeroes. ++ } ++ Clz32(rd, rd); ++ { ++ // Subtract number of leading zeroes from 32 to get number of trailing ++ // ones. Remember that the trailing ones were formerly trailing zeroes. ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ ma_li(scratch, Imm32(32)); ++ ma_sub32(rd, scratch, rd); ++ } ++} ++#if JS_CODEGEN_RISCV64 ++void MacroAssemblerRiscv64::Ctz64(Register rd, Register rs) { ++ // Convert trailing zeroes to trailing ones, and bits to their left ++ // to zeroes. ++ BlockTrampolinePoolScope block_trampoline_pool(this, 40); ++ { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ ma_add64(scratch, rs, Operand(-1)); ++ ma_xor(rd, scratch, rs); ++ ma_and(rd, rd, scratch); ++ // Count number of leading zeroes. ++ } ++ Clz64(rd, rd); ++ { ++ // Subtract number of leading zeroes from 64 to get number of trailing ++ // ones. Remember that the trailing ones were formerly trailing zeroes. ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ ma_li(scratch, 64); ++ ma_sub64(rd, scratch, rd); ++ } ++} ++#endif ++void MacroAssemblerRiscv64::Popcnt32(Register rd, Register rs, ++ Register scratch) { ++ MOZ_ASSERT(scratch != rs); ++ MOZ_ASSERT(scratch != rd); ++ // https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel ++ // ++ // A generalization of the best bit counting method to integers of ++ // bit-widths up to 128 (parameterized by type T) is this: ++ // ++ // v = v - ((v >> 1) & (T)~(T)0/3); // temp ++ // v = (v & (T)~(T)0/15*3) + ((v >> 2) & (T)~(T)0/15*3); // temp ++ // v = (v + (v >> 4)) & (T)~(T)0/255*15; // temp ++ // c = (T)(v * ((T)~(T)0/255)) >> (sizeof(T) - 1) * BITS_PER_BYTE; //count ++ // ++ // There are algorithms which are faster in the cases where very few ++ // bits are set but the algorithm here attempts to minimize the total ++ // number of instructions executed even when a large number of bits ++ // are set. ++ // The number of instruction is 20. ++ // uint32_t B0 = 0x55555555; // (T)~(T)0/3 ++ // uint32_t B1 = 0x33333333; // (T)~(T)0/15*3 ++ // uint32_t B2 = 0x0F0F0F0F; // (T)~(T)0/255*15 ++ // uint32_t value = 0x01010101; // (T)~(T)0/255 ++ ++ uint32_t shift = 24; ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ Register value = temps.Acquire(); ++ MOZ_ASSERT((rd != value) && (rs != value)); ++ ma_li(value, 0x01010101); // value = 0x01010101; ++ ma_li(scratch2, 0x55555555); // B0 = 0x55555555; ++ ma_srl32(scratch, rs, Operand(1)); ++ ma_and(scratch, scratch, scratch2); ++ ma_sub32(scratch, rs, scratch); ++ ma_li(scratch2, 0x33333333); // B1 = 0x33333333; ++ slli(rd, scratch2, 4); ++ or_(scratch2, scratch2, rd); ++ ma_and(rd, scratch, scratch2); ++ ma_srl32(scratch, scratch, Operand(2)); ++ ma_and(scratch, scratch, scratch2); ++ ma_add32(scratch, rd, scratch); ++ ma_srl32(rd, scratch, Operand(4)); ++ ma_add32(rd, rd, scratch); ++ ma_li(scratch2, 0xF); ++ ma_mul32(scratch2, value, scratch2); // B2 = 0x0F0F0F0F; ++ ma_and(rd, rd, scratch2); ++ ma_mul32(rd, rd, value); ++ ma_srl32(rd, rd, Operand(shift)); ++} ++ ++#if JS_CODEGEN_RISCV64 ++void MacroAssemblerRiscv64::Popcnt64(Register rd, Register rs, ++ Register scratch) { ++ MOZ_ASSERT(scratch != rs); ++ MOZ_ASSERT(scratch != rd); ++ // uint64_t B0 = 0x5555555555555555l; // (T)~(T)0/3 ++ // uint64_t B1 = 0x3333333333333333l; // (T)~(T)0/15*3 ++ // uint64_t B2 = 0x0F0F0F0F0F0F0F0Fl; // (T)~(T)0/255*15 ++ // uint64_t value = 0x0101010101010101l; // (T)~(T)0/255 ++ // uint64_t shift = 24; // (sizeof(T) - 1) * BITS_PER_BYTE ++ uint64_t shift = 24; ++ UseScratchRegisterScope temps(this); ++ Register scratch2 = temps.Acquire(); ++ Register value = temps.Acquire(); ++ MOZ_ASSERT((rd != value) && (rs != value)); ++ ma_li(value, 0x1111111111111111l); // value = 0x1111111111111111l; ++ ma_li(scratch2, 5); ++ ma_mul64(scratch2, value, scratch2); // B0 = 0x5555555555555555l; ++ ma_srl64(scratch, rs, Operand(1)); ++ ma_and(scratch, scratch, scratch2); ++ ma_sub64(scratch, rs, scratch); ++ ma_li(scratch2, 3); ++ ma_mul64(scratch2, value, scratch2); // B1 = 0x3333333333333333l; ++ ma_and(rd, scratch, scratch2); ++ ma_srl64(scratch, scratch, Operand(2)); ++ ma_and(scratch, scratch, scratch2); ++ ma_add64(scratch, rd, scratch); ++ ma_srl64(rd, scratch, Operand(4)); ++ ma_add64(rd, rd, scratch); ++ ma_li(scratch2, 0xF); ++ ma_li(value, 0x0101010101010101l); // value = 0x0101010101010101l; ++ ma_mul64(scratch2, value, scratch2); // B2 = 0x0F0F0F0F0F0F0F0Fl; ++ ma_and(rd, rd, scratch2); ++ ma_mul64(rd, rd, value); ++ srli(rd, rd, 32 + shift); ++} ++#endif ++ ++void MacroAssemblerRiscv64::ma_div_branch_overflow(Register rd, Register rj, ++ Register rk, ++ Label* overflow) { ++ ScratchRegisterScope scratch(asMasm()); ++ ma_mod32(scratch, rj, rk); ++ ma_b(scratch, scratch, overflow, Assembler::NonZero); ++ divw(rd, rj, rk); ++} ++ ++void MacroAssemblerRiscv64::ma_div_branch_overflow(Register rd, Register rj, ++ Imm32 imm, Label* overflow) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ ma_li(scratch, imm); ++ ma_div_branch_overflow(rd, rj, scratch, overflow); ++} ++ ++void MacroAssemblerRiscv64::ma_mod_mask(Register src, Register dest, ++ Register hold, Register remain, ++ int32_t shift, Label* negZero) { ++ // MATH: ++ // We wish to compute x % (1< 0, store sum - C back into sum, thus performing a ++ // modulus. ++ ma_b(scratch2, Register(scratch2), &sumSigned, Signed, ShortJump); ++ or_(dest, scratch2, zero); ++ bind(&sumSigned); ++ // Get rid of the bits that we extracted before. ++ srliw(remain, remain, shift); ++ // If the shift produced zero, finish, otherwise, continue in the loop. ++ ma_b(remain, remain, &head, NonZero, ShortJump); ++ // Check the hold to see if we need to negate the result. ++ ma_b(hold, hold, &done, NotSigned, ShortJump); ++ ++ // If the hold was non-zero, negate the result to be in line with ++ // what JS wants ++ if (negZero != nullptr) { ++ // Jump out in case of negative zero. ++ ma_b(hold, hold, negZero, Zero); ++ subw(dest, zero, dest); ++ } else { ++ subw(dest, zero, dest); ++ } ++ ++ bind(&done); ++} ++ ++void MacroAssemblerRiscv64::ma_fmovz(FloatFormat fmt, FloatRegister fd, ++ FloatRegister fj, Register rk) { ++ Label done; ++ ma_b(rk, zero, &done, Assembler::NotEqual); ++ if (fmt == SingleFloat) { ++ fmv_s(fd, fj); ++ } else { ++ fmv_d(fd, fj); ++ } ++ bind(&done); ++} ++ ++void MacroAssemblerRiscv64::ByteSwap(Register rd, Register rs, int operand_size, ++ Register scratch) { ++ MOZ_ASSERT(scratch != rs); ++ MOZ_ASSERT(scratch != rd); ++ MOZ_ASSERT(operand_size == 4 || operand_size == 8); ++ if (operand_size == 4) { ++ // Uint32_t x1 = 0x00FF00FF; ++ // x0 = (x0 << 16 | x0 >> 16); ++ // x0 = (((x0 & x1) << 8) | ((x0 & (x1 << 8)) >> 8)); ++ UseScratchRegisterScope temps(this); ++ BlockTrampolinePoolScope block_trampoline_pool(this, 17); ++ MOZ_ASSERT((rd != t6) && (rs != t6)); ++ Register x0 = temps.Acquire(); ++ Register x1 = temps.Acquire(); ++ Register x2 = scratch; ++ RV_li(x1, 0x00FF00FF); ++ slliw(x0, rs, 16); ++ srliw(rd, rs, 16); ++ or_(x0, rd, x0); // x0 <- x0 << 16 | x0 >> 16 ++ and_(x2, x0, x1); // x2 <- x0 & 0x00FF00FF ++ slliw(x2, x2, 8); // x2 <- (x0 & x1) << 8 ++ slliw(x1, x1, 8); // x1 <- 0xFF00FF00 ++ and_(rd, x0, x1); // x0 & 0xFF00FF00 ++ srliw(rd, rd, 8); ++ or_(rd, rd, x2); // (((x0 & x1) << 8) | ((x0 & (x1 << 8)) >> 8)) ++ } else { ++ // uinx24_t x1 = 0x0000FFFF0000FFFFl; ++ // uinx24_t x1 = 0x00FF00FF00FF00FFl; ++ // x0 = (x0 << 32 | x0 >> 32); ++ // x0 = (x0 & x1) << 16 | (x0 & (x1 << 16)) >> 16; ++ // x0 = (x0 & x1) << 8 | (x0 & (x1 << 8)) >> 8; ++ UseScratchRegisterScope temps(this); ++ BlockTrampolinePoolScope block_trampoline_pool(this, 30); ++ MOZ_ASSERT((rd != t6) && (rs != t6)); ++ Register x0 = temps.Acquire(); ++ Register x1 = temps.Acquire(); ++ Register x2 = scratch; ++ RV_li(x1, 0x0000FFFF0000FFFFl); ++ slli(x0, rs, 32); ++ srli(rd, rs, 32); ++ or_(x0, rd, x0); // x0 <- x0 << 32 | x0 >> 32 ++ and_(x2, x0, x1); // x2 <- x0 & 0x0000FFFF0000FFFF ++ slli(x2, x2, 16); // x2 <- (x0 & 0x0000FFFF0000FFFF) << 16 ++ slli(x1, x1, 16); // x1 <- 0xFFFF0000FFFF0000 ++ and_(rd, x0, x1); // rd <- x0 & 0xFFFF0000FFFF0000 ++ srli(rd, rd, 16); // rd <- x0 & (x1 << 16)) >> 16 ++ or_(x0, rd, x2); // (x0 & x1) << 16 | (x0 & (x1 << 16)) >> 16; ++ RV_li(x1, 0x00FF00FF00FF00FFl); ++ and_(x2, x0, x1); // x2 <- x0 & 0x00FF00FF00FF00FF ++ slli(x2, x2, 8); // x2 <- (x0 & x1) << 8 ++ slli(x1, x1, 8); // x1 <- 0xFF00FF00FF00FF00 ++ and_(rd, x0, x1); ++ srli(rd, rd, 8); // rd <- (x0 & (x1 << 8)) >> 8 ++ or_(rd, rd, x2); // (((x0 & x1) << 8) | ((x0 & (x1 << 8)) >> 8)) ++ } ++} ++ ++template ++void MacroAssemblerRiscv64::FloatMinMaxHelper(FPURegister dst, FPURegister src1, ++ FPURegister src2, ++ MaxMinKind kind) { ++ MOZ_ASSERT((std::is_same::value) || ++ (std::is_same::value)); ++ ++ if (src1 == src2 && dst != src1) { ++ if (std::is_same::value) { ++ fmv_s(dst, src1); ++ } else { ++ fmv_d(dst, src1); ++ } ++ return; ++ } ++ ++ Label done, nan; ++ ++ // For RISCV, fmin_s returns the other non-NaN operand as result if only one ++ // operand is NaN; but for JS, if any operand is NaN, result is Nan. The ++ // following handles the discrepency between handling of NaN between ISA and ++ // JS semantics ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ if (std::is_same::value) { ++ CompareIsNotNanF32(scratch, src1, src2); ++ } else { ++ CompareIsNotNanF64(scratch, src1, src2); ++ } ++ BranchFalseF(scratch, &nan); ++ ++ if (kind == MaxMinKind::kMax) { ++ if (std::is_same::value) { ++ fmax_s(dst, src1, src2); ++ } else { ++ fmax_d(dst, src1, src2); ++ } ++ } else { ++ if (std::is_same::value) { ++ fmin_s(dst, src1, src2); ++ } else { ++ fmin_d(dst, src1, src2); ++ } ++ } ++ jump(&done); ++ ++ bind(&nan); ++ // if any operand is NaN, return NaN (fadd returns NaN if any operand is NaN) ++ if (std::is_same::value) { ++ fadd_s(dst, src1, src2); ++ } else { ++ fadd_d(dst, src1, src2); ++ } ++ ++ bind(&done); ++} ++ ++void MacroAssemblerRiscv64::Float32Max(FPURegister dst, FPURegister src1, ++ FPURegister src2) { ++ comment(__FUNCTION__); ++ FloatMinMaxHelper(dst, src1, src2, MaxMinKind::kMax); ++} ++ ++void MacroAssemblerRiscv64::Float32Min(FPURegister dst, FPURegister src1, ++ FPURegister src2) { ++ comment(__FUNCTION__); ++ FloatMinMaxHelper(dst, src1, src2, MaxMinKind::kMin); ++} ++ ++void MacroAssemblerRiscv64::Float64Max(FPURegister dst, FPURegister src1, ++ FPURegister src2) { ++ comment(__FUNCTION__); ++ FloatMinMaxHelper(dst, src1, src2, MaxMinKind::kMax); ++} ++ ++void MacroAssemblerRiscv64::Float64Min(FPURegister dst, FPURegister src1, ++ FPURegister src2) { ++ comment(__FUNCTION__); ++ FloatMinMaxHelper(dst, src1, src2, MaxMinKind::kMin); ++} ++ ++void MacroAssemblerRiscv64::BranchTrueShortF(Register rs, Label* target) { ++ ma_branch(target, NotEqual, rs, Operand(zero_reg)); ++} ++ ++void MacroAssemblerRiscv64::BranchFalseShortF(Register rs, Label* target) { ++ ma_branch(target, Equal, rs, Operand(zero_reg)); ++} ++ ++void MacroAssemblerRiscv64::BranchTrueF(Register rs, Label* target) { ++ bool long_branch = target->bound() ? !is_near(target) : false; ++ if (long_branch) { ++ Label skip; ++ BranchFalseShortF(rs, &skip); ++ BranchLong(target); ++ bind(&skip); ++ } else { ++ BranchTrueShortF(rs, target); ++ } ++} ++ ++void MacroAssemblerRiscv64::BranchFalseF(Register rs, Label* target) { ++ bool long_branch = target->bound() ? !is_near(target) : false; ++ if (long_branch) { ++ Label skip; ++ BranchTrueShortF(rs, &skip); ++ BranchLong(target); ++ bind(&skip); ++ } else { ++ BranchFalseShortF(rs, target); ++ } ++} ++ ++void MacroAssemblerRiscv64::Ror(Register rd, Register rs, const Operand& rt) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ BlockTrampolinePoolScope block_trampoline_pool(this, 8); ++ if (rt.is_reg()) { ++ negw(scratch, rt.rm()); ++ sllw(scratch, rs, scratch); ++ srlw(rd, rs, rt.rm()); ++ or_(rd, scratch, rd); ++ sext_w(rd, rd); ++ } else { ++ int64_t ror_value = rt.immediate() % 32; ++ if (ror_value == 0) { ++ mv(rd, rs); ++ return; ++ } else if (ror_value < 0) { ++ ror_value += 32; ++ } ++ srliw(scratch, rs, ror_value); ++ slliw(rd, rs, 32 - ror_value); ++ or_(rd, scratch, rd); ++ sext_w(rd, rd); ++ } ++} ++ ++void MacroAssemblerRiscv64::Dror(Register rd, Register rs, const Operand& rt) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ BlockTrampolinePoolScope block_trampoline_pool(this, 8); ++ if (rt.is_reg()) { ++ negw(scratch, rt.rm()); ++ sll(scratch, rs, scratch); ++ srl(rd, rs, rt.rm()); ++ or_(rd, scratch, rd); ++ } else { ++ int64_t dror_value = rt.immediate() % 64; ++ if (dror_value == 0) { ++ mv(rd, rs); ++ return; ++ } else if (dror_value < 0) { ++ dror_value += 64; ++ } ++ srli(scratch, rs, dror_value); ++ slli(rd, rs, 64 - dror_value); ++ or_(rd, scratch, rd); ++ } ++} ++ ++void MacroAssemblerRiscv64::wasmLoadImpl(const wasm::MemoryAccessDesc& access, ++ Register memoryBase, Register ptr, ++ Register ptrScratch, ++ AnyRegister output, Register tmp) { ++ uint32_t offset = access.offset(); ++ MOZ_ASSERT(offset < asMasm().wasmMaxOffsetGuardLimit()); ++ MOZ_ASSERT_IF(offset, ptrScratch != InvalidReg); ++ ++ // Maybe add the offset. ++ if (offset) { ++ asMasm().addPtr(ImmWord(offset), ptrScratch); ++ ptr = ptrScratch; ++ } ++ ++ asMasm().memoryBarrierBefore(access.sync()); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ switch (access.type()) { ++ case Scalar::Int8: ++ add(scratch, memoryBase, ptr); ++ lb(output.gpr(), scratch, 0); ++ break; ++ case Scalar::Uint8: ++ add(scratch, memoryBase, ptr); ++ lbu(output.gpr(), scratch, 0); ++ break; ++ case Scalar::Int16: ++ add(scratch, memoryBase, ptr); ++ lh(output.gpr(), scratch, 0); ++ break; ++ case Scalar::Uint16: ++ add(scratch, memoryBase, ptr); ++ lhu(output.gpr(), scratch, 0); ++ break; ++ case Scalar::Int32: ++ add(scratch, memoryBase, ptr); ++ lw(output.gpr(), scratch, 0); ++ break; ++ case Scalar::Uint32: ++ add(scratch, memoryBase, ptr); ++ lwu(output.gpr(), scratch, 0); ++ break; ++ case Scalar::Float64: ++ add(scratch, memoryBase, ptr); ++ fld(output.fpu(), scratch, 0); ++ break; ++ case Scalar::Float32: ++ add(scratch, memoryBase, ptr); ++ flw(output.fpu(), scratch, 0); ++ break; ++ default: ++ MOZ_CRASH("unexpected array type"); ++ } ++ ++ asMasm().append(access, asMasm().size() - 4); ++ asMasm().memoryBarrierAfter(access.sync()); ++} ++ ++void MacroAssemblerRiscv64::wasmStoreImpl(const wasm::MemoryAccessDesc& access, ++ AnyRegister value, ++ Register memoryBase, Register ptr, ++ Register ptrScratch, Register tmp) { ++ uint32_t offset = access.offset(); ++ MOZ_ASSERT(offset < asMasm().wasmMaxOffsetGuardLimit()); ++ MOZ_ASSERT_IF(offset, ptrScratch != InvalidReg); ++ ++ // Maybe add the offset. ++ if (offset) { ++ asMasm().addPtr(ImmWord(offset), ptrScratch); ++ ptr = ptrScratch; ++ } ++ ++ unsigned byteSize = access.byteSize(); ++ bool isSigned; ++ bool isFloat = false; ++ ++ switch (access.type()) { ++ case Scalar::Int8: ++ isSigned = true; ++ break; ++ case Scalar::Uint8: ++ isSigned = false; ++ break; ++ case Scalar::Int16: ++ isSigned = true; ++ break; ++ case Scalar::Uint16: ++ isSigned = false; ++ break; ++ case Scalar::Int32: ++ isSigned = true; ++ break; ++ case Scalar::Uint32: ++ isSigned = false; ++ break; ++ case Scalar::Int64: ++ isSigned = true; ++ break; ++ case Scalar::Float64: ++ isFloat = true; ++ break; ++ case Scalar::Float32: ++ isFloat = true; ++ break; ++ default: ++ MOZ_CRASH("unexpected array type"); ++ } ++ ++ BaseIndex address(memoryBase, ptr, TimesOne); ++ asMasm().memoryBarrierBefore(access.sync()); ++ if (isFloat) { ++ if (byteSize == 4) { ++ asMasm().ma_fst_s(value.fpu(), address); ++ } else { ++ asMasm().ma_fst_d(value.fpu(), address); ++ } ++ } else { ++ asMasm().ma_store(value.gpr(), address, ++ static_cast(8 * byteSize), ++ isSigned ? SignExtend : ZeroExtend); ++ } ++ // Only the last emitted instruction is a memory access. ++ asMasm().append(access, asMasm().size() - 4); ++ asMasm().memoryBarrierAfter(access.sync()); ++} ++ ++void MacroAssemblerRiscv64::GenPCRelativeJumpAndLink(Register rd, ++ int32_t imm32) { ++ MOZ_ASSERT(is_int32(imm32 + 0x800)); ++ int32_t Hi20 = ((imm32 + 0x800) >> 12); ++ int32_t Lo12 = imm32 << 20 >> 20; ++ auipc(rd, Hi20); // Read PC + Hi20 into scratch. ++ jalr(rd, Lo12); // jump PC + Hi20 + Lo12 ++} ++ ++void MacroAssemblerRiscv64::BranchAndLinkShortHelper(int32_t offset, Label* L) { ++ MOZ_ASSERT(L == nullptr || offset == 0); ++ offset = GetOffset(offset, L, OffsetSize::kOffset21); ++ jal(offset); ++} ++ ++void MacroAssemblerRiscv64::BranchAndLinkShort(int32_t offset) { ++ MOZ_ASSERT(is_int21(offset)); ++ BranchAndLinkShortHelper(offset, nullptr); ++} ++ ++void MacroAssemblerRiscv64::BranchAndLinkShort(Label* L) { ++ BranchAndLinkShortHelper(0, L); ++} ++ ++void MacroAssemblerRiscv64::BranchAndLink(Label* L) { ++ if (L->bound()) { ++ if (is_near(L)) { ++ BranchAndLinkShort(L); ++ } else { ++ BranchAndLinkLong(L); ++ } ++ } else { ++ BranchAndLinkShort(L); ++ } ++} ++ ++void MacroAssemblerRiscv64::ma_fmv_d(FloatRegister src, ValueOperand dest) { ++ fmv_x_d(dest.valueReg(), src); ++} ++ ++void MacroAssemblerRiscv64::ma_fmv_d(ValueOperand src, FloatRegister dest) { ++ fmv_d_x(dest, src.valueReg()); ++} ++ ++void MacroAssemblerRiscv64::ma_fmv_w(FloatRegister src, ValueOperand dest) { ++ fmv_x_w(dest.valueReg(), src); ++} ++ ++void MacroAssemblerRiscv64::ma_fmv_w(ValueOperand src, FloatRegister dest) { ++ fmv_w_x(dest, src.valueReg()); ++} ++ ++} // namespace jit ++} // namespace js +diff --git a/js/src/jit/riscv64/MacroAssembler-riscv64.h b/js/src/jit/riscv64/MacroAssembler-riscv64.h +new file mode 100644 +index 0000000000..d0c2bda638 +--- /dev/null ++++ b/js/src/jit/riscv64/MacroAssembler-riscv64.h +@@ -0,0 +1,1206 @@ ++/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- ++ * vim: set ts=8 sts=2 et sw=2 tw=80: ++ * 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/. */ ++ ++// Copyright 2021 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++#ifndef jit_riscv64_MacroAssembler_riscv64_h ++#define jit_riscv64_MacroAssembler_riscv64_h ++ ++#include ++ ++#include "jit/MoveResolver.h" ++#include "jit/riscv64/Assembler-riscv64.h" ++#include "wasm/WasmTypeDecls.h" ++ ++namespace js { ++namespace jit { ++ ++static Register CallReg = t6; ++ ++enum LiFlags { ++ Li64 = 0, ++ Li48 = 1, ++}; ++ ++class CompactBufferReader; ++enum LoadStoreSize { ++ SizeByte = 8, ++ SizeHalfWord = 16, ++ SizeWord = 32, ++ SizeDouble = 64 ++}; ++ ++enum LoadStoreExtension { ZeroExtend = 0, SignExtend = 1 }; ++enum JumpKind { LongJump = 0, ShortJump = 1 }; ++enum FloatFormat { SingleFloat, DoubleFloat }; ++class ScratchTagScope : public ScratchRegisterScope { ++ public: ++ ScratchTagScope(MacroAssembler& masm, const ValueOperand&) ++ : ScratchRegisterScope(masm) {} ++}; ++ ++class ScratchTagScopeRelease { ++ ScratchTagScope* ts_; ++ ++ public: ++ explicit ScratchTagScopeRelease(ScratchTagScope* ts) : ts_(ts) { ++ ts_->release(); ++ } ++ ~ScratchTagScopeRelease() { ts_->reacquire(); } ++}; ++ ++struct ImmTag : public Imm32 { ++ ImmTag(JSValueTag mask) : Imm32(int32_t(mask)) {} ++}; ++ ++class MacroAssemblerRiscv64 : public Assembler { ++ public: ++ MacroAssemblerRiscv64() {} ++ ++#ifdef JS_SIMULATOR_RISCV64 ++ // See riscv64/base-constants-riscv.h DebugParameters. ++ void Debug(uint32_t parameters) { break_(parameters, false); } ++#endif ++ ++ // Perform a downcast. Should be removed by Bug 996602. ++ MacroAssembler& asMasm(); ++ const MacroAssembler& asMasm() const; ++ ++ MoveResolver moveResolver_; ++ ++ static bool SupportsFloatingPoint() { return true; } ++ static bool SupportsUnalignedAccesses() { return true; } ++ static bool SupportsFastUnalignedFPAccesses() { return true; } ++ void haltingAlign(int alignment) { ++ // TODO(loong64): Implement a proper halting align. ++ nopAlign(alignment); ++ } ++ ++ // TODO(RISCV) Reorder parameters so out parameters come last. ++ bool CalculateOffset(Label* L, int32_t* offset, OffsetSize bits); ++ int32_t GetOffset(int32_t offset, Label* L, OffsetSize bits); ++ ++ inline void GenPCRelativeJump(Register rd, int32_t imm32) { ++ MOZ_ASSERT(is_int32(imm32 + 0x800)); ++ int32_t Hi20 = ((imm32 + 0x800) >> 12); ++ int32_t Lo12 = imm32 << 20 >> 20; ++ auipc(rd, Hi20); // Read PC + Hi20 into scratch. ++ jr(rd, Lo12); // jump PC + Hi20 + Lo12 ++ } ++ ++ // load ++ void ma_load(Register dest, Address address, LoadStoreSize size = SizeWord, ++ LoadStoreExtension extension = SignExtend); ++ void ma_load(Register dest, const BaseIndex& src, ++ LoadStoreSize size = SizeWord, ++ LoadStoreExtension extension = SignExtend); ++ void ma_loadDouble(FloatRegister dest, Address address); ++ void ma_loadFloat(FloatRegister dest, Address address); ++ // store ++ void ma_store(Register data, Address address, LoadStoreSize size = SizeWord, ++ LoadStoreExtension extension = SignExtend); ++ void ma_store(Register data, const BaseIndex& dest, ++ LoadStoreSize size = SizeWord, ++ LoadStoreExtension extension = SignExtend); ++ void ma_store(Imm32 imm, const BaseIndex& dest, LoadStoreSize size = SizeWord, ++ LoadStoreExtension extension = SignExtend); ++ void ma_store(Imm32 imm, Address address, LoadStoreSize size = SizeWord, ++ LoadStoreExtension extension = SignExtend); ++ void ma_storeDouble(FloatRegister dest, Address address); ++ void ma_storeFloat(FloatRegister dest, Address address); ++ void ma_liPatchable(Register dest, Imm32 imm); ++ void ma_liPatchable(Register dest, ImmPtr imm); ++ void ma_liPatchable(Register dest, ImmWord imm, LiFlags flags = Li48); ++ void ma_li(Register dest, ImmGCPtr ptr); ++ void ma_li(Register dest, Imm32 imm); ++ void ma_li(Register dest, Imm64 imm); ++ void ma_li(Register dest, intptr_t imm) { RV_li(dest, imm); } ++ void ma_li(Register dest, CodeLabel* label); ++ void ma_li(Register dest, ImmWord imm); ++ ++ // branches when done from within la-specific code ++ void ma_b(Register lhs, Register rhs, Label* l, Condition c, ++ JumpKind jumpKind = LongJump); ++ void ma_b(Register lhs, Imm32 imm, Label* l, Condition c, ++ JumpKind jumpKind = LongJump); ++ void BranchAndLinkShort(Label* L); ++ void BranchAndLink(Label* label); ++ void BranchAndLinkShort(int32_t offset); ++ void BranchAndLinkShortHelper(int32_t offset, Label* L); ++ void BranchAndLinkLong(Label* L); ++ void GenPCRelativeJumpAndLink(Register rd, int32_t imm32); ++ ++#define DEFINE_INSTRUCTION(instr) \ ++ void instr(Register rd, Register rj, Operand rt); \ ++ void instr(Register rd, Register rj, Imm32 imm) { \ ++ instr(rd, rj, Operand(imm.value)); \ ++ } \ ++ void instr(Register rd, Imm32 imm) { instr(rd, rd, Operand(imm.value)); } \ ++ void instr(Register rd, Register rs) { instr(rd, rd, Operand(rs)); } ++ ++#define DEFINE_INSTRUCTION2(instr) \ ++ void instr(Register rs, const Operand& rt); \ ++ void instr(Register rs, Register rt) { instr(rs, Operand(rt)); } \ ++ void instr(Register rs, Imm32 j) { instr(rs, Operand(j.value)); } ++ ++ DEFINE_INSTRUCTION(ma_and); ++ DEFINE_INSTRUCTION(ma_or); ++ DEFINE_INSTRUCTION(ma_xor); ++ DEFINE_INSTRUCTION(ma_nor); ++ DEFINE_INSTRUCTION(ma_sub32) ++ DEFINE_INSTRUCTION(ma_sub64) ++ DEFINE_INSTRUCTION(ma_add32) ++ DEFINE_INSTRUCTION(ma_add64) ++ DEFINE_INSTRUCTION(ma_div32) ++ DEFINE_INSTRUCTION(ma_divu32) ++ DEFINE_INSTRUCTION(ma_div64) ++ DEFINE_INSTRUCTION(ma_divu64) ++ DEFINE_INSTRUCTION(ma_mod32) ++ DEFINE_INSTRUCTION(ma_modu32) ++ DEFINE_INSTRUCTION(ma_mod64) ++ DEFINE_INSTRUCTION(ma_modu64) ++ DEFINE_INSTRUCTION(ma_mul32) ++ DEFINE_INSTRUCTION(ma_mulh32) ++ DEFINE_INSTRUCTION(ma_mulhu32) ++ DEFINE_INSTRUCTION(ma_mul64) ++ DEFINE_INSTRUCTION(ma_mulh64) ++ DEFINE_INSTRUCTION(ma_sll64) ++ DEFINE_INSTRUCTION(ma_sra64) ++ DEFINE_INSTRUCTION(ma_srl64) ++ DEFINE_INSTRUCTION(ma_sll32) ++ DEFINE_INSTRUCTION(ma_sra32) ++ DEFINE_INSTRUCTION(ma_srl32) ++ DEFINE_INSTRUCTION(ma_slt) ++ DEFINE_INSTRUCTION(ma_sltu) ++ DEFINE_INSTRUCTION(ma_sle) ++ DEFINE_INSTRUCTION(ma_sleu) ++ DEFINE_INSTRUCTION(ma_sgt) ++ DEFINE_INSTRUCTION(ma_sgtu) ++ DEFINE_INSTRUCTION(ma_sge) ++ DEFINE_INSTRUCTION(ma_sgeu) ++ DEFINE_INSTRUCTION(ma_seq) ++ DEFINE_INSTRUCTION(ma_sne) ++ ++ DEFINE_INSTRUCTION2(ma_seqz) ++ DEFINE_INSTRUCTION2(ma_snez) ++ DEFINE_INSTRUCTION2(ma_neg); ++ ++#undef DEFINE_INSTRUCTION2 ++#undef DEFINE_INSTRUCTION ++ // arithmetic based ops ++ void ma_add32TestOverflow(Register rd, Register rj, Register rk, ++ Label* overflow); ++ void ma_add32TestOverflow(Register rd, Register rj, Imm32 imm, ++ Label* overflow); ++ void ma_addPtrTestOverflow(Register rd, Register rj, Register rk, ++ Label* overflow); ++ void ma_addPtrTestOverflow(Register rd, Register rj, Imm32 imm, ++ Label* overflow); ++ void ma_addPtrTestOverflow(Register rd, Register rj, ImmWord imm, ++ Label* overflow); ++ void ma_addPtrTestCarry(Condition cond, Register rd, Register rj, Register rk, ++ Label* overflow); ++ void ma_addPtrTestCarry(Condition cond, Register rd, Register rj, Imm32 imm, ++ Label* overflow); ++ void ma_addPtrTestCarry(Condition cond, Register rd, Register rj, ImmWord imm, ++ Label* overflow); ++ ++ // subtract ++ void ma_sub32TestOverflow(Register rd, Register rj, Register rk, ++ Label* overflow); ++ void ma_subPtrTestOverflow(Register rd, Register rj, Register rk, ++ Label* overflow); ++ void ma_subPtrTestOverflow(Register rd, Register rj, Imm32 imm, ++ Label* overflow); ++ ++ // multiplies. For now, there are only few that we care about. ++ void ma_mulPtrTestOverflow(Register rd, Register rj, Register rk, ++ Label* overflow); ++ ++ // branches when done from within la-specific code ++ void ma_b(Register lhs, ImmWord imm, Label* l, Condition c, ++ JumpKind jumpKind = LongJump); ++ void ma_b(Register lhs, ImmPtr imm, Label* l, Condition c, ++ JumpKind jumpKind = LongJump); ++ void ma_b(Register lhs, ImmGCPtr imm, Label* l, Condition c, ++ JumpKind jumpKind = LongJump) { ++ UseScratchRegisterScope temps(this); ++ Register ScratchRegister = temps.Acquire(); ++ ma_li(ScratchRegister, imm); ++ ma_b(lhs, ScratchRegister, l, c, jumpKind); ++ } ++ void ma_b(Register lhs, Address addr, Label* l, Condition c, ++ JumpKind jumpKind = LongJump); ++ void ma_b(Address addr, Imm32 imm, Label* l, Condition c, ++ JumpKind jumpKind = LongJump); ++ void ma_b(Address addr, ImmGCPtr imm, Label* l, Condition c, ++ JumpKind jumpKind = LongJump); ++ void ma_b(Address addr, Register rhs, Label* l, Condition c, ++ JumpKind jumpKind = LongJump) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ MOZ_ASSERT(rhs != scratch); ++ ma_load(scratch, addr, SizeDouble); ++ ma_b(scratch, rhs, l, c, jumpKind); ++ } ++ ++ void ma_branch(Label* target, Condition cond, Register r1, const Operand& r2, ++ JumpKind jumpKind = ShortJump); ++ ++ void ma_branch(Label* target, JumpKind jumpKind = ShortJump) { ++ ma_branch(target, Always, zero, zero, jumpKind); ++ } ++ ++ // fp instructions ++ void ma_lid(FloatRegister dest, double value); ++ ++ // fp instructions ++ void ma_lis(FloatRegister dest, float value); ++ ++ void ma_fst_d(FloatRegister src, BaseIndex address); ++ void ma_fst_s(FloatRegister src, BaseIndex address); ++ ++ void ma_fld_d(FloatRegister dest, const BaseIndex& src); ++ void ma_fld_s(FloatRegister dest, const BaseIndex& src); ++ ++ void ma_fmv_d(FloatRegister src, ValueOperand dest); ++ void ma_fmv_d(ValueOperand src, FloatRegister dest); ++ ++ void ma_fmv_w(FloatRegister src, ValueOperand dest); ++ void ma_fmv_w(ValueOperand src, FloatRegister dest); ++ ++ void ma_fld_s(FloatRegister ft, Address address); ++ void ma_fld_d(FloatRegister ft, Address address); ++ void ma_fst_d(FloatRegister ft, Address address); ++ void ma_fst_s(FloatRegister ft, Address address); ++ ++ // stack ++ void ma_pop(Register r); ++ void ma_push(Register r); ++ void ma_pop(FloatRegister f); ++ void ma_push(FloatRegister f); ++ ++ Condition ma_cmp(Register rd, Register lhs, Register rhs, Condition c); ++ Condition ma_cmp(Register rd, Register lhs, Imm32 imm, Condition c); ++ void ma_cmp_set(Register dst, Register lhs, ImmWord imm, Condition c); ++ void ma_cmp_set(Register dst, Register lhs, ImmPtr imm, Condition c); ++ void ma_cmp_set(Register dst, Address address, Imm32 imm, Condition c); ++ void ma_cmp_set(Register dst, Address address, ImmWord imm, Condition c); ++ ++ void ma_rotr_w(Register rd, Register rj, Imm32 shift); ++ ++ void ma_fmovz(FloatFormat fmt, FloatRegister fd, FloatRegister fj, ++ Register rk); ++ void ma_fmovn(FloatFormat fmt, FloatRegister fd, FloatRegister fj, ++ Register rk); ++ ++ // arithmetic based ops ++ void ma_add32TestCarry(Condition cond, Register rd, Register rj, Register rk, ++ Label* overflow); ++ void ma_add32TestCarry(Condition cond, Register rd, Register rj, Imm32 imm, ++ Label* overflow); ++ ++ // subtract ++ void ma_sub32TestOverflow(Register rd, Register rj, Imm32 imm, ++ Label* overflow); ++ ++ void MulOverflow32(Register dst, Register left, const Operand& right, ++ Register overflow); ++ // multiplies. For now, there are only few that we care about. ++ void ma_mul32TestOverflow(Register rd, Register rj, Register rk, ++ Label* overflow); ++ void ma_mul32TestOverflow(Register rd, Register rj, Imm32 imm, ++ Label* overflow); ++ ++ // divisions ++ void ma_div_branch_overflow(Register rd, Register rj, Register rk, ++ Label* overflow); ++ void ma_div_branch_overflow(Register rd, Register rj, Imm32 imm, ++ Label* overflow); ++ ++ // fast mod, uses scratch registers, and thus needs to be in the assembler ++ // implicitly assumes that we can overwrite dest at the beginning of the ++ // sequence ++ void ma_mod_mask(Register src, Register dest, Register hold, Register remain, ++ int32_t shift, Label* negZero = nullptr); ++ ++ // FP branches ++ void ma_compareF32(Register rd, DoubleCondition cc, FloatRegister cmp1, ++ FloatRegister cmp2); ++ void ma_compareF64(Register rd, DoubleCondition cc, FloatRegister cmp1, ++ FloatRegister cmp2); ++ ++ void CompareIsNotNanF32(Register rd, FPURegister cmp1, FPURegister cmp2); ++ void CompareIsNotNanF64(Register rd, FPURegister cmp1, FPURegister cmp2); ++ void CompareIsNanF32(Register rd, FPURegister cmp1, FPURegister cmp2); ++ void CompareIsNanF64(Register rd, FPURegister cmp1, FPURegister cmp2); ++ ++ void ma_call(ImmPtr dest); ++ ++ void ma_jump(ImmPtr dest); ++ ++ void jump(Label* label) { ma_branch(label); } ++ void jump(Register reg) { jr(reg); } ++ ++ void ma_cmp_set(Register dst, Register lhs, Register rhs, Condition c); ++ void ma_cmp_set(Register dst, Register lhs, Imm32 imm, Condition c); ++ ++ void computeScaledAddress(const BaseIndex& address, Register dest); ++ ++ void BranchShort(Label* L); ++ ++ void BranchShort(int32_t offset, Condition cond, Register rs, ++ const Operand& rt); ++ void BranchShort(Label* L, Condition cond, Register rs, const Operand& rt); ++ void BranchShortHelper(int32_t offset, Label* L); ++ bool BranchShortHelper(int32_t offset, Label* L, Condition cond, Register rs, ++ const Operand& rt); ++ bool BranchShortCheck(int32_t offset, Label* L, Condition cond, Register rs, ++ const Operand& rt); ++ void BranchLong(Label* L); ++ ++ // Floating point branches ++ void BranchTrueShortF(Register rs, Label* target); ++ void BranchFalseShortF(Register rs, Label* target); ++ ++ void BranchTrueF(Register rs, Label* target); ++ void BranchFalseF(Register rs, Label* target); ++ ++ void moveFromDoubleHi(FloatRegister src, Register dest) { ++ fmv_x_d(dest, src); ++ srli(dest, dest, 32); ++ } ++ // Bit field starts at bit pos and extending for size bits is extracted from ++ // rs and stored zero/sign-extended and right-justified in rt ++ void ExtractBits(Register rt, Register rs, uint16_t pos, uint16_t size, ++ bool sign_extend = false); ++ void ExtractBits(Register dest, Register source, Register pos, int size, ++ bool sign_extend = false) { ++ sra(dest, source, pos); ++ ExtractBits(dest, dest, 0, size, sign_extend); ++ } ++ ++ // Insert bits [0, size) of source to bits [pos, pos+size) of dest ++ void InsertBits(Register dest, Register source, Register pos, int size); ++ ++ // Insert bits [0, size) of source to bits [pos, pos+size) of dest ++ void InsertBits(Register dest, Register source, int pos, int size); ++ ++ template ++ void RoundHelper(FPURegister dst, FPURegister src, FPURegister fpu_scratch, ++ FPURoundingMode mode); ++ ++ template ++ void RoundFloatingPointToInteger(Register rd, FPURegister fs, Register result, ++ TruncFunc trunc, bool Inexact = false); ++ ++ void Clear_if_nan_d(Register rd, FPURegister fs); ++ void Clear_if_nan_s(Register rd, FPURegister fs); ++ // Convert double to unsigned word. ++ void Trunc_uw_d(Register rd, FPURegister fs, Register result = InvalidReg, ++ bool Inexact = false); ++ ++ // Convert double to signed word. ++ void Trunc_w_d(Register rd, FPURegister fs, Register result = InvalidReg, ++ bool Inexact = false); ++ ++ // Convert double to unsigned long. ++ void Trunc_ul_d(Register rd, FPURegister fs, Register result = InvalidReg, ++ bool Inexact = false); ++ ++ // Convert singled to signed long. ++ void Trunc_l_d(Register rd, FPURegister fs, Register result = InvalidReg, ++ bool Inexact = false); ++ ++ // Convert single to signed word. ++ void Trunc_w_s(Register rd, FPURegister fs, Register result = InvalidReg, ++ bool Inexact = false); ++ ++ // Convert single to unsigned word. ++ void Trunc_uw_s(Register rd, FPURegister fs, Register result = InvalidReg, ++ bool Inexact = false); ++ ++ // Convert single to unsigned long. ++ void Trunc_ul_s(Register rd, FPURegister fs, Register result = InvalidReg, ++ bool Inexact = false); ++ ++ // Convert singled to signed long. ++ void Trunc_l_s(Register rd, FPURegister fs, Register result = InvalidReg, ++ bool Inexact = false); ++ ++ // Round double functions ++ void Trunc_d_d(FPURegister fd, FPURegister fs, FPURegister fpu_scratch); ++ void Round_d_d(FPURegister fd, FPURegister fs, FPURegister fpu_scratch); ++ void Floor_d_d(FPURegister fd, FPURegister fs, FPURegister fpu_scratch); ++ void Ceil_d_d(FPURegister fd, FPURegister fs, FPURegister fpu_scratch); ++ ++ // Round float functions ++ void Trunc_s_s(FPURegister fd, FPURegister fs, FPURegister fpu_scratch); ++ void Round_s_s(FPURegister fd, FPURegister fs, FPURegister fpu_scratch); ++ void Floor_s_s(FPURegister fd, FPURegister fs, FPURegister fpu_scratch); ++ void Ceil_s_s(FPURegister fd, FPURegister fs, FPURegister fpu_scratch); ++ ++ // Round single to signed word. ++ void Round_w_s(Register rd, FPURegister fs, Register result = InvalidReg, ++ bool Inexact = false); ++ ++ // Round double to signed word. ++ void Round_w_d(Register rd, FPURegister fs, Register result = InvalidReg, ++ bool Inexact = false); ++ ++ // Ceil single to signed word. ++ void Ceil_w_s(Register rd, FPURegister fs, Register result = InvalidReg, ++ bool Inexact = false); ++ ++ // Ceil double to signed word. ++ void Ceil_w_d(Register rd, FPURegister fs, Register result = InvalidReg, ++ bool Inexact = false); ++ ++ // Floor single to signed word. ++ void Floor_w_s(Register rd, FPURegister fs, Register result = InvalidReg, ++ bool Inexact = false); ++ ++ // Floor double to signed word. ++ void Floor_w_d(Register rd, FPURegister fs, Register result = InvalidReg, ++ bool Inexact = false); ++ ++ void Clz32(Register rd, Register rs); ++ void Ctz32(Register rd, Register rs); ++ void Popcnt32(Register rd, Register rs, Register scratch); ++ ++ void Popcnt64(Register rd, Register rs, Register scratch); ++ void Ctz64(Register rd, Register rs); ++ void Clz64(Register rd, Register rs); ++ ++ // Change endianness ++ void ByteSwap(Register dest, Register src, int operand_size, ++ Register scratch); ++ ++ void Ror(Register rd, Register rs, const Operand& rt); ++ void Dror(Register rd, Register rs, const Operand& rt); ++ ++ void Float32Max(FPURegister dst, FPURegister src1, FPURegister src2); ++ void Float32Min(FPURegister dst, FPURegister src1, FPURegister src2); ++ void Float64Max(FPURegister dst, FPURegister src1, FPURegister src2); ++ void Float64Min(FPURegister dst, FPURegister src1, FPURegister src2); ++ ++ template ++ void FloatMinMaxHelper(FPURegister dst, FPURegister src1, FPURegister src2, ++ MaxMinKind kind); ++ ++ inline void NegateBool(Register rd, Register rs) { xori(rd, rs, 1); } ++ ++ protected: ++ void wasmLoadImpl(const wasm::MemoryAccessDesc& access, Register memoryBase, ++ Register ptr, Register ptrScratch, AnyRegister output, ++ Register tmp); ++ void wasmStoreImpl(const wasm::MemoryAccessDesc& access, AnyRegister value, ++ Register memoryBase, Register ptr, Register ptrScratch, ++ Register tmp); ++}; ++ ++class MacroAssemblerRiscv64Compat : public MacroAssemblerRiscv64 { ++ public: ++ using MacroAssemblerRiscv64::call; ++ ++ MacroAssemblerRiscv64Compat() {} ++ ++ void convertBoolToInt32(Register src, Register dest) { ++ ma_and(dest, src, Imm32(0xff)); ++ }; ++ void convertInt32ToDouble(Register src, FloatRegister dest) { ++ fcvt_d_w(dest, src); ++ }; ++ void convertInt32ToDouble(const Address& src, FloatRegister dest) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ ma_load(scratch, src, SizeWord, SignExtend); ++ fcvt_d_w(dest, scratch); ++ }; ++ void convertInt32ToDouble(const BaseIndex& src, FloatRegister dest) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ MOZ_ASSERT(scratch != src.base); ++ MOZ_ASSERT(scratch != src.index); ++ computeScaledAddress(src, scratch); ++ convertInt32ToDouble(Address(scratch, src.offset), dest); ++ }; ++ void convertUInt32ToDouble(Register src, FloatRegister dest); ++ void convertUInt32ToFloat32(Register src, FloatRegister dest); ++ void convertDoubleToFloat32(FloatRegister src, FloatRegister dest); ++ void convertDoubleToInt32(FloatRegister src, Register dest, Label* fail, ++ bool negativeZeroCheck = true); ++ void convertDoubleToPtr(FloatRegister src, Register dest, Label* fail, ++ bool negativeZeroCheck = true); ++ void convertFloat32ToInt32(FloatRegister src, Register dest, Label* fail, ++ bool negativeZeroCheck = true); ++ ++ void convertFloat32ToDouble(FloatRegister src, FloatRegister dest); ++ void convertInt32ToFloat32(Register src, FloatRegister dest); ++ void convertInt32ToFloat32(const Address& src, FloatRegister dest); ++ ++ void movq(Register rj, Register rd); ++ ++ void computeEffectiveAddress(const Address& address, Register dest) { ++ ma_add64(dest, address.base, Imm32(address.offset)); ++ } ++ ++ void computeEffectiveAddress(const BaseIndex& address, Register dest) { ++ computeScaledAddress(address, dest); ++ if (address.offset) { ++ ma_add64(dest, dest, Imm32(address.offset)); ++ } ++ } ++ ++ void j(Label* dest) { ma_branch(dest); } ++ ++ void mov(Register src, Register dest) { addi(dest, src, 0); } ++ void mov(ImmWord imm, Register dest) { ma_li(dest, imm); } ++ void mov(ImmPtr imm, Register dest) { ++ mov(ImmWord(uintptr_t(imm.value)), dest); ++ } ++ void mov(CodeLabel* label, Register dest) { ma_li(dest, label); } ++ void mov(Register src, Address dest) { MOZ_CRASH("NYI-IC"); } ++ void mov(Address src, Register dest) { MOZ_CRASH("NYI-IC"); } ++ ++ void writeDataRelocation(const Value& val) { ++ // Raw GC pointer relocations and Value relocations both end up in ++ // TraceOneDataRelocation. ++ if (val.isGCThing()) { ++ gc::Cell* cell = val.toGCThing(); ++ if (cell && gc::IsInsideNursery(cell)) { ++ embedsNurseryPointers_ = true; ++ } ++ dataRelocations_.writeUnsigned(currentOffset()); ++ } ++ } ++ ++ void branch(JitCode* c) { ++ BlockTrampolinePoolScope block_trampoline_pool(this, 7); ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ BufferOffset bo = m_buffer.nextOffset(); ++ addPendingJump(bo, ImmPtr(c->raw()), RelocationKind::JITCODE); ++ ma_liPatchable(scratch, ImmPtr(c->raw())); ++ jr(scratch); ++ } ++ void branch(const Register reg) { jr(reg); } ++ void ret() { ++ ma_pop(ra); ++ jalr(zero_reg, ra, 0); ++ } ++ inline void retn(Imm32 n); ++ void push(Imm32 imm) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ ma_li(scratch, imm); ++ ma_push(scratch); ++ } ++ void push(ImmWord imm) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ ma_li(scratch, imm); ++ ma_push(scratch); ++ } ++ void push(ImmGCPtr imm) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ ma_li(scratch, imm); ++ ma_push(scratch); ++ } ++ void push(const Address& address) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ loadPtr(address, scratch); ++ ma_push(scratch); ++ } ++ void push(Register reg) { ma_push(reg); } ++ void push(FloatRegister reg) { ma_push(reg); } ++ void pop(Register reg) { ma_pop(reg); } ++ void pop(FloatRegister reg) { ma_pop(reg); } ++ ++ // Emit a branch that can be toggled to a non-operation. On LOONG64 we use ++ // "andi" instruction to toggle the branch. ++ // See ToggleToJmp(), ToggleToCmp(). ++ CodeOffset toggledJump(Label* label); ++ ++ // Emit a "jalr" or "nop" instruction. ToggleCall can be used to patch ++ // this instruction. ++ CodeOffset toggledCall(JitCode* target, bool enabled); ++ ++ static size_t ToggledCallSize(uint8_t* code) { ++ // Four instructions used in: MacroAssemblerRiscv64Compat::toggledCall ++ return 7 * sizeof(uint32_t); ++ } ++ ++ CodeOffset pushWithPatch(ImmWord imm) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ CodeOffset offset = movWithPatch(imm, scratch); ++ ma_push(scratch); ++ return offset; ++ } ++ ++ CodeOffset movWithPatch(ImmWord imm, Register dest) { ++ BlockTrampolinePoolScope block_trampoline_pool(this, 8); ++ CodeOffset offset = CodeOffset(currentOffset()); ++ ma_liPatchable(dest, imm, Li64); ++ return offset; ++ } ++ CodeOffset movWithPatch(ImmPtr imm, Register dest) { ++ BlockTrampolinePoolScope block_trampoline_pool(this, 6); ++ CodeOffset offset = CodeOffset(currentOffset()); ++ ma_liPatchable(dest, imm); ++ return offset; ++ } ++ ++ void writeCodePointer(CodeLabel* label) { ++ label->patchAt()->bind(currentOffset()); ++ label->setLinkMode(CodeLabel::RawPointer); ++ m_buffer.ensureSpace(sizeof(void*)); ++ emit(uint32_t(-1)); ++ emit(uint32_t(-1)); ++ } ++ ++ void jump(Label* label) { ma_branch(label); } ++ void jump(Register reg) { jr(reg); } ++ void jump(const Address& address) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ loadPtr(address, scratch); ++ jr(scratch); ++ } ++ ++ void jump(JitCode* code) { branch(code); } ++ ++ void jump(ImmPtr ptr) { ++ BufferOffset bo = m_buffer.nextOffset(); ++ addPendingJump(bo, ptr, RelocationKind::HARDCODED); ++ ma_jump(ptr); ++ } ++ ++ void jump(TrampolinePtr code) { jump(ImmPtr(code.value)); } ++ ++ void splitTag(Register src, Register dest) { ++ srli(dest, src, JSVAL_TAG_SHIFT); ++ } ++ ++ void splitTag(const ValueOperand& operand, Register dest) { ++ splitTag(operand.valueReg(), dest); ++ } ++ ++ void splitTagForTest(const ValueOperand& value, ScratchTagScope& tag) { ++ splitTag(value, tag); ++ } ++ ++ void moveIfZero(Register dst, Register src, Register cond) { ++ ScratchRegisterScope scratch(asMasm()); ++ MOZ_ASSERT(dst != scratch && cond != scratch); ++ Label done; ++ ma_branch(&done, NotEqual, cond, zero); ++ mv(dst, src); ++ bind(&done); ++ } ++ ++ void moveIfNotZero(Register dst, Register src, Register cond) { ++ ScratchRegisterScope scratch(asMasm()); ++ MOZ_ASSERT(dst != scratch && cond != scratch); ++ Label done; ++ ma_branch(&done, Equal, cond, zero); ++ mv(dst, src); ++ bind(&done); ++ } ++ // unboxing code ++ void unboxNonDouble(const ValueOperand& operand, Register dest, ++ JSValueType type) { ++ unboxNonDouble(operand.valueReg(), dest, type); ++ } ++ ++ template ++ void unboxNonDouble(T src, Register dest, JSValueType type) { ++ MOZ_ASSERT(type != JSVAL_TYPE_DOUBLE); ++ if (type == JSVAL_TYPE_INT32 || type == JSVAL_TYPE_BOOLEAN) { ++ load32(src, dest); ++ return; ++ } ++ loadPtr(src, dest); ++ unboxNonDouble(dest, dest, type); ++ } ++ ++ void unboxNonDouble(Register src, Register dest, JSValueType type) { ++ MOZ_ASSERT(type != JSVAL_TYPE_DOUBLE); ++ if (type == JSVAL_TYPE_INT32 || type == JSVAL_TYPE_BOOLEAN) { ++ slliw(dest, src, 0); ++ return; ++ } ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ MOZ_ASSERT(scratch != src); ++ mov(ImmWord(JSVAL_TYPE_TO_SHIFTED_TAG(type)), scratch); ++ xor_(dest, src, scratch); ++ } ++ ++ template ++ void unboxObjectOrNull(const T& src, Register dest) { ++ unboxNonDouble(src, dest, JSVAL_TYPE_OBJECT); ++ static_assert(JS::detail::ValueObjectOrNullBit == ++ (uint64_t(0x8) << JSVAL_TAG_SHIFT)); ++ InsertBits(dest, zero, JSVAL_TAG_SHIFT + 3, 1); ++ } ++ ++ void unboxGCThingForGCBarrier(const Address& src, Register dest) { ++ loadPtr(src, dest); ++ ExtractBits(dest, dest, 0, JSVAL_TAG_SHIFT - 1); ++ } ++ void unboxGCThingForGCBarrier(const ValueOperand& src, Register dest) { ++ ExtractBits(dest, src.valueReg(), 0, JSVAL_TAG_SHIFT - 1); ++ } ++ ++ void unboxInt32(const ValueOperand& operand, Register dest); ++ void unboxInt32(Register src, Register dest); ++ void unboxInt32(const Address& src, Register dest); ++ void unboxInt32(const BaseIndex& src, Register dest); ++ void unboxBoolean(const ValueOperand& operand, Register dest); ++ void unboxBoolean(Register src, Register dest); ++ void unboxBoolean(const Address& src, Register dest); ++ void unboxBoolean(const BaseIndex& src, Register dest); ++ void unboxDouble(const ValueOperand& operand, FloatRegister dest); ++ void unboxDouble(Register src, Register dest); ++ void unboxDouble(const Address& src, FloatRegister dest); ++ void unboxDouble(const BaseIndex& src, FloatRegister dest); ++ void unboxString(const ValueOperand& operand, Register dest); ++ void unboxString(Register src, Register dest); ++ void unboxString(const Address& src, Register dest); ++ void unboxSymbol(const ValueOperand& src, Register dest); ++ void unboxSymbol(Register src, Register dest); ++ void unboxSymbol(const Address& src, Register dest); ++ void unboxBigInt(const ValueOperand& operand, Register dest); ++ void unboxBigInt(Register src, Register dest); ++ void unboxBigInt(const Address& src, Register dest); ++ void unboxObject(const ValueOperand& src, Register dest); ++ void unboxObject(Register src, Register dest); ++ void unboxObject(const Address& src, Register dest); ++ void unboxObject(const BaseIndex& src, Register dest) { ++ unboxNonDouble(src, dest, JSVAL_TYPE_OBJECT); ++ } ++ void unboxValue(const ValueOperand& src, AnyRegister dest, JSValueType type); ++ ++ void notBoolean(const ValueOperand& val) { ++ xori(val.valueReg(), val.valueReg(), 1); ++ } ++ ++ // boxing code ++ void boxDouble(FloatRegister src, const ValueOperand& dest, FloatRegister); ++ void boxNonDouble(JSValueType type, Register src, const ValueOperand& dest); ++ ++ // Extended unboxing API. If the payload is already in a register, returns ++ // that register. Otherwise, provides a move to the given scratch register, ++ // and returns that. ++ [[nodiscard]] Register extractObject(const Address& address, ++ Register scratch); ++ [[nodiscard]] Register extractObject(const ValueOperand& value, ++ Register scratch) { ++ unboxObject(value, scratch); ++ return scratch; ++ } ++ [[nodiscard]] Register extractString(const ValueOperand& value, ++ Register scratch) { ++ unboxString(value, scratch); ++ return scratch; ++ } ++ [[nodiscard]] Register extractSymbol(const ValueOperand& value, ++ Register scratch) { ++ unboxSymbol(value, scratch); ++ return scratch; ++ } ++ [[nodiscard]] Register extractInt32(const ValueOperand& value, ++ Register scratch) { ++ unboxInt32(value, scratch); ++ return scratch; ++ } ++ [[nodiscard]] Register extractBoolean(const ValueOperand& value, ++ Register scratch) { ++ unboxBoolean(value, scratch); ++ return scratch; ++ } ++ [[nodiscard]] Register extractTag(const Address& address, Register scratch); ++ [[nodiscard]] Register extractTag(const BaseIndex& address, Register scratch); ++ [[nodiscard]] Register extractTag(const ValueOperand& value, ++ Register scratch) { ++ splitTag(value, scratch); ++ return scratch; ++ } ++ ++ void ensureDouble(const ValueOperand& source, FloatRegister dest, ++ Label* failure); ++ ++ void boolValueToDouble(const ValueOperand& operand, FloatRegister dest); ++ void int32ValueToDouble(const ValueOperand& operand, FloatRegister dest); ++ void loadInt32OrDouble(const Address& src, FloatRegister dest); ++ void loadInt32OrDouble(const BaseIndex& addr, FloatRegister dest); ++ void loadConstantDouble(double dp, FloatRegister dest); ++ ++ void boolValueToFloat32(const ValueOperand& operand, FloatRegister dest); ++ void int32ValueToFloat32(const ValueOperand& operand, FloatRegister dest); ++ void loadConstantFloat32(float f, FloatRegister dest); ++ ++ void testNullSet(Condition cond, const ValueOperand& value, Register dest); ++ ++ void testObjectSet(Condition cond, const ValueOperand& value, Register dest); ++ ++ void testUndefinedSet(Condition cond, const ValueOperand& value, ++ Register dest); ++ ++ // higher level tag testing code ++ Address ToPayload(Address value) { return value; } ++ ++ template ++ void loadUnboxedValue(const T& address, MIRType type, AnyRegister dest) { ++ if (dest.isFloat()) { ++ loadInt32OrDouble(address, dest.fpu()); ++ } else { ++ unboxNonDouble(address, dest.gpr(), ValueTypeFromMIRType(type)); ++ } ++ } ++ ++ void storeUnboxedPayload(ValueOperand value, BaseIndex address, size_t nbytes, ++ JSValueType type) { ++ switch (nbytes) { ++ case 8: { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ Register scratch2 = temps.Acquire(); ++ if (type == JSVAL_TYPE_OBJECT) { ++ unboxObjectOrNull(value, scratch2); ++ } else { ++ unboxNonDouble(value, scratch2, type); ++ } ++ computeEffectiveAddress(address, scratch); ++ sd(scratch2, scratch, 0); ++ return; ++ } ++ case 4: ++ store32(value.valueReg(), address); ++ return; ++ case 1: ++ store8(value.valueReg(), address); ++ return; ++ default: ++ MOZ_CRASH("Bad payload width"); ++ } ++ } ++ ++ void storeUnboxedPayload(ValueOperand value, Address address, size_t nbytes, ++ JSValueType type) { ++ switch (nbytes) { ++ case 8: { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ if (type == JSVAL_TYPE_OBJECT) { ++ unboxObjectOrNull(value, scratch); ++ } else { ++ unboxNonDouble(value, scratch, type); ++ } ++ storePtr(scratch, address); ++ return; ++ } ++ case 4: ++ store32(value.valueReg(), address); ++ return; ++ case 1: ++ store8(value.valueReg(), address); ++ return; ++ default: ++ MOZ_CRASH("Bad payload width"); ++ } ++ } ++ ++ void boxValue(JSValueType type, Register src, Register dest) { ++ MOZ_ASSERT(src != dest); ++ ++ JSValueTag tag = (JSValueTag)JSVAL_TYPE_TO_TAG(type); ++ ma_li(dest, Imm32(tag)); ++ slli(dest, dest, JSVAL_TAG_SHIFT); ++ if (type == JSVAL_TYPE_INT32 || type == JSVAL_TYPE_BOOLEAN) { ++ InsertBits(dest, src, 0, 32); ++ } else { ++ InsertBits(dest, src, 0, JSVAL_TAG_SHIFT); ++ } ++ } ++ ++ void storeValue(ValueOperand val, const Address& dest); ++ void storeValue(ValueOperand val, const BaseIndex& dest); ++ void storeValue(JSValueType type, Register reg, Address dest); ++ void storeValue(JSValueType type, Register reg, BaseIndex dest); ++ void storeValue(const Value& val, Address dest); ++ void storeValue(const Value& val, BaseIndex dest); ++ void storeValue(const Address& src, const Address& dest, Register temp) { ++ loadPtr(src, temp); ++ storePtr(temp, dest); ++ } ++ ++ void storePrivateValue(Register src, const Address& dest) { ++ storePtr(src, dest); ++ } ++ void storePrivateValue(ImmGCPtr imm, const Address& dest) { ++ storePtr(imm, dest); ++ } ++ ++ void loadValue(Address src, ValueOperand val); ++ void loadValue(const BaseIndex& src, ValueOperand val); ++ ++ void loadUnalignedValue(const Address& src, ValueOperand dest) { ++ loadValue(src, dest); ++ } ++ ++ void tagValue(JSValueType type, Register payload, ValueOperand dest); ++ ++ void pushValue(ValueOperand val); ++ void popValue(ValueOperand val); ++ void pushValue(const Value& val) { ++ if (val.isGCThing()) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ writeDataRelocation(val); ++ movWithPatch(ImmWord(val.asRawBits()), scratch); ++ push(scratch); ++ } else { ++ push(ImmWord(val.asRawBits())); ++ } ++ } ++ void pushValue(JSValueType type, Register reg) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ boxValue(type, reg, scratch); ++ push(scratch); ++ } ++ void pushValue(const Address& addr); ++ ++ void handleFailureWithHandlerTail(Label* profilerExitTail, ++ Label* bailoutTail); ++ ++ ///////////////////////////////////////////////////////////////// ++ // Common interface. ++ ///////////////////////////////////////////////////////////////// ++ public: ++ // The following functions are exposed for use in platform-shared code. ++ ++ inline void incrementInt32Value(const Address& addr); ++ ++ void move32(Imm32 imm, Register dest); ++ void move32(Register src, Register dest); ++ ++ void movePtr(Register src, Register dest); ++ void movePtr(ImmWord imm, Register dest); ++ void movePtr(ImmPtr imm, Register dest); ++ void movePtr(wasm::SymbolicAddress imm, Register dest); ++ void movePtr(ImmGCPtr imm, Register dest); ++ ++ void load8SignExtend(const Address& address, Register dest); ++ void load8SignExtend(const BaseIndex& src, Register dest); ++ ++ void load8ZeroExtend(const Address& address, Register dest); ++ void load8ZeroExtend(const BaseIndex& src, Register dest); ++ ++ void load16SignExtend(const Address& address, Register dest); ++ void load16SignExtend(const BaseIndex& src, Register dest); ++ ++ template ++ void load16UnalignedSignExtend(const S& src, Register dest) { ++ load16SignExtend(src, dest); ++ } ++ ++ void load16ZeroExtend(const Address& address, Register dest); ++ void load16ZeroExtend(const BaseIndex& src, Register dest); ++ ++ void SignExtendByte(Register rd, Register rs) { ++ slli(rd, rs, xlen - 8); ++ srai(rd, rd, xlen - 8); ++ } ++ ++ void SignExtendShort(Register rd, Register rs) { ++ slli(rd, rs, xlen - 16); ++ srai(rd, rd, xlen - 16); ++ } ++ ++ void SignExtendWord(Register rd, Register rs) { sext_w(rd, rs); } ++ void ZeroExtendWord(Register rd, Register rs) { ++ slli(rd, rs, 32); ++ srli(rd, rd, 32); ++ } ++ ++ template ++ void load16UnalignedZeroExtend(const S& src, Register dest) { ++ load16ZeroExtend(src, dest); ++ } ++ ++ void load32(const Address& address, Register dest); ++ void load32(const BaseIndex& address, Register dest); ++ void load32(AbsoluteAddress address, Register dest); ++ void load32(wasm::SymbolicAddress address, Register dest); ++ ++ template ++ void load32Unaligned(const S& src, Register dest) { ++ load32(src, dest); ++ } ++ ++ void load64(const Address& address, Register64 dest) { ++ loadPtr(address, dest.reg); ++ } ++ void load64(const BaseIndex& address, Register64 dest) { ++ loadPtr(address, dest.reg); ++ } ++ ++ void loadDouble(const Address& addr, FloatRegister dest) { ++ ma_loadDouble(dest, addr); ++ } ++ void loadDouble(const BaseIndex& src, FloatRegister dest) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ computeScaledAddress(src, scratch); ++ fld(dest, scratch, 0); ++ } ++ ++ void loadFloat32(const Address& addr, FloatRegister dest) { ++ ma_loadFloat(dest, addr); ++ } ++ void loadFloat32(const BaseIndex& src, FloatRegister dest) { ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ computeScaledAddress(src, scratch); ++ flw(dest, scratch, 0); ++ } ++ ++ template ++ void load64Unaligned(const S& src, Register64 dest) { ++ load64(src, dest); ++ } ++ ++ void loadPtr(const Address& address, Register dest); ++ void loadPtr(const BaseIndex& src, Register dest); ++ void loadPtr(AbsoluteAddress address, Register dest); ++ void loadPtr(wasm::SymbolicAddress address, Register dest); ++ ++ void loadPrivate(const Address& address, Register dest); ++ ++ void store8(Register src, const Address& address); ++ void store8(Imm32 imm, const Address& address); ++ void store8(Register src, const BaseIndex& address); ++ void store8(Imm32 imm, const BaseIndex& address); ++ ++ void store16(Register src, const Address& address); ++ void store16(Imm32 imm, const Address& address); ++ void store16(Register src, const BaseIndex& address); ++ void store16(Imm32 imm, const BaseIndex& address); ++ ++ template ++ void store16Unaligned(Register src, const T& dest) { ++ store16(src, dest); ++ } ++ ++ void store32(Register src, AbsoluteAddress address); ++ void store32(Register src, const Address& address); ++ void store32(Register src, const BaseIndex& address); ++ void store32(Imm32 src, const Address& address); ++ void store32(Imm32 src, const BaseIndex& address); ++ ++ // NOTE: This will use second scratch on LOONG64. Only ARM needs the ++ // implementation without second scratch. ++ void store32_NoSecondScratch(Imm32 src, const Address& address) { ++ store32(src, address); ++ } ++ ++ template ++ void store32Unaligned(Register src, const T& dest) { ++ store32(src, dest); ++ } ++ ++ void store64(Imm64 imm, Address address) { ++ storePtr(ImmWord(imm.value), address); ++ } ++ void store64(Imm64 imm, const BaseIndex& address) { ++ storePtr(ImmWord(imm.value), address); ++ } ++ ++ void store64(Register64 src, Address address) { storePtr(src.reg, address); } ++ void store64(Register64 src, const BaseIndex& address) { ++ storePtr(src.reg, address); ++ } ++ ++ template ++ void store64Unaligned(Register64 src, const T& dest) { ++ store64(src, dest); ++ } ++ ++ template ++ void storePtr(ImmWord imm, T address); ++ template ++ void storePtr(ImmPtr imm, T address); ++ template ++ void storePtr(ImmGCPtr imm, T address); ++ void storePtr(Register src, const Address& address); ++ void storePtr(Register src, const BaseIndex& address); ++ void storePtr(Register src, AbsoluteAddress dest); ++ ++ void moveDouble(FloatRegister src, FloatRegister dest) { fmv_d(dest, src); } ++ ++ void zeroDouble(FloatRegister reg) { fmv_d_x(reg, zero); } ++ ++ void convertUInt64ToDouble(Register src, FloatRegister dest); ++ ++ void breakpoint(uint32_t value = 0); ++ ++ void checkStackAlignment() { ++#ifdef DEBUG ++ Label aligned; ++ UseScratchRegisterScope temps(this); ++ Register scratch = temps.Acquire(); ++ andi(scratch, sp, ABIStackAlignment - 1); ++ ma_b(scratch, zero, &aligned, Equal, ShortJump); ++ breakpoint(); ++ bind(&aligned); ++#endif ++ }; ++ ++ static void calculateAlignedStackPointer(void** stackPointer); ++ ++ void cmpPtrSet(Assembler::Condition cond, Address lhs, ImmPtr rhs, ++ Register dest); ++ void cmpPtrSet(Assembler::Condition cond, Register lhs, Address rhs, ++ Register dest); ++ void cmpPtrSet(Assembler::Condition cond, Address lhs, Register rhs, ++ Register dest); ++ ++ void cmp32Set(Assembler::Condition cond, Register lhs, Address rhs, ++ Register dest); ++ ++ protected: ++ bool buildOOLFakeExitFrame(void* fakeReturnAddr); ++ ++ void wasmLoadI64Impl(const wasm::MemoryAccessDesc& access, ++ Register memoryBase, Register ptr, Register ptrScratch, ++ Register64 output, Register tmp); ++ void wasmStoreI64Impl(const wasm::MemoryAccessDesc& access, Register64 value, ++ Register memoryBase, Register ptr, Register ptrScratch, ++ Register tmp); ++ ++ public: ++ void abiret() { jr(ra); } ++ ++ void moveFloat32(FloatRegister src, FloatRegister dest) { fmv_s(dest, src); } ++ ++ // Instrumentation for entering and leaving the profiler. ++ void profilerEnterFrame(Register framePtr, Register scratch); ++ void profilerExitFrame(); ++}; ++ ++typedef MacroAssemblerRiscv64Compat MacroAssemblerSpecific; ++ ++} // namespace jit ++} // namespace js ++ ++#endif /* jit_riscv64_MacroAssembler_riscv64_h */ +diff --git a/js/src/jit/riscv64/MoveEmitter-riscv64.cpp b/js/src/jit/riscv64/MoveEmitter-riscv64.cpp +new file mode 100644 +index 0000000000..79f8d176b2 +--- /dev/null ++++ b/js/src/jit/riscv64/MoveEmitter-riscv64.cpp +@@ -0,0 +1,333 @@ ++/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- ++ * vim: set ts=8 sts=2 et sw=2 tw=80: ++ * 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/riscv64/MoveEmitter-riscv64.h" ++ ++#include "jit/MacroAssembler-inl.h" ++ ++using namespace js; ++using namespace js::jit; ++ ++void MoveEmitterRiscv64::breakCycle(const MoveOperand& from, ++ const MoveOperand& to, MoveOp::Type type, ++ uint32_t slotId) { ++ // There is some pattern: ++ // (A -> B) ++ // (B -> A) ++ // ++ // This case handles (A -> B), which we reach first. We save B, then allow ++ // the original move to continue. ++ switch (type) { ++ case MoveOp::FLOAT32: ++ if (to.isMemory()) { ++ ScratchFloat32Scope fpscratch32(masm); ++ masm.loadFloat32(getAdjustedAddress(to), fpscratch32); ++ masm.storeFloat32(fpscratch32, cycleSlot(slotId)); ++ } else { ++ masm.storeFloat32(to.floatReg(), cycleSlot(slotId)); ++ } ++ break; ++ case MoveOp::DOUBLE: ++ if (to.isMemory()) { ++ ScratchDoubleScope fpscratch64(masm); ++ masm.loadDouble(getAdjustedAddress(to), fpscratch64); ++ masm.storeDouble(fpscratch64, cycleSlot(slotId)); ++ } else { ++ masm.storeDouble(to.floatReg(), cycleSlot(slotId)); ++ } ++ break; ++ case MoveOp::INT32: ++ if (to.isMemory()) { ++ UseScratchRegisterScope temps(&masm); ++ Register scratch2 = temps.Acquire(); ++ masm.load32(getAdjustedAddress(to), scratch2); ++ masm.store32(scratch2, cycleSlot(0)); ++ } else { ++ masm.store32(to.reg(), cycleSlot(0)); ++ } ++ break; ++ case MoveOp::GENERAL: ++ if (to.isMemory()) { ++ UseScratchRegisterScope temps(&masm); ++ Register scratch2 = temps.Acquire(); ++ masm.loadPtr(getAdjustedAddress(to), scratch2); ++ masm.storePtr(scratch2, cycleSlot(0)); ++ } else { ++ masm.storePtr(to.reg(), cycleSlot(0)); ++ } ++ break; ++ default: ++ MOZ_CRASH("Unexpected move type"); ++ } ++} ++ ++void MoveEmitterRiscv64::completeCycle(const MoveOperand& from, ++ const MoveOperand& to, MoveOp::Type type, ++ uint32_t slotId) { ++ // There is some pattern: ++ // (A -> B) ++ // (B -> A) ++ // ++ // This case handles (B -> A), which we reach last. We emit a move from the ++ // saved value of B, to A. ++ switch (type) { ++ case MoveOp::FLOAT32: ++ if (to.isMemory()) { ++ ScratchFloat32Scope fpscratch32(masm); ++ masm.loadFloat32(cycleSlot(slotId), fpscratch32); ++ masm.storeFloat32(fpscratch32, getAdjustedAddress(to)); ++ } else { ++ masm.loadFloat32(cycleSlot(slotId), to.floatReg()); ++ } ++ break; ++ case MoveOp::DOUBLE: ++ if (to.isMemory()) { ++ ScratchDoubleScope fpscratch64(masm); ++ masm.loadDouble(cycleSlot(slotId), fpscratch64); ++ masm.storeDouble(fpscratch64, getAdjustedAddress(to)); ++ } else { ++ masm.loadDouble(cycleSlot(slotId), to.floatReg()); ++ } ++ break; ++ case MoveOp::INT32: ++ MOZ_ASSERT(slotId == 0); ++ if (to.isMemory()) { ++ UseScratchRegisterScope temps(&masm); ++ Register scratch2 = temps.Acquire(); ++ masm.load32(cycleSlot(0), scratch2); ++ masm.store32(scratch2, getAdjustedAddress(to)); ++ } else { ++ masm.load32(cycleSlot(0), to.reg()); ++ } ++ break; ++ case MoveOp::GENERAL: ++ MOZ_ASSERT(slotId == 0); ++ if (to.isMemory()) { ++ UseScratchRegisterScope temps(&masm); ++ Register scratch2 = temps.Acquire(); ++ masm.loadPtr(cycleSlot(0), scratch2); ++ masm.storePtr(scratch2, getAdjustedAddress(to)); ++ } else { ++ masm.loadPtr(cycleSlot(0), to.reg()); ++ } ++ break; ++ default: ++ MOZ_CRASH("Unexpected move type"); ++ } ++} ++ ++void MoveEmitterRiscv64::emit(const MoveResolver& moves) { ++ if (moves.numCycles()) { ++ // Reserve stack for cycle resolution ++ static_assert(SpillSlotSize == 8); ++ masm.reserveStack(moves.numCycles() * SpillSlotSize); ++ pushedAtCycle_ = masm.framePushed(); ++ } ++ ++ for (size_t i = 0; i < moves.numMoves(); i++) { ++ emit(moves.getMove(i)); ++ } ++} ++ ++void MoveEmitterRiscv64::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: ++ emitInt32Move(from, to); ++ break; ++ case MoveOp::GENERAL: ++ emitMove(from, to); ++ break; ++ default: ++ MOZ_CRASH("Unexpected move type"); ++ } ++} ++ ++void MoveEmitterRiscv64::emitMove(const MoveOperand& from, ++ const MoveOperand& to) { ++ if (from.isGeneralReg()) { ++ 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()) { ++ UseScratchRegisterScope temps(&masm); ++ Register scratch2 = temps.Acquire(); ++ masm.loadPtr(getAdjustedAddress(from), scratch2); ++ masm.storePtr(scratch2, 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()) { ++ UseScratchRegisterScope temps(&masm); ++ Register scratch2 = temps.Acquire(); ++ masm.computeEffectiveAddress(getAdjustedAddress(from), scratch2); ++ masm.storePtr(scratch2, getAdjustedAddress(to)); ++ } else { ++ MOZ_CRASH("Invalid emitMove arguments."); ++ } ++ } else { ++ MOZ_CRASH("Invalid emitMove arguments."); ++ } ++} ++ ++void MoveEmitterRiscv64::emitInt32Move(const MoveOperand& from, ++ const MoveOperand& to) { ++ if (from.isGeneralReg()) { ++ if (to.isGeneralReg()) { ++ masm.move32(from.reg(), to.reg()); ++ } else if (to.isMemory()) { ++ masm.store32(from.reg(), getAdjustedAddress(to)); ++ } else { ++ MOZ_CRASH("Invalid emitInt32Move arguments."); ++ } ++ } else if (from.isMemory()) { ++ if (to.isGeneralReg()) { ++ masm.load32(getAdjustedAddress(from), to.reg()); ++ } else if (to.isMemory()) { ++ UseScratchRegisterScope temps(&masm); ++ Register scratch2 = temps.Acquire(); ++ masm.load32(getAdjustedAddress(from), scratch2); ++ masm.store32(scratch2, getAdjustedAddress(to)); ++ } else { ++ MOZ_CRASH("Invalid emitInt32Move arguments."); ++ } ++ } else if (from.isEffectiveAddress()) { ++ if (to.isGeneralReg()) { ++ masm.computeEffectiveAddress(getAdjustedAddress(from), to.reg()); ++ } else if (to.isMemory()) { ++ UseScratchRegisterScope temps(&masm); ++ Register scratch2 = temps.Acquire(); ++ masm.computeEffectiveAddress(getAdjustedAddress(from), scratch2); ++ masm.store32(scratch2, getAdjustedAddress(to)); ++ } else { ++ MOZ_CRASH("Invalid emitInt32Move arguments."); ++ } ++ } else { ++ MOZ_CRASH("Invalid emitInt32Move arguments."); ++ } ++} ++ ++void MoveEmitterRiscv64::emitFloat32Move(const MoveOperand& from, ++ const MoveOperand& to) { ++ if (from.isFloatReg()) { ++ if (to.isFloatReg()) { ++ masm.fmv_s(to.floatReg(), from.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.fmv_x_w(to.reg(), from.floatReg()); ++ } 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()); ++ ScratchFloat32Scope fpscratch32(masm); ++ masm.loadFloat32(getAdjustedAddress(from), fpscratch32); ++ masm.storeFloat32(fpscratch32, getAdjustedAddress(to)); ++ } ++} ++ ++void MoveEmitterRiscv64::emitDoubleMove(const MoveOperand& from, ++ const MoveOperand& to) { ++ if (from.isFloatReg()) { ++ if (to.isFloatReg()) { ++ masm.fmv_d(to.floatReg(), from.floatReg()); ++ } else if (to.isGeneralReg()) { ++ masm.fmv_x_d(to.reg(), from.floatReg()); ++ } else { ++ MOZ_ASSERT(to.isMemory()); ++ masm.storeDouble(from.floatReg(), getAdjustedAddress(to)); ++ } ++ } else if (to.isFloatReg()) { ++ if (from.isMemory()) { ++ masm.loadDouble(getAdjustedAddress(from), to.floatReg()); ++ } else { ++ masm.fmv_d_x(to.floatReg(), from.reg()); ++ } ++ } else { ++ MOZ_ASSERT(from.isMemory()); ++ MOZ_ASSERT(to.isMemory()); ++ ScratchDoubleScope fpscratch64(masm); ++ masm.loadDouble(getAdjustedAddress(from), fpscratch64); ++ masm.storeDouble(fpscratch64, getAdjustedAddress(to)); ++ } ++} ++ ++Address MoveEmitterRiscv64::cycleSlot(uint32_t slot, uint32_t subslot) const { ++ int32_t offset = masm.framePushed() - pushedAtCycle_; ++ return Address(StackPointer, offset + slot * sizeof(double) + subslot); ++} ++ ++int32_t MoveEmitterRiscv64::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 MoveEmitterRiscv64::getAdjustedAddress(const MoveOperand& operand) { ++ return Address(operand.base(), getAdjustedOffset(operand)); ++} ++ ++void MoveEmitterRiscv64::assertDone() { MOZ_ASSERT(inCycle_ == 0); } ++ ++void MoveEmitterRiscv64::finish() { ++ assertDone(); ++ ++ masm.freeStack(masm.framePushed() - pushedAtStart_); ++} +diff --git a/js/src/jit/riscv64/MoveEmitter-riscv64.h b/js/src/jit/riscv64/MoveEmitter-riscv64.h +new file mode 100644 +index 0000000000..34d86b5794 +--- /dev/null ++++ b/js/src/jit/riscv64/MoveEmitter-riscv64.h +@@ -0,0 +1,70 @@ ++/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- ++ * vim: set ts=8 sts=2 et sw=2 tw=80: ++ * 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_riscv64_MoveEmitter_riscv64_h ++#define jit_riscv64_MoveEmitter_riscv64_h ++ ++#include "mozilla/Assertions.h" ++#include "jit/MacroAssembler.h" ++#include "jit/riscv64/Architecture-riscv64.h" ++namespace js { ++namespace jit { ++ ++class MacroAssemblerRiscv64; ++class MoveResolver; ++struct Register; ++ ++class MoveEmitterRiscv64 { ++ 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_; ++ ++ // 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_; ++ ++ public: ++ explicit MoveEmitterRiscv64(MacroAssembler& m) ++ : inCycle_(0), ++ masm(m), ++ pushedAtStart_(masm.framePushed()), ++ pushedAtCycle_(-1), ++ spilledReg_(InvalidReg), ++ spilledFloatReg_(InvalidFloatReg) {} ++ void emit(const MoveResolver&); ++ void emit(const MoveOp& move); ++ void emitMove(const MoveOperand& from, const MoveOperand& to); ++ void emitInt32Move(const MoveOperand& from, const MoveOperand& to); ++ void emitFloat32Move(const MoveOperand& from, const MoveOperand& to); ++ void emitDoubleMove(const MoveOperand& from, const MoveOperand& to); ++ void finish(); ++ void assertDone(); ++ void setScratchRegister(Register) { MOZ_CRASH("Unimplement on riscv"); } ++ Address cycleSlot(uint32_t slot, uint32_t subslot = 0) const; ++ int32_t getAdjustedOffset(const MoveOperand& operand); ++ Address getAdjustedAddress(const MoveOperand& operand); ++ ++ void breakCycle(const MoveOperand& from, const MoveOperand& to, ++ MoveOp::Type type, uint32_t slotId); ++ void completeCycle(const MoveOperand& from, const MoveOperand& to, ++ MoveOp::Type type, uint32_t slot); ++}; ++ ++typedef MoveEmitterRiscv64 MoveEmitter; ++ ++} // namespace jit ++} // namespace js ++ ++#endif /* jit_riscv64_MoveEmitter_riscv64_h */ +diff --git a/js/src/jit/riscv64/Register-riscv64.h b/js/src/jit/riscv64/Register-riscv64.h +new file mode 100644 +index 0000000000..3fae7ee194 +--- /dev/null ++++ b/js/src/jit/riscv64/Register-riscv64.h +@@ -0,0 +1,181 @@ ++/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- ++ * vim: set ts=8 sts=2 et sw=2 tw=80: ++ * 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_riscv64_Register_riscv64_h ++#define jit_riscv64_Register_riscv64_h ++ ++#include "mozilla/Assertions.h" ++ ++#include ++ ++#include "jit/Registers.h" ++#include "jit/RegisterSets.h" ++ ++namespace js { ++namespace jit { ++ ++static constexpr Register zero{Registers::zero}; ++static constexpr Register ra{Registers::ra}; ++static constexpr Register tp{Registers::tp}; ++static constexpr Register sp{Registers::sp}; ++static constexpr Register gp{Registers::gp}; ++static constexpr Register a0{Registers::a0}; ++static constexpr Register a1{Registers::a1}; ++static constexpr Register a2{Registers::a2}; ++static constexpr Register a3{Registers::a3}; ++static constexpr Register a4{Registers::a4}; ++static constexpr Register a5{Registers::a5}; ++static constexpr Register a6{Registers::a6}; ++static constexpr Register a7{Registers::a7}; ++static constexpr Register t0{Registers::t0}; ++static constexpr Register t1{Registers::t1}; ++static constexpr Register t2{Registers::t2}; ++static constexpr Register t3{Registers::t3}; ++static constexpr Register t4{Registers::t4}; ++static constexpr Register t5{Registers::t5}; ++static constexpr Register t6{Registers::t6}; ++static constexpr Register fp{Registers::fp}; ++static constexpr Register s1{Registers::s1}; ++static constexpr Register s2{Registers::s2}; ++static constexpr Register s3{Registers::s3}; ++static constexpr Register s4{Registers::s4}; ++static constexpr Register s5{Registers::s5}; ++static constexpr Register s6{Registers::s6}; ++static constexpr Register s7{Registers::s7}; ++static constexpr Register s8{Registers::s8}; ++static constexpr Register s9{Registers::s9}; ++static constexpr Register s10{Registers::s10}; ++static constexpr Register s11{Registers::s11}; ++ ++static constexpr FloatRegister ft0{FloatRegisters::f0}; ++static constexpr FloatRegister ft1{FloatRegisters::f1}; ++static constexpr FloatRegister ft2{FloatRegisters::f2}; ++static constexpr FloatRegister ft3{FloatRegisters::f3}; ++static constexpr FloatRegister ft4{FloatRegisters::f4}; ++static constexpr FloatRegister ft5{FloatRegisters::f5}; ++static constexpr FloatRegister ft6{FloatRegisters::f6}; ++static constexpr FloatRegister ft7{FloatRegisters::f7}; ++static constexpr FloatRegister fs0{FloatRegisters::f8}; ++static constexpr FloatRegister fs1{FloatRegisters::f9}; ++static constexpr FloatRegister fa0{FloatRegisters::f10}; ++static constexpr FloatRegister fa1{FloatRegisters::f11}; ++static constexpr FloatRegister fa2{FloatRegisters::f12}; ++static constexpr FloatRegister fa3{FloatRegisters::f13}; ++static constexpr FloatRegister fa4{FloatRegisters::f14}; ++static constexpr FloatRegister fa5{FloatRegisters::f15}; ++static constexpr FloatRegister fa6{FloatRegisters::f16}; ++static constexpr FloatRegister fa7{FloatRegisters::f17}; ++static constexpr FloatRegister fs2{FloatRegisters::f18}; ++static constexpr FloatRegister fs3{FloatRegisters::f19}; ++static constexpr FloatRegister fs4{FloatRegisters::f20}; ++static constexpr FloatRegister fs5{FloatRegisters::f21}; ++static constexpr FloatRegister fs6{FloatRegisters::f22}; ++static constexpr FloatRegister fs7{FloatRegisters::f23}; ++static constexpr FloatRegister fs8{FloatRegisters::f24}; ++static constexpr FloatRegister fs9{FloatRegisters::f25}; ++static constexpr FloatRegister fs10{FloatRegisters::f26}; ++static constexpr FloatRegister fs11{FloatRegisters::f27}; ++static constexpr FloatRegister ft8{FloatRegisters::f28}; ++static constexpr FloatRegister ft9{FloatRegisters::f29}; ++static constexpr FloatRegister ft10{FloatRegisters::f30}; ++static constexpr FloatRegister ft11{FloatRegisters::f31}; ++ ++static constexpr Register StackPointer{Registers::sp}; ++static constexpr Register FramePointer{Registers::fp}; ++static constexpr Register ReturnReg{Registers::a0}; ++static constexpr Register ScratchRegister{Registers::s11}; ++static constexpr Register64 ReturnReg64(ReturnReg); ++ ++static constexpr FloatRegister ReturnFloat32Reg{FloatRegisters::fa0}; ++static constexpr FloatRegister ReturnDoubleReg{FloatRegisters::fa0}; ++#ifdef ENABLE_WASM_SIMD ++static constexpr FloatRegister ReturnSimd128Reg{FloatRegisters::invalid_reg}; ++static constexpr FloatRegister ScratchSimd128Reg{FloatRegisters::invalid_reg}; ++#endif ++static constexpr FloatRegister InvalidFloatReg{}; ++ ++static constexpr FloatRegister ScratchFloat32Reg{FloatRegisters::ft10}; ++static constexpr FloatRegister ScratchDoubleReg{FloatRegisters::ft10}; ++static constexpr FloatRegister ScratchDoubleReg2{FloatRegisters::fs11}; ++ ++static constexpr Register OsrFrameReg{Registers::a3}; ++static constexpr Register PreBarrierReg{Registers::a1}; ++static constexpr Register InterpreterPCReg{Registers::t0}; ++static constexpr Register CallTempReg0{Registers::t0}; ++static constexpr Register CallTempReg1{Registers::t1}; ++static constexpr Register CallTempReg2{Registers::t2}; ++static constexpr Register CallTempReg3{Registers::t3}; ++static constexpr Register CallTempReg4{Registers::a6}; ++static constexpr Register CallTempReg5{Registers::a7}; ++static constexpr Register InvalidReg{Registers::invalid_reg}; ++static constexpr Register CallTempNonArgRegs[] = {t0, t1, t2, t3}; ++static const uint32_t NumCallTempNonArgRegs = std::size(CallTempNonArgRegs); ++ ++static constexpr Register IntArgReg0{Registers::a0}; ++static constexpr Register IntArgReg1{Registers::a1}; ++static constexpr Register IntArgReg2{Registers::a2}; ++static constexpr Register IntArgReg3{Registers::a3}; ++static constexpr Register IntArgReg4{Registers::a4}; ++static constexpr Register IntArgReg5{Registers::a5}; ++static constexpr Register IntArgReg6{Registers::a6}; ++static constexpr Register IntArgReg7{Registers::a7}; ++static constexpr Register HeapReg{Registers::s7}; ++ ++static constexpr Register RegExpTesterRegExpReg{CallTempReg0}; ++static constexpr Register RegExpTesterStringReg{CallTempReg1}; ++static constexpr Register RegExpTesterLastIndexReg{CallTempReg2}; ++static constexpr Register RegExpTesterStickyReg{Registers::invalid_reg}; ++ ++static constexpr Register RegExpMatcherRegExpReg{CallTempReg0}; ++static constexpr Register RegExpMatcherStringReg{CallTempReg1}; ++static constexpr Register RegExpMatcherLastIndexReg{CallTempReg2}; ++static constexpr Register RegExpMatcherStickyReg{Registers::invalid_reg}; ++ ++static constexpr Register JSReturnReg_Type{Registers::a3}; ++static constexpr Register JSReturnReg_Data{Registers::s2}; ++static constexpr Register JSReturnReg{Registers::a2}; ++static constexpr ValueOperand JSReturnOperand = ValueOperand(JSReturnReg); ++ ++// These registers may be volatile or nonvolatile. ++static constexpr Register ABINonArgReg0{Registers::t0}; ++static constexpr Register ABINonArgReg1{Registers::t1}; ++static constexpr Register ABINonArgReg2{Registers::t2}; ++static constexpr Register ABINonArgReg3{Registers::t3}; ++ ++// These registers may be volatile or nonvolatile. ++// Note: these three registers are all guaranteed to be different ++static constexpr Register ABINonArgReturnReg0{Registers::t0}; ++static constexpr Register ABINonArgReturnReg1{Registers::t1}; ++static constexpr Register ABINonVolatileReg{Registers::s1}; ++ ++// This register is guaranteed to be clobberable during the prologue and ++// epilogue of an ABI call which must preserve both ABI argument, return ++// and non-volatile registers. ++static constexpr Register ABINonArgReturnVolatileReg{Registers::t0}; ++ ++// This register may be volatile or nonvolatile. ++// Avoid ft11 which is the scratch register. ++static constexpr FloatRegister ABINonArgDoubleReg{FloatRegisters::ft11}; ++ ++static constexpr Register WasmTableCallScratchReg0{ABINonArgReg0}; ++static constexpr Register WasmTableCallScratchReg1{ABINonArgReg1}; ++static constexpr Register WasmTableCallSigReg{ABINonArgReg2}; ++static constexpr Register WasmTableCallIndexReg{ABINonArgReg3}; ++ ++// Instance pointer argument register for WebAssembly functions. This must not ++// alias any other register used for passing function arguments or return ++// values. Preserved by WebAssembly functions. Must be nonvolatile. ++static constexpr Register InstanceReg{Registers::s4}; ++ ++static constexpr Register WasmJitEntryReturnScratch{Registers::t1}; ++ ++static constexpr Register WasmCallRefCallScratchReg0{ABINonArgReg0}; ++static constexpr Register WasmCallRefCallScratchReg1{ABINonArgReg1}; ++static constexpr Register WasmCallRefReg{ABINonArgReg3}; ++} // namespace jit ++} // namespace js ++ ++#endif // jit_riscv64_Register_riscv64_h +diff --git a/js/src/jit/riscv64/SharedICHelpers-riscv64-inl.h b/js/src/jit/riscv64/SharedICHelpers-riscv64-inl.h +new file mode 100644 +index 0000000000..bd8667c5ec +--- /dev/null ++++ b/js/src/jit/riscv64/SharedICHelpers-riscv64-inl.h +@@ -0,0 +1,80 @@ ++/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- ++ * vim: set ts=8 sts=2 et sw=2 tw=80: ++ * 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_riscv64_SharedICHelpers_riscv64_inl_h ++#define jit_riscv64_SharedICHelpers_riscv64_inl_h ++ ++#include "jit/SharedICHelpers.h" ++ ++namespace js { ++namespace jit { ++ ++inline void EmitBaselineTailCallVM(TrampolinePtr target, MacroAssembler& masm, ++ uint32_t argSize) { ++#ifdef DEBUG ++ Register scratch = R2.scratchReg(); ++ ++ // Compute frame size. ++ masm.movePtr(FramePointer, scratch); ++ masm.subPtr(StackPointer, scratch); ++ ++ // Store frame size without VMFunction arguments for debug assertions. ++ masm.subPtr(Imm32(argSize), scratch); ++ Address frameSizeAddr(FramePointer, ++ BaselineFrame::reverseOffsetOfDebugFrameSize()); ++ masm.store32(scratch, frameSizeAddr); ++ masm.addPtr(Imm32(argSize), scratch); ++#endif ++ ++ // Push frame descriptor and perform the tail call. ++ // ICTailCallReg (ra) already contains the return address (as we ++ // keep it there through the stub calls), but the VMWrapper code being ++ // called expects the return address to also be pushed on the stack. ++ MOZ_ASSERT(ICTailCallReg == ra); ++ masm.pushFrameDescriptor(FrameType::BaselineJS); ++ masm.push(ra); ++ ++ masm.jump(target); ++} ++ ++inline void EmitBaselineCallVM(TrampolinePtr target, MacroAssembler& masm) { ++ masm.pushFrameDescriptor(FrameType::BaselineStub); ++ masm.call(target); ++} ++ ++inline void EmitBaselineEnterStubFrame(MacroAssembler& masm, Register scratch) { ++ MOZ_ASSERT(scratch != ICTailCallReg); ++ ++#ifdef DEBUG ++ // Compute frame size. ++ masm.movePtr(FramePointer, scratch); ++ masm.subPtr(StackPointer, scratch); ++ ++ Address frameSizeAddr(FramePointer, ++ BaselineFrame::reverseOffsetOfDebugFrameSize()); ++ masm.store32(scratch, frameSizeAddr); ++#endif ++ ++ // Note: when making changes here, don't forget to update ++ // BaselineStubFrame if needed. ++ ++ // Push frame descriptor and return address. ++ masm.PushFrameDescriptor(FrameType::BaselineJS); ++ masm.Push(ICTailCallReg); ++ ++ // Save old frame pointer, stack pointer and stub reg. ++ masm.Push(FramePointer); ++ masm.movePtr(StackPointer, FramePointer); ++ masm.Push(ICStubReg); ++ ++ // Stack should remain aligned. ++ masm.assertStackAlignment(sizeof(Value), 0); ++} ++ ++} // namespace jit ++} // namespace js ++ ++#endif /* jit_riscv64_SharedICHelpers_riscv64_inl_h */ +diff --git a/js/src/jit/riscv64/SharedICHelpers-riscv64.h b/js/src/jit/riscv64/SharedICHelpers-riscv64.h +new file mode 100644 +index 0000000000..3411c6727e +--- /dev/null ++++ b/js/src/jit/riscv64/SharedICHelpers-riscv64.h +@@ -0,0 +1,77 @@ ++/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- ++ * vim: set ts=8 sts=2 et sw=2 tw=80: ++ * 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_riscv64_SharedICHelpers_riscv64_h ++#define jit_riscv64_SharedICHelpers_riscv64_h ++#include "jit/BaselineIC.h" ++#include "jit/JitFrames.h" ++#include "jit/MacroAssembler.h" ++#include "jit/SharedICRegisters.h" ++namespace js { ++namespace jit { ++ ++static const size_t ICStackValueOffset = 0; ++ ++inline void EmitRestoreTailCallReg(MacroAssembler& masm) { ++ // No-op on RISC-V because ra register is always holding the return address. ++} ++ ++inline void EmitRepushTailCallReg(MacroAssembler& masm) { ++ // No-op on RISC-V because ra register is always holding the return address. ++} ++inline void EmitCallIC(MacroAssembler& masm, CodeOffset* callOffset) { ++ // The stub pointer must already be in ICStubReg. ++ // Load stubcode pointer from the ICStub. ++ // R2 won't be active when we call ICs, so we can use it as scratch. ++ masm.loadPtr(Address(ICStubReg, ICStub::offsetOfStubCode()), R2.scratchReg()); ++ ++ // Call the stubcode via a direct jump-and-link ++ masm.call(R2.scratchReg()); ++ *callOffset = CodeOffset(masm.currentOffset()); ++} ++inline void EmitReturnFromIC(MacroAssembler& masm) { masm.branch(ra); } ++inline void EmitBaselineLeaveStubFrame(MacroAssembler& masm) { ++ masm.loadPtr( ++ Address(FramePointer, BaselineStubFrameLayout::ICStubOffsetFromFP), ++ ICStubReg); ++ ++ masm.movePtr(FramePointer, StackPointer); ++ masm.Pop(FramePointer); ++ ++ // Load the return address. ++ masm.Pop(ICTailCallReg); ++ ++ // Discard the frame descriptor. ++ { ++ UseScratchRegisterScope temps(&masm); ++ Register scratch2 = temps.Acquire(); ++ masm.Pop(scratch2); ++ } ++ ++ masm.checkStackAlignment(); ++} ++ ++inline void EmitStubGuardFailure(MacroAssembler& masm) { ++ // Load next stub into ICStubReg ++ masm.loadPtr(Address(ICStubReg, ICCacheIRStub::offsetOfNext()), ICStubReg); ++ ++ // Return address is already loaded, just jump to the next stubcode. ++ MOZ_ASSERT(ICTailCallReg == ra); ++ masm.jump(Address(ICStubReg, ICStub::offsetOfStubCode())); ++} ++template ++inline void EmitPreBarrier(MacroAssembler& masm, const AddrType& addr, ++ MIRType type) { ++ // On RISC-V, $ra is clobbered by guardedCallPreBarrier. Save it first. ++ masm.push(ra); ++ masm.guardedCallPreBarrier(addr, type); ++ masm.pop(ra); ++} ++ ++} // namespace jit ++} // namespace js ++ ++#endif /* jit_riscv64_SharedICHelpers_riscv64_h */ +diff --git a/js/src/jit/riscv64/SharedICRegisters-riscv64.h b/js/src/jit/riscv64/SharedICRegisters-riscv64.h +new file mode 100644 +index 0000000000..3dcefe51c7 +--- /dev/null ++++ b/js/src/jit/riscv64/SharedICRegisters-riscv64.h +@@ -0,0 +1,38 @@ ++/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- ++ * vim: set ts=8 sts=2 et sw=2 tw=80: ++ * 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_riscv64_SharedICRegisters_riscv64_h ++#define jit_riscv64_SharedICRegisters_riscv64_h ++ ++#include "jit/Registers.h" ++#include "jit/RegisterSets.h" ++#include "jit/riscv64/MacroAssembler-riscv64.h" ++ ++namespace js { ++namespace jit { ++ ++// ValueOperands R0, R1, and R2. ++// R0 == JSReturnReg, and R2 uses registers not preserved across calls. R1 value ++// should be preserved across calls. ++static constexpr ValueOperand R0(a2); ++static constexpr ValueOperand R1(s1); ++static constexpr ValueOperand R2(a0); ++ ++// ICTailCallReg and ICStubReg ++// These use registers that are not preserved across calls. ++static constexpr Register ICTailCallReg = ra; ++static constexpr Register ICStubReg = t0; ++ ++// FloatReg0 must be equal to ReturnFloatReg. ++static constexpr FloatRegister FloatReg0 = fa0; ++static constexpr FloatRegister FloatReg1 = fa1; ++static constexpr FloatRegister FloatReg2 = fa2; ++static constexpr FloatRegister FloatReg3 = fa3; ++ ++} // namespace jit ++} // namespace js ++ ++#endif /* jit_riscv64_SharedICRegisters_riscv64_h */ +diff --git a/js/src/jit/riscv64/Simulator-riscv64.cpp b/js/src/jit/riscv64/Simulator-riscv64.cpp +new file mode 100644 +index 0000000000..d5171abb09 +--- /dev/null ++++ b/js/src/jit/riscv64/Simulator-riscv64.cpp +@@ -0,0 +1,4699 @@ ++/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- ++ * vim: set ts=8 sts=2 et sw=2 tw=80: */ ++// Copyright 2021 the V8 project authors. All rights reserved. ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions are ++// met: ++// ++// * Redistributions of source code must retain the above copyright ++// notice, this list of conditions and the following disclaimer. ++// * Redistributions in binary form must reproduce the above ++// copyright notice, this list of conditions and the following ++// disclaimer in the documentation and/or other materials provided ++// with the distribution. ++// * Neither the name of Google Inc. nor the names of its ++// contributors may be used to endorse or promote products derived ++// from this software without specific prior written permission. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++#ifdef JS_SIMULATOR_RISCV64 ++# include "jit/riscv64/Simulator-riscv64.h" ++ ++# include "mozilla/Casting.h" ++# include "mozilla/FloatingPoint.h" ++# include "mozilla/IntegerPrintfMacros.h" ++# include "mozilla/Likely.h" ++# include "mozilla/MathAlgorithms.h" ++ ++# include ++# include ++# include ++ ++# include "jit/AtomicOperations.h" ++# include "jit/riscv64/Assembler-riscv64.h" ++# include "js/Conversions.h" ++# include "js/UniquePtr.h" ++# include "js/Utility.h" ++# include "threading/LockGuard.h" ++# include "vm/JSContext.h" ++# include "vm/Runtime.h" ++# include "wasm/WasmInstance.h" ++# include "wasm/WasmSignalHandlers.h" ++ ++# define I8(v) static_cast(v) ++# define I16(v) static_cast(v) ++# define U16(v) static_cast(v) ++# define I32(v) static_cast(v) ++# define U32(v) static_cast(v) ++# define I64(v) static_cast(v) ++# define U64(v) static_cast(v) ++# define I128(v) static_cast<__int128_t>(v) ++# define U128(v) static_cast<__uint128_t>(v) ++ ++# define REGIx_FORMAT PRIx64 ++# define REGId_FORMAT PRId64 ++ ++# define I32_CHECK(v) \ ++ ({ \ ++ MOZ_ASSERT(I64(I32(v)) == I64(v)); \ ++ I32((v)); \ ++ }) ++ ++namespace js { ++namespace jit { ++ ++bool Simulator::FLAG_trace_sim = false; ++bool Simulator::FLAG_debug_sim = false; ++bool Simulator::FLAG_riscv_trap_to_simulator_debugger = false; ++bool Simulator::FLAG_riscv_print_watchpoint = false; ++ ++static void UNIMPLEMENTED() { ++ printf("UNIMPLEMENTED instruction.\n"); ++ MOZ_CRASH(); ++} ++static void UNREACHABLE() { ++ printf("UNREACHABLE instruction.\n"); ++ MOZ_CRASH(); ++} ++# define UNSUPPORTED() \ ++ std::cout << "Unrecognized instruction [@pc=0x" << std::hex \ ++ << registers_[pc] << "]: 0x" << instr_.InstructionBits() \ ++ << std::endl; \ ++ printf("Unsupported instruction.\n"); \ ++ MOZ_CRASH(); ++ ++static char* ReadLine(const char* prompt) { ++ UniqueChars result; ++ char lineBuf[256]; ++ int offset = 0; ++ bool keepGoing = true; ++ fprintf(stdout, "%s", prompt); ++ fflush(stdout); ++ while (keepGoing) { ++ if (fgets(lineBuf, sizeof(lineBuf), stdin) == nullptr) { ++ // fgets got an error. Just give up. ++ return nullptr; ++ } ++ int len = strlen(lineBuf); ++ if (len > 0 && lineBuf[len - 1] == '\n') { ++ // Since we read a new line we are done reading the line. This ++ // will exit the loop after copying this buffer into the result. ++ keepGoing = false; ++ } ++ if (!result) { ++ // Allocate the initial result and make room for the terminating '\0' ++ result.reset(js_pod_malloc(len + 1)); ++ if (!result) { ++ return nullptr; ++ } ++ } else { ++ // Allocate a new result with enough room for the new addition. ++ int new_len = offset + len + 1; ++ char* new_result = js_pod_malloc(new_len); ++ if (!new_result) { ++ return nullptr; ++ } ++ // Copy the existing input into the new array and set the new ++ // array as the result. ++ memcpy(new_result, result.get(), offset * sizeof(char)); ++ result.reset(new_result); ++ } ++ // Copy the newly read line into the result. ++ memcpy(result.get() + offset, lineBuf, len * sizeof(char)); ++ offset += len; ++ } ++ ++ MOZ_ASSERT(result); ++ result[offset] = '\0'; ++ return result.release(); ++} ++ ++// ----------------------------------------------------------------------------- ++// Riscv assembly various constants. ++ ++// C/C++ argument slots size. ++const int kCArgSlotCount = 0; ++const int kCArgsSlotsSize = kCArgSlotCount * sizeof(uintptr_t); ++const int kBranchReturnOffset = 2 * kInstrSize; ++ ++class CachePage { ++ public: ++ static const int LINE_VALID = 0; ++ static const int LINE_INVALID = 1; ++ ++ static const int kPageShift = 12; ++ static const int kPageSize = 1 << kPageShift; ++ static const int kPageMask = kPageSize - 1; ++ static const int kLineShift = 2; // The cache line is only 4 bytes right now. ++ static const int kLineLength = 1 << kLineShift; ++ static const int kLineMask = kLineLength - 1; ++ ++ CachePage() { memset(&validity_map_, LINE_INVALID, sizeof(validity_map_)); } ++ ++ char* validityByte(int offset) { ++ return &validity_map_[offset >> kLineShift]; ++ } ++ ++ char* cachedData(int offset) { return &data_[offset]; } ++ ++ private: ++ char data_[kPageSize]; // The cached data. ++ static const int kValidityMapSize = kPageSize >> kLineShift; ++ char validity_map_[kValidityMapSize]; // One byte per line. ++}; ++ ++// Protects the icache() and redirection() properties of the ++// Simulator. ++class AutoLockSimulatorCache : public LockGuard { ++ using Base = LockGuard; ++ ++ public: ++ explicit AutoLockSimulatorCache() ++ : Base(SimulatorProcess::singleton_->cacheLock_) {} ++}; ++ ++mozilla::Atomic ++ SimulatorProcess::ICacheCheckingDisableCount( ++ 1); // Checking is disabled by default. ++SimulatorProcess* SimulatorProcess::singleton_ = nullptr; ++ ++int64_t Simulator::StopSimAt = -1; ++ ++static bool IsFlag(const char* found, const char* flag) { ++ return strlen(found) == strlen(flag) && strcmp(found, flag) == 0; ++} ++ ++Simulator* Simulator::Create() { ++ auto sim = MakeUnique(); ++ if (!sim) { ++ return nullptr; ++ } ++ ++ if (!sim->init()) { ++ return nullptr; ++ } ++ ++ int64_t stopAt; ++ char* stopAtStr = getenv("RISCV_SIM_STOP_AT"); ++ if (stopAtStr && sscanf(stopAtStr, "%" PRIi64, &stopAt) == 1) { ++ fprintf(stderr, "\nStopping simulation at icount %" PRIi64 "\n", stopAt); ++ Simulator::StopSimAt = stopAt; ++ } ++ char* str = getenv("RISCV_TRACE_SIM"); ++ if (str != nullptr && IsFlag(str, "true")) { ++ FLAG_trace_sim = true; ++ } ++ ++ return sim.release(); ++} ++ ++void Simulator::Destroy(Simulator* sim) { js_delete(sim); } ++ ++# if JS_CODEGEN_RISCV64 ++void Simulator::TraceRegWr(int64_t value, TraceType t) { ++ if (FLAG_trace_sim) { ++ union { ++ int64_t fmt_int64; ++ int32_t fmt_int32[2]; ++ float fmt_float[2]; ++ double fmt_double; ++ } v; ++ v.fmt_int64 = value; ++ ++ switch (t) { ++ case WORD: ++ SNPrintF(trace_buf_, ++ "%016" REGIx_FORMAT " (%" PRId64 ") int32:%" PRId32 ++ " uint32:%" PRIu32, ++ v.fmt_int64, icount_, v.fmt_int32[0], v.fmt_int32[0]); ++ break; ++ case DWORD: ++ SNPrintF(trace_buf_, ++ "%016" REGIx_FORMAT " (%" PRId64 ") int64:%" REGId_FORMAT ++ " uint64:%" PRIu64, ++ value, icount_, value, value); ++ break; ++ case FLOAT: ++ SNPrintF(trace_buf_, "%016" REGIx_FORMAT " (%" PRId64 ") flt:%e", ++ v.fmt_int64, icount_, v.fmt_float[0]); ++ break; ++ case DOUBLE: ++ SNPrintF(trace_buf_, "%016" REGIx_FORMAT " (%" PRId64 ") dbl:%e", ++ v.fmt_int64, icount_, v.fmt_double); ++ break; ++ default: ++ UNREACHABLE(); ++ } ++ } ++} ++ ++# elif JS_CODEGEN_RISCV32 ++template ++void Simulator::TraceRegWr(T value, TraceType t) { ++ if (::v8::internal::FLAG_trace_sim) { ++ union { ++ int32_t fmt_int32; ++ float fmt_float; ++ double fmt_double; ++ } v; ++ if (t != DOUBLE) { ++ v.fmt_int32 = value; ++ } else { ++ DCHECK_EQ(sizeof(T), 8); ++ v.fmt_double = value; ++ } ++ switch (t) { ++ case WORD: ++ SNPrintF(trace_buf_, ++ "%016" REGIx_FORMAT " (%" PRId64 ") int32:%" REGId_FORMAT ++ " uint32:%" PRIu32, ++ v.fmt_int32, icount_, v.fmt_int32, v.fmt_int32); ++ break; ++ case FLOAT: ++ SNPrintF(trace_buf_, "%016" REGIx_FORMAT " (%" PRId64 ") flt:%e", ++ v.fmt_int32, icount_, v.fmt_float); ++ break; ++ case DOUBLE: ++ SNPrintF(trace_buf_, "%016" PRIx64 " (%" PRId64 ") dbl:%e", ++ static_cast(v.fmt_double), icount_, v.fmt_double); ++ break; ++ default: ++ UNREACHABLE(); ++ } ++ } ++} ++# endif ++// The RiscvDebugger class is used by the simulator while debugging simulated ++// code. ++class RiscvDebugger { ++ public: ++ explicit RiscvDebugger(Simulator* sim) : sim_(sim) {} ++ ++ void Debug(); ++ // Print all registers with a nice formatting. ++ void PrintRegs(char name_prefix, int start_index, int end_index); ++ void printAllRegs(); ++ void printAllRegsIncludingFPU(); ++ ++ static const Instr kNopInstr = 0x0; ++ ++ private: ++ Simulator* sim_; ++ ++ int64_t GetRegisterValue(int regnum); ++ int64_t GetFPURegisterValue(int regnum); ++ float GetFPURegisterValueFloat(int regnum); ++ double GetFPURegisterValueDouble(int regnum); ++# ifdef CAN_USE_RVV_INSTRUCTIONS ++ __int128_t GetVRegisterValue(int regnum); ++# endif ++ bool GetValue(const char* desc, int64_t* value); ++}; ++ ++int64_t RiscvDebugger::GetRegisterValue(int regnum) { ++ if (regnum == Simulator::Register::kNumSimuRegisters) { ++ return sim_->get_pc(); ++ } else { ++ return sim_->getRegister(regnum); ++ } ++} ++ ++int64_t RiscvDebugger::GetFPURegisterValue(int regnum) { ++ if (regnum == Simulator::FPURegister::kNumFPURegisters) { ++ return sim_->get_pc(); ++ } else { ++ return sim_->getFpuRegister(regnum); ++ } ++} ++ ++float RiscvDebugger::GetFPURegisterValueFloat(int regnum) { ++ if (regnum == Simulator::FPURegister::kNumFPURegisters) { ++ return sim_->get_pc(); ++ } else { ++ return sim_->getFpuRegisterFloat(regnum); ++ } ++} ++ ++double RiscvDebugger::GetFPURegisterValueDouble(int regnum) { ++ if (regnum == Simulator::FPURegister::kNumFPURegisters) { ++ return sim_->get_pc(); ++ } else { ++ return sim_->getFpuRegisterDouble(regnum); ++ } ++} ++ ++# ifdef CAN_USE_RVV_INSTRUCTIONS ++__int128_t RiscvDebugger::GetVRegisterValue(int regnum) { ++ if (regnum == kNumVRegisters) { ++ return sim_->get_pc(); ++ } else { ++ return sim_->get_vregister(regnum); ++ } ++} ++# endif ++ ++bool RiscvDebugger::GetValue(const char* desc, int64_t* value) { ++ int regnum = Registers::FromName(desc); ++ int fpuregnum = FloatRegisters::FromName(desc); ++ ++ if (regnum != Registers::invalid_reg) { ++ *value = GetRegisterValue(regnum); ++ return true; ++ } else if (fpuregnum != FloatRegisters::invalid_reg) { ++ *value = GetFPURegisterValue(fpuregnum); ++ return true; ++ } else if (strncmp(desc, "0x", 2) == 0) { ++ return sscanf(desc + 2, "%" SCNx64, reinterpret_cast(value)) == 1; ++ } else { ++ return sscanf(desc, "%" SCNu64, reinterpret_cast(value)) == 1; ++ } ++} ++ ++# define REG_INFO(name) \ ++ name, GetRegisterValue(Registers::FromName(name)), \ ++ GetRegisterValue(Registers::FromName(name)) ++ ++void RiscvDebugger::PrintRegs(char name_prefix, int start_index, ++ int end_index) { ++ EmbeddedVector name1, name2; ++ MOZ_ASSERT(name_prefix == 'a' || name_prefix == 't' || name_prefix == 's'); ++ MOZ_ASSERT(start_index >= 0 && end_index <= 99); ++ int num_registers = (end_index - start_index) + 1; ++ for (int i = 0; i < num_registers / 2; i++) { ++ SNPrintF(name1, "%c%d", name_prefix, start_index + 2 * i); ++ SNPrintF(name2, "%c%d", name_prefix, start_index + 2 * i + 1); ++ printf("%3s: 0x%016" REGIx_FORMAT " %14" REGId_FORMAT ++ " \t%3s: 0x%016" REGIx_FORMAT " %14" REGId_FORMAT " \n", ++ REG_INFO(name1.start()), REG_INFO(name2.start())); ++ } ++ if (num_registers % 2 == 1) { ++ SNPrintF(name1, "%c%d", name_prefix, end_index); ++ printf("%3s: 0x%016" REGIx_FORMAT " %14" REGId_FORMAT " \n", ++ REG_INFO(name1.start())); ++ } ++} ++ ++void RiscvDebugger::printAllRegs() { ++ printf("\n"); ++ // ra, sp, gp ++ printf("%3s: 0x%016" REGIx_FORMAT " %14" REGId_FORMAT ++ "\t%3s: 0x%016" REGIx_FORMAT " %14" REGId_FORMAT ++ "\t%3s: 0x%016" REGIx_FORMAT " %14" REGId_FORMAT "\n", ++ REG_INFO("ra"), REG_INFO("sp"), REG_INFO("gp")); ++ ++ // tp, fp, pc ++ printf("%3s: 0x%016" REGIx_FORMAT " %14" REGId_FORMAT ++ "\t%3s: 0x%016" REGIx_FORMAT " %14" REGId_FORMAT ++ "\t%3s: 0x%016" REGIx_FORMAT " %14" REGId_FORMAT "\n", ++ REG_INFO("tp"), REG_INFO("fp"), REG_INFO("pc")); ++ ++ // print register a0, .., a7 ++ PrintRegs('a', 0, 7); ++ // print registers s1, ..., s11 ++ PrintRegs('s', 1, 11); ++ // print registers t0, ..., t6 ++ PrintRegs('t', 0, 6); ++} ++ ++# undef REG_INFO ++ ++void RiscvDebugger::printAllRegsIncludingFPU() { ++# define FPU_REG_INFO(n) \ ++ FloatRegisters::GetName(n), GetFPURegisterValue(n), \ ++ GetFPURegisterValueDouble(n) ++ ++ printAllRegs(); ++ ++ printf("\n\n"); ++ // f0, f1, f2, ... f31. ++ MOZ_ASSERT(kNumFPURegisters % 2 == 0); ++ for (int i = 0; i < kNumFPURegisters; i += 2) ++ printf("%3s: 0x%016" PRIx64 " %16.4e \t%3s: 0x%016" PRIx64 " %16.4e\n", ++ FPU_REG_INFO(i), FPU_REG_INFO(i + 1)); ++# undef FPU_REG_INFO ++} ++ ++void RiscvDebugger::Debug() { ++ intptr_t last_pc = -1; ++ bool done = false; ++ ++# define COMMAND_SIZE 63 ++# define ARG_SIZE 255 ++ ++# define STR(a) #a ++# define XSTR(a) STR(a) ++ ++ char cmd[COMMAND_SIZE + 1]; ++ char arg1[ARG_SIZE + 1]; ++ char arg2[ARG_SIZE + 1]; ++ char* argv[3] = {cmd, arg1, arg2}; ++ ++ // Make sure to have a proper terminating character if reaching the limit. ++ cmd[COMMAND_SIZE] = 0; ++ arg1[ARG_SIZE] = 0; ++ arg2[ARG_SIZE] = 0; ++ ++ while (!done && (sim_->get_pc() != Simulator::end_sim_pc)) { ++ if (last_pc != sim_->get_pc()) { ++ disasm::NameConverter converter; ++ disasm::Disassembler dasm(converter); ++ // Use a reasonably large buffer. ++ EmbeddedVector buffer; ++ dasm.InstructionDecode(buffer, reinterpret_cast(sim_->get_pc())); ++ printf(" 0x%016" REGIx_FORMAT " %s\n", sim_->get_pc(), buffer.start()); ++ last_pc = sim_->get_pc(); ++ } ++ char* line = ReadLine("sim> "); ++ if (line == nullptr) { ++ break; ++ } else { ++ char* last_input = sim_->lastDebuggerInput(); ++ if (strcmp(line, "\n") == 0 && last_input != nullptr) { ++ line = last_input; ++ } else { ++ // Ownership is transferred to sim_; ++ sim_->setLastDebuggerInput(line); ++ } ++ // Use sscanf to parse the individual parts of the command line. At the ++ // moment no command expects more than two parameters. ++ int argc = sscanf( ++ line, ++ "%" XSTR(COMMAND_SIZE) "s " ++ "%" XSTR(ARG_SIZE) "s " ++ "%" XSTR(ARG_SIZE) "s", ++ cmd, arg1, arg2); ++ if ((strcmp(cmd, "si") == 0) || (strcmp(cmd, "stepi") == 0)) { ++ SimInstruction* instr = ++ reinterpret_cast(sim_->get_pc()); ++ if (!(instr->IsTrap()) || ++ instr->InstructionBits() == rtCallRedirInstr) { ++ sim_->icount_++; ++ sim_->InstructionDecode( ++ reinterpret_cast(sim_->get_pc())); ++ } else { ++ // Allow si to jump over generated breakpoints. ++ printf("/!\\ Jumping over generated breakpoint.\n"); ++ sim_->set_pc(sim_->get_pc() + kInstrSize); ++ } ++ } else if ((strcmp(cmd, "c") == 0) || (strcmp(cmd, "cont") == 0)) { ++ // Leave the debugger shell. ++ done = true; ++ } else if ((strcmp(cmd, "p") == 0) || (strcmp(cmd, "print") == 0)) { ++ if (argc == 2) { ++ int64_t value; ++ int64_t fvalue; ++ double dvalue; ++ if (strcmp(arg1, "all") == 0) { ++ printAllRegs(); ++ } else if (strcmp(arg1, "allf") == 0) { ++ printAllRegsIncludingFPU(); ++ } else { ++ int regnum = Registers::FromName(arg1); ++ int fpuregnum = FloatRegisters::FromName(arg1); ++# ifdef CAN_USE_RVV_INSTRUCTIONS ++ int vregnum = VRegisters::FromName(arg1); ++# endif ++ if (regnum != Registers::invalid_reg) { ++ value = GetRegisterValue(regnum); ++ printf("%s: 0x%08" REGIx_FORMAT " %" REGId_FORMAT " \n", arg1, ++ value, value); ++ } else if (fpuregnum != FloatRegisters::invalid_reg) { ++ fvalue = GetFPURegisterValue(fpuregnum); ++ dvalue = GetFPURegisterValueDouble(fpuregnum); ++ printf("%3s: 0x%016" PRIx64 " %16.4e\n", ++ FloatRegisters::GetName(fpuregnum), fvalue, dvalue); ++# ifdef CAN_USE_RVV_INSTRUCTIONS ++ } else if (vregnum != kInvalidVRegister) { ++ __int128_t v = GetVRegisterValue(vregnum); ++ printf("\t%s:0x%016" REGIx_FORMAT "%016" REGIx_FORMAT "\n", ++ VRegisters::GetName(vregnum), (uint64_t)(v >> 64), ++ (uint64_t)v); ++# endif ++ } else { ++ printf("%s unrecognized\n", arg1); ++ } ++ } ++ } else { ++ if (argc == 3) { ++ if (strcmp(arg2, "single") == 0) { ++ int64_t value; ++ float fvalue; ++ int fpuregnum = FloatRegisters::FromName(arg1); ++ ++ if (fpuregnum != FloatRegisters::invalid_reg) { ++ value = GetFPURegisterValue(fpuregnum); ++ value &= 0xFFFFFFFFUL; ++ fvalue = GetFPURegisterValueFloat(fpuregnum); ++ printf("%s: 0x%08" PRIx64 " %11.4e\n", arg1, value, fvalue); ++ } else { ++ printf("%s unrecognized\n", arg1); ++ } ++ } else { ++ printf("print single\n"); ++ } ++ } else { ++ printf("print or print single\n"); ++ } ++ } ++ } else if ((strcmp(cmd, "po") == 0) || ++ (strcmp(cmd, "printobject") == 0)) { ++ UNIMPLEMENTED(); ++ } else if (strcmp(cmd, "stack") == 0 || strcmp(cmd, "mem") == 0) { ++ int64_t* cur = nullptr; ++ int64_t* end = nullptr; ++ int next_arg = 1; ++ if (argc < 2) { ++ printf("Need to specify
to memhex command\n"); ++ continue; ++ } ++ int64_t value; ++ if (!GetValue(arg1, &value)) { ++ printf("%s unrecognized\n", arg1); ++ continue; ++ } ++ cur = reinterpret_cast(value); ++ next_arg++; ++ ++ int64_t words; ++ if (argc == next_arg) { ++ words = 10; ++ } else { ++ if (!GetValue(argv[next_arg], &words)) { ++ words = 10; ++ } ++ } ++ end = cur + words; ++ ++ while (cur < end) { ++ printf(" 0x%012" PRIxPTR " : 0x%016" REGIx_FORMAT ++ " %14" REGId_FORMAT " ", ++ reinterpret_cast(cur), *cur, *cur); ++ printf("\n"); ++ cur++; ++ } ++ } else if ((strcmp(cmd, "watch") == 0)) { ++ if (argc < 2) { ++ printf("Need to specify
to mem command\n"); ++ continue; ++ } ++ int64_t value; ++ if (!GetValue(arg1, &value)) { ++ printf("%s unrecognized\n", arg1); ++ continue; ++ } ++ sim_->watch_address_ = reinterpret_cast(value); ++ sim_->watch_value_ = *(sim_->watch_address_); ++ } else if ((strcmp(cmd, "disasm") == 0) || (strcmp(cmd, "dpc") == 0) || ++ (strcmp(cmd, "di") == 0)) { ++ disasm::NameConverter converter; ++ disasm::Disassembler dasm(converter); ++ // Use a reasonably large buffer. ++ EmbeddedVector buffer; ++ ++ byte* cur = nullptr; ++ byte* end = nullptr; ++ ++ if (argc == 1) { ++ cur = reinterpret_cast(sim_->get_pc()); ++ end = cur + (10 * kInstrSize); ++ } else if (argc == 2) { ++ auto regnum = Registers::FromName(arg1); ++ if (regnum != Registers::invalid_reg || strncmp(arg1, "0x", 2) == 0) { ++ // The argument is an address or a register name. ++ sreg_t value; ++ if (GetValue(arg1, &value)) { ++ cur = reinterpret_cast(value); ++ // Disassemble 10 instructions at . ++ end = cur + (10 * kInstrSize); ++ } ++ } else { ++ // The argument is the number of instructions. ++ sreg_t value; ++ if (GetValue(arg1, &value)) { ++ cur = reinterpret_cast(sim_->get_pc()); ++ // Disassemble instructions. ++ end = cur + (value * kInstrSize); ++ } ++ } ++ } else { ++ sreg_t value1; ++ sreg_t value2; ++ if (GetValue(arg1, &value1) && GetValue(arg2, &value2)) { ++ cur = reinterpret_cast(value1); ++ end = cur + (value2 * kInstrSize); ++ } ++ } ++ while (cur < end) { ++ dasm.InstructionDecode(buffer, cur); ++ printf(" 0x%08" PRIxPTR " %s\n", reinterpret_cast(cur), ++ buffer.start()); ++ cur += kInstrSize; ++ } ++ } else if (strcmp(cmd, "trace") == 0) { ++ Simulator::FLAG_trace_sim = true; ++ Simulator::FLAG_riscv_print_watchpoint = true; ++ } else if (strcmp(cmd, "break") == 0 || strcmp(cmd, "b") == 0 || ++ strcmp(cmd, "tbreak") == 0) { ++ bool is_tbreak = strcmp(cmd, "tbreak") == 0; ++ if (argc == 2) { ++ int64_t value; ++ if (GetValue(arg1, &value)) { ++ sim_->SetBreakpoint(reinterpret_cast(value), ++ is_tbreak); ++ } else { ++ printf("%s unrecognized\n", arg1); ++ } ++ } else { ++ sim_->ListBreakpoints(); ++ printf("Use `break
` to set or disable a breakpoint\n"); ++ printf( ++ "Use `tbreak
` to set or disable a temporary " ++ "breakpoint\n"); ++ } ++ } else if (strcmp(cmd, "flags") == 0) { ++ printf("No flags on RISC-V !\n"); ++ } else if (strcmp(cmd, "stop") == 0) { ++ int64_t value; ++ if (argc == 3) { ++ // Print information about all/the specified breakpoint(s). ++ if (strcmp(arg1, "info") == 0) { ++ if (strcmp(arg2, "all") == 0) { ++ printf("Stop information:\n"); ++ for (uint32_t i = kMaxWatchpointCode + 1; i <= kMaxStopCode; ++ i++) { ++ sim_->printStopInfo(i); ++ } ++ } else if (GetValue(arg2, &value)) { ++ sim_->printStopInfo(value); ++ } else { ++ printf("Unrecognized argument.\n"); ++ } ++ } else if (strcmp(arg1, "enable") == 0) { ++ // Enable all/the specified breakpoint(s). ++ if (strcmp(arg2, "all") == 0) { ++ for (uint32_t i = kMaxWatchpointCode + 1; i <= kMaxStopCode; ++ i++) { ++ sim_->enableStop(i); ++ } ++ } else if (GetValue(arg2, &value)) { ++ sim_->enableStop(value); ++ } else { ++ printf("Unrecognized argument.\n"); ++ } ++ } else if (strcmp(arg1, "disable") == 0) { ++ // Disable all/the specified breakpoint(s). ++ if (strcmp(arg2, "all") == 0) { ++ for (uint32_t i = kMaxWatchpointCode + 1; i <= kMaxStopCode; ++ i++) { ++ sim_->disableStop(i); ++ } ++ } else if (GetValue(arg2, &value)) { ++ sim_->disableStop(value); ++ } else { ++ printf("Unrecognized argument.\n"); ++ } ++ } ++ } else { ++ printf("Wrong usage. Use help command for more information.\n"); ++ } ++ } else if ((strcmp(cmd, "stat") == 0) || (strcmp(cmd, "st") == 0)) { ++ UNIMPLEMENTED(); ++ } else if ((strcmp(cmd, "h") == 0) || (strcmp(cmd, "help") == 0)) { ++ printf("cont (alias 'c')\n"); ++ printf(" Continue execution\n"); ++ printf("stepi (alias 'si')\n"); ++ printf(" Step one instruction\n"); ++ printf("print (alias 'p')\n"); ++ printf(" print \n"); ++ printf(" Print register content\n"); ++ printf(" Use register name 'all' to print all GPRs\n"); ++ printf(" Use register name 'allf' to print all GPRs and FPRs\n"); ++ printf("printobject (alias 'po')\n"); ++ printf(" printobject \n"); ++ printf(" Print an object from a register\n"); ++ printf("stack\n"); ++ printf(" stack []\n"); ++ printf(" Dump stack content, default dump 10 words)\n"); ++ printf("mem\n"); ++ printf(" mem
[]\n"); ++ printf(" Dump memory content, default dump 10 words)\n"); ++ printf("watch\n"); ++ printf(" watch
\n"); ++ printf(" watch memory content.)\n"); ++ printf("flags\n"); ++ printf(" print flags\n"); ++ printf("disasm (alias 'di')\n"); ++ printf(" disasm []\n"); ++ printf(" disasm [
] (e.g., disasm pc) \n"); ++ printf(" disasm [[
] ]\n"); ++ printf(" Disassemble code, default is 10 instructions\n"); ++ printf(" from pc\n"); ++ printf("gdb \n"); ++ printf(" Return to gdb if the simulator was started with gdb\n"); ++ printf("break (alias 'b')\n"); ++ printf(" break : list all breakpoints\n"); ++ printf(" break
: set / enable / disable a breakpoint.\n"); ++ printf("tbreak\n"); ++ printf(" tbreak : list all breakpoints\n"); ++ printf( ++ " tbreak
: set / enable / disable a temporary " ++ "breakpoint.\n"); ++ printf(" Set a breakpoint enabled only for one stop. \n"); ++ printf("stop feature:\n"); ++ printf(" Description:\n"); ++ printf(" Stops are debug instructions inserted by\n"); ++ printf(" the Assembler::stop() function.\n"); ++ printf(" When hitting a stop, the Simulator will\n"); ++ printf(" stop and give control to the Debugger.\n"); ++ printf(" All stop codes are watched:\n"); ++ printf(" - They can be enabled / disabled: the Simulator\n"); ++ printf(" will / won't stop when hitting them.\n"); ++ printf(" - The Simulator keeps track of how many times they \n"); ++ printf(" are met. (See the info command.) Going over a\n"); ++ printf(" disabled stop still increases its counter. \n"); ++ printf(" Commands:\n"); ++ printf(" stop info all/ : print infos about number \n"); ++ printf(" or all stop(s).\n"); ++ printf(" stop enable/disable all/ : enables / disables\n"); ++ printf(" all or number stop(s)\n"); ++ } else { ++ printf("Unknown command: %s\n", cmd); ++ } ++ } ++ } ++ ++# undef COMMAND_SIZE ++# undef ARG_SIZE ++ ++# undef STR ++# undef XSTR ++} ++ ++void Simulator::SetBreakpoint(SimInstruction* location, bool is_tbreak) { ++ for (unsigned i = 0; i < breakpoints_.size(); i++) { ++ if (breakpoints_.at(i).location == location) { ++ if (breakpoints_.at(i).is_tbreak != is_tbreak) { ++ printf("Change breakpoint at %p to %s breakpoint\n", ++ reinterpret_cast(location), ++ is_tbreak ? "temporary" : "regular"); ++ breakpoints_.at(i).is_tbreak = is_tbreak; ++ return; ++ } ++ printf("Existing breakpoint at %p was %s\n", ++ reinterpret_cast(location), ++ breakpoints_.at(i).enabled ? "disabled" : "enabled"); ++ breakpoints_.at(i).enabled = !breakpoints_.at(i).enabled; ++ return; ++ } ++ } ++ Breakpoint new_breakpoint = {location, true, is_tbreak}; ++ breakpoints_.push_back(new_breakpoint); ++ printf("Set a %sbreakpoint at %p\n", is_tbreak ? "temporary " : "", ++ reinterpret_cast(location)); ++} ++ ++void Simulator::ListBreakpoints() { ++ printf("Breakpoints:\n"); ++ for (unsigned i = 0; i < breakpoints_.size(); i++) { ++ printf("%p : %s %s\n", ++ reinterpret_cast(breakpoints_.at(i).location), ++ breakpoints_.at(i).enabled ? "enabled" : "disabled", ++ breakpoints_.at(i).is_tbreak ? ": temporary" : ""); ++ } ++} ++ ++void Simulator::CheckBreakpoints() { ++ bool hit_a_breakpoint = false; ++ bool is_tbreak = false; ++ SimInstruction* pc_ = reinterpret_cast(get_pc()); ++ for (unsigned i = 0; i < breakpoints_.size(); i++) { ++ if ((breakpoints_.at(i).location == pc_) && breakpoints_.at(i).enabled) { ++ hit_a_breakpoint = true; ++ if (breakpoints_.at(i).is_tbreak) { ++ // Disable a temporary breakpoint. ++ is_tbreak = true; ++ breakpoints_.at(i).enabled = false; ++ } ++ break; ++ } ++ } ++ if (hit_a_breakpoint) { ++ printf("Hit %sa breakpoint at %p.\n", is_tbreak ? "and disabled " : "", ++ reinterpret_cast(pc_)); ++ RiscvDebugger dbg(this); ++ dbg.Debug(); ++ } ++} ++ ++static bool AllOnOnePage(uintptr_t start, int size) { ++ intptr_t start_page = (start & ~CachePage::kPageMask); ++ intptr_t end_page = ((start + size) & ~CachePage::kPageMask); ++ return start_page == end_page; ++} ++ ++void Simulator::setLastDebuggerInput(char* input) { ++ js_free(lastDebuggerInput_); ++ lastDebuggerInput_ = input; ++} ++ ++static CachePage* GetCachePageLocked(SimulatorProcess::ICacheMap& i_cache, ++ void* page) { ++ SimulatorProcess::ICacheMap::AddPtr p = i_cache.lookupForAdd(page); ++ if (p) { ++ return p->value(); ++ } ++ AutoEnterOOMUnsafeRegion oomUnsafe; ++ CachePage* new_page = js_new(); ++ if (!new_page || !i_cache.add(p, page, new_page)) { ++ oomUnsafe.crash("Simulator CachePage"); ++ } ++ return new_page; ++} ++ ++// Flush from start up to and not including start + size. ++static void FlushOnePageLocked(SimulatorProcess::ICacheMap& i_cache, ++ intptr_t start, int size) { ++ MOZ_ASSERT(size <= CachePage::kPageSize); ++ MOZ_ASSERT(AllOnOnePage(start, size - 1)); ++ MOZ_ASSERT((start & CachePage::kLineMask) == 0); ++ MOZ_ASSERT((size & CachePage::kLineMask) == 0); ++ void* page = reinterpret_cast(start & (~CachePage::kPageMask)); ++ int offset = (start & CachePage::kPageMask); ++ CachePage* cache_page = GetCachePageLocked(i_cache, page); ++ char* valid_bytemap = cache_page->validityByte(offset); ++ memset(valid_bytemap, CachePage::LINE_INVALID, size >> CachePage::kLineShift); ++} ++ ++static void FlushICacheLocked(SimulatorProcess::ICacheMap& i_cache, ++ void* start_addr, size_t size) { ++ intptr_t start = reinterpret_cast(start_addr); ++ int intra_line = (start & CachePage::kLineMask); ++ start -= intra_line; ++ size += intra_line; ++ size = ((size - 1) | CachePage::kLineMask) + 1; ++ int offset = (start & CachePage::kPageMask); ++ while (!AllOnOnePage(start, size - 1)) { ++ int bytes_to_flush = CachePage::kPageSize - offset; ++ FlushOnePageLocked(i_cache, start, bytes_to_flush); ++ start += bytes_to_flush; ++ size -= bytes_to_flush; ++ MOZ_ASSERT((start & CachePage::kPageMask) == 0); ++ offset = 0; ++ } ++ if (size != 0) { ++ FlushOnePageLocked(i_cache, start, size); ++ } ++} ++ ++/* static */ ++void SimulatorProcess::checkICacheLocked(SimInstruction* instr) { ++ intptr_t address = reinterpret_cast(instr); ++ void* page = reinterpret_cast(address & (~CachePage::kPageMask)); ++ void* line = reinterpret_cast(address & (~CachePage::kLineMask)); ++ int offset = (address & CachePage::kPageMask); ++ CachePage* cache_page = GetCachePageLocked(icache(), page); ++ char* cache_valid_byte = cache_page->validityByte(offset); ++ bool cache_hit = (*cache_valid_byte == CachePage::LINE_VALID); ++ char* cached_line = cache_page->cachedData(offset & ~CachePage::kLineMask); ++ ++ if (cache_hit) { ++ // Check that the data in memory matches the contents of the I-cache. ++ int cmpret = memcmp(reinterpret_cast(instr), ++ cache_page->cachedData(offset), kInstrSize); ++ MOZ_ASSERT(cmpret == 0); ++ } else { ++ // Cache miss. Load memory into the cache. ++ memcpy(cached_line, line, CachePage::kLineLength); ++ *cache_valid_byte = CachePage::LINE_VALID; ++ } ++} ++ ++HashNumber SimulatorProcess::ICacheHasher::hash(const Lookup& l) { ++ return U32(reinterpret_cast(l)) >> 2; ++} ++ ++bool SimulatorProcess::ICacheHasher::match(const Key& k, const Lookup& l) { ++ MOZ_ASSERT((reinterpret_cast(k) & CachePage::kPageMask) == 0); ++ MOZ_ASSERT((reinterpret_cast(l) & CachePage::kPageMask) == 0); ++ return k == l; ++} ++ ++/* static */ ++void SimulatorProcess::FlushICache(void* start_addr, size_t size) { ++ if (!ICacheCheckingDisableCount) { ++ AutoLockSimulatorCache als; ++ js::jit::FlushICacheLocked(icache(), start_addr, size); ++ } ++} ++ ++Simulator::Simulator() { ++ // Set up simulator support first. Some of this information is needed to ++ // setup the architecture state. ++ ++ // Note, allocation and anything that depends on allocated memory is ++ // deferred until init(), in order to handle OOM properly. ++ ++ stack_ = nullptr; ++ stackLimit_ = 0; ++ pc_modified_ = false; ++ icount_ = 0; ++ break_count_ = 0; ++ break_pc_ = nullptr; ++ break_instr_ = 0; ++ single_stepping_ = false; ++ single_step_callback_ = nullptr; ++ single_step_callback_arg_ = nullptr; ++ ++ // Set up architecture state. ++ // All registers are initialized to zero to start with. ++ for (int i = 0; i < Simulator::Register::kNumSimuRegisters; i++) { ++ registers_[i] = 0; ++ } ++ for (int i = 0; i < Simulator::FPURegister::kNumFPURegisters; i++) { ++ FPUregisters_[i] = 0; ++ } ++ FCSR_ = 0; ++ LLBit_ = false; ++ LLAddr_ = 0; ++ lastLLValue_ = 0; ++ ++ // The ra and pc are initialized to a known bad value that will cause an ++ // access violation if the simulator ever tries to execute it. ++ registers_[pc] = bad_ra; ++ registers_[ra] = bad_ra; ++ ++ for (int i = 0; i < kNumExceptions; i++) { ++ exceptions[i] = 0; ++ } ++ ++ lastDebuggerInput_ = nullptr; ++} ++ ++bool Simulator::init() { ++ // Allocate 2MB for the stack. Note that we will only use 1MB, see below. ++ static const size_t stackSize = 2 * 1024 * 1024; ++ stack_ = js_pod_malloc(stackSize); ++ if (!stack_) { ++ return false; ++ } ++ ++ // Leave a safety margin of 1MB to prevent overrunning the stack when ++ // pushing values (total stack size is 2MB). ++ stackLimit_ = reinterpret_cast(stack_) + 1024 * 1024; ++ ++ // The sp is initialized to point to the bottom (high address) of the ++ // allocated stack area. To be safe in potential stack underflows we leave ++ // some buffer below. ++ registers_[sp] = reinterpret_cast(stack_) + stackSize - 64; ++ ++ return true; ++} ++ ++// When the generated code calls an external reference we need to catch that in ++// the simulator. The external reference will be a function compiled for the ++// host architecture. We need to call that function instead of trying to ++// execute it with the simulator. We do that by redirecting the external ++// reference to a swi (software-interrupt) instruction that is handled by ++// the simulator. We write the original destination of the jump just at a known ++// offset from the swi instruction so the simulator knows what to call. ++class Redirection { ++ friend class SimulatorProcess; ++ ++ // sim's lock must already be held. ++ Redirection(void* nativeFunction, ABIFunctionType type) ++ : nativeFunction_(nativeFunction), ++ swiInstruction_(rtCallRedirInstr), ++ type_(type), ++ next_(nullptr) { ++ next_ = SimulatorProcess::redirection(); ++ if (!SimulatorProcess::ICacheCheckingDisableCount) { ++ FlushICacheLocked(SimulatorProcess::icache(), addressOfSwiInstruction(), ++ kInstrSize); ++ } ++ SimulatorProcess::setRedirection(this); ++ } ++ ++ public: ++ void* addressOfSwiInstruction() { return &swiInstruction_; } ++ void* nativeFunction() const { return nativeFunction_; } ++ ABIFunctionType type() const { return type_; } ++ ++ static Redirection* Get(void* nativeFunction, ABIFunctionType type) { ++ AutoLockSimulatorCache als; ++ ++ Redirection* current = SimulatorProcess::redirection(); ++ for (; current != nullptr; current = current->next_) { ++ if (current->nativeFunction_ == nativeFunction) { ++ MOZ_ASSERT(current->type() == type); ++ return current; ++ } ++ } ++ ++ // Note: we can't use js_new here because the constructor is private. ++ AutoEnterOOMUnsafeRegion oomUnsafe; ++ Redirection* redir = js_pod_malloc(1); ++ if (!redir) { ++ oomUnsafe.crash("Simulator redirection"); ++ } ++ new (redir) Redirection(nativeFunction, type); ++ return redir; ++ } ++ ++ static Redirection* FromSwiInstruction(Instruction* swiInstruction) { ++ uint8_t* addrOfSwi = reinterpret_cast(swiInstruction); ++ uint8_t* addrOfRedirection = ++ addrOfSwi - offsetof(Redirection, swiInstruction_); ++ return reinterpret_cast(addrOfRedirection); ++ } ++ ++ private: ++ void* nativeFunction_; ++ uint32_t swiInstruction_; ++ ABIFunctionType type_; ++ Redirection* next_; ++}; ++ ++Simulator::~Simulator() { js_free(stack_); } ++ ++SimulatorProcess::SimulatorProcess() ++ : cacheLock_(mutexid::SimulatorCacheLock), redirection_(nullptr) { ++ if (getenv("MIPS_SIM_ICACHE_CHECKS")) { ++ ICacheCheckingDisableCount = 0; ++ } ++} ++ ++SimulatorProcess::~SimulatorProcess() { ++ Redirection* r = redirection_; ++ while (r) { ++ Redirection* next = r->next_; ++ js_delete(r); ++ r = next; ++ } ++} ++ ++/* static */ ++void* Simulator::RedirectNativeFunction(void* nativeFunction, ++ ABIFunctionType type) { ++ Redirection* redirection = Redirection::Get(nativeFunction, type); ++ return redirection->addressOfSwiInstruction(); ++} ++ ++// Get the active Simulator for the current thread. ++Simulator* Simulator::Current() { ++ JSContext* cx = TlsContext.get(); ++ MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime())); ++ return cx->simulator(); ++} ++ ++// Sets the register in the architecture state. It will also deal with updating ++// Simulator internal state for special registers such as PC. ++void Simulator::setRegister(int reg, int64_t value) { ++ MOZ_ASSERT((reg >= 0) && (reg < Simulator::Register::kNumSimuRegisters)); ++ if (reg == pc) { ++ pc_modified_ = true; ++ } ++ ++ // Zero register always holds 0. ++ registers_[reg] = (reg == 0) ? 0 : value; ++} ++ ++void Simulator::setFpuRegister(int fpureg, int64_t value) { ++ MOZ_ASSERT((fpureg >= 0) && ++ (fpureg < Simulator::FPURegister::kNumFPURegisters)); ++ FPUregisters_[fpureg] = value; ++} ++ ++void Simulator::setFpuRegisterLo(int fpureg, int32_t value) { ++ MOZ_ASSERT((fpureg >= 0) && ++ (fpureg < Simulator::FPURegister::kNumFPURegisters)); ++ *mozilla::BitwiseCast(&FPUregisters_[fpureg]) = value; ++} ++ ++void Simulator::setFpuRegisterHi(int fpureg, int32_t value) { ++ MOZ_ASSERT((fpureg >= 0) && ++ (fpureg < Simulator::FPURegister::kNumFPURegisters)); ++ *((mozilla::BitwiseCast(&FPUregisters_[fpureg])) + 1) = value; ++} ++ ++void Simulator::setFpuRegisterFloat(int fpureg, float value) { ++ MOZ_ASSERT((fpureg >= 0) && ++ (fpureg < Simulator::FPURegister::kNumFPURegisters)); ++ *mozilla::BitwiseCast(&FPUregisters_[fpureg]) = box_float(value); ++} ++ ++void Simulator::setFpuRegisterFloat(int fpureg, Float32 value) { ++ MOZ_ASSERT((fpureg >= 0) && (fpureg < kNumFPURegisters)); ++ Float64 t = Float64::FromBits(box_float(value.get_bits())); ++ memcpy(&FPUregisters_[fpureg], &t, 8); ++} ++ ++void Simulator::setFpuRegisterDouble(int fpureg, double value) { ++ MOZ_ASSERT((fpureg >= 0) && ++ (fpureg < Simulator::FPURegister::kNumFPURegisters)); ++ *mozilla::BitwiseCast(&FPUregisters_[fpureg]) = value; ++} ++ ++void Simulator::setFpuRegisterDouble(int fpureg, Float64 value) { ++ MOZ_ASSERT((fpureg >= 0) && (fpureg < kNumFPURegisters)); ++ memcpy(&FPUregisters_[fpureg], &value, 8); ++} ++ ++// Get the register from the architecture state. This function does handle ++// the special case of accessing the PC register. ++int64_t Simulator::getRegister(int reg) const { ++ MOZ_ASSERT((reg >= 0) && (reg < Simulator::Register::kNumSimuRegisters)); ++ if (reg == 0) { ++ return 0; ++ } ++ return registers_[reg] + ((reg == pc) ? SimInstruction::kPCReadOffset : 0); ++} ++ ++int64_t Simulator::getFpuRegister(int fpureg) const { ++ MOZ_ASSERT((fpureg >= 0) && ++ (fpureg < Simulator::FPURegister::kNumFPURegisters)); ++ return FPUregisters_[fpureg]; ++} ++ ++int32_t Simulator::getFpuRegisterLo(int fpureg) const { ++ MOZ_ASSERT((fpureg >= 0) && ++ (fpureg < Simulator::FPURegister::kNumFPURegisters)); ++ return *mozilla::BitwiseCast(&FPUregisters_[fpureg]); ++} ++ ++int32_t Simulator::getFpuRegisterHi(int fpureg) const { ++ MOZ_ASSERT((fpureg >= 0) && ++ (fpureg < Simulator::FPURegister::kNumFPURegisters)); ++ return *((mozilla::BitwiseCast(&FPUregisters_[fpureg])) + 1); ++} ++ ++float Simulator::getFpuRegisterFloat(int fpureg) const { ++ MOZ_ASSERT((fpureg >= 0) && ++ (fpureg < Simulator::FPURegister::kNumFPURegisters)); ++ return *mozilla::BitwiseCast(&FPUregisters_[fpureg]); ++} ++ ++Float32 Simulator::getFpuRegisterFloat32(int fpureg) const { ++ MOZ_ASSERT((fpureg >= 0) && (fpureg < kNumFPURegisters)); ++ if (!is_boxed_float(FPUregisters_[fpureg])) { ++ return Float32::FromBits(0x7ffc0000); ++ } ++ return Float32::FromBits( ++ *bit_cast(const_cast(&FPUregisters_[fpureg]))); ++} ++ ++double Simulator::getFpuRegisterDouble(int fpureg) const { ++ MOZ_ASSERT((fpureg >= 0) && ++ (fpureg < Simulator::FPURegister::kNumFPURegisters)); ++ return *mozilla::BitwiseCast(&FPUregisters_[fpureg]); ++} ++ ++Float64 Simulator::getFpuRegisterFloat64(int fpureg) const { ++ MOZ_ASSERT((fpureg >= 0) && (fpureg < kNumFPURegisters)); ++ return Float64::FromBits(FPUregisters_[fpureg]); ++} ++ ++void Simulator::setCallResultDouble(double result) { ++ setFpuRegisterDouble(fa0, result); ++} ++ ++void Simulator::setCallResultFloat(float result) { ++ setFpuRegisterFloat(fa0, result); ++} ++ ++void Simulator::setCallResult(int64_t res) { setRegister(a0, res); } ++ ++void Simulator::setCallResult(__int128_t res) { ++ setRegister(a0, I64(res)); ++ setRegister(a1, I64(res >> 64)); ++} ++ ++// Raw access to the PC register. ++void Simulator::set_pc(int64_t value) { ++ pc_modified_ = true; ++ registers_[pc] = value; ++} ++ ++bool Simulator::has_bad_pc() const { ++ return ((registers_[pc] == bad_ra) || (registers_[pc] == end_sim_pc)); ++} ++ ++// Raw access to the PC register without the special adjustment when reading. ++int64_t Simulator::get_pc() const { return registers_[pc]; } ++ ++JS::ProfilingFrameIterator::RegisterState Simulator::registerState() { ++ wasm::RegisterState state; ++ state.pc = (void*)get_pc(); ++ state.fp = (void*)getRegister(fp); ++ state.sp = (void*)getRegister(sp); ++ state.lr = (void*)getRegister(ra); ++ return state; ++} ++ ++// TODO(plind): consider making icount_ printing a flag option. ++template ++void Simulator::TraceMemRd(sreg_t addr, T value, sreg_t reg_value) { ++ if (FLAG_trace_sim) { ++ if (std::is_integral::value) { ++ switch (sizeof(T)) { ++ case 1: ++ SNPrintF(trace_buf_, ++ "%016" REGIx_FORMAT " (%" PRId64 ") int8:%" PRId8 ++ " uint8:%" PRIu8 " <-- [addr: %" REGIx_FORMAT "]", ++ reg_value, icount_, static_cast(value), ++ static_cast(value), addr); ++ break; ++ case 2: ++ SNPrintF(trace_buf_, ++ "%016" REGIx_FORMAT " (%" PRId64 ") int16:%" PRId16 ++ " uint16:%" PRIu16 " <-- [addr: %" REGIx_FORMAT "]", ++ reg_value, icount_, static_cast(value), ++ static_cast(value), addr); ++ break; ++ case 4: ++ SNPrintF(trace_buf_, ++ "%016" REGIx_FORMAT " (%" PRId64 ") int32:%" PRId32 ++ " uint32:%" PRIu32 " <-- [addr: %" REGIx_FORMAT "]", ++ reg_value, icount_, static_cast(value), ++ static_cast(value), addr); ++ break; ++ case 8: ++ SNPrintF(trace_buf_, ++ "%016" REGIx_FORMAT " (%" PRId64 ") int64:%" PRId64 ++ " uint64:%" PRIu64 " <-- [addr: %" REGIx_FORMAT "]", ++ reg_value, icount_, static_cast(value), ++ static_cast(value), addr); ++ break; ++ default: ++ UNREACHABLE(); ++ } ++ } else if (std::is_same::value) { ++ SNPrintF(trace_buf_, ++ "%016" REGIx_FORMAT " (%" PRId64 ++ ") flt:%e <-- [addr: %" REGIx_FORMAT "]", ++ reg_value, icount_, static_cast(value), addr); ++ } else if (std::is_same::value) { ++ SNPrintF(trace_buf_, ++ "%016" REGIx_FORMAT " (%" PRId64 ++ ") dbl:%e <-- [addr: %" REGIx_FORMAT "]", ++ reg_value, icount_, static_cast(value), addr); ++ } else { ++ UNREACHABLE(); ++ } ++ } ++} ++ ++void Simulator::TraceMemRdFloat(sreg_t addr, Float32 value, int64_t reg_value) { ++ if (FLAG_trace_sim) { ++ SNPrintF(trace_buf_, ++ "%016" PRIx64 " (%" PRId64 ++ ") flt:%e <-- [addr: %" REGIx_FORMAT "]", ++ reg_value, icount_, static_cast(value.get_scalar()), addr); ++ } ++} ++ ++void Simulator::TraceMemRdDouble(sreg_t addr, double value, int64_t reg_value) { ++ if (FLAG_trace_sim) { ++ SNPrintF(trace_buf_, ++ "%016" PRIx64 " (%" PRId64 ++ ") dbl:%e <-- [addr: %" REGIx_FORMAT "]", ++ reg_value, icount_, static_cast(value), addr); ++ } ++} ++ ++void Simulator::TraceMemRdDouble(sreg_t addr, Float64 value, ++ int64_t reg_value) { ++ if (FLAG_trace_sim) { ++ SNPrintF(trace_buf_, ++ "%016" PRIx64 " (%" PRId64 ++ ") dbl:%e <-- [addr: %" REGIx_FORMAT "]", ++ reg_value, icount_, static_cast(value.get_scalar()), addr); ++ } ++} ++ ++template ++void Simulator::TraceMemWr(sreg_t addr, T value) { ++ if (FLAG_trace_sim) { ++ switch (sizeof(T)) { ++ case 1: ++ SNPrintF(trace_buf_, ++ " (%" PRIu64 ") int8:%" PRId8 ++ " uint8:%" PRIu8 " --> [addr: %" REGIx_FORMAT "]", ++ icount_, static_cast(value), ++ static_cast(value), addr); ++ break; ++ case 2: ++ SNPrintF(trace_buf_, ++ " (%" PRIu64 ") int16:%" PRId16 ++ " uint16:%" PRIu16 " --> [addr: %" REGIx_FORMAT "]", ++ icount_, static_cast(value), ++ static_cast(value), addr); ++ break; ++ case 4: ++ if (std::is_integral::value) { ++ SNPrintF(trace_buf_, ++ " (%" PRIu64 ") int32:%" PRId32 ++ " uint32:%" PRIu32 " --> [addr: %" REGIx_FORMAT "]", ++ icount_, static_cast(value), ++ static_cast(value), addr); ++ } else { ++ SNPrintF(trace_buf_, ++ " (%" PRIu64 ++ ") flt:%e --> [addr: %" REGIx_FORMAT "]", ++ icount_, static_cast(value), addr); ++ } ++ break; ++ case 8: ++ if (std::is_integral::value) { ++ SNPrintF(trace_buf_, ++ " (%" PRIu64 ") int64:%" PRId64 ++ " uint64:%" PRIu64 " --> [addr: %" REGIx_FORMAT "]", ++ icount_, static_cast(value), ++ static_cast(value), addr); ++ } else { ++ SNPrintF(trace_buf_, ++ " (%" PRIu64 ++ ") dbl:%e --> [addr: %" REGIx_FORMAT "]", ++ icount_, static_cast(value), addr); ++ } ++ break; ++ default: ++ UNREACHABLE(); ++ } ++ } ++} ++ ++void Simulator::TraceMemWrDouble(sreg_t addr, double value) { ++ if (FLAG_trace_sim) { ++ SNPrintF(trace_buf_, ++ " (%" PRIu64 ++ ") dbl:%e --> [addr: %" REGIx_FORMAT "]", ++ icount_, value, addr); ++ } ++} ++ ++template ++void Simulator::TraceLr(sreg_t addr, T value, sreg_t reg_value) { ++ if (FLAG_trace_sim) { ++ if (std::is_integral::value) { ++ switch (sizeof(T)) { ++ case 4: ++ SNPrintF(trace_buf_, ++ "%016" REGIx_FORMAT " (%" PRId64 ") int32:%" PRId32 ++ " uint32:%" PRIu32 " <-- [addr: %" REGIx_FORMAT "]", ++ reg_value, icount_, static_cast(value), ++ static_cast(value), addr); ++ break; ++ case 8: ++ SNPrintF(trace_buf_, ++ "%016" REGIx_FORMAT " (%" PRId64 ") int64:%" PRId64 ++ " uint64:%" PRIu64 " <-- [addr: %" REGIx_FORMAT "]", ++ reg_value, icount_, static_cast(value), ++ static_cast(value), addr); ++ break; ++ default: ++ UNREACHABLE(); ++ } ++ } else { ++ UNREACHABLE(); ++ } ++ } ++} ++ ++template ++void Simulator::TraceSc(sreg_t addr, T value) { ++ if (FLAG_trace_sim) { ++ switch (sizeof(T)) { ++ case 4: ++ SNPrintF(trace_buf_, ++ "%016" REGIx_FORMAT " (%" PRIu64 ") int32:%" PRId32 ++ " uint32:%" PRIu32 " --> [addr: %" REGIx_FORMAT "]", ++ getRegister(rd_reg()), icount_, static_cast(value), ++ static_cast(value), addr); ++ break; ++ case 8: ++ SNPrintF(trace_buf_, ++ "%016" REGIx_FORMAT " (%" PRIu64 ") int64:%" PRId64 ++ " uint64:%" PRIu64 " --> [addr: %" REGIx_FORMAT "]", ++ getRegister(rd_reg()), icount_, static_cast(value), ++ static_cast(value), addr); ++ break; ++ default: ++ UNREACHABLE(); ++ } ++ } ++} ++ ++// TODO(RISCV): check whether the specific board supports unaligned load/store ++// (determined by EEI). For now, we assume the board does not support unaligned ++// load/store (e.g., trapping) ++template ++T Simulator::ReadMem(sreg_t addr, Instruction* instr) { ++ if (handleWasmSegFault(addr, sizeof(T))) { ++ return -1; ++ } ++ if (addr >= 0 && addr < 0x400) { ++ // This has to be a nullptr-dereference, drop into debugger. ++ printf("Memory read from bad address: 0x%08" REGIx_FORMAT ++ " , pc=0x%08" PRIxPTR " \n", ++ addr, reinterpret_cast(instr)); ++ DieOrDebug(); ++ } ++ T* ptr = reinterpret_cast(addr); ++ T value = *ptr; ++ return value; ++} ++ ++template ++void Simulator::WriteMem(sreg_t addr, T value, Instruction* instr) { ++ if (handleWasmSegFault(addr, sizeof(T))) { ++ value = -1; ++ return; ++ } ++ if (addr >= 0 && addr < 0x400) { ++ // This has to be a nullptr-dereference, drop into debugger. ++ printf("Memory write to bad address: 0x%08" REGIx_FORMAT ++ " , pc=0x%08" PRIxPTR " \n", ++ addr, reinterpret_cast(instr)); ++ DieOrDebug(); ++ } ++ T* ptr = reinterpret_cast(addr); ++ if (!std::is_same::value) { ++ TraceMemWr(addr, value); ++ } else { ++ TraceMemWrDouble(addr, value); ++ } ++ *ptr = value; ++} ++ ++template <> ++void Simulator::WriteMem(sreg_t addr, Float32 value, Instruction* instr) { ++ if (handleWasmSegFault(addr, 4)) { ++ value = Float32(-1.0f); ++ return; ++ } ++ if (addr >= 0 && addr < 0x400) { ++ // This has to be a nullptr-dereference, drop into debugger. ++ printf("Memory write to bad address: 0x%08" REGIx_FORMAT ++ " , pc=0x%08" PRIxPTR " \n", ++ addr, reinterpret_cast(instr)); ++ DieOrDebug(); ++ } ++ float* ptr = reinterpret_cast(addr); ++ TraceMemWr(addr, value.get_scalar()); ++ memcpy(ptr, &value, 4); ++} ++ ++template <> ++void Simulator::WriteMem(sreg_t addr, Float64 value, Instruction* instr) { ++ if (handleWasmSegFault(addr, 8)) { ++ value = Float64(-1.0); ++ return; ++ } ++ if (addr >= 0 && addr < 0x400) { ++ // This has to be a nullptr-dereference, drop into debugger. ++ printf("Memory write to bad address: 0x%08" REGIx_FORMAT ++ " , pc=0x%08" PRIxPTR " \n", ++ addr, reinterpret_cast(instr)); ++ DieOrDebug(); ++ } ++ double* ptr = reinterpret_cast(addr); ++ TraceMemWrDouble(addr, value.get_scalar()); ++ memcpy(ptr, &value, 8); ++} ++ ++uintptr_t Simulator::stackLimit() const { return stackLimit_; } ++ ++uintptr_t* Simulator::addressOfStackLimit() { return &stackLimit_; } ++ ++bool Simulator::overRecursed(uintptr_t newsp) const { ++ if (newsp == 0) { ++ newsp = getRegister(sp); ++ } ++ return newsp <= stackLimit(); ++} ++ ++bool Simulator::overRecursedWithExtra(uint32_t extra) const { ++ uintptr_t newsp = getRegister(sp) - extra; ++ return newsp <= stackLimit(); ++} ++ ++// Unsupported instructions use format to print an error and stop execution. ++void Simulator::format(SimInstruction* instr, const char* format) { ++ printf("Simulator found unsupported instruction:\n 0x%016lx: %s\n", ++ reinterpret_cast(instr), format); ++ MOZ_CRASH(); ++} ++ ++// Note: With the code below we assume that all runtime calls return a 64 bits ++// result. If they don't, the v1 result register contains a bogus value, which ++// is fine because it is caller-saved. ++typedef int64_t (*Prototype_General0)(); ++typedef int64_t (*Prototype_General1)(int64_t arg0); ++typedef int64_t (*Prototype_General2)(int64_t arg0, int64_t arg1); ++typedef int64_t (*Prototype_General3)(int64_t arg0, int64_t arg1, int64_t arg2); ++typedef int64_t (*Prototype_General4)(int64_t arg0, int64_t arg1, int64_t arg2, ++ int64_t arg3); ++typedef int64_t (*Prototype_General5)(int64_t arg0, int64_t arg1, int64_t arg2, ++ int64_t arg3, int64_t arg4); ++typedef int64_t (*Prototype_General6)(int64_t arg0, int64_t arg1, int64_t arg2, ++ int64_t arg3, int64_t arg4, int64_t arg5); ++typedef int64_t (*Prototype_General7)(int64_t arg0, int64_t arg1, int64_t arg2, ++ int64_t arg3, int64_t arg4, int64_t arg5, ++ int64_t arg6); ++typedef int64_t (*Prototype_General8)(int64_t arg0, int64_t arg1, int64_t arg2, ++ int64_t arg3, int64_t arg4, int64_t arg5, ++ int64_t arg6, int64_t arg7); ++typedef int64_t (*Prototype_GeneralGeneralGeneralInt64)(int64_t arg0, ++ int64_t arg1, ++ int64_t arg2, ++ int64_t arg3); ++typedef int64_t (*Prototype_GeneralGeneralInt64Int64)(int64_t arg0, ++ int64_t arg1, ++ int64_t arg2, ++ int64_t arg3); ++ ++typedef int64_t (*Prototype_Int_Double)(double arg0); ++typedef int64_t (*Prototype_Int_IntDouble)(int64_t arg0, double arg1); ++typedef int64_t (*Prototype_Int_DoubleInt)(double arg0, int64_t arg1); ++typedef int64_t (*Prototype_Int_DoubleIntInt)(double arg0, int64_t arg1, ++ int64_t arg2); ++typedef int64_t (*Prototype_Int_IntDoubleIntInt)(int64_t arg0, double arg1, ++ int64_t arg2, int64_t arg3); ++ ++typedef float (*Prototype_Float32_Float32)(float arg0); ++typedef int64_t (*Prototype_Int_Float32)(float arg0); ++typedef float (*Prototype_Float32_Float32Float32)(float arg0, float arg1); ++ ++typedef double (*Prototype_Double_None)(); ++typedef double (*Prototype_Double_Double)(double arg0); ++typedef double (*Prototype_Double_Int)(int64_t arg0); ++typedef double (*Prototype_Double_DoubleInt)(double arg0, int64_t arg1); ++typedef double (*Prototype_Double_IntDouble)(int64_t arg0, double arg1); ++typedef double (*Prototype_Double_DoubleDouble)(double arg0, double arg1); ++typedef double (*Prototype_Double_DoubleDoubleDouble)(double arg0, double arg1, ++ double arg2); ++typedef double (*Prototype_Double_DoubleDoubleDoubleDouble)(double arg0, ++ double arg1, ++ double arg2, ++ double arg3); ++ ++typedef int32_t (*Prototype_Int32_General)(int64_t); ++typedef int32_t (*Prototype_Int32_GeneralInt32)(int64_t, int32_t); ++typedef int32_t (*Prototype_Int32_GeneralInt32Int32)(int64_t, int32_t, int32_t); ++typedef int32_t (*Prototype_Int32_GeneralInt32Int32Int32Int32)(int64_t, int32_t, ++ int32_t, int32_t, ++ int32_t); ++typedef int32_t (*Prototype_Int32_GeneralInt32Int32Int32Int32Int32)( ++ int64_t, int32_t, int32_t, int32_t, int32_t, int32_t); ++typedef int32_t (*Prototype_Int32_GeneralInt32Int32Int32Int32General)( ++ int64_t, int32_t, int32_t, int32_t, int32_t, int64_t); ++typedef int32_t (*Prototype_Int32_GeneralInt32Int32Int32General)( ++ int64_t, int32_t, int32_t, int32_t, int64_t); ++typedef int32_t (*Prototype_Int32_GeneralInt32Int32Int64)(int64_t, int32_t, ++ int32_t, int64_t); ++typedef int32_t (*Prototype_Int32_GeneralInt32Int32General)(int64_t, int32_t, ++ int32_t, int64_t); ++typedef int32_t (*Prototype_Int32_GeneralInt32Int64Int64)(int64_t, int32_t, ++ int64_t, int64_t); ++typedef int32_t (*Prototype_Int32_GeneralInt32GeneralInt32)(int64_t, int32_t, ++ int64_t, int32_t); ++typedef int32_t (*Prototype_Int32_GeneralInt32GeneralInt32Int32)( ++ int64_t, int32_t, int64_t, int32_t, int32_t); ++typedef int32_t (*Prototype_Int32_GeneralGeneral)(int64_t, int64_t); ++typedef int32_t (*Prototype_Int32_GeneralGeneralGeneral)(int64_t, int64_t, ++ int64_t); ++typedef int32_t (*Prototype_Int32_GeneralGeneralInt32Int32)(int64_t, int64_t, ++ int32_t, int32_t); ++typedef int32_t (*Prototype_Int32_GeneralInt64Int32Int32Int32)(int64_t, int64_t, ++ int32_t, int32_t, ++ int32_t); ++typedef int32_t (*Prototype_Int32_GeneralInt64Int32)(int64_t, int64_t, int32_t); ++typedef int32_t (*Prototype_Int32_GeneralInt64Int32Int64)(int64_t, int64_t, ++ int32_t, int64_t); ++typedef int32_t (*Prototype_Int32_GeneralInt64Int32Int64General)( ++ int64_t, int64_t, int32_t, int64_t, int64_t); ++typedef int32_t (*Prototype_Int32_GeneralInt64Int64Int64)(int64_t, int64_t, ++ int64_t, int64_t); ++typedef int32_t (*Prototype_Int32_GeneralInt64Int64Int64General)( ++ int64_t, int64_t, int64_t, int64_t, int64_t); ++typedef int64_t (*Prototype_General_GeneralInt32)(int64_t, int32_t); ++typedef int64_t (*Prototype_General_GeneralInt32Int32)(int64_t, int32_t, ++ int32_t); ++typedef int64_t (*Prototype_General_GeneralInt32General)(int64_t, int32_t, ++ int64_t); ++typedef int64_t (*Prototype_General_GeneralInt32Int32GeneralInt32)( ++ int64_t, int32_t, int32_t, int64_t, int32_t); ++typedef int32_t (*Prototype_Int32_GeneralGeneralInt32GeneralInt32Int32Int32)( ++ int64_t, int64_t, int32_t, int64_t, int32_t, int32_t, int32_t); ++typedef int64_t (*Prototype_Int64_General)(int64_t); ++typedef int64_t (*Prototype_Int64_GeneralInt64)(int64_t, int64_t); ++ ++// Generated by Assembler::break_()/stop(), ebreak code is passed as immediate ++// field of a subsequent LUI instruction; otherwise returns -1 ++static inline uint32_t get_ebreak_code(Instruction* instr) { ++ MOZ_ASSERT(instr->InstructionBits() == kBreakInstr); ++ uint8_t* cur = reinterpret_cast(instr); ++ Instruction* next_instr = reinterpret_cast(cur + kInstrSize); ++ if (next_instr->BaseOpcodeFieldRaw() == LUI) ++ return (next_instr->Imm20UValue()); ++ else ++ return -1; ++} ++ ++// Software interrupt instructions are used by the simulator to call into C++. ++void Simulator::SoftwareInterrupt() { ++ // There are two instructions that could get us here, the ebreak or ecall ++ // instructions are "SYSTEM" class opcode distinuished by Imm12Value field w/ ++ // the rest of instruction fields being zero ++ // We first check if we met a call_rt_redirected. ++ if (instr_.InstructionBits() == rtCallRedirInstr) { ++ Redirection* redirection = Redirection::FromSwiInstruction(instr_.instr()); ++ uintptr_t nativeFn = ++ reinterpret_cast(redirection->nativeFunction()); ++ ++ intptr_t arg0 = getRegister(a0); ++ intptr_t arg1 = getRegister(a1); ++ intptr_t arg2 = getRegister(a2); ++ intptr_t arg3 = getRegister(a3); ++ intptr_t arg4 = getRegister(a4); ++ intptr_t arg5 = getRegister(a5); ++ intptr_t arg6 = getRegister(a6); ++ intptr_t arg7 = getRegister(a7); ++ ++ // This is dodgy but it works because the C entry stubs are never moved. ++ // See comment in codegen-arm.cc and bug 1242173. ++ intptr_t saved_ra = getRegister(ra); ++ ++ intptr_t external = ++ reinterpret_cast(redirection->nativeFunction()); ++ ++ bool stack_aligned = (getRegister(sp) & (ABIStackAlignment - 1)) == 0; ++ if (!stack_aligned) { ++ fprintf(stderr, "Runtime call with unaligned stack!\n"); ++ MOZ_CRASH(); ++ } ++ ++ if (single_stepping_) { ++ single_step_callback_(single_step_callback_arg_, this, nullptr); ++ } ++ if (FLAG_trace_sim) { ++ printf( ++ "Call to host function at %p with args %ld, %ld, %ld, %ld, %ld, %ld, " ++ "%ld, %ld\n", ++ reinterpret_cast(external), arg0, arg1, arg2, arg3, arg4, arg5, ++ arg6, arg7); ++ } ++ switch (redirection->type()) { ++ case Args_General0: { ++ Prototype_General0 target = ++ reinterpret_cast(external); ++ int64_t result = target(); ++ if (FLAG_trace_sim) printf("ret %ld\n", result); ++ setCallResult(result); ++ break; ++ } ++ case Args_General1: { ++ Prototype_General1 target = ++ reinterpret_cast(external); ++ int64_t result = target(arg0); ++ if (FLAG_trace_sim) printf("ret %ld\n", result); ++ setCallResult(result); ++ break; ++ } ++ case Args_General2: { ++ Prototype_General2 target = ++ reinterpret_cast(external); ++ int64_t result = target(arg0, arg1); ++ if (FLAG_trace_sim) printf("ret %ld\n", result); ++ setCallResult(result); ++ break; ++ } ++ case Args_General3: { ++ Prototype_General3 target = ++ reinterpret_cast(external); ++ int64_t result = target(arg0, arg1, arg2); ++ if (FLAG_trace_sim) printf("ret %ld\n", result); ++ if (external == intptr_t(&js::wasm::Instance::wake_m32)) { ++ result = int32_t(result); ++ } ++ setCallResult(result); ++ break; ++ } ++ case Args_General4: { ++ Prototype_General4 target = ++ reinterpret_cast(external); ++ int64_t result = target(arg0, arg1, arg2, arg3); ++ if (FLAG_trace_sim) printf("ret %ld\n", result); ++ setCallResult(result); ++ break; ++ } ++ case Args_General5: { ++ Prototype_General5 target = ++ reinterpret_cast(external); ++ int64_t result = target(arg0, arg1, arg2, arg3, arg4); ++ if (FLAG_trace_sim) printf("ret %ld\n", result); ++ setCallResult(result); ++ break; ++ } ++ case Args_General6: { ++ Prototype_General6 target = ++ reinterpret_cast(external); ++ int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5); ++ if (FLAG_trace_sim) printf("ret %ld\n", result); ++ setCallResult(result); ++ break; ++ } ++ case Args_General7: { ++ Prototype_General7 target = ++ reinterpret_cast(external); ++ int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5, arg6); ++ if (FLAG_trace_sim) printf("ret %ld\n", result); ++ setCallResult(result); ++ break; ++ } ++ case Args_General8: { ++ Prototype_General8 target = ++ reinterpret_cast(external); ++ int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7); ++ if (FLAG_trace_sim) printf("ret %ld\n", result); ++ setCallResult(result); ++ break; ++ } ++ case Args_Double_None: { ++ Prototype_Double_None target = ++ reinterpret_cast(external); ++ double dresult = target(); ++ if (FLAG_trace_sim) printf("ret %f\n", dresult); ++ setCallResultDouble(dresult); ++ break; ++ } ++ case Args_Int_Double: { ++ double dval0 = getFpuRegisterDouble(fa0); ++ Prototype_Int_Double target = ++ reinterpret_cast(external); ++ int64_t result = target(dval0); ++ if (FLAG_trace_sim) printf("ret %ld\n", result); ++ if (external == intptr_t((int32_t(*)(double))JS::ToInt32)) { ++ result = int32_t(result); ++ } ++ setRegister(a0, result); ++ break; ++ } ++ case Args_Int_GeneralGeneralGeneralInt64: { ++ Prototype_GeneralGeneralGeneralInt64 target = ++ reinterpret_cast(external); ++ int64_t result = target(arg0, arg1, arg2, arg3); ++ if (FLAG_trace_sim) printf("ret %ld\n", result); ++ if (external == intptr_t(&js::wasm::Instance::wait_i32_m32)) { ++ result = int32_t(result); ++ } ++ setRegister(a0, result); ++ break; ++ } ++ case Args_Int_GeneralGeneralInt64Int64: { ++ Prototype_GeneralGeneralInt64Int64 target = ++ reinterpret_cast(external); ++ int64_t result = target(arg0, arg1, arg2, arg3); ++ if (FLAG_trace_sim) printf("ret %ld\n", result); ++ if (external == intptr_t(&js::wasm::Instance::wait_i64_m32)) { ++ result = int32_t(result); ++ } ++ setRegister(a0, result); ++ break; ++ } ++ case Args_Int_DoubleInt: { ++ double dval = getFpuRegisterDouble(fa0); ++ Prototype_Int_DoubleInt target = ++ reinterpret_cast(external); ++ int64_t result = target(dval, arg0); ++ if (FLAG_trace_sim) printf("ret %ld\n", result); ++ setRegister(a0, result); ++ break; ++ } ++ case Args_Int_DoubleIntInt: { ++ double dval = getFpuRegisterDouble(fa0); ++ Prototype_Int_DoubleIntInt target = ++ reinterpret_cast(external); ++ int64_t result = target(dval, arg1, arg2); ++ if (FLAG_trace_sim) printf("ret %ld\n", result); ++ setRegister(a0, result); ++ break; ++ } ++ case Args_Int_IntDoubleIntInt: { ++ double dval = getFpuRegisterDouble(fa0); ++ Prototype_Int_IntDoubleIntInt target = ++ reinterpret_cast(external); ++ int64_t result = target(arg0, dval, arg2, arg3); ++ if (FLAG_trace_sim) printf("ret %ld\n", result); ++ setRegister(a0, result); ++ break; ++ } ++ case Args_Double_Double: { ++ double dval0 = getFpuRegisterDouble(fa0); ++ Prototype_Double_Double target = ++ reinterpret_cast(external); ++ double dresult = target(dval0); ++ if (FLAG_trace_sim) printf("ret %f\n", dresult); ++ setCallResultDouble(dresult); ++ break; ++ } ++ case Args_Float32_Float32: { ++ float fval0; ++ fval0 = getFpuRegisterFloat(fa0); ++ Prototype_Float32_Float32 target = ++ reinterpret_cast(external); ++ float fresult = target(fval0); ++ if (FLAG_trace_sim) printf("ret %f\n", fresult); ++ setCallResultFloat(fresult); ++ break; ++ } ++ case Args_Int_Float32: { ++ float fval0; ++ fval0 = getFpuRegisterFloat(fa0); ++ Prototype_Int_Float32 target = ++ reinterpret_cast(external); ++ int64_t result = target(fval0); ++ if (FLAG_trace_sim) printf("ret %ld\n", result); ++ setRegister(a0, result); ++ break; ++ } ++ case Args_Float32_Float32Float32: { ++ float fval0; ++ float fval1; ++ fval0 = getFpuRegisterFloat(fa0); ++ fval1 = getFpuRegisterFloat(fa1); ++ Prototype_Float32_Float32Float32 target = ++ reinterpret_cast(external); ++ float fresult = target(fval0, fval1); ++ if (FLAG_trace_sim) printf("ret %f\n", fresult); ++ setCallResultFloat(fresult); ++ break; ++ } ++ case Args_Double_Int: { ++ Prototype_Double_Int target = ++ reinterpret_cast(external); ++ double dresult = target(arg0); ++ if (FLAG_trace_sim) printf("ret %f\n", dresult); ++ setCallResultDouble(dresult); ++ break; ++ } ++ case Args_Double_DoubleInt: { ++ double dval0 = getFpuRegisterDouble(fa0); ++ Prototype_Double_DoubleInt target = ++ reinterpret_cast(external); ++ double dresult = target(dval0, arg0); ++ if (FLAG_trace_sim) printf("ret %f\n", dresult); ++ setCallResultDouble(dresult); ++ break; ++ } ++ case Args_Double_DoubleDouble: { ++ double dval0 = getFpuRegisterDouble(fa0); ++ double dval1 = getFpuRegisterDouble(fa1); ++ Prototype_Double_DoubleDouble target = ++ reinterpret_cast(external); ++ double dresult = target(dval0, dval1); ++ if (FLAG_trace_sim) printf("ret %f\n", dresult); ++ setCallResultDouble(dresult); ++ break; ++ } ++ case Args_Double_IntDouble: { ++ double dval0 = getFpuRegisterDouble(fa0); ++ Prototype_Double_IntDouble target = ++ reinterpret_cast(external); ++ double dresult = target(arg0, dval0); ++ if (FLAG_trace_sim) printf("ret %f\n", dresult); ++ setCallResultDouble(dresult); ++ break; ++ } ++ case Args_Int_IntDouble: { ++ double dval0 = getFpuRegisterDouble(fa0); ++ Prototype_Int_IntDouble target = ++ reinterpret_cast(external); ++ int64_t result = target(arg0, dval0); ++ if (FLAG_trace_sim) printf("ret %ld\n", result); ++ setRegister(a0, result); ++ break; ++ } ++ case Args_Double_DoubleDoubleDouble: { ++ double dval0 = getFpuRegisterDouble(fa0); ++ double dval1 = getFpuRegisterDouble(fa1); ++ double dval2 = getFpuRegisterDouble(fa2); ++ Prototype_Double_DoubleDoubleDouble target = ++ reinterpret_cast(external); ++ double dresult = target(dval0, dval1, dval2); ++ if (FLAG_trace_sim) printf("ret %f\n", dresult); ++ setCallResultDouble(dresult); ++ break; ++ } ++ case Args_Double_DoubleDoubleDoubleDouble: { ++ double dval0 = getFpuRegisterDouble(fa0); ++ double dval1 = getFpuRegisterDouble(fa1); ++ double dval2 = getFpuRegisterDouble(fa2); ++ double dval3 = getFpuRegisterDouble(fa3); ++ Prototype_Double_DoubleDoubleDoubleDouble target = ++ reinterpret_cast( ++ external); ++ double dresult = target(dval0, dval1, dval2, dval3); ++ if (FLAG_trace_sim) printf("ret %f\n", dresult); ++ setCallResultDouble(dresult); ++ break; ++ } ++ case Args_Int32_General: { ++ int32_t ret = reinterpret_cast(nativeFn)(arg0); ++ if (FLAG_trace_sim) printf("ret %d\n", ret); ++ setRegister(a0, I64(ret)); ++ break; ++ } ++ case Args_Int32_GeneralInt32: { ++ int32_t ret = reinterpret_cast(nativeFn)( ++ arg0, I32(arg1)); ++ if (FLAG_trace_sim) printf("ret %d\n", ret); ++ setRegister(a0, I64(ret)); ++ break; ++ } ++ case Args_Int32_GeneralInt32Int32: { ++ int32_t ret = reinterpret_cast( ++ nativeFn)(arg0, I32(arg1), I32(arg2)); ++ if (FLAG_trace_sim) printf("ret %d\n", ret); ++ setRegister(a0, I64(ret)); ++ break; ++ } ++ case Args_Int32_GeneralInt32Int32Int32Int32: { ++ int32_t ret = ++ reinterpret_cast( ++ nativeFn)(arg0, I32(arg1), I32(arg2), I32(arg3), I32(arg4)); ++ if (FLAG_trace_sim) printf("ret %d\n", ret); ++ setRegister(a0, I64(ret)); ++ break; ++ } ++ case Args_Int32_GeneralInt32Int32Int32Int32Int32: { ++ int32_t ret = ++ reinterpret_cast( ++ nativeFn)(arg0, I32(arg1), I32(arg2), I32(arg3), I32(arg4), ++ I32(arg5)); ++ if (FLAG_trace_sim) printf("ret %d\n", ret); ++ setRegister(a0, I64(ret)); ++ break; ++ } ++ case Args_Int32_GeneralInt32Int32Int32Int32General: { ++ int32_t ret = reinterpret_cast< ++ Prototype_Int32_GeneralInt32Int32Int32Int32General>(nativeFn)( ++ arg0, I32(arg1), I32(arg2), I32(arg3), I32(arg4), arg5); ++ if (FLAG_trace_sim) printf("ret %d\n", ret); ++ setRegister(a0, I64(ret)); ++ break; ++ } ++ case Args_Int32_GeneralInt32Int32Int32General: { ++ int32_t ret = ++ reinterpret_cast( ++ nativeFn)(arg0, I32(arg1), I32(arg2), I32(arg3), arg4); ++ if (FLAG_trace_sim) printf("ret %d\n", ret); ++ setRegister(a0, I64(ret)); ++ break; ++ } ++ case Args_Int32_GeneralInt32Int32Int64: { ++ int32_t ret = reinterpret_cast( ++ nativeFn)(arg0, I32(arg1), I32(arg2), arg3); ++ if (FLAG_trace_sim) printf("ret %d\n", ret); ++ setRegister(a0, I64(ret)); ++ break; ++ } ++ case Args_Int32_GeneralInt32Int32General: { ++ int32_t ret = ++ reinterpret_cast( ++ nativeFn)(arg0, I32(arg1), I32(arg2), arg3); ++ if (FLAG_trace_sim) printf("ret %d\n", ret); ++ setRegister(a0, I64(ret)); ++ break; ++ } ++ case Args_Int32_GeneralInt32Int64Int64: { ++ int32_t ret = reinterpret_cast( ++ nativeFn)(arg0, I32(arg1), arg2, arg3); ++ if (FLAG_trace_sim) printf("ret %d\n", ret); ++ setRegister(a0, I64(ret)); ++ break; ++ } ++ case Args_Int32_GeneralInt32GeneralInt32: { ++ int32_t ret = ++ reinterpret_cast( ++ nativeFn)(arg0, I32(arg1), arg2, I32(arg3)); ++ if (FLAG_trace_sim) printf("ret %d\n", ret); ++ setRegister(a0, I64(ret)); ++ break; ++ } ++ case Args_Int32_GeneralInt32GeneralInt32Int32: { ++ int32_t ret = ++ reinterpret_cast( ++ nativeFn)(arg0, I32(arg1), arg2, I32(arg3), I32(arg4)); ++ if (FLAG_trace_sim) printf("ret %d\n", ret); ++ setRegister(a0, I64(ret)); ++ break; ++ } ++ case Args_Int32_GeneralGeneral: { ++ int32_t ret = reinterpret_cast( ++ nativeFn)(arg0, arg1); ++ if (FLAG_trace_sim) printf("ret %d\n", ret); ++ setRegister(a0, I64(ret)); ++ break; ++ } ++ case Args_Int32_GeneralGeneralGeneral: { ++ int32_t ret = reinterpret_cast( ++ nativeFn)(arg0, arg1, arg2); ++ if (FLAG_trace_sim) printf("ret %d\n", ret); ++ setRegister(a0, I64(ret)); ++ break; ++ } ++ case Args_Int32_GeneralGeneralInt32Int32: { ++ int32_t ret = ++ reinterpret_cast( ++ nativeFn)(arg0, arg1, I32(arg2), I32(arg3)); ++ if (FLAG_trace_sim) printf("ret %d\n", ret); ++ setRegister(a0, I64(ret)); ++ break; ++ } ++ case js::jit::Args_Int32_GeneralInt64Int32Int32Int32: { ++ int32_t ret = ++ reinterpret_cast( ++ nativeFn)(arg0, arg1, I32(arg2), I32(arg3), I32(arg4)); ++ if (FLAG_trace_sim) printf("ret %d\n", ret); ++ setRegister(a0, I64(ret)); ++ break; ++ } ++ case js::jit::Args_Int32_GeneralInt64Int32: { ++ int32_t ret = reinterpret_cast( ++ nativeFn)(arg0, arg1, I32(arg2)); ++ if (FLAG_trace_sim) printf("ret %d\n", ret); ++ setRegister(a0, I64(ret)); ++ break; ++ } ++ case js::jit::Args_Int32_GeneralInt64Int32Int64: { ++ int32_t ret = reinterpret_cast( ++ nativeFn)(arg0, arg1, I32(arg2), arg3); ++ if (FLAG_trace_sim) printf("ret %d\n", ret); ++ setRegister(a0, I64(ret)); ++ break; ++ } ++ case js::jit::Args_Int32_GeneralInt64Int32Int64General: { ++ int32_t ret = ++ reinterpret_cast( ++ nativeFn)(arg0, arg1, I32(arg2), arg3, arg4); ++ if (FLAG_trace_sim) printf("ret %d\n", ret); ++ setRegister(a0, I64(ret)); ++ break; ++ } ++ case js::jit::Args_Int32_GeneralInt64Int64Int64: { ++ int32_t ret = reinterpret_cast( ++ nativeFn)(arg0, arg1, arg2, arg3); ++ if (FLAG_trace_sim) printf("ret %d\n", ret); ++ setRegister(a0, I64(ret)); ++ break; ++ } ++ case js::jit::Args_Int32_GeneralInt64Int64Int64General: { ++ int32_t ret = ++ reinterpret_cast( ++ nativeFn)(arg0, arg1, arg2, arg3, arg4); ++ if (FLAG_trace_sim) printf("ret %d\n", ret); ++ setRegister(a0, I64(ret)); ++ break; ++ } ++ case Args_General_GeneralInt32: { ++ int64_t ret = reinterpret_cast( ++ nativeFn)(arg0, I32(arg1)); ++ if (FLAG_trace_sim) printf("ret %ld\n", ret); ++ setRegister(a0, ret); ++ break; ++ } ++ case Args_General_GeneralInt32Int32: { ++ int64_t ret = reinterpret_cast( ++ nativeFn)(arg0, I32(arg1), I32(arg2)); ++ if (FLAG_trace_sim) printf("ret %ld\n", ret); ++ setRegister(a0, ret); ++ break; ++ } ++ case Args_General_GeneralInt32General: { ++ int64_t ret = reinterpret_cast( ++ nativeFn)(arg0, I32(arg1), arg2); ++ if (FLAG_trace_sim) printf("ret %ld\n", ret); ++ setRegister(a0, ret); ++ break; ++ } ++ case js::jit::Args_General_GeneralInt32Int32GeneralInt32: { ++ int64_t ret = ++ reinterpret_cast( ++ nativeFn)(arg0, I32(arg1), I32(arg2), arg3, I32(arg4)); ++ setRegister(a0, ret); ++ break; ++ } ++ case js::jit::Args_Int32_GeneralGeneralInt32GeneralInt32Int32Int32: { ++ int64_t arg6 = getRegister(a6); ++ int32_t ret = reinterpret_cast< ++ Prototype_Int32_GeneralGeneralInt32GeneralInt32Int32Int32>( ++ nativeFn)(arg0, arg1, I32(arg2), arg3, I32(arg4), I32(arg5), ++ I32(arg6)); ++ setRegister(a0, I64(ret)); ++ break; ++ } ++ case js::jit::Args_Int64_General: { ++ int64_t ret = reinterpret_cast(nativeFn)(arg0); ++ if (FLAG_trace_sim) printf("ret %ld\n", ret); ++ setRegister(a0, ret); ++ break; ++ } ++ case js::jit::Args_Int64_GeneralInt64: { ++ int64_t ret = reinterpret_cast(nativeFn)( ++ arg0, arg1); ++ if (FLAG_trace_sim) printf("ret %ld\n", ret); ++ setRegister(a0, ret); ++ break; ++ } ++ default: ++ MOZ_CRASH("Unknown function type."); ++ } ++ ++ if (single_stepping_) { ++ single_step_callback_(single_step_callback_arg_, this, nullptr); ++ } ++ ++ setRegister(ra, saved_ra); ++ set_pc(getRegister(ra)); ++ ++ } else if (instr_.InstructionBits() == kBreakInstr && ++ (get_ebreak_code(instr_.instr()) <= kMaxStopCode)) { ++ uint32_t code = get_ebreak_code(instr_.instr()); ++ if (isWatchpoint(code)) { ++ printWatchpoint(code); ++ } else if (IsTracepoint(code)) { ++ if (!FLAG_debug_sim) { ++ MOZ_CRASH("Add --debug-sim when tracepoint instruction is used.\n"); ++ } ++ // printf("%d %d %d %d %d %d %d\n", code, code & LOG_TRACE, code & ++ // LOG_REGS, ++ // code & kDebuggerTracingDirectivesMask, TRACE_ENABLE, ++ // TRACE_DISABLE, kDebuggerTracingDirectivesMask); ++ switch (code & kDebuggerTracingDirectivesMask) { ++ case TRACE_ENABLE: ++ if (code & LOG_TRACE) { ++ FLAG_trace_sim = true; ++ } ++ if (code & LOG_REGS) { ++ RiscvDebugger dbg(this); ++ dbg.printAllRegs(); ++ } ++ break; ++ case TRACE_DISABLE: ++ if (code & LOG_TRACE) { ++ FLAG_trace_sim = false; ++ } ++ break; ++ default: ++ UNREACHABLE(); ++ } ++ } else { ++ increaseStopCounter(code); ++ handleStop(code); ++ } ++ } else { ++ // uint8_t code = get_ebreak_code(instr_.instr()) - kMaxStopCode - 1; ++ // switch (LNode::Opcode(code)) { ++ // #define EMIT_OP(OP, ...) \ ++// case LNode::Opcode::OP:\ ++// std::cout << #OP << std::endl; \ ++// break; ++ // LIR_OPCODE_LIST(EMIT_OP); ++ // #undef EMIT_OP ++ // } ++ DieOrDebug(); ++ } ++} ++ ++// Stop helper functions. ++bool Simulator::isWatchpoint(uint32_t code) { ++ return (code <= kMaxWatchpointCode); ++} ++ ++bool Simulator::IsTracepoint(uint32_t code) { ++ return (code <= kMaxTracepointCode && code > kMaxWatchpointCode); ++} ++ ++void Simulator::printWatchpoint(uint32_t code) { ++ RiscvDebugger dbg(this); ++ ++break_count_; ++ if (FLAG_riscv_print_watchpoint) { ++ printf("\n---- break %d marker: %20" PRIi64 " (instr count: %20" PRIi64 ++ ") ----\n", ++ code, break_count_, icount_); ++ dbg.printAllRegs(); // Print registers and continue running. ++ } ++} ++ ++void Simulator::handleStop(uint32_t code) { ++ // Stop if it is enabled, otherwise go on jumping over the stop ++ // and the message address. ++ if (isEnabledStop(code)) { ++ RiscvDebugger dbg(this); ++ dbg.Debug(); ++ } else { ++ set_pc(get_pc() + 2 * kInstrSize); ++ } ++} ++ ++bool Simulator::isStopInstruction(SimInstruction* instr) { ++ if (instr->InstructionBits() != kBreakInstr) return false; ++ int32_t code = get_ebreak_code(instr->instr()); ++ return code != -1 && static_cast(code) > kMaxWatchpointCode && ++ static_cast(code) <= kMaxStopCode; ++} ++ ++bool Simulator::isEnabledStop(uint32_t code) { ++ MOZ_ASSERT(code <= kMaxStopCode); ++ MOZ_ASSERT(code > kMaxWatchpointCode); ++ return !(watchedStops_[code].count_ & kStopDisabledBit); ++} ++ ++void Simulator::enableStop(uint32_t code) { ++ if (!isEnabledStop(code)) { ++ watchedStops_[code].count_ &= ~kStopDisabledBit; ++ } ++} ++ ++void Simulator::disableStop(uint32_t code) { ++ if (isEnabledStop(code)) { ++ watchedStops_[code].count_ |= kStopDisabledBit; ++ } ++} ++ ++void Simulator::increaseStopCounter(uint32_t code) { ++ MOZ_ASSERT(code <= kMaxStopCode); ++ if ((watchedStops_[code].count_ & ~(1 << 31)) == 0x7fffffff) { ++ printf( ++ "Stop counter for code %i has overflowed.\n" ++ "Enabling this code and reseting the counter to 0.\n", ++ code); ++ watchedStops_[code].count_ = 0; ++ enableStop(code); ++ } else { ++ watchedStops_[code].count_++; ++ } ++} ++ ++// Print a stop status. ++void Simulator::printStopInfo(uint32_t code) { ++ if (code <= kMaxWatchpointCode) { ++ printf("That is a watchpoint, not a stop.\n"); ++ return; ++ } else if (code > kMaxStopCode) { ++ printf("Code too large, only %u stops can be used\n", kMaxStopCode + 1); ++ return; ++ } ++ const char* state = isEnabledStop(code) ? "Enabled" : "Disabled"; ++ int32_t count = watchedStops_[code].count_ & ~kStopDisabledBit; ++ // Don't print the state of unused breakpoints. ++ if (count != 0) { ++ if (watchedStops_[code].desc_) { ++ printf("stop %i - 0x%x: \t%s, \tcounter = %i, \t%s\n", code, code, state, ++ count, watchedStops_[code].desc_); ++ } else { ++ printf("stop %i - 0x%x: \t%s, \tcounter = %i\n", code, code, state, ++ count); ++ } ++ } ++} ++ ++void Simulator::SignalException(Exception e) { ++ printf("Error: Exception %i raised.", static_cast(e)); ++ MOZ_CRASH(); ++} ++ ++// TODO(plind): refactor this messy debug code when we do unaligned access. ++void Simulator::DieOrDebug() { ++ if (FLAG_riscv_trap_to_simulator_debugger) { ++ RiscvDebugger dbg(this); ++ dbg.Debug(); ++ } else { ++ MOZ_CRASH("Die"); ++ } ++} ++ ++// Executes the current instruction. ++void Simulator::InstructionDecode(Instruction* instr) { ++ // if (FLAG_check_icache) { ++ // CheckICache(SimulatorProcess::icache(), instr); ++ // } ++ pc_modified_ = false; ++ ++ EmbeddedVector buffer; ++ ++ if (FLAG_trace_sim || FLAG_debug_sim) { ++ SNPrintF(trace_buf_, " "); ++ disasm::NameConverter converter; ++ disasm::Disassembler dasm(converter); ++ // Use a reasonably large buffer. ++ dasm.InstructionDecode(buffer, reinterpret_cast(instr)); ++ ++ // printf("EXECUTING 0x%08" PRIxPTR " %-44s\n", ++ // reinterpret_cast(instr), buffer.begin()); ++ } ++ ++ instr_ = instr; ++ switch (instr_.InstructionType()) { ++ case Instruction::kRType: ++ DecodeRVRType(); ++ break; ++ case Instruction::kR4Type: ++ DecodeRVR4Type(); ++ break; ++ case Instruction::kIType: ++ DecodeRVIType(); ++ break; ++ case Instruction::kSType: ++ DecodeRVSType(); ++ break; ++ case Instruction::kBType: ++ DecodeRVBType(); ++ break; ++ case Instruction::kUType: ++ DecodeRVUType(); ++ break; ++ case Instruction::kJType: ++ DecodeRVJType(); ++ break; ++ case Instruction::kCRType: ++ DecodeCRType(); ++ break; ++ case Instruction::kCAType: ++ DecodeCAType(); ++ break; ++ case Instruction::kCJType: ++ DecodeCJType(); ++ break; ++ case Instruction::kCBType: ++ DecodeCBType(); ++ break; ++ case Instruction::kCIType: ++ DecodeCIType(); ++ break; ++ case Instruction::kCIWType: ++ DecodeCIWType(); ++ break; ++ case Instruction::kCSSType: ++ DecodeCSSType(); ++ break; ++ case Instruction::kCLType: ++ DecodeCLType(); ++ break; ++ case Instruction::kCSType: ++ DecodeCSType(); ++ break; ++# ifdef CAN_USE_RVV_INSTRUCTIONS ++ case Instruction::kVType: ++ DecodeVType(); ++ break; ++# endif ++ default: ++ UNSUPPORTED(); ++ } ++ ++ if (FLAG_trace_sim) { ++ printf(" 0x%012" PRIxPTR " %-44s\t%s\n", ++ reinterpret_cast(instr), buffer.start(), ++ trace_buf_.start()); ++ } ++ ++ if (!pc_modified_) { ++ setRegister(pc, reinterpret_cast(instr) + instr->InstructionSize()); ++ } ++ ++ if (watch_address_ != nullptr) { ++ printf(" 0x%012" PRIxPTR " : 0x%016" REGIx_FORMAT " %14" REGId_FORMAT ++ " \n", ++ reinterpret_cast(watch_address_), *watch_address_, ++ *watch_address_); ++ if (watch_value_ != *watch_address_) { ++ RiscvDebugger dbg(this); ++ dbg.Debug(); ++ watch_value_ = *watch_address_; ++ } ++ } ++} ++ ++void Simulator::enable_single_stepping(SingleStepCallback cb, void* arg) { ++ single_stepping_ = true; ++ single_step_callback_ = cb; ++ single_step_callback_arg_ = arg; ++ single_step_callback_(single_step_callback_arg_, this, (void*)get_pc()); ++} ++ ++void Simulator::disable_single_stepping() { ++ if (!single_stepping_) { ++ return; ++ } ++ single_step_callback_(single_step_callback_arg_, this, (void*)get_pc()); ++ single_stepping_ = false; ++ single_step_callback_ = nullptr; ++ single_step_callback_arg_ = nullptr; ++} ++ ++template ++void Simulator::execute() { ++ if (single_stepping_) { ++ single_step_callback_(single_step_callback_arg_, this, nullptr); ++ } ++ ++ // Get the PC to simulate. Cannot use the accessor here as we need the ++ // raw PC value and not the one used as input to arithmetic instructions. ++ int64_t program_counter = get_pc(); ++ ++ while (program_counter != end_sim_pc) { ++ if (enableStopSimAt && (icount_ == Simulator::StopSimAt)) { ++ RiscvDebugger dbg(this); ++ dbg.Debug(); ++ } ++ if (single_stepping_) { ++ single_step_callback_(single_step_callback_arg_, this, ++ (void*)program_counter); ++ } ++ Instruction* instr = reinterpret_cast(program_counter); ++ InstructionDecode(instr); ++ icount_++; ++ program_counter = get_pc(); ++ } ++ ++ if (single_stepping_) { ++ single_step_callback_(single_step_callback_arg_, this, nullptr); ++ } ++} ++ ++// RISCV Instruction Decode Routine ++void Simulator::DecodeRVRType() { ++ switch (instr_.InstructionBits() & kRTypeMask) { ++ case RO_ADD: { ++ set_rd(sext_xlen(rs1() + rs2())); ++ break; ++ } ++ case RO_SUB: { ++ set_rd(sext_xlen(rs1() - rs2())); ++ break; ++ } ++ case RO_SLL: { ++ set_rd(sext_xlen(rs1() << (rs2() & (xlen - 1)))); ++ break; ++ } ++ case RO_SLT: { ++ set_rd(sreg_t(rs1()) < sreg_t(rs2())); ++ break; ++ } ++ case RO_SLTU: { ++ set_rd(reg_t(rs1()) < reg_t(rs2())); ++ break; ++ } ++ case RO_XOR: { ++ set_rd(rs1() ^ rs2()); ++ break; ++ } ++ case RO_SRL: { ++ set_rd(sext_xlen(zext_xlen(rs1()) >> (rs2() & (xlen - 1)))); ++ break; ++ } ++ case RO_SRA: { ++ set_rd(sext_xlen(sext_xlen(rs1()) >> (rs2() & (xlen - 1)))); ++ break; ++ } ++ case RO_OR: { ++ set_rd(rs1() | rs2()); ++ break; ++ } ++ case RO_AND: { ++ set_rd(rs1() & rs2()); ++ break; ++ } ++# ifdef JS_CODEGEN_RISCV64 ++ case RO_ADDW: { ++ set_rd(sext32(rs1() + rs2())); ++ break; ++ } ++ case RO_SUBW: { ++ set_rd(sext32(rs1() - rs2())); ++ break; ++ } ++ case RO_SLLW: { ++ set_rd(sext32(rs1() << (rs2() & 0x1F))); ++ break; ++ } ++ case RO_SRLW: { ++ set_rd(sext32(uint32_t(rs1()) >> (rs2() & 0x1F))); ++ break; ++ } ++ case RO_SRAW: { ++ set_rd(sext32(int32_t(rs1()) >> (rs2() & 0x1F))); ++ break; ++ } ++# endif /* JS_CODEGEN_RISCV64 */ ++ // TODO(riscv): Add RISCV M extension macro ++ case RO_MUL: { ++ set_rd(rs1() * rs2()); ++ break; ++ } ++ case RO_MULH: { ++ set_rd(mulh(rs1(), rs2())); ++ break; ++ } ++ case RO_MULHSU: { ++ set_rd(mulhsu(rs1(), rs2())); ++ break; ++ } ++ case RO_MULHU: { ++ set_rd(mulhu(rs1(), rs2())); ++ break; ++ } ++ case RO_DIV: { ++ sreg_t lhs = sext_xlen(rs1()); ++ sreg_t rhs = sext_xlen(rs2()); ++ if (rhs == 0) { ++ set_rd(-1); ++ } else if (lhs == INTPTR_MIN && rhs == -1) { ++ set_rd(lhs); ++ } else { ++ set_rd(sext_xlen(lhs / rhs)); ++ } ++ break; ++ } ++ case RO_DIVU: { ++ reg_t lhs = zext_xlen(rs1()); ++ reg_t rhs = zext_xlen(rs2()); ++ if (rhs == 0) { ++ set_rd(UINTPTR_MAX); ++ } else { ++ set_rd(zext_xlen(lhs / rhs)); ++ } ++ break; ++ } ++ case RO_REM: { ++ sreg_t lhs = sext_xlen(rs1()); ++ sreg_t rhs = sext_xlen(rs2()); ++ if (rhs == 0) { ++ set_rd(lhs); ++ } else if (lhs == INTPTR_MIN && rhs == -1) { ++ set_rd(0); ++ } else { ++ set_rd(sext_xlen(lhs % rhs)); ++ } ++ break; ++ } ++ case RO_REMU: { ++ reg_t lhs = zext_xlen(rs1()); ++ reg_t rhs = zext_xlen(rs2()); ++ if (rhs == 0) { ++ set_rd(lhs); ++ } else { ++ set_rd(zext_xlen(lhs % rhs)); ++ } ++ break; ++ } ++# ifdef JS_CODEGEN_RISCV64 ++ case RO_MULW: { ++ set_rd(sext32(sext32(rs1()) * sext32(rs2()))); ++ break; ++ } ++ case RO_DIVW: { ++ sreg_t lhs = sext32(rs1()); ++ sreg_t rhs = sext32(rs2()); ++ if (rhs == 0) { ++ set_rd(-1); ++ } else if (lhs == INT32_MIN && rhs == -1) { ++ set_rd(lhs); ++ } else { ++ set_rd(sext32(lhs / rhs)); ++ } ++ break; ++ } ++ case RO_DIVUW: { ++ reg_t lhs = zext32(rs1()); ++ reg_t rhs = zext32(rs2()); ++ if (rhs == 0) { ++ set_rd(UINT32_MAX); ++ } else { ++ set_rd(zext32(lhs / rhs)); ++ } ++ break; ++ } ++ case RO_REMW: { ++ sreg_t lhs = sext32(rs1()); ++ sreg_t rhs = sext32(rs2()); ++ if (rhs == 0) { ++ set_rd(lhs); ++ } else if (lhs == INT32_MIN && rhs == -1) { ++ set_rd(0); ++ } else { ++ set_rd(sext32(lhs % rhs)); ++ } ++ break; ++ } ++ case RO_REMUW: { ++ reg_t lhs = zext32(rs1()); ++ reg_t rhs = zext32(rs2()); ++ if (rhs == 0) { ++ set_rd(zext32(lhs)); ++ } else { ++ set_rd(zext32(lhs % rhs)); ++ } ++ break; ++ } ++# endif /*JS_CODEGEN_RISCV64*/ ++ // TODO(riscv): End Add RISCV M extension macro ++ default: { ++ switch (instr_.BaseOpcode()) { ++ case AMO: ++ DecodeRVRAType(); ++ break; ++ case OP_FP: ++ DecodeRVRFPType(); ++ break; ++ default: ++ UNSUPPORTED(); ++ } ++ } ++ } ++} ++ ++template ++T Simulator::FMaxMinHelper(T a, T b, MaxMinKind kind) { ++ // set invalid bit for signaling nan ++ if ((a == std::numeric_limits::signaling_NaN()) || ++ (b == std::numeric_limits::signaling_NaN())) { ++ set_csr_bits(csr_fflags, kInvalidOperation); ++ } ++ ++ T result = 0; ++ if (std::isnan(a) && std::isnan(b)) { ++ result = std::numeric_limits::quiet_NaN(); ++ } else if (std::isnan(a)) { ++ result = b; ++ } else if (std::isnan(b)) { ++ result = a; ++ } else if (b == a) { // Handle -0.0 == 0.0 case. ++ if (kind == MaxMinKind::kMax) { ++ result = std::signbit(b) ? a : b; ++ } else { ++ result = std::signbit(b) ? b : a; ++ } ++ } else { ++ result = (kind == MaxMinKind::kMax) ? fmax(a, b) : fmin(a, b); ++ } ++ ++ return result; ++} ++ ++float Simulator::RoundF2FHelper(float input_val, int rmode) { ++ if (rmode == DYN) rmode = get_dynamic_rounding_mode(); ++ ++ float rounded = 0; ++ switch (rmode) { ++ case RNE: { // Round to Nearest, tiest to Even ++ rounded = floorf(input_val); ++ float error = input_val - rounded; ++ ++ // Take care of correctly handling the range [-0.5, -0.0], which must ++ // yield -0.0. ++ if ((-0.5 <= input_val) && (input_val < 0.0)) { ++ rounded = -0.0; ++ ++ // If the error is greater than 0.5, or is equal to 0.5 and the integer ++ // result is odd, round up. ++ } else if ((error > 0.5) || ++ ((error == 0.5) && (std::fmod(rounded, 2) != 0))) { ++ rounded++; ++ } ++ break; ++ } ++ case RTZ: // Round towards Zero ++ rounded = std::truncf(input_val); ++ break; ++ case RDN: // Round Down (towards -infinity) ++ rounded = floorf(input_val); ++ break; ++ case RUP: // Round Up (towards +infinity) ++ rounded = ceilf(input_val); ++ break; ++ case RMM: // Round to Nearest, tiest to Max Magnitude ++ rounded = std::roundf(input_val); ++ break; ++ default: ++ UNREACHABLE(); ++ } ++ ++ return rounded; ++} ++ ++double Simulator::RoundF2FHelper(double input_val, int rmode) { ++ if (rmode == DYN) rmode = get_dynamic_rounding_mode(); ++ ++ double rounded = 0; ++ switch (rmode) { ++ case RNE: { // Round to Nearest, tiest to Even ++ rounded = std::floor(input_val); ++ double error = input_val - rounded; ++ ++ // Take care of correctly handling the range [-0.5, -0.0], which must ++ // yield -0.0. ++ if ((-0.5 <= input_val) && (input_val < 0.0)) { ++ rounded = -0.0; ++ ++ // If the error is greater than 0.5, or is equal to 0.5 and the integer ++ // result is odd, round up. ++ } else if ((error > 0.5) || ++ ((error == 0.5) && (std::fmod(rounded, 2) != 0))) { ++ rounded++; ++ } ++ break; ++ } ++ case RTZ: // Round towards Zero ++ rounded = std::trunc(input_val); ++ break; ++ case RDN: // Round Down (towards -infinity) ++ rounded = std::floor(input_val); ++ break; ++ case RUP: // Round Up (towards +infinity) ++ rounded = std::ceil(input_val); ++ break; ++ case RMM: // Round to Nearest, tiest to Max Magnitude ++ rounded = std::round(input_val); ++ break; ++ default: ++ UNREACHABLE(); ++ } ++ return rounded; ++} ++ ++// convert rounded floating-point to integer types, handle input values that ++// are out-of-range, underflow, or NaN, and set appropriate fflags ++template ++I_TYPE Simulator::RoundF2IHelper(F_TYPE original, int rmode) { ++ MOZ_ASSERT(std::is_integral::value); ++ ++ MOZ_ASSERT((std::is_same::value || ++ std::is_same::value)); ++ ++ I_TYPE max_i = std::numeric_limits::max(); ++ I_TYPE min_i = std::numeric_limits::min(); ++ ++ if (!std::isfinite(original)) { ++ set_fflags(kInvalidOperation); ++ if (std::isnan(original) || ++ original == std::numeric_limits::infinity()) { ++ return max_i; ++ } else { ++ MOZ_ASSERT(original == -std::numeric_limits::infinity()); ++ return min_i; ++ } ++ } ++ ++ F_TYPE rounded = RoundF2FHelper(original, rmode); ++ if (original != rounded) set_fflags(kInexact); ++ ++ if (!std::isfinite(rounded)) { ++ set_fflags(kInvalidOperation); ++ if (std::isnan(rounded) || ++ rounded == std::numeric_limits::infinity()) { ++ return max_i; ++ } else { ++ MOZ_ASSERT(rounded == -std::numeric_limits::infinity()); ++ return min_i; ++ } ++ } ++ ++ // Since integer max values are either all 1s (for unsigned) or all 1s ++ // except for sign-bit (for signed), they cannot be represented precisely in ++ // floating point, in order to precisely tell whether the rounded floating ++ // point is within the max range, we compare against (max_i+1) which would ++ // have a single 1 w/ many trailing zeros ++ float max_i_plus_1 = ++ std::is_same::value ++ ? 0x1p64f // uint64_t::max + 1 cannot be represented in integers, ++ // so use its float representation directly ++ : static_cast(static_cast(max_i) + 1); ++ if (rounded >= max_i_plus_1) { ++ set_fflags(kOverflow | kInvalidOperation); ++ return max_i; ++ } ++ ++ // Since min_i (either 0 for unsigned, or for signed) is represented ++ // precisely in floating-point, comparing rounded directly against min_i ++ if (rounded <= min_i) { ++ if (rounded < min_i) set_fflags(kOverflow | kInvalidOperation); ++ return min_i; ++ } ++ ++ F_TYPE underflow_fval = ++ std::is_same::value ? FLT_MIN : DBL_MIN; ++ if (rounded < underflow_fval && rounded > -underflow_fval && rounded != 0) { ++ set_fflags(kUnderflow); ++ } ++ ++ return static_cast(rounded); ++} ++ ++template ++static int64_t FclassHelper(T value) { ++ switch (std::fpclassify(value)) { ++ case FP_INFINITE: ++ return (std::signbit(value) ? kNegativeInfinity : kPositiveInfinity); ++ case FP_NAN: ++ return (isSnan(value) ? kSignalingNaN : kQuietNaN); ++ case FP_NORMAL: ++ return (std::signbit(value) ? kNegativeNormalNumber ++ : kPositiveNormalNumber); ++ case FP_SUBNORMAL: ++ return (std::signbit(value) ? kNegativeSubnormalNumber ++ : kPositiveSubnormalNumber); ++ case FP_ZERO: ++ return (std::signbit(value) ? kNegativeZero : kPositiveZero); ++ default: ++ UNREACHABLE(); ++ } ++ UNREACHABLE(); ++ return FP_ZERO; ++} ++ ++template ++bool Simulator::CompareFHelper(T input1, T input2, FPUCondition cc) { ++ MOZ_ASSERT(std::is_floating_point::value); ++ bool result = false; ++ switch (cc) { ++ case LT: ++ case LE: ++ // FLT, FLE are signaling compares ++ if (std::isnan(input1) || std::isnan(input2)) { ++ set_fflags(kInvalidOperation); ++ result = false; ++ } else { ++ result = (cc == LT) ? (input1 < input2) : (input1 <= input2); ++ } ++ break; ++ case EQ: ++ if (std::numeric_limits::signaling_NaN() == input1 || ++ std::numeric_limits::signaling_NaN() == input2) { ++ set_fflags(kInvalidOperation); ++ } ++ if (std::isnan(input1) || std::isnan(input2)) { ++ result = false; ++ } else { ++ result = (input1 == input2); ++ } ++ break; ++ case NE: ++ if (std::numeric_limits::signaling_NaN() == input1 || ++ std::numeric_limits::signaling_NaN() == input2) { ++ set_fflags(kInvalidOperation); ++ } ++ if (std::isnan(input1) || std::isnan(input2)) { ++ result = true; ++ } else { ++ result = (input1 != input2); ++ } ++ break; ++ default: ++ UNREACHABLE(); ++ } ++ return result; ++} ++ ++template ++static inline bool is_invalid_fmul(T src1, T src2) { ++ return (isinf(src1) && src2 == static_cast(0.0)) || ++ (src1 == static_cast(0.0) && isinf(src2)); ++} ++ ++template ++static inline bool is_invalid_fadd(T src1, T src2) { ++ return (isinf(src1) && isinf(src2) && ++ std::signbit(src1) != std::signbit(src2)); ++} ++ ++template ++static inline bool is_invalid_fsub(T src1, T src2) { ++ return (isinf(src1) && isinf(src2) && ++ std::signbit(src1) == std::signbit(src2)); ++} ++ ++template ++static inline bool is_invalid_fdiv(T src1, T src2) { ++ return ((src1 == 0 && src2 == 0) || (isinf(src1) && isinf(src2))); ++} ++ ++template ++static inline bool is_invalid_fsqrt(T src1) { ++ return (src1 < 0); ++} ++ ++int Simulator::loadLinkedW(uint64_t addr, SimInstruction* instr) { ++ if ((addr & 3) == 0) { ++ if (handleWasmSegFault(addr, 4)) { ++ return -1; ++ } ++ ++ volatile int32_t* ptr = reinterpret_cast(addr); ++ int32_t value = *ptr; ++ lastLLValue_ = value; ++ LLAddr_ = addr; ++ // Note that any memory write or "external" interrupt should reset this ++ // value to false. ++ LLBit_ = true; ++ return value; ++ } ++ printf("Unaligned write at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n", addr, ++ reinterpret_cast(instr)); ++ MOZ_CRASH(); ++ return 0; ++} ++ ++int Simulator::storeConditionalW(uint64_t addr, int value, ++ SimInstruction* instr) { ++ // Correct behavior in this case, as defined by architecture, is to just ++ // return 0, but there is no point at allowing that. It is certainly an ++ // indicator of a bug. ++ if (addr != LLAddr_) { ++ printf("SC to bad address: 0x%016" PRIx64 ", pc=0x%016" PRIx64 ++ ", expected: 0x%016" PRIx64 "\n", ++ addr, reinterpret_cast(instr), LLAddr_); ++ MOZ_CRASH(); ++ } ++ ++ if ((addr & 3) == 0) { ++ SharedMem ptr = ++ SharedMem::shared(reinterpret_cast(addr)); ++ ++ if (!LLBit_) { ++ return 1; ++ } ++ ++ LLBit_ = false; ++ LLAddr_ = 0; ++ int32_t expected = int32_t(lastLLValue_); ++ int32_t old = ++ AtomicOperations::compareExchangeSeqCst(ptr, expected, int32_t(value)); ++ return (old == expected) ? 0 : 1; ++ } ++ printf("Unaligned SC at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n", addr, ++ reinterpret_cast(instr)); ++ MOZ_CRASH(); ++ return 0; ++} ++ ++int64_t Simulator::loadLinkedD(uint64_t addr, SimInstruction* instr) { ++ if ((addr & kPointerAlignmentMask) == 0) { ++ if (handleWasmSegFault(addr, 8)) { ++ return -1; ++ } ++ ++ volatile int64_t* ptr = reinterpret_cast(addr); ++ int64_t value = *ptr; ++ lastLLValue_ = value; ++ LLAddr_ = addr; ++ // Note that any memory write or "external" interrupt should reset this ++ // value to false. ++ LLBit_ = true; ++ return value; ++ } ++ printf("Unaligned write at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n", addr, ++ reinterpret_cast(instr)); ++ MOZ_CRASH(); ++ return 0; ++} ++ ++int Simulator::storeConditionalD(uint64_t addr, int64_t value, ++ SimInstruction* instr) { ++ // Correct behavior in this case, as defined by architecture, is to just ++ // return 0, but there is no point at allowing that. It is certainly an ++ // indicator of a bug. ++ if (addr != LLAddr_) { ++ printf("SC to bad address: 0x%016" PRIx64 ", pc=0x%016" PRIx64 ++ ", expected: 0x%016" PRIx64 "\n", ++ addr, reinterpret_cast(instr), LLAddr_); ++ MOZ_CRASH(); ++ } ++ ++ if ((addr & kPointerAlignmentMask) == 0) { ++ SharedMem ptr = ++ SharedMem::shared(reinterpret_cast(addr)); ++ ++ if (!LLBit_) { ++ return 1; ++ } ++ ++ LLBit_ = false; ++ LLAddr_ = 0; ++ int64_t expected = lastLLValue_; ++ int64_t old = ++ AtomicOperations::compareExchangeSeqCst(ptr, expected, int64_t(value)); ++ return (old == expected) ? 0 : 1; ++ } ++ printf("Unaligned SC at 0x%016" PRIx64 ", pc=0x%016" PRIxPTR "\n", addr, ++ reinterpret_cast(instr)); ++ MOZ_CRASH(); ++ return 0; ++} ++ ++void Simulator::DecodeRVRAType() { ++ // TODO(riscv): Add macro for RISCV A extension ++ // Special handling for A extension instructions because it uses func5 ++ // For all A extension instruction, V8 simulator is pure sequential. No ++ // Memory address lock or other synchronizaiton behaviors. ++ switch (instr_.InstructionBits() & kRATypeMask) { ++ case RO_LR_W: { ++ sreg_t addr = rs1(); ++ set_rd(loadLinkedW(addr, &instr_)); ++ TraceLr(addr, getRegister(rd_reg()), getRegister(rd_reg())); ++ break; ++ } ++ case RO_SC_W: { ++ sreg_t addr = rs1(); ++ auto value = static_cast(rs2()); ++ auto result = ++ storeConditionalW(addr, static_cast(rs2()), &instr_); ++ set_rd(result); ++ if (!result) { ++ TraceSc(addr, value); ++ } ++ break; ++ } ++ case RO_AMOSWAP_W: { ++ if ((rs1() & 0x3) != 0) { ++ DieOrDebug(); ++ } ++ set_rd(sext32(amo( ++ rs1(), [&](uint32_t lhs) { return (uint32_t)rs2(); }, instr_.instr(), ++ WORD))); ++ break; ++ } ++ case RO_AMOADD_W: { ++ if ((rs1() & 0x3) != 0) { ++ DieOrDebug(); ++ } ++ set_rd(sext32(amo( ++ rs1(), [&](uint32_t lhs) { return lhs + (uint32_t)rs2(); }, ++ instr_.instr(), WORD))); ++ break; ++ } ++ case RO_AMOXOR_W: { ++ if ((rs1() & 0x3) != 0) { ++ DieOrDebug(); ++ } ++ set_rd(sext32(amo( ++ rs1(), [&](uint32_t lhs) { return lhs ^ (uint32_t)rs2(); }, ++ instr_.instr(), WORD))); ++ break; ++ } ++ case RO_AMOAND_W: { ++ if ((rs1() & 0x3) != 0) { ++ DieOrDebug(); ++ } ++ set_rd(sext32(amo( ++ rs1(), [&](uint32_t lhs) { return lhs & (uint32_t)rs2(); }, ++ instr_.instr(), WORD))); ++ break; ++ } ++ case RO_AMOOR_W: { ++ if ((rs1() & 0x3) != 0) { ++ DieOrDebug(); ++ } ++ set_rd(sext32(amo( ++ rs1(), [&](uint32_t lhs) { return lhs | (uint32_t)rs2(); }, ++ instr_.instr(), WORD))); ++ break; ++ } ++ case RO_AMOMIN_W: { ++ if ((rs1() & 0x3) != 0) { ++ DieOrDebug(); ++ } ++ set_rd(sext32(amo( ++ rs1(), [&](int32_t lhs) { return std::min(lhs, (int32_t)rs2()); }, ++ instr_.instr(), WORD))); ++ break; ++ } ++ case RO_AMOMAX_W: { ++ if ((rs1() & 0x3) != 0) { ++ DieOrDebug(); ++ } ++ set_rd(sext32(amo( ++ rs1(), [&](int32_t lhs) { return std::max(lhs, (int32_t)rs2()); }, ++ instr_.instr(), WORD))); ++ break; ++ } ++ case RO_AMOMINU_W: { ++ if ((rs1() & 0x3) != 0) { ++ DieOrDebug(); ++ } ++ set_rd(sext32(amo( ++ rs1(), [&](uint32_t lhs) { return std::min(lhs, (uint32_t)rs2()); }, ++ instr_.instr(), WORD))); ++ break; ++ } ++ case RO_AMOMAXU_W: { ++ if ((rs1() & 0x3) != 0) { ++ DieOrDebug(); ++ } ++ set_rd(sext32(amo( ++ rs1(), [&](uint32_t lhs) { return std::max(lhs, (uint32_t)rs2()); }, ++ instr_.instr(), WORD))); ++ break; ++ } ++# ifdef JS_CODEGEN_RISCV64 ++ case RO_LR_D: { ++ sreg_t addr = rs1(); ++ set_rd(loadLinkedD(addr, &instr_)); ++ TraceLr(addr, getRegister(rd_reg()), getRegister(rd_reg())); ++ break; ++ } ++ case RO_SC_D: { ++ sreg_t addr = rs1(); ++ auto value = static_cast(rs2()); ++ auto result = ++ storeConditionalD(addr, static_cast(rs2()), &instr_); ++ set_rd(result); ++ if (!result) { ++ TraceSc(addr, value); ++ } ++ break; ++ } ++ case RO_AMOSWAP_D: { ++ set_rd(amo( ++ rs1(), [&](int64_t lhs) { return rs2(); }, instr_.instr(), DWORD)); ++ break; ++ } ++ case RO_AMOADD_D: { ++ set_rd(amo( ++ rs1(), [&](int64_t lhs) { return lhs + rs2(); }, instr_.instr(), ++ DWORD)); ++ break; ++ } ++ case RO_AMOXOR_D: { ++ set_rd(amo( ++ rs1(), [&](int64_t lhs) { return lhs ^ rs2(); }, instr_.instr(), ++ DWORD)); ++ break; ++ } ++ case RO_AMOAND_D: { ++ set_rd(amo( ++ rs1(), [&](int64_t lhs) { return lhs & rs2(); }, instr_.instr(), ++ DWORD)); ++ break; ++ } ++ case RO_AMOOR_D: { ++ set_rd(amo( ++ rs1(), [&](int64_t lhs) { return lhs | rs2(); }, instr_.instr(), ++ DWORD)); ++ break; ++ } ++ case RO_AMOMIN_D: { ++ set_rd(amo( ++ rs1(), [&](int64_t lhs) { return std::min(lhs, rs2()); }, ++ instr_.instr(), DWORD)); ++ break; ++ } ++ case RO_AMOMAX_D: { ++ set_rd(amo( ++ rs1(), [&](int64_t lhs) { return std::max(lhs, rs2()); }, ++ instr_.instr(), DWORD)); ++ break; ++ } ++ case RO_AMOMINU_D: { ++ set_rd(amo( ++ rs1(), [&](uint64_t lhs) { return std::min(lhs, (uint64_t)rs2()); }, ++ instr_.instr(), DWORD)); ++ break; ++ } ++ case RO_AMOMAXU_D: { ++ set_rd(amo( ++ rs1(), [&](uint64_t lhs) { return std::max(lhs, (uint64_t)rs2()); }, ++ instr_.instr(), DWORD)); ++ break; ++ } ++# endif /*JS_CODEGEN_RISCV64*/ ++ // TODO(riscv): End Add macro for RISCV A extension ++ default: { ++ UNSUPPORTED(); ++ } ++ } ++} ++ ++void Simulator::DecodeRVRFPType() { ++ // OP_FP instructions (F/D) uses func7 first. Some further uses func3 and ++ // rs2() ++ ++ // kRATypeMask is only for func7 ++ switch (instr_.InstructionBits() & kRFPTypeMask) { ++ // TODO(riscv): Add macro for RISCV F extension ++ case RO_FADD_S: { ++ // TODO(riscv): use rm value (round mode) ++ auto fn = [this](float frs1, float frs2) { ++ if (is_invalid_fadd(frs1, frs2)) { ++ this->set_fflags(kInvalidOperation); ++ return std::numeric_limits::quiet_NaN(); ++ } else { ++ return frs1 + frs2; ++ } ++ }; ++ set_frd(CanonicalizeFPUOp2(fn)); ++ break; ++ } ++ case RO_FSUB_S: { ++ // TODO(riscv): use rm value (round mode) ++ auto fn = [this](float frs1, float frs2) { ++ if (is_invalid_fsub(frs1, frs2)) { ++ this->set_fflags(kInvalidOperation); ++ return std::numeric_limits::quiet_NaN(); ++ } else { ++ return frs1 - frs2; ++ } ++ }; ++ set_frd(CanonicalizeFPUOp2(fn)); ++ break; ++ } ++ case RO_FMUL_S: { ++ // TODO(riscv): use rm value (round mode) ++ auto fn = [this](float frs1, float frs2) { ++ if (is_invalid_fmul(frs1, frs2)) { ++ this->set_fflags(kInvalidOperation); ++ return std::numeric_limits::quiet_NaN(); ++ } else { ++ return frs1 * frs2; ++ } ++ }; ++ set_frd(CanonicalizeFPUOp2(fn)); ++ break; ++ } ++ case RO_FDIV_S: { ++ // TODO(riscv): use rm value (round mode) ++ auto fn = [this](float frs1, float frs2) { ++ if (is_invalid_fdiv(frs1, frs2)) { ++ this->set_fflags(kInvalidOperation); ++ return std::numeric_limits::quiet_NaN(); ++ } else if (frs2 == 0.0f) { ++ this->set_fflags(kDivideByZero); ++ return (std::signbit(frs1) == std::signbit(frs2) ++ ? std::numeric_limits::infinity() ++ : -std::numeric_limits::infinity()); ++ } else { ++ return frs1 / frs2; ++ } ++ }; ++ set_frd(CanonicalizeFPUOp2(fn)); ++ break; ++ } ++ case RO_FSQRT_S: { ++ if (instr_.Rs2Value() == 0b00000) { ++ // TODO(riscv): use rm value (round mode) ++ auto fn = [this](float frs) { ++ if (is_invalid_fsqrt(frs)) { ++ this->set_fflags(kInvalidOperation); ++ return std::numeric_limits::quiet_NaN(); ++ } else { ++ return std::sqrt(frs); ++ } ++ }; ++ set_frd(CanonicalizeFPUOp1(fn)); ++ } else { ++ UNSUPPORTED(); ++ } ++ break; ++ } ++ case RO_FSGNJ_S: { // RO_FSGNJN_S RO_FSQNJX_S ++ switch (instr_.Funct3Value()) { ++ case 0b000: { // RO_FSGNJ_S ++ set_frd(fsgnj32(frs1_boxed(), frs2_boxed(), false, false)); ++ break; ++ } ++ case 0b001: { // RO_FSGNJN_S ++ set_frd(fsgnj32(frs1_boxed(), frs2_boxed(), true, false)); ++ break; ++ } ++ case 0b010: { // RO_FSQNJX_S ++ set_frd(fsgnj32(frs1_boxed(), frs2_boxed(), false, true)); ++ break; ++ } ++ default: { ++ UNSUPPORTED(); ++ } ++ } ++ break; ++ } ++ case RO_FMIN_S: { // RO_FMAX_S ++ switch (instr_.Funct3Value()) { ++ case 0b000: { // RO_FMIN_S ++ set_frd(FMaxMinHelper(frs1(), frs2(), MaxMinKind::kMin)); ++ break; ++ } ++ case 0b001: { // RO_FMAX_S ++ set_frd(FMaxMinHelper(frs1(), frs2(), MaxMinKind::kMax)); ++ break; ++ } ++ default: { ++ UNSUPPORTED(); ++ } ++ } ++ break; ++ } ++ case RO_FCVT_W_S: { // RO_FCVT_WU_S , 64F RO_FCVT_L_S RO_FCVT_LU_S ++ float original_val = frs1(); ++ switch (instr_.Rs2Value()) { ++ case 0b00000: { // RO_FCVT_W_S ++ set_rd(RoundF2IHelper(original_val, instr_.RoundMode())); ++ break; ++ } ++ case 0b00001: { // RO_FCVT_WU_S ++ set_rd(sext32( ++ RoundF2IHelper(original_val, instr_.RoundMode()))); ++ break; ++ } ++# ifdef JS_CODEGEN_RISCV64 ++ case 0b00010: { // RO_FCVT_L_S ++ set_rd(RoundF2IHelper(original_val, instr_.RoundMode())); ++ break; ++ } ++ case 0b00011: { // RO_FCVT_LU_S ++ set_rd(RoundF2IHelper(original_val, instr_.RoundMode())); ++ break; ++ } ++# endif /* JS_CODEGEN_RISCV64 */ ++ default: { ++ UNSUPPORTED(); ++ } ++ } ++ break; ++ } ++ case RO_FMV: { // RO_FCLASS_S ++ switch (instr_.Funct3Value()) { ++ case 0b000: { ++ if (instr_.Rs2Value() == 0b00000) { ++ // RO_FMV_X_W ++ set_rd(sext32(getFpuRegister(rs1_reg()))); ++ } else { ++ UNSUPPORTED(); ++ } ++ break; ++ } ++ case 0b001: { // RO_FCLASS_S ++ set_rd(FclassHelper(frs1())); ++ break; ++ } ++ default: { ++ UNSUPPORTED(); ++ } ++ } ++ break; ++ } ++ case RO_FLE_S: { // RO_FEQ_S RO_FLT_S RO_FLE_S ++ switch (instr_.Funct3Value()) { ++ case 0b010: { // RO_FEQ_S ++ set_rd(CompareFHelper(frs1(), frs2(), EQ)); ++ break; ++ } ++ case 0b001: { // RO_FLT_S ++ set_rd(CompareFHelper(frs1(), frs2(), LT)); ++ break; ++ } ++ case 0b000: { // RO_FLE_S ++ set_rd(CompareFHelper(frs1(), frs2(), LE)); ++ break; ++ } ++ default: { ++ UNSUPPORTED(); ++ } ++ } ++ break; ++ } ++ case RO_FCVT_S_W: { // RO_FCVT_S_WU , 64F RO_FCVT_S_L RO_FCVT_S_LU ++ switch (instr_.Rs2Value()) { ++ case 0b00000: { // RO_FCVT_S_W ++ set_frd(static_cast((int32_t)rs1())); ++ break; ++ } ++ case 0b00001: { // RO_FCVT_S_WU ++ set_frd(static_cast((uint32_t)rs1())); ++ break; ++ } ++# ifdef JS_CODEGEN_RISCV64 ++ case 0b00010: { // RO_FCVT_S_L ++ set_frd(static_cast((int64_t)rs1())); ++ break; ++ } ++ case 0b00011: { // RO_FCVT_S_LU ++ set_frd(static_cast((uint64_t)rs1())); ++ break; ++ } ++# endif /* JS_CODEGEN_RISCV64 */ ++ default: { ++ UNSUPPORTED(); ++ } ++ } ++ break; ++ } ++ case RO_FMV_W_X: { ++ if (instr_.Funct3Value() == 0b000) { ++ // since FMV preserves source bit-pattern, no need to canonize ++ Float32 result = Float32::FromBits((uint32_t)rs1()); ++ set_frd(result); ++ } else { ++ UNSUPPORTED(); ++ } ++ break; ++ } ++ // TODO(riscv): Add macro for RISCV D extension ++ case RO_FADD_D: { ++ // TODO(riscv): use rm value (round mode) ++ auto fn = [this](double drs1, double drs2) { ++ if (is_invalid_fadd(drs1, drs2)) { ++ this->set_fflags(kInvalidOperation); ++ return std::numeric_limits::quiet_NaN(); ++ } else { ++ return drs1 + drs2; ++ } ++ }; ++ set_drd(CanonicalizeFPUOp2(fn)); ++ break; ++ } ++ case RO_FSUB_D: { ++ // TODO(riscv): use rm value (round mode) ++ auto fn = [this](double drs1, double drs2) { ++ if (is_invalid_fsub(drs1, drs2)) { ++ this->set_fflags(kInvalidOperation); ++ return std::numeric_limits::quiet_NaN(); ++ } else { ++ return drs1 - drs2; ++ } ++ }; ++ set_drd(CanonicalizeFPUOp2(fn)); ++ break; ++ } ++ case RO_FMUL_D: { ++ // TODO(riscv): use rm value (round mode) ++ auto fn = [this](double drs1, double drs2) { ++ if (is_invalid_fmul(drs1, drs2)) { ++ this->set_fflags(kInvalidOperation); ++ return std::numeric_limits::quiet_NaN(); ++ } else { ++ return drs1 * drs2; ++ } ++ }; ++ set_drd(CanonicalizeFPUOp2(fn)); ++ break; ++ } ++ case RO_FDIV_D: { ++ // TODO(riscv): use rm value (round mode) ++ auto fn = [this](double drs1, double drs2) { ++ if (is_invalid_fdiv(drs1, drs2)) { ++ this->set_fflags(kInvalidOperation); ++ return std::numeric_limits::quiet_NaN(); ++ } else if (drs2 == 0.0) { ++ this->set_fflags(kDivideByZero); ++ return (std::signbit(drs1) == std::signbit(drs2) ++ ? std::numeric_limits::infinity() ++ : -std::numeric_limits::infinity()); ++ } else { ++ return drs1 / drs2; ++ } ++ }; ++ set_drd(CanonicalizeFPUOp2(fn)); ++ break; ++ } ++ case RO_FSQRT_D: { ++ if (instr_.Rs2Value() == 0b00000) { ++ // TODO(riscv): use rm value (round mode) ++ auto fn = [this](double drs) { ++ if (is_invalid_fsqrt(drs)) { ++ this->set_fflags(kInvalidOperation); ++ return std::numeric_limits::quiet_NaN(); ++ } else { ++ return std::sqrt(drs); ++ } ++ }; ++ set_drd(CanonicalizeFPUOp1(fn)); ++ } else { ++ UNSUPPORTED(); ++ } ++ break; ++ } ++ case RO_FSGNJ_D: { // RO_FSGNJN_D RO_FSQNJX_D ++ switch (instr_.Funct3Value()) { ++ case 0b000: { // RO_FSGNJ_D ++ set_drd(fsgnj64(drs1_boxed(), drs2_boxed(), false, false)); ++ break; ++ } ++ case 0b001: { // RO_FSGNJN_D ++ set_drd(fsgnj64(drs1_boxed(), drs2_boxed(), true, false)); ++ break; ++ } ++ case 0b010: { // RO_FSQNJX_D ++ set_drd(fsgnj64(drs1_boxed(), drs2_boxed(), false, true)); ++ break; ++ } ++ default: { ++ UNSUPPORTED(); ++ } ++ } ++ break; ++ } ++ case RO_FMIN_D: { // RO_FMAX_D ++ switch (instr_.Funct3Value()) { ++ case 0b000: { // RO_FMIN_D ++ set_drd(FMaxMinHelper(drs1(), drs2(), MaxMinKind::kMin)); ++ break; ++ } ++ case 0b001: { // RO_FMAX_D ++ set_drd(FMaxMinHelper(drs1(), drs2(), MaxMinKind::kMax)); ++ break; ++ } ++ default: { ++ UNSUPPORTED(); ++ } ++ } ++ break; ++ } ++ case (RO_FCVT_S_D & kRFPTypeMask): { ++ if (instr_.Rs2Value() == 0b00001) { ++ auto fn = [](double drs) { return static_cast(drs); }; ++ set_frd(CanonicalizeDoubleToFloatOperation(fn)); ++ } else { ++ UNSUPPORTED(); ++ } ++ break; ++ } ++ case RO_FCVT_D_S: { ++ if (instr_.Rs2Value() == 0b00000) { ++ auto fn = [](float frs) { return static_cast(frs); }; ++ set_drd(CanonicalizeFloatToDoubleOperation(fn)); ++ } else { ++ UNSUPPORTED(); ++ } ++ break; ++ } ++ case RO_FLE_D: { // RO_FEQ_D RO_FLT_D RO_FLE_D ++ switch (instr_.Funct3Value()) { ++ case 0b010: { // RO_FEQ_S ++ set_rd(CompareFHelper(drs1(), drs2(), EQ)); ++ break; ++ } ++ case 0b001: { // RO_FLT_D ++ set_rd(CompareFHelper(drs1(), drs2(), LT)); ++ break; ++ } ++ case 0b000: { // RO_FLE_D ++ set_rd(CompareFHelper(drs1(), drs2(), LE)); ++ break; ++ } ++ default: { ++ UNSUPPORTED(); ++ } ++ } ++ break; ++ } ++ case (RO_FCLASS_D & kRFPTypeMask): { // RO_FCLASS_D , 64D RO_FMV_X_D ++ if (instr_.Rs2Value() != 0b00000) { ++ UNSUPPORTED(); ++ } ++ switch (instr_.Funct3Value()) { ++ case 0b001: { // RO_FCLASS_D ++ set_rd(FclassHelper(drs1())); ++ break; ++ } ++# ifdef JS_CODEGEN_RISCV64 ++ case 0b000: { // RO_FMV_X_D ++ set_rd(bit_cast(drs1())); ++ break; ++ } ++# endif /* JS_CODEGEN_RISCV64 */ ++ default: { ++ UNSUPPORTED(); ++ } ++ } ++ break; ++ } ++ case RO_FCVT_W_D: { // RO_FCVT_WU_D , 64F RO_FCVT_L_D RO_FCVT_LU_D ++ double original_val = drs1(); ++ switch (instr_.Rs2Value()) { ++ case 0b00000: { // RO_FCVT_W_D ++ set_rd(RoundF2IHelper(original_val, instr_.RoundMode())); ++ break; ++ } ++ case 0b00001: { // RO_FCVT_WU_D ++ set_rd(sext32( ++ RoundF2IHelper(original_val, instr_.RoundMode()))); ++ break; ++ } ++# ifdef JS_CODEGEN_RISCV64 ++ case 0b00010: { // RO_FCVT_L_D ++ set_rd(RoundF2IHelper(original_val, instr_.RoundMode())); ++ break; ++ } ++ case 0b00011: { // RO_FCVT_LU_D ++ set_rd(RoundF2IHelper(original_val, instr_.RoundMode())); ++ break; ++ } ++# endif /* JS_CODEGEN_RISCV64 */ ++ default: { ++ UNSUPPORTED(); ++ } ++ } ++ break; ++ } ++ case RO_FCVT_D_W: { // RO_FCVT_D_WU , 64F RO_FCVT_D_L RO_FCVT_D_LU ++ switch (instr_.Rs2Value()) { ++ case 0b00000: { // RO_FCVT_D_W ++ set_drd((int32_t)rs1()); ++ break; ++ } ++ case 0b00001: { // RO_FCVT_D_WU ++ set_drd((uint32_t)rs1()); ++ break; ++ } ++# ifdef JS_CODEGEN_RISCV64 ++ case 0b00010: { // RO_FCVT_D_L ++ set_drd((int64_t)rs1()); ++ break; ++ } ++ case 0b00011: { // RO_FCVT_D_LU ++ set_drd((uint64_t)rs1()); ++ break; ++ } ++# endif /* JS_CODEGEN_RISCV64 */ ++ default: { ++ UNSUPPORTED(); ++ } ++ } ++ break; ++ } ++# ifdef JS_CODEGEN_RISCV64 ++ case RO_FMV_D_X: { ++ if (instr_.Funct3Value() == 0b000 && instr_.Rs2Value() == 0b00000) { ++ // Since FMV preserves source bit-pattern, no need to canonize ++ set_drd(bit_cast(rs1())); ++ } else { ++ UNSUPPORTED(); ++ } ++ break; ++ } ++# endif /* JS_CODEGEN_RISCV64 */ ++ default: { ++ UNSUPPORTED(); ++ } ++ } ++} ++ ++void Simulator::DecodeRVR4Type() { ++ switch (instr_.InstructionBits() & kR4TypeMask) { ++ // TODO(riscv): use F Extension macro block ++ case RO_FMADD_S: { ++ // TODO(riscv): use rm value (round mode) ++ auto fn = [this](float frs1, float frs2, float frs3) { ++ if (is_invalid_fmul(frs1, frs2) || is_invalid_fadd(frs1 * frs2, frs3)) { ++ this->set_fflags(kInvalidOperation); ++ return std::numeric_limits::quiet_NaN(); ++ } else { ++ return std::fma(frs1, frs2, frs3); ++ } ++ }; ++ set_frd(CanonicalizeFPUOp3(fn)); ++ break; ++ } ++ case RO_FMSUB_S: { ++ // TODO(riscv): use rm value (round mode) ++ auto fn = [this](float frs1, float frs2, float frs3) { ++ if (is_invalid_fmul(frs1, frs2) || is_invalid_fsub(frs1 * frs2, frs3)) { ++ this->set_fflags(kInvalidOperation); ++ return std::numeric_limits::quiet_NaN(); ++ } else { ++ return std::fma(frs1, frs2, -frs3); ++ } ++ }; ++ set_frd(CanonicalizeFPUOp3(fn)); ++ break; ++ } ++ case RO_FNMSUB_S: { ++ // TODO(riscv): use rm value (round mode) ++ auto fn = [this](float frs1, float frs2, float frs3) { ++ if (is_invalid_fmul(frs1, frs2) || is_invalid_fsub(frs3, frs1 * frs2)) { ++ this->set_fflags(kInvalidOperation); ++ return std::numeric_limits::quiet_NaN(); ++ } else { ++ return -std::fma(frs1, frs2, -frs3); ++ } ++ }; ++ set_frd(CanonicalizeFPUOp3(fn)); ++ break; ++ } ++ case RO_FNMADD_S: { ++ // TODO(riscv): use rm value (round mode) ++ auto fn = [this](float frs1, float frs2, float frs3) { ++ if (is_invalid_fmul(frs1, frs2) || is_invalid_fadd(frs1 * frs2, frs3)) { ++ this->set_fflags(kInvalidOperation); ++ return std::numeric_limits::quiet_NaN(); ++ } else { ++ return -std::fma(frs1, frs2, frs3); ++ } ++ }; ++ set_frd(CanonicalizeFPUOp3(fn)); ++ break; ++ } ++ // TODO(riscv): use F Extension macro block ++ case RO_FMADD_D: { ++ // TODO(riscv): use rm value (round mode) ++ auto fn = [this](double drs1, double drs2, double drs3) { ++ if (is_invalid_fmul(drs1, drs2) || is_invalid_fadd(drs1 * drs2, drs3)) { ++ this->set_fflags(kInvalidOperation); ++ return std::numeric_limits::quiet_NaN(); ++ } else { ++ return std::fma(drs1, drs2, drs3); ++ } ++ }; ++ set_drd(CanonicalizeFPUOp3(fn)); ++ break; ++ } ++ case RO_FMSUB_D: { ++ // TODO(riscv): use rm value (round mode) ++ auto fn = [this](double drs1, double drs2, double drs3) { ++ if (is_invalid_fmul(drs1, drs2) || is_invalid_fsub(drs1 * drs2, drs3)) { ++ this->set_fflags(kInvalidOperation); ++ return std::numeric_limits::quiet_NaN(); ++ } else { ++ return std::fma(drs1, drs2, -drs3); ++ } ++ }; ++ set_drd(CanonicalizeFPUOp3(fn)); ++ break; ++ } ++ case RO_FNMSUB_D: { ++ // TODO(riscv): use rm value (round mode) ++ auto fn = [this](double drs1, double drs2, double drs3) { ++ if (is_invalid_fmul(drs1, drs2) || is_invalid_fsub(drs3, drs1 * drs2)) { ++ this->set_fflags(kInvalidOperation); ++ return std::numeric_limits::quiet_NaN(); ++ } else { ++ return -std::fma(drs1, drs2, -drs3); ++ } ++ }; ++ set_drd(CanonicalizeFPUOp3(fn)); ++ break; ++ } ++ case RO_FNMADD_D: { ++ // TODO(riscv): use rm value (round mode) ++ auto fn = [this](double drs1, double drs2, double drs3) { ++ if (is_invalid_fmul(drs1, drs2) || is_invalid_fadd(drs1 * drs2, drs3)) { ++ this->set_fflags(kInvalidOperation); ++ return std::numeric_limits::quiet_NaN(); ++ } else { ++ return -std::fma(drs1, drs2, drs3); ++ } ++ }; ++ set_drd(CanonicalizeFPUOp3(fn)); ++ break; ++ } ++ default: ++ UNSUPPORTED(); ++ } ++} ++ ++# ifdef CAN_USE_RVV_INSTRUCTIONS ++bool Simulator::DecodeRvvVL() { ++ uint32_t instr_temp = ++ instr_.InstructionBits() & (kRvvMopMask | kRvvNfMask | kBaseOpcodeMask); ++ if (RO_V_VL == instr_temp) { ++ if (!(instr_.InstructionBits() & (kRvvRs2Mask))) { ++ switch (instr_.vl_vs_width()) { ++ case 8: { ++ RVV_VI_LD(0, (i * nf + fn), int8, false); ++ break; ++ } ++ case 16: { ++ RVV_VI_LD(0, (i * nf + fn), int16, false); ++ break; ++ } ++ case 32: { ++ RVV_VI_LD(0, (i * nf + fn), int32, false); ++ break; ++ } ++ case 64: { ++ RVV_VI_LD(0, (i * nf + fn), int64, false); ++ break; ++ } ++ default: ++ UNIMPLEMENTED_RISCV(); ++ break; ++ } ++ return true; ++ } else { ++ UNIMPLEMENTED_RISCV(); ++ return true; ++ } ++ } else if (RO_V_VLS == instr_temp) { ++ UNIMPLEMENTED_RISCV(); ++ return true; ++ } else if (RO_V_VLX == instr_temp) { ++ UNIMPLEMENTED_RISCV(); ++ return true; ++ } else if (RO_V_VLSEG2 == instr_temp || RO_V_VLSEG3 == instr_temp || ++ RO_V_VLSEG4 == instr_temp || RO_V_VLSEG5 == instr_temp || ++ RO_V_VLSEG6 == instr_temp || RO_V_VLSEG7 == instr_temp || ++ RO_V_VLSEG8 == instr_temp) { ++ if (!(instr_.InstructionBits() & (kRvvRs2Mask))) { ++ UNIMPLEMENTED_RISCV(); ++ return true; ++ } else { ++ UNIMPLEMENTED_RISCV(); ++ return true; ++ } ++ } else if (RO_V_VLSSEG2 == instr_temp || RO_V_VLSSEG3 == instr_temp || ++ RO_V_VLSSEG4 == instr_temp || RO_V_VLSSEG5 == instr_temp || ++ RO_V_VLSSEG6 == instr_temp || RO_V_VLSSEG7 == instr_temp || ++ RO_V_VLSSEG8 == instr_temp) { ++ UNIMPLEMENTED_RISCV(); ++ return true; ++ } else if (RO_V_VLXSEG2 == instr_temp || RO_V_VLXSEG3 == instr_temp || ++ RO_V_VLXSEG4 == instr_temp || RO_V_VLXSEG5 == instr_temp || ++ RO_V_VLXSEG6 == instr_temp || RO_V_VLXSEG7 == instr_temp || ++ RO_V_VLXSEG8 == instr_temp) { ++ UNIMPLEMENTED_RISCV(); ++ return true; ++ } else { ++ return false; ++ } ++} ++ ++bool Simulator::DecodeRvvVS() { ++ uint32_t instr_temp = ++ instr_.InstructionBits() & (kRvvMopMask | kRvvNfMask | kBaseOpcodeMask); ++ if (RO_V_VS == instr_temp) { ++ if (!(instr_.InstructionBits() & (kRvvRs2Mask))) { ++ switch (instr_.vl_vs_width()) { ++ case 8: { ++ RVV_VI_ST(0, (i * nf + fn), uint8, false); ++ break; ++ } ++ case 16: { ++ RVV_VI_ST(0, (i * nf + fn), uint16, false); ++ break; ++ } ++ case 32: { ++ RVV_VI_ST(0, (i * nf + fn), uint32, false); ++ break; ++ } ++ case 64: { ++ RVV_VI_ST(0, (i * nf + fn), uint64, false); ++ break; ++ } ++ default: ++ UNIMPLEMENTED_RISCV(); ++ break; ++ } ++ } else { ++ UNIMPLEMENTED_RISCV(); ++ } ++ return true; ++ } else if (RO_V_VSS == instr_temp) { ++ UNIMPLEMENTED_RISCV(); ++ return true; ++ } else if (RO_V_VSX == instr_temp) { ++ UNIMPLEMENTED_RISCV(); ++ return true; ++ } else if (RO_V_VSU == instr_temp) { ++ UNIMPLEMENTED_RISCV(); ++ return true; ++ } else if (RO_V_VSSEG2 == instr_temp || RO_V_VSSEG3 == instr_temp || ++ RO_V_VSSEG4 == instr_temp || RO_V_VSSEG5 == instr_temp || ++ RO_V_VSSEG6 == instr_temp || RO_V_VSSEG7 == instr_temp || ++ RO_V_VSSEG8 == instr_temp) { ++ UNIMPLEMENTED_RISCV(); ++ return true; ++ } else if (RO_V_VSSSEG2 == instr_temp || RO_V_VSSSEG3 == instr_temp || ++ RO_V_VSSSEG4 == instr_temp || RO_V_VSSSEG5 == instr_temp || ++ RO_V_VSSSEG6 == instr_temp || RO_V_VSSSEG7 == instr_temp || ++ RO_V_VSSSEG8 == instr_temp) { ++ UNIMPLEMENTED_RISCV(); ++ return true; ++ } else if (RO_V_VSXSEG2 == instr_temp || RO_V_VSXSEG3 == instr_temp || ++ RO_V_VSXSEG4 == instr_temp || RO_V_VSXSEG5 == instr_temp || ++ RO_V_VSXSEG6 == instr_temp || RO_V_VSXSEG7 == instr_temp || ++ RO_V_VSXSEG8 == instr_temp) { ++ UNIMPLEMENTED_RISCV(); ++ return true; ++ } else { ++ return false; ++ } ++} ++# endif ++ ++void Simulator::DecodeRVIType() { ++ switch (instr_.InstructionBits() & kITypeMask) { ++ case RO_JALR: { ++ set_rd(get_pc() + kInstrSize); ++ // Note: No need to shift 2 for JALR's imm12, but set lowest bit to 0. ++ sreg_t next_pc = (rs1() + imm12()) & ~sreg_t(1); ++ set_pc(next_pc); ++ break; ++ } ++ case RO_LB: { ++ sreg_t addr = rs1() + imm12(); ++ int8_t val = ReadMem(addr, instr_.instr()); ++ set_rd(sext_xlen(val), false); ++ TraceMemRd(addr, val, getRegister(rd_reg())); ++ break; ++ } ++ case RO_LH: { ++ sreg_t addr = rs1() + imm12(); ++ int16_t val = ReadMem(addr, instr_.instr()); ++ set_rd(sext_xlen(val), false); ++ TraceMemRd(addr, val, getRegister(rd_reg())); ++ break; ++ } ++ case RO_LW: { ++ sreg_t addr = rs1() + imm12(); ++ int32_t val = ReadMem(addr, instr_.instr()); ++ set_rd(sext_xlen(val), false); ++ TraceMemRd(addr, val, getRegister(rd_reg())); ++ break; ++ } ++ case RO_LBU: { ++ sreg_t addr = rs1() + imm12(); ++ uint8_t val = ReadMem(addr, instr_.instr()); ++ set_rd(zext_xlen(val), false); ++ TraceMemRd(addr, val, getRegister(rd_reg())); ++ break; ++ } ++ case RO_LHU: { ++ sreg_t addr = rs1() + imm12(); ++ uint16_t val = ReadMem(addr, instr_.instr()); ++ set_rd(zext_xlen(val), false); ++ TraceMemRd(addr, val, getRegister(rd_reg())); ++ break; ++ } ++# ifdef JS_CODEGEN_RISCV64 ++ case RO_LWU: { ++ int64_t addr = rs1() + imm12(); ++ uint32_t val = ReadMem(addr, instr_.instr()); ++ set_rd(zext_xlen(val), false); ++ TraceMemRd(addr, val, getRegister(rd_reg())); ++ break; ++ } ++ case RO_LD: { ++ int64_t addr = rs1() + imm12(); ++ int64_t val = ReadMem(addr, instr_.instr()); ++ set_rd(sext_xlen(val), false); ++ TraceMemRd(addr, val, getRegister(rd_reg())); ++ break; ++ } ++# endif /*JS_CODEGEN_RISCV64*/ ++ case RO_ADDI: { ++ set_rd(sext_xlen(rs1() + imm12())); ++ break; ++ } ++ case RO_SLTI: { ++ set_rd(sreg_t(rs1()) < sreg_t(imm12())); ++ break; ++ } ++ case RO_SLTIU: { ++ set_rd(reg_t(rs1()) < reg_t(imm12())); ++ break; ++ } ++ case RO_XORI: { ++ set_rd(imm12() ^ rs1()); ++ break; ++ } ++ case RO_ORI: { ++ set_rd(imm12() | rs1()); ++ break; ++ } ++ case RO_ANDI: { ++ set_rd(imm12() & rs1()); ++ break; ++ } ++ case RO_SLLI: { ++ require(shamt6() < xlen); ++ set_rd(sext_xlen(rs1() << shamt6())); ++ break; ++ } ++ case RO_SRLI: { // RO_SRAI ++ if (!instr_.IsArithShift()) { ++ require(shamt6() < xlen); ++ set_rd(sext_xlen(zext_xlen(rs1()) >> shamt6())); ++ } else { ++ require(shamt6() < xlen); ++ set_rd(sext_xlen(sext_xlen(rs1()) >> shamt6())); ++ } ++ break; ++ } ++# ifdef JS_CODEGEN_RISCV64 ++ case RO_ADDIW: { ++ set_rd(sext32(rs1() + imm12())); ++ break; ++ } ++ case RO_SLLIW: { ++ set_rd(sext32(rs1() << shamt5())); ++ break; ++ } ++ case RO_SRLIW: { // RO_SRAIW ++ if (!instr_.IsArithShift()) { ++ set_rd(sext32(uint32_t(rs1()) >> shamt5())); ++ } else { ++ set_rd(sext32(int32_t(rs1()) >> shamt5())); ++ } ++ break; ++ } ++# endif /*JS_CODEGEN_RISCV64*/ ++ case RO_FENCE: { ++ // DO nothing in sumulator ++ break; ++ } ++ case RO_ECALL: { // RO_EBREAK ++ if (instr_.Imm12Value() == 0) { // ECALL ++ SoftwareInterrupt(); ++ } else if (instr_.Imm12Value() == 1) { // EBREAK ++ uint8_t code = get_ebreak_code(instr_.instr()); ++ if (code == kWasmTrapCode) { ++ uint8_t* newPC; ++ if (wasm::HandleIllegalInstruction(registerState(), &newPC)) { ++ set_pc(int64_t(newPC)); ++ return; ++ } ++ } ++ SoftwareInterrupt(); ++ } else { ++ UNSUPPORTED(); ++ } ++ break; ++ } ++ // TODO(riscv): use Zifencei Standard Extension macro block ++ case RO_FENCE_I: { ++ // spike: flush icache. ++ break; ++ } ++ // TODO(riscv): use Zicsr Standard Extension macro block ++ case RO_CSRRW: { ++ if (rd_reg() != zero_reg) { ++ set_rd(zext_xlen(read_csr_value(csr_reg()))); ++ } ++ write_csr_value(csr_reg(), rs1()); ++ break; ++ } ++ case RO_CSRRS: { ++ set_rd(zext_xlen(read_csr_value(csr_reg()))); ++ if (rs1_reg() != zero_reg) { ++ set_csr_bits(csr_reg(), rs1()); ++ } ++ break; ++ } ++ case RO_CSRRC: { ++ set_rd(zext_xlen(read_csr_value(csr_reg()))); ++ if (rs1_reg() != zero_reg) { ++ clear_csr_bits(csr_reg(), rs1()); ++ } ++ break; ++ } ++ case RO_CSRRWI: { ++ if (rd_reg() != zero_reg) { ++ set_rd(zext_xlen(read_csr_value(csr_reg()))); ++ } ++ write_csr_value(csr_reg(), imm5CSR()); ++ break; ++ } ++ case RO_CSRRSI: { ++ set_rd(zext_xlen(read_csr_value(csr_reg()))); ++ if (imm5CSR() != 0) { ++ set_csr_bits(csr_reg(), imm5CSR()); ++ } ++ break; ++ } ++ case RO_CSRRCI: { ++ set_rd(zext_xlen(read_csr_value(csr_reg()))); ++ if (imm5CSR() != 0) { ++ clear_csr_bits(csr_reg(), imm5CSR()); ++ } ++ break; ++ } ++ // TODO(riscv): use F Extension macro block ++ case RO_FLW: { ++ sreg_t addr = rs1() + imm12(); ++ uint32_t val = ReadMem(addr, instr_.instr()); ++ set_frd(Float32::FromBits(val), false); ++ TraceMemRdFloat(addr, Float32::FromBits(val), getFpuRegister(frd_reg())); ++ break; ++ } ++ // TODO(riscv): use D Extension macro block ++ case RO_FLD: { ++ sreg_t addr = rs1() + imm12(); ++ uint64_t val = ReadMem(addr, instr_.instr()); ++ set_drd(Float64::FromBits(val), false); ++ TraceMemRdDouble(addr, Float64::FromBits(val), getFpuRegister(frd_reg())); ++ break; ++ } ++ default: { ++# ifdef CAN_USE_RVV_INSTRUCTIONS ++ if (!DecodeRvvVL()) { ++ UNSUPPORTED(); ++ } ++ break; ++# else ++ UNSUPPORTED(); ++# endif ++ } ++ } ++} ++ ++void Simulator::DecodeRVSType() { ++ switch (instr_.InstructionBits() & kSTypeMask) { ++ case RO_SB: ++ WriteMem(rs1() + s_imm12(), (uint8_t)rs2(), instr_.instr()); ++ break; ++ case RO_SH: ++ WriteMem(rs1() + s_imm12(), (uint16_t)rs2(), instr_.instr()); ++ break; ++ case RO_SW: ++ WriteMem(rs1() + s_imm12(), (uint32_t)rs2(), instr_.instr()); ++ break; ++# ifdef JS_CODEGEN_RISCV64 ++ case RO_SD: ++ WriteMem(rs1() + s_imm12(), (uint64_t)rs2(), instr_.instr()); ++ break; ++# endif /*JS_CODEGEN_RISCV64*/ ++ // TODO(riscv): use F Extension macro block ++ case RO_FSW: { ++ WriteMem(rs1() + s_imm12(), getFpuRegisterFloat32(rs2_reg()), ++ instr_.instr()); ++ break; ++ } ++ // TODO(riscv): use D Extension macro block ++ case RO_FSD: { ++ WriteMem(rs1() + s_imm12(), getFpuRegisterFloat64(rs2_reg()), ++ instr_.instr()); ++ break; ++ } ++ default: ++# ifdef CAN_USE_RVV_INSTRUCTIONS ++ if (!DecodeRvvVS()) { ++ UNSUPPORTED(); ++ } ++ break; ++# else ++ UNSUPPORTED(); ++# endif ++ } ++} ++ ++void Simulator::DecodeRVBType() { ++ switch (instr_.InstructionBits() & kBTypeMask) { ++ case RO_BEQ: ++ if (rs1() == rs2()) { ++ int64_t next_pc = get_pc() + boffset(); ++ set_pc(next_pc); ++ } ++ break; ++ case RO_BNE: ++ if (rs1() != rs2()) { ++ int64_t next_pc = get_pc() + boffset(); ++ set_pc(next_pc); ++ } ++ break; ++ case RO_BLT: ++ if (rs1() < rs2()) { ++ int64_t next_pc = get_pc() + boffset(); ++ set_pc(next_pc); ++ } ++ break; ++ case RO_BGE: ++ if (rs1() >= rs2()) { ++ int64_t next_pc = get_pc() + boffset(); ++ set_pc(next_pc); ++ } ++ break; ++ case RO_BLTU: ++ if ((reg_t)rs1() < (reg_t)rs2()) { ++ int64_t next_pc = get_pc() + boffset(); ++ set_pc(next_pc); ++ } ++ break; ++ case RO_BGEU: ++ if ((reg_t)rs1() >= (reg_t)rs2()) { ++ int64_t next_pc = get_pc() + boffset(); ++ set_pc(next_pc); ++ } ++ break; ++ default: ++ UNSUPPORTED(); ++ } ++} ++void Simulator::DecodeRVUType() { ++ // U Type doesn't have additoinal mask ++ switch (instr_.BaseOpcodeFieldRaw()) { ++ case LUI: ++ set_rd(u_imm20()); ++ break; ++ case AUIPC: ++ set_rd(sext_xlen(u_imm20() + get_pc())); ++ break; ++ default: ++ UNSUPPORTED(); ++ } ++} ++void Simulator::DecodeRVJType() { ++ // J Type doesn't have additional mask ++ switch (instr_.BaseOpcodeValue()) { ++ case JAL: { ++ set_rd(get_pc() + kInstrSize); ++ int64_t next_pc = get_pc() + imm20J(); ++ set_pc(next_pc); ++ break; ++ } ++ default: ++ UNSUPPORTED(); ++ } ++} ++void Simulator::DecodeCRType() { ++ switch (instr_.RvcFunct4Value()) { ++ case 0b1000: ++ if (instr_.RvcRs1Value() != 0 && instr_.RvcRs2Value() == 0) { // c.jr ++ set_pc(rvc_rs1()); ++ } else if (instr_.RvcRdValue() != 0 && ++ instr_.RvcRs2Value() != 0) { // c.mv ++ set_rvc_rd(sext_xlen(rvc_rs2())); ++ } else { ++ UNSUPPORTED(); ++ } ++ break; ++ case 0b1001: ++ if (instr_.RvcRs1Value() == 0 && instr_.RvcRs2Value() == 0) { // c.ebreak ++ DieOrDebug(); ++ } else if (instr_.RvcRdValue() != 0 && ++ instr_.RvcRs2Value() == 0) { // c.jalr ++ setRegister(ra, get_pc() + kShortInstrSize); ++ set_pc(rvc_rs1()); ++ } else if (instr_.RvcRdValue() != 0 && ++ instr_.RvcRs2Value() != 0) { // c.add ++ set_rvc_rd(sext_xlen(rvc_rs1() + rvc_rs2())); ++ } else { ++ UNSUPPORTED(); ++ } ++ break; ++ default: ++ UNSUPPORTED(); ++ } ++} ++ ++void Simulator::DecodeCAType() { ++ switch (instr_.InstructionBits() & kCATypeMask) { ++ case RO_C_SUB: ++ set_rvc_rs1s(sext_xlen(rvc_rs1s() - rvc_rs2s())); ++ break; ++ case RO_C_XOR: ++ set_rvc_rs1s(rvc_rs1s() ^ rvc_rs2s()); ++ break; ++ case RO_C_OR: ++ set_rvc_rs1s(rvc_rs1s() | rvc_rs2s()); ++ break; ++ case RO_C_AND: ++ set_rvc_rs1s(rvc_rs1s() & rvc_rs2s()); ++ break; ++# if JS_CODEGEN_RISCV64 ++ case RO_C_SUBW: ++ set_rvc_rs1s(sext32(rvc_rs1s() - rvc_rs2s())); ++ break; ++ case RO_C_ADDW: ++ set_rvc_rs1s(sext32(rvc_rs1s() + rvc_rs2s())); ++ break; ++# endif ++ default: ++ UNSUPPORTED(); ++ } ++} ++ ++void Simulator::DecodeCIType() { ++ switch (instr_.RvcOpcode()) { ++ case RO_C_NOP_ADDI: ++ if (instr_.RvcRdValue() == 0) // c.nop ++ break; ++ else // c.addi ++ set_rvc_rd(sext_xlen(rvc_rs1() + rvc_imm6())); ++ break; ++# if JS_CODEGEN_RISCV64 ++ case RO_C_ADDIW: ++ set_rvc_rd(sext32(rvc_rs1() + rvc_imm6())); ++ break; ++# endif ++ case RO_C_LI: ++ set_rvc_rd(sext_xlen(rvc_imm6())); ++ break; ++ case RO_C_LUI_ADD: ++ if (instr_.RvcRdValue() == 2) { ++ // c.addi16sp ++ int64_t value = getRegister(sp) + rvc_imm6_addi16sp(); ++ setRegister(sp, value); ++ } else if (instr_.RvcRdValue() != 0 && instr_.RvcRdValue() != 2) { ++ // c.lui ++ set_rvc_rd(rvc_u_imm6()); ++ } else { ++ UNSUPPORTED(); ++ } ++ break; ++ case RO_C_SLLI: ++ set_rvc_rd(sext_xlen(rvc_rs1() << rvc_shamt6())); ++ break; ++ case RO_C_FLDSP: { ++ sreg_t addr = getRegister(sp) + rvc_imm6_ldsp(); ++ uint64_t val = ReadMem(addr, instr_.instr()); ++ set_rvc_drd(Float64::FromBits(val), false); ++ TraceMemRdDouble(addr, Float64::FromBits(val), ++ getFpuRegister(rvc_frd_reg())); ++ break; ++ } ++# if JS_CODEGEN_RISCV64 ++ case RO_C_LWSP: { ++ sreg_t addr = getRegister(sp) + rvc_imm6_lwsp(); ++ int64_t val = ReadMem(addr, instr_.instr()); ++ set_rvc_rd(sext_xlen(val), false); ++ TraceMemRd(addr, val, getRegister(rvc_rd_reg())); ++ break; ++ } ++ case RO_C_LDSP: { ++ sreg_t addr = getRegister(sp) + rvc_imm6_ldsp(); ++ int64_t val = ReadMem(addr, instr_.instr()); ++ set_rvc_rd(sext_xlen(val), false); ++ TraceMemRd(addr, val, getRegister(rvc_rd_reg())); ++ break; ++ } ++# elif JS_CODEGEN_RISCV32 ++ case RO_C_FLWSP: { ++ sreg_t addr = getRegister(sp) + rvc_imm6_ldsp(); ++ uint32_t val = ReadMem(addr, instr_.instr()); ++ set_rvc_frd(Float32::FromBits(val), false); ++ TraceMemRdFloat(addr, Float32::FromBits(val), ++ getFpuRegister(rvc_frd_reg())); ++ break; ++ } ++ case RO_C_LWSP: { ++ sreg_t addr = getRegister(sp) + rvc_imm6_lwsp(); ++ int32_t val = ReadMem(addr, instr_.instr()); ++ set_rvc_rd(sext_xlen(val), false); ++ TraceMemRd(addr, val, getRegister(rvc_rd_reg())); ++ break; ++ } ++# endif ++ default: ++ UNSUPPORTED(); ++ } ++} ++ ++void Simulator::DecodeCIWType() { ++ switch (instr_.RvcOpcode()) { ++ case RO_C_ADDI4SPN: { ++ set_rvc_rs2s(getRegister(sp) + rvc_imm8_addi4spn()); ++ break; ++ default: ++ UNSUPPORTED(); ++ } ++ } ++} ++ ++void Simulator::DecodeCSSType() { ++ switch (instr_.RvcOpcode()) { ++ case RO_C_FSDSP: { ++ sreg_t addr = getRegister(sp) + rvc_imm6_sdsp(); ++ WriteMem(addr, getFpuRegisterFloat64(rvc_rs2_reg()), ++ instr_.instr()); ++ break; ++ } ++# if JS_CODEGEN_RISCV32 ++ case RO_C_FSWSP: { ++ sreg_t addr = getRegister(sp) + rvc_imm6_sdsp(); ++ WriteMem(addr, getFpuRegisterFloat32(rvc_rs2_reg()), ++ instr_.instr()); ++ break; ++ } ++# endif ++ case RO_C_SWSP: { ++ sreg_t addr = getRegister(sp) + rvc_imm6_swsp(); ++ WriteMem(addr, (int32_t)rvc_rs2(), instr_.instr()); ++ break; ++ } ++# if JS_CODEGEN_RISCV64 ++ case RO_C_SDSP: { ++ sreg_t addr = getRegister(sp) + rvc_imm6_sdsp(); ++ WriteMem(addr, (int64_t)rvc_rs2(), instr_.instr()); ++ break; ++ } ++# endif ++ default: ++ UNSUPPORTED(); ++ } ++} ++ ++void Simulator::DecodeCLType() { ++ switch (instr_.RvcOpcode()) { ++ case RO_C_LW: { ++ sreg_t addr = rvc_rs1s() + rvc_imm5_w(); ++ int64_t val = ReadMem(addr, instr_.instr()); ++ set_rvc_rs2s(sext_xlen(val), false); ++ TraceMemRd(addr, val, getRegister(rvc_rs2s_reg())); ++ break; ++ } ++ case RO_C_FLD: { ++ sreg_t addr = rvc_rs1s() + rvc_imm5_d(); ++ uint64_t val = ReadMem(addr, instr_.instr()); ++ set_rvc_drs2s(Float64::FromBits(val), false); ++ break; ++ } ++# if JS_CODEGEN_RISCV64 ++ case RO_C_LD: { ++ sreg_t addr = rvc_rs1s() + rvc_imm5_d(); ++ int64_t val = ReadMem(addr, instr_.instr()); ++ set_rvc_rs2s(sext_xlen(val), false); ++ TraceMemRd(addr, val, getRegister(rvc_rs2s_reg())); ++ break; ++ } ++# elif JS_CODEGEN_RISCV32 ++ case RO_C_FLW: { ++ sreg_t addr = rvc_rs1s() + rvc_imm5_d(); ++ uint32_t val = ReadMem(addr, instr_.instr()); ++ set_rvc_frs2s(Float32::FromBits(val), false); ++ break; ++ } ++# endif ++ default: ++ UNSUPPORTED(); ++ } ++} ++ ++void Simulator::DecodeCSType() { ++ switch (instr_.RvcOpcode()) { ++ case RO_C_SW: { ++ sreg_t addr = rvc_rs1s() + rvc_imm5_w(); ++ WriteMem(addr, (int32_t)rvc_rs2s(), instr_.instr()); ++ break; ++ } ++# if JS_CODEGEN_RISCV64 ++ case RO_C_SD: { ++ sreg_t addr = rvc_rs1s() + rvc_imm5_d(); ++ WriteMem(addr, (int64_t)rvc_rs2s(), instr_.instr()); ++ break; ++ } ++# endif ++ case RO_C_FSD: { ++ sreg_t addr = rvc_rs1s() + rvc_imm5_d(); ++ WriteMem(addr, static_cast(rvc_drs2s()), instr_.instr()); ++ break; ++ } ++ default: ++ UNSUPPORTED(); ++ } ++} ++ ++void Simulator::DecodeCJType() { ++ switch (instr_.RvcOpcode()) { ++ case RO_C_J: { ++ set_pc(get_pc() + instr_.RvcImm11CJValue()); ++ break; ++ } ++ default: ++ UNSUPPORTED(); ++ } ++} ++ ++void Simulator::DecodeCBType() { ++ switch (instr_.RvcOpcode()) { ++ case RO_C_BNEZ: ++ if (rvc_rs1() != 0) { ++ sreg_t next_pc = get_pc() + rvc_imm8_b(); ++ set_pc(next_pc); ++ } ++ break; ++ case RO_C_BEQZ: ++ if (rvc_rs1() == 0) { ++ sreg_t next_pc = get_pc() + rvc_imm8_b(); ++ set_pc(next_pc); ++ } ++ break; ++ case RO_C_MISC_ALU: ++ if (instr_.RvcFunct2BValue() == 0b00) { // c.srli ++ set_rvc_rs1s(sext_xlen(sext_xlen(rvc_rs1s()) >> rvc_shamt6())); ++ } else if (instr_.RvcFunct2BValue() == 0b01) { // c.srai ++ require(rvc_shamt6() < xlen); ++ set_rvc_rs1s(sext_xlen(sext_xlen(rvc_rs1s()) >> rvc_shamt6())); ++ } else if (instr_.RvcFunct2BValue() == 0b10) { // c.andi ++ set_rvc_rs1s(rvc_imm6() & rvc_rs1s()); ++ } else { ++ UNSUPPORTED(); ++ } ++ break; ++ default: ++ UNSUPPORTED(); ++ } ++} ++ ++void Simulator::callInternal(uint8_t* entry) { ++ // Prepare to execute the code at entry. ++ setRegister(pc, reinterpret_cast(entry)); ++ // Put down marker for end of simulation. The simulator will stop simulation ++ // when the PC reaches this value. By saving the "end simulation" value into ++ // the LR the simulation stops when returning to this call point. ++ setRegister(ra, end_sim_pc); ++ // Remember the values of callee-saved registers. ++ intptr_t s0_val = getRegister(Simulator::Register::fp); ++ intptr_t s1_val = getRegister(Simulator::Register::s1); ++ intptr_t s2_val = getRegister(Simulator::Register::s2); ++ intptr_t s3_val = getRegister(Simulator::Register::s3); ++ intptr_t s4_val = getRegister(Simulator::Register::s4); ++ intptr_t s5_val = getRegister(Simulator::Register::s5); ++ intptr_t s6_val = getRegister(Simulator::Register::s6); ++ intptr_t s7_val = getRegister(Simulator::Register::s7); ++ intptr_t s8_val = getRegister(Simulator::Register::s8); ++ intptr_t s9_val = getRegister(Simulator::Register::s9); ++ intptr_t s10_val = getRegister(Simulator::Register::s10); ++ intptr_t s11_val = getRegister(Simulator::Register::s11); ++ intptr_t gp_val = getRegister(Simulator::Register::gp); ++ intptr_t sp_val = getRegister(Simulator::Register::sp); ++ ++ // Set up the callee-saved registers with a known value. To be able to check ++ // that they are preserved properly across JS execution. If this value is ++ // small int, it should be SMI. ++ intptr_t callee_saved_value = icount_; ++ setRegister(Simulator::Register::fp, callee_saved_value); ++ setRegister(Simulator::Register::s1, callee_saved_value); ++ setRegister(Simulator::Register::s2, callee_saved_value); ++ setRegister(Simulator::Register::s3, callee_saved_value); ++ setRegister(Simulator::Register::s4, callee_saved_value); ++ setRegister(Simulator::Register::s5, callee_saved_value); ++ setRegister(Simulator::Register::s6, callee_saved_value); ++ setRegister(Simulator::Register::s7, callee_saved_value); ++ setRegister(Simulator::Register::s8, callee_saved_value); ++ setRegister(Simulator::Register::s9, callee_saved_value); ++ setRegister(Simulator::Register::s10, callee_saved_value); ++ setRegister(Simulator::Register::s11, callee_saved_value); ++ setRegister(Simulator::Register::gp, callee_saved_value); ++ ++ // Start the simulation. ++ if (Simulator::StopSimAt != -1) { ++ execute(); ++ } else { ++ execute(); ++ } ++ ++ // Check that the callee-saved registers have been preserved. ++ MOZ_ASSERT(callee_saved_value == getRegister(Simulator::Register::fp)); ++ MOZ_ASSERT(callee_saved_value == getRegister(Simulator::Register::s1)); ++ MOZ_ASSERT(callee_saved_value == getRegister(Simulator::Register::s2)); ++ MOZ_ASSERT(callee_saved_value == getRegister(Simulator::Register::s3)); ++ MOZ_ASSERT(callee_saved_value == getRegister(Simulator::Register::s4)); ++ MOZ_ASSERT(callee_saved_value == getRegister(Simulator::Register::s5)); ++ MOZ_ASSERT(callee_saved_value == getRegister(Simulator::Register::s6)); ++ MOZ_ASSERT(callee_saved_value == getRegister(Simulator::Register::s7)); ++ MOZ_ASSERT(callee_saved_value == getRegister(Simulator::Register::s8)); ++ MOZ_ASSERT(callee_saved_value == getRegister(Simulator::Register::s9)); ++ MOZ_ASSERT(callee_saved_value == getRegister(Simulator::Register::s10)); ++ MOZ_ASSERT(callee_saved_value == getRegister(Simulator::Register::s11)); ++ MOZ_ASSERT(callee_saved_value == getRegister(Simulator::Register::gp)); ++ ++ // Restore callee-saved registers with the original value. ++ setRegister(Simulator::Register::fp, s0_val); ++ setRegister(Simulator::Register::s1, s1_val); ++ setRegister(Simulator::Register::s2, s2_val); ++ setRegister(Simulator::Register::s3, s3_val); ++ setRegister(Simulator::Register::s4, s4_val); ++ setRegister(Simulator::Register::s5, s5_val); ++ setRegister(Simulator::Register::s6, s6_val); ++ setRegister(Simulator::Register::s7, s7_val); ++ setRegister(Simulator::Register::s8, s8_val); ++ setRegister(Simulator::Register::s9, s9_val); ++ setRegister(Simulator::Register::s10, s10_val); ++ setRegister(Simulator::Register::s11, s11_val); ++ setRegister(Simulator::Register::gp, gp_val); ++ setRegister(Simulator::Register::sp, sp_val); ++} ++ ++int64_t Simulator::call(uint8_t* entry, int argument_count, ...) { ++ va_list parameters; ++ va_start(parameters, argument_count); ++ ++ int64_t original_stack = getRegister(sp); ++ // Compute position of stack on entry to generated code. ++ int64_t entry_stack = original_stack; ++ if (argument_count > kCArgSlotCount) { ++ entry_stack = entry_stack - argument_count * sizeof(int64_t); ++ } else { ++ entry_stack = entry_stack - kCArgsSlotsSize; ++ } ++ ++ entry_stack &= ~U64(ABIStackAlignment - 1); ++ ++ intptr_t* stack_argument = reinterpret_cast(entry_stack); ++ ++ // Setup the arguments. ++ for (int i = 0; i < argument_count; i++) { ++ js::jit::Register argReg; ++ if (GetIntArgReg(i, &argReg)) { ++ setRegister(argReg.code(), va_arg(parameters, int64_t)); ++ } else { ++ stack_argument[i] = va_arg(parameters, int64_t); ++ } ++ } ++ ++ va_end(parameters); ++ setRegister(sp, entry_stack); ++ ++ callInternal(entry); ++ ++ // Pop stack passed arguments. ++ MOZ_ASSERT(entry_stack == getRegister(sp)); ++ setRegister(sp, original_stack); ++ ++ int64_t result = getRegister(a0); ++ return result; ++} ++ ++uintptr_t Simulator::pushAddress(uintptr_t address) { ++ int new_sp = getRegister(sp) - sizeof(uintptr_t); ++ uintptr_t* stack_slot = reinterpret_cast(new_sp); ++ *stack_slot = address; ++ setRegister(sp, new_sp); ++ return new_sp; ++} ++ ++uintptr_t Simulator::popAddress() { ++ int current_sp = getRegister(sp); ++ uintptr_t* stack_slot = reinterpret_cast(current_sp); ++ uintptr_t address = *stack_slot; ++ setRegister(sp, current_sp + sizeof(uintptr_t)); ++ return address; ++} ++ ++} // namespace jit ++} // namespace js ++ ++js::jit::Simulator* JSContext::simulator() const { return simulator_; } ++ ++#endif // JS_SIMULATOR_RISCV64 +diff --git a/js/src/jit/riscv64/Simulator-riscv64.h b/js/src/jit/riscv64/Simulator-riscv64.h +new file mode 100644 +index 0000000000..20a3f6e97c +--- /dev/null ++++ b/js/src/jit/riscv64/Simulator-riscv64.h +@@ -0,0 +1,1281 @@ ++/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- ++ * vim: set ts=8 sts=2 et sw=2 tw=80: */ ++// Copyright 2021 the V8 project authors. All rights reserved. ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions are ++// met: ++// ++// * Redistributions of source code must retain the above copyright ++// notice, this list of conditions and the following disclaimer. ++// * Redistributions in binary form must reproduce the above ++// copyright notice, this list of conditions and the following ++// disclaimer in the documentation and/or other materials provided ++// with the distribution. ++// * Neither the name of Google Inc. nor the names of its ++// contributors may be used to endorse or promote products derived ++// from this software without specific prior written permission. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ ++#ifndef jit_riscv64_Simulator_riscv64_h ++#define jit_riscv64_Simulator_riscv64_h ++ ++#ifdef JS_SIMULATOR_RISCV64 ++# include "mozilla/Atomics.h" ++ ++# include ++ ++# include "jit/IonTypes.h" ++# include "jit/riscv64/constant/Constant-riscv64.h" ++# include "jit/riscv64/constant/util-riscv64.h" ++# include "jit/riscv64/disasm/Disasm-riscv64.h" ++# include "js/ProfilingFrameIterator.h" ++# include "threading/Thread.h" ++# include "vm/MutexIDs.h" ++# include "wasm/WasmSignalHandlers.h" ++ ++namespace js { ++ ++namespace jit { ++ ++template ++inline Dest bit_cast(const Source& source) { ++ static_assert(sizeof(Dest) == sizeof(Source), ++ "bit_cast requires source and destination to be the same size"); ++ static_assert(std::is_trivially_copyable::value, ++ "bit_cast requires the destination type to be copyable"); ++ static_assert(std::is_trivially_copyable::value, ++ "bit_cast requires the source type to be copyable"); ++ ++ Dest dest; ++ memcpy(&dest, &source, sizeof(dest)); ++ return dest; ++} ++ ++# define ASSERT_TRIVIALLY_COPYABLE(T) \ ++ static_assert(std::is_trivially_copyable::value, \ ++ #T " should be trivially copyable") ++# define ASSERT_NOT_TRIVIALLY_COPYABLE(T) \ ++ static_assert(!std::is_trivially_copyable::value, \ ++ #T " should not be trivially copyable") ++ ++constexpr uint32_t kHoleNanUpper32 = 0xFFF7FFFF; ++constexpr uint32_t kHoleNanLower32 = 0xFFF7FFFF; ++ ++constexpr uint64_t kHoleNanInt64 = ++ (static_cast(kHoleNanUpper32) << 32) | kHoleNanLower32; ++// Safety wrapper for a 32-bit floating-point value to make sure we don't lose ++// the exact bit pattern during deoptimization when passing this value. ++class Float32 { ++ public: ++ Float32() = default; ++ ++ // This constructor does not guarantee that bit pattern of the input value ++ // is preserved if the input is a NaN. ++ explicit Float32(float value) : bit_pattern_(bit_cast(value)) { ++ // Check that the provided value is not a NaN, because the bit pattern of a ++ // NaN may be changed by a bit_cast, e.g. for signalling NaNs on ++ // ia32. ++ MOZ_ASSERT(!std::isnan(value)); ++ } ++ ++ uint32_t get_bits() const { return bit_pattern_; } ++ ++ float get_scalar() const { return bit_cast(bit_pattern_); } ++ ++ bool is_nan() const { ++ // Even though {get_scalar()} might flip the quiet NaN bit, it's ok here, ++ // because this does not change the is_nan property. ++ return std::isnan(get_scalar()); ++ } ++ ++ // Return a pointer to the field storing the bit pattern. Used in code ++ // generation tests to store generated values there directly. ++ uint32_t* get_bits_address() { return &bit_pattern_; } ++ ++ static constexpr Float32 FromBits(uint32_t bits) { return Float32(bits); } ++ ++ private: ++ uint32_t bit_pattern_ = 0; ++ ++ explicit constexpr Float32(uint32_t bit_pattern) ++ : bit_pattern_(bit_pattern) {} ++}; ++ ++ASSERT_TRIVIALLY_COPYABLE(Float32); ++ ++// Safety wrapper for a 64-bit floating-point value to make sure we don't lose ++// the exact bit pattern during deoptimization when passing this value. ++// TODO(ahaas): Unify this class with Double in double.h ++class Float64 { ++ public: ++ Float64() = default; ++ ++ // This constructor does not guarantee that bit pattern of the input value ++ // is preserved if the input is a NaN. ++ explicit Float64(double value) : bit_pattern_(bit_cast(value)) { ++ // Check that the provided value is not a NaN, because the bit pattern of a ++ // NaN may be changed by a bit_cast, e.g. for signalling NaNs on ++ // ia32. ++ MOZ_ASSERT(!std::isnan(value)); ++ } ++ ++ uint64_t get_bits() const { return bit_pattern_; } ++ double get_scalar() const { return bit_cast(bit_pattern_); } ++ bool is_hole_nan() const { return bit_pattern_ == kHoleNanInt64; } ++ bool is_nan() const { ++ // Even though {get_scalar()} might flip the quiet NaN bit, it's ok here, ++ // because this does not change the is_nan property. ++ return std::isnan(get_scalar()); ++ } ++ ++ // Return a pointer to the field storing the bit pattern. Used in code ++ // generation tests to store generated values there directly. ++ uint64_t* get_bits_address() { return &bit_pattern_; } ++ ++ static constexpr Float64 FromBits(uint64_t bits) { return Float64(bits); } ++ ++ private: ++ uint64_t bit_pattern_ = 0; ++ ++ explicit constexpr Float64(uint64_t bit_pattern) ++ : bit_pattern_(bit_pattern) {} ++}; ++ ++ASSERT_TRIVIALLY_COPYABLE(Float64); ++ ++class JitActivation; ++ ++class Simulator; ++class Redirection; ++class CachePage; ++class AutoLockSimulator; ++ ++// When the SingleStepCallback is called, the simulator is about to execute ++// sim->get_pc() and the current machine state represents the completed ++// execution of the previous pc. ++typedef void (*SingleStepCallback)(void* arg, Simulator* sim, void* pc); ++ ++const intptr_t kPointerAlignment = 8; ++const intptr_t kPointerAlignmentMask = kPointerAlignment - 1; ++ ++const intptr_t kDoubleAlignment = 8; ++const intptr_t kDoubleAlignmentMask = kDoubleAlignment - 1; ++ ++// Number of general purpose registers. ++const int kNumRegisters = 32; ++ ++// In the simulator, the PC register is simulated as the 34th register. ++const int kPCRegister = 32; ++ ++// Number coprocessor registers. ++const int kNumFPURegisters = 32; ++ ++// FPU (coprocessor 1) control registers. Currently only FCSR is implemented. ++const int kFCSRRegister = 31; ++const int kInvalidFPUControlRegister = -1; ++const uint32_t kFPUInvalidResult = static_cast(1 << 31) - 1; ++const uint64_t kFPUInvalidResult64 = static_cast(1ULL << 63) - 1; ++ ++// FCSR constants. ++const uint32_t kFCSRInexactFlagBit = 2; ++const uint32_t kFCSRUnderflowFlagBit = 3; ++const uint32_t kFCSROverflowFlagBit = 4; ++const uint32_t kFCSRDivideByZeroFlagBit = 5; ++const uint32_t kFCSRInvalidOpFlagBit = 6; ++ ++const uint32_t kFCSRInexactCauseBit = 12; ++const uint32_t kFCSRUnderflowCauseBit = 13; ++const uint32_t kFCSROverflowCauseBit = 14; ++const uint32_t kFCSRDivideByZeroCauseBit = 15; ++const uint32_t kFCSRInvalidOpCauseBit = 16; ++ ++const uint32_t kFCSRInexactFlagMask = 1 << kFCSRInexactFlagBit; ++const uint32_t kFCSRUnderflowFlagMask = 1 << kFCSRUnderflowFlagBit; ++const uint32_t kFCSROverflowFlagMask = 1 << kFCSROverflowFlagBit; ++const uint32_t kFCSRDivideByZeroFlagMask = 1 << kFCSRDivideByZeroFlagBit; ++const uint32_t kFCSRInvalidOpFlagMask = 1 << kFCSRInvalidOpFlagBit; ++ ++const uint32_t kFCSRFlagMask = ++ kFCSRInexactFlagMask | kFCSRUnderflowFlagMask | kFCSROverflowFlagMask | ++ kFCSRDivideByZeroFlagMask | kFCSRInvalidOpFlagMask; ++ ++const uint32_t kFCSRExceptionFlagMask = kFCSRFlagMask ^ kFCSRInexactFlagMask; ++ ++// ----------------------------------------------------------------------------- ++// Utility types and functions for RISCV ++# ifdef JS_CODEGEN_RISCV32 ++using sreg_t = int32_t; ++using reg_t = uint32_t; ++using freg_t = uint64_t; ++using sfreg_t = int64_t; ++# elif JS_CODEGEN_RISCV64 ++using sreg_t = int64_t; ++using reg_t = uint64_t; ++using freg_t = uint64_t; ++using sfreg_t = int64_t; ++# else ++# error "Cannot detect Riscv's bitwidth" ++# endif ++ ++# define sext32(x) ((sreg_t)(int32_t)(x)) ++# define zext32(x) ((reg_t)(uint32_t)(x)) ++ ++# ifdef JS_CODEGEN_RISCV64 ++# define sext_xlen(x) (((sreg_t)(x) << (64 - xlen)) >> (64 - xlen)) ++# define zext_xlen(x) (((reg_t)(x) << (64 - xlen)) >> (64 - xlen)) ++# elif JS_CODEGEN_RISCV32 ++# define sext_xlen(x) (((sreg_t)(x) << (32 - xlen)) >> (32 - xlen)) ++# define zext_xlen(x) (((reg_t)(x) << (32 - xlen)) >> (32 - xlen)) ++# endif ++ ++# define BIT(n) (0x1LL << n) ++# define QUIET_BIT_S(nan) (bit_cast(nan) & BIT(22)) ++# define QUIET_BIT_D(nan) (bit_cast(nan) & BIT(51)) ++static inline bool isSnan(float fp) { return !QUIET_BIT_S(fp); } ++static inline bool isSnan(double fp) { return !QUIET_BIT_D(fp); } ++# undef QUIET_BIT_S ++# undef QUIET_BIT_D ++ ++# ifdef JS_CODEGEN_RISCV64 ++inline uint64_t mulhu(uint64_t a, uint64_t b) { ++ __uint128_t full_result = ((__uint128_t)a) * ((__uint128_t)b); ++ return full_result >> 64; ++} ++ ++inline int64_t mulh(int64_t a, int64_t b) { ++ __int128_t full_result = ((__int128_t)a) * ((__int128_t)b); ++ return full_result >> 64; ++} ++ ++inline int64_t mulhsu(int64_t a, uint64_t b) { ++ __int128_t full_result = ((__int128_t)a) * ((__uint128_t)b); ++ return full_result >> 64; ++} ++# elif JS_CODEGEN_RISCV32 ++inline uint32_t mulhu(uint32_t a, uint32_t b) { ++ uint64_t full_result = ((uint64_t)a) * ((uint64_t)b); ++ uint64_t upper_part = full_result >> 32; ++ return (uint32_t)upper_part; ++} ++ ++inline int32_t mulh(int32_t a, int32_t b) { ++ int64_t full_result = ((int64_t)a) * ((int64_t)b); ++ int64_t upper_part = full_result >> 32; ++ return (int32_t)upper_part; ++} ++ ++inline int32_t mulhsu(int32_t a, uint32_t b) { ++ int64_t full_result = ((int64_t)a) * ((uint64_t)b); ++ int64_t upper_part = full_result >> 32; ++ return (int32_t)upper_part; ++} ++# endif ++ ++// Floating point helpers ++# define F32_SIGN ((uint32_t)1 << 31) ++union u32_f32 { ++ uint32_t u; ++ float f; ++}; ++inline float fsgnj32(float rs1, float rs2, bool n, bool x) { ++ u32_f32 a = {.f = rs1}, b = {.f = rs2}; ++ u32_f32 res; ++ res.u = (a.u & ~F32_SIGN) | ((((x) ? a.u ++ : (n) ? F32_SIGN ++ : 0) ^ ++ b.u) & ++ F32_SIGN); ++ return res.f; ++} ++ ++inline Float32 fsgnj32(Float32 rs1, Float32 rs2, bool n, bool x) { ++ u32_f32 a = {.u = rs1.get_bits()}, b = {.u = rs2.get_bits()}; ++ u32_f32 res; ++ if (x) { // RO_FSQNJX_S ++ res.u = (a.u & ~F32_SIGN) | ((a.u ^ b.u) & F32_SIGN); ++ } else { ++ if (n) { // RO_FSGNJN_S ++ res.u = (a.u & ~F32_SIGN) | ((F32_SIGN ^ b.u) & F32_SIGN); ++ } else { // RO_FSGNJ_S ++ res.u = (a.u & ~F32_SIGN) | ((0 ^ b.u) & F32_SIGN); ++ } ++ } ++ return Float32::FromBits(res.u); ++} ++# define F64_SIGN ((uint64_t)1 << 63) ++union u64_f64 { ++ uint64_t u; ++ double d; ++}; ++inline double fsgnj64(double rs1, double rs2, bool n, bool x) { ++ u64_f64 a = {.d = rs1}, b = {.d = rs2}; ++ u64_f64 res; ++ res.u = (a.u & ~F64_SIGN) | ((((x) ? a.u ++ : (n) ? F64_SIGN ++ : 0) ^ ++ b.u) & ++ F64_SIGN); ++ return res.d; ++} ++ ++inline Float64 fsgnj64(Float64 rs1, Float64 rs2, bool n, bool x) { ++ u64_f64 a = {.d = rs1.get_scalar()}, b = {.d = rs2.get_scalar()}; ++ u64_f64 res; ++ if (x) { // RO_FSQNJX_D ++ res.u = (a.u & ~F64_SIGN) | ((a.u ^ b.u) & F64_SIGN); ++ } else { ++ if (n) { // RO_FSGNJN_D ++ res.u = (a.u & ~F64_SIGN) | ((F64_SIGN ^ b.u) & F64_SIGN); ++ } else { // RO_FSGNJ_D ++ res.u = (a.u & ~F64_SIGN) | ((0 ^ b.u) & F64_SIGN); ++ } ++ } ++ return Float64::FromBits(res.u); ++} ++inline bool is_boxed_float(int64_t v) { return (uint32_t)((v >> 32) + 1) == 0; } ++inline int64_t box_float(float v) { ++ return (0xFFFFFFFF00000000 | bit_cast(v)); ++} ++ ++inline uint64_t box_float(uint32_t v) { return (0xFFFFFFFF00000000 | v); } ++ ++// ----------------------------------------------------------------------------- ++// Utility functions ++ ++class SimInstructionBase : public InstructionBase { ++ public: ++ Type InstructionType() const { return type_; } ++ inline Instruction* instr() const { return instr_; } ++ inline int32_t operand() const { return operand_; } ++ ++ protected: ++ SimInstructionBase() : operand_(-1), instr_(nullptr), type_(kUnsupported) {} ++ explicit SimInstructionBase(Instruction* instr) {} ++ ++ int32_t operand_; ++ Instruction* instr_; ++ Type type_; ++ ++ private: ++ SimInstructionBase& operator=(const SimInstructionBase&) = delete; ++}; ++ ++class SimInstruction : public InstructionGetters { ++ public: ++ SimInstruction() {} ++ ++ explicit SimInstruction(Instruction* instr) { *this = instr; } ++ ++ SimInstruction& operator=(Instruction* instr) { ++ operand_ = *reinterpret_cast(instr); ++ instr_ = instr; ++ type_ = InstructionBase::InstructionType(); ++ MOZ_ASSERT(reinterpret_cast(&operand_) == this); ++ return *this; ++ } ++}; ++ ++// Per thread simulator state. ++class Simulator { ++ friend class RiscvDebugger; ++ ++ public: ++ static bool FLAG_riscv_trap_to_simulator_debugger; ++ static bool FLAG_trace_sim; ++ static bool FLAG_debug_sim; ++ static bool FLAG_riscv_print_watchpoint; ++ // Registers are declared in order. ++ enum Register { ++ no_reg = -1, ++ x0 = 0, ++ x1, ++ x2, ++ x3, ++ x4, ++ x5, ++ x6, ++ x7, ++ x8, ++ x9, ++ x10, ++ x11, ++ x12, ++ x13, ++ x14, ++ x15, ++ x16, ++ x17, ++ x18, ++ x19, ++ x20, ++ x21, ++ x22, ++ x23, ++ x24, ++ x25, ++ x26, ++ x27, ++ x28, ++ x29, ++ x30, ++ x31, ++ pc, ++ kNumSimuRegisters, ++ // alias ++ zero = x0, ++ ra = x1, ++ sp = x2, ++ gp = x3, ++ tp = x4, ++ t0 = x5, ++ t1 = x6, ++ t2 = x7, ++ fp = x8, ++ s1 = x9, ++ a0 = x10, ++ a1 = x11, ++ a2 = x12, ++ a3 = x13, ++ a4 = x14, ++ a5 = x15, ++ a6 = x16, ++ a7 = x17, ++ s2 = x18, ++ s3 = x19, ++ s4 = x20, ++ s5 = x21, ++ s6 = x22, ++ s7 = x23, ++ s8 = x24, ++ s9 = x25, ++ s10 = x26, ++ s11 = x27, ++ t3 = x28, ++ t4 = x29, ++ t5 = x30, ++ t6 = x31, ++ }; ++ ++ // Coprocessor registers. ++ enum FPURegister { ++ f0, ++ f1, ++ f2, ++ f3, ++ f4, ++ f5, ++ f6, ++ f7, ++ f8, ++ f9, ++ f10, ++ f11, ++ f12, ++ f13, ++ f14, ++ f15, ++ f16, ++ f17, ++ f18, ++ f19, ++ f20, ++ f21, ++ f22, ++ f23, ++ f24, ++ f25, ++ f26, ++ f27, ++ f28, ++ f29, ++ f30, ++ f31, ++ kNumFPURegisters, ++ // alias ++ ft0 = f0, ++ ft1 = f1, ++ ft2 = f2, ++ ft3 = f3, ++ ft4 = f4, ++ ft5 = f5, ++ ft6 = f6, ++ ft7 = f7, ++ fs0 = f8, ++ fs1 = f9, ++ fa0 = f10, ++ fa1 = f11, ++ fa2 = f12, ++ fa3 = f13, ++ fa4 = f14, ++ fa5 = f15, ++ fa6 = f16, ++ fa7 = f17, ++ fs2 = f18, ++ fs3 = f19, ++ fs4 = f20, ++ fs5 = f21, ++ fs6 = f22, ++ fs7 = f23, ++ fs8 = f24, ++ fs9 = f25, ++ fs10 = f26, ++ fs11 = f27, ++ ft8 = f28, ++ ft9 = f29, ++ ft10 = f30, ++ ft11 = f31 ++ }; ++ ++ // Returns nullptr on OOM. ++ static Simulator* Create(); ++ ++ static void Destroy(Simulator* simulator); ++ ++ // Constructor/destructor are for internal use only; use the static methods ++ // above. ++ Simulator(); ++ ~Simulator(); ++ ++ // RISCV decoding routine ++ void DecodeRVRType(); ++ void DecodeRVR4Type(); ++ void DecodeRVRFPType(); // Special routine for R/OP_FP type ++ void DecodeRVRAType(); // Special routine for R/AMO type ++ void DecodeRVIType(); ++ void DecodeRVSType(); ++ void DecodeRVBType(); ++ void DecodeRVUType(); ++ void DecodeRVJType(); ++ void DecodeCRType(); ++ void DecodeCAType(); ++ void DecodeCIType(); ++ void DecodeCIWType(); ++ void DecodeCSSType(); ++ void DecodeCLType(); ++ void DecodeCSType(); ++ void DecodeCJType(); ++ void DecodeCBType(); ++# ifdef CAN_USE_RVV_INSTRUCTIONS ++ void DecodeVType(); ++ void DecodeRvvIVV(); ++ void DecodeRvvIVI(); ++ void DecodeRvvIVX(); ++ void DecodeRvvMVV(); ++ void DecodeRvvMVX(); ++ void DecodeRvvFVV(); ++ void DecodeRvvFVF(); ++ bool DecodeRvvVL(); ++ bool DecodeRvvVS(); ++# endif ++ // The currently executing Simulator instance. Potentially there can be one ++ // for each native thread. ++ static Simulator* Current(); ++ ++ static inline uintptr_t StackLimit() { ++ return Simulator::Current()->stackLimit(); ++ } ++ ++ uintptr_t* addressOfStackLimit(); ++ ++ // Accessors for register state. Reading the pc value adheres to the MIPS ++ // architecture specification and is off by a 8 from the currently executing ++ // instruction. ++ void setRegister(int reg, int64_t value); ++ int64_t getRegister(int reg) const; ++ // Same for FPURegisters. ++ void setFpuRegister(int fpureg, int64_t value); ++ void setFpuRegisterLo(int fpureg, int32_t value); ++ void setFpuRegisterHi(int fpureg, int32_t value); ++ void setFpuRegisterFloat(int fpureg, float value); ++ void setFpuRegisterDouble(int fpureg, double value); ++ void setFpuRegisterFloat(int fpureg, Float32 value); ++ void setFpuRegisterDouble(int fpureg, Float64 value); ++ ++ int64_t getFpuRegister(int fpureg) const; ++ int32_t getFpuRegisterLo(int fpureg) const; ++ int32_t getFpuRegisterHi(int fpureg) const; ++ float getFpuRegisterFloat(int fpureg) const; ++ double getFpuRegisterDouble(int fpureg) const; ++ Float32 getFpuRegisterFloat32(int fpureg) const; ++ Float64 getFpuRegisterFloat64(int fpureg) const; ++ ++ inline int16_t shamt6() const { return (imm12() & 0x3F); } ++ inline int16_t shamt5() const { return (imm12() & 0x1F); } ++ inline int16_t rvc_shamt6() const { return instr_.RvcShamt6(); } ++ inline int32_t s_imm12() const { return instr_.StoreOffset(); } ++ inline int32_t u_imm20() const { return instr_.Imm20UValue() << 12; } ++ inline int32_t rvc_u_imm6() const { return instr_.RvcImm6Value() << 12; } ++ inline void require(bool check) { ++ if (!check) { ++ SignalException(kIllegalInstruction); ++ } ++ } ++ ++ // Special case of setRegister and getRegister to access the raw PC value. ++ void set_pc(int64_t value); ++ int64_t get_pc() const; ++ ++ SimInstruction instr_; ++ // RISCV utlity API to access register value ++ // Helpers for data value tracing. ++ enum TraceType { ++ BYTE, ++ HALF, ++ WORD, ++# if JS_CODEGEN_RISCV64 ++ DWORD, ++# endif ++ FLOAT, ++ DOUBLE, ++ // FLOAT_DOUBLE, ++ // WORD_DWORD ++ }; ++ inline int32_t rs1_reg() const { return instr_.Rs1Value(); } ++ inline sreg_t rs1() const { return getRegister(rs1_reg()); } ++ inline float frs1() const { return getFpuRegisterFloat(rs1_reg()); } ++ inline double drs1() const { return getFpuRegisterDouble(rs1_reg()); } ++ inline Float32 frs1_boxed() const { return getFpuRegisterFloat32(rs1_reg()); } ++ inline Float64 drs1_boxed() const { return getFpuRegisterFloat64(rs1_reg()); } ++ inline int32_t rs2_reg() const { return instr_.Rs2Value(); } ++ inline sreg_t rs2() const { return getRegister(rs2_reg()); } ++ inline float frs2() const { return getFpuRegisterFloat(rs2_reg()); } ++ inline double drs2() const { return getFpuRegisterDouble(rs2_reg()); } ++ inline Float32 frs2_boxed() const { return getFpuRegisterFloat32(rs2_reg()); } ++ inline Float64 drs2_boxed() const { return getFpuRegisterFloat64(rs2_reg()); } ++ inline int32_t rs3_reg() const { return instr_.Rs3Value(); } ++ inline sreg_t rs3() const { return getRegister(rs3_reg()); } ++ inline float frs3() const { return getFpuRegisterFloat(rs3_reg()); } ++ inline double drs3() const { return getFpuRegisterDouble(rs3_reg()); } ++ inline Float32 frs3_boxed() const { return getFpuRegisterFloat32(rs3_reg()); } ++ inline Float64 drs3_boxed() const { return getFpuRegisterFloat64(rs3_reg()); } ++ inline int32_t rd_reg() const { return instr_.RdValue(); } ++ inline int32_t frd_reg() const { return instr_.RdValue(); } ++ inline int32_t rvc_rs1_reg() const { return instr_.RvcRs1Value(); } ++ inline sreg_t rvc_rs1() const { return getRegister(rvc_rs1_reg()); } ++ inline int32_t rvc_rs2_reg() const { return instr_.RvcRs2Value(); } ++ inline sreg_t rvc_rs2() const { return getRegister(rvc_rs2_reg()); } ++ inline double rvc_drs2() const { return getFpuRegisterDouble(rvc_rs2_reg()); } ++ inline int32_t rvc_rs1s_reg() const { return instr_.RvcRs1sValue(); } ++ inline sreg_t rvc_rs1s() const { return getRegister(rvc_rs1s_reg()); } ++ inline int32_t rvc_rs2s_reg() const { return instr_.RvcRs2sValue(); } ++ inline sreg_t rvc_rs2s() const { return getRegister(rvc_rs2s_reg()); } ++ inline double rvc_drs2s() const { ++ return getFpuRegisterDouble(rvc_rs2s_reg()); ++ } ++ inline int32_t rvc_rd_reg() const { return instr_.RvcRdValue(); } ++ inline int32_t rvc_frd_reg() const { return instr_.RvcRdValue(); } ++ inline int16_t boffset() const { return instr_.BranchOffset(); } ++ inline int16_t imm12() const { return instr_.Imm12Value(); } ++ inline int32_t imm20J() const { return instr_.Imm20JValue(); } ++ inline int32_t imm5CSR() const { return instr_.Rs1Value(); } ++ inline int16_t csr_reg() const { return instr_.CsrValue(); } ++ inline int16_t rvc_imm6() const { return instr_.RvcImm6Value(); } ++ inline int16_t rvc_imm6_addi16sp() const { ++ return instr_.RvcImm6Addi16spValue(); ++ } ++ inline int16_t rvc_imm8_addi4spn() const { ++ return instr_.RvcImm8Addi4spnValue(); ++ } ++ inline int16_t rvc_imm6_lwsp() const { return instr_.RvcImm6LwspValue(); } ++ inline int16_t rvc_imm6_ldsp() const { return instr_.RvcImm6LdspValue(); } ++ inline int16_t rvc_imm6_swsp() const { return instr_.RvcImm6SwspValue(); } ++ inline int16_t rvc_imm6_sdsp() const { return instr_.RvcImm6SdspValue(); } ++ inline int16_t rvc_imm5_w() const { return instr_.RvcImm5WValue(); } ++ inline int16_t rvc_imm5_d() const { return instr_.RvcImm5DValue(); } ++ inline int16_t rvc_imm8_b() const { return instr_.RvcImm8BValue(); } ++ ++ // Helper for debugging memory access. ++ inline void DieOrDebug(); ++ ++# if JS_CODEGEN_RISCV32 ++ template ++ void TraceRegWr(T value, TraceType t = WORD); ++# elif JS_CODEGEN_RISCV64 ++ void TraceRegWr(sreg_t value, TraceType t = DWORD); ++# endif ++ void TraceMemWr(sreg_t addr, sreg_t value, TraceType t); ++ template ++ void TraceMemRd(sreg_t addr, T value, sreg_t reg_value); ++ void TraceMemRdDouble(sreg_t addr, double value, int64_t reg_value); ++ void TraceMemRdDouble(sreg_t addr, Float64 value, int64_t reg_value); ++ void TraceMemRdFloat(sreg_t addr, Float32 value, int64_t reg_value); ++ ++ template ++ void TraceLr(sreg_t addr, T value, sreg_t reg_value); ++ ++ template ++ void TraceSc(sreg_t addr, T value); ++ ++ template ++ void TraceMemWr(sreg_t addr, T value); ++ void TraceMemWrDouble(sreg_t addr, double value); ++ ++ inline void set_rd(sreg_t value, bool trace = true) { ++ setRegister(rd_reg(), value); ++# if JS_CODEGEN_RISCV64 ++ if (trace) TraceRegWr(getRegister(rd_reg()), DWORD); ++# elif JS_CODEGEN_RISCV32 ++ if (trace) TraceRegWr(getRegister(rd_reg()), WORD); ++# endif ++ } ++ inline void set_frd(float value, bool trace = true) { ++ setFpuRegisterFloat(rd_reg(), value); ++ if (trace) TraceRegWr(getFpuRegister(rd_reg()), FLOAT); ++ } ++ inline void set_frd(Float32 value, bool trace = true) { ++ setFpuRegisterFloat(rd_reg(), value); ++ if (trace) TraceRegWr(getFpuRegister(rd_reg()), FLOAT); ++ } ++ inline void set_drd(double value, bool trace = true) { ++ setFpuRegisterDouble(rd_reg(), value); ++ if (trace) TraceRegWr(getFpuRegister(rd_reg()), DOUBLE); ++ } ++ inline void set_drd(Float64 value, bool trace = true) { ++ setFpuRegisterDouble(rd_reg(), value); ++ if (trace) TraceRegWr(getFpuRegister(rd_reg()), DOUBLE); ++ } ++ inline void set_rvc_rd(sreg_t value, bool trace = true) { ++ setRegister(rvc_rd_reg(), value); ++# if JS_CODEGEN_RISCV64 ++ if (trace) TraceRegWr(getRegister(rvc_rd_reg()), DWORD); ++# elif JS_CODEGEN_RISCV32 ++ if (trace) TraceRegWr(getRegister(rvc_rd_reg()), WORD); ++# endif ++ } ++ inline void set_rvc_rs1s(sreg_t value, bool trace = true) { ++ setRegister(rvc_rs1s_reg(), value); ++# if JS_CODEGEN_RISCV64 ++ if (trace) TraceRegWr(getRegister(rvc_rs1s_reg()), DWORD); ++# elif JS_CODEGEN_RISCV32 ++ if (trace) TraceRegWr(getRegister(rvc_rs1s_reg()), WORD); ++# endif ++ } ++ inline void set_rvc_rs2(sreg_t value, bool trace = true) { ++ setRegister(rvc_rs2_reg(), value); ++# if JS_CODEGEN_RISCV64 ++ if (trace) TraceRegWr(getRegister(rvc_rs2_reg()), DWORD); ++# elif JS_CODEGEN_RISCV32 ++ if (trace) TraceRegWr(getRegister(rvc_rs2_reg()), WORD); ++# endif ++ } ++ inline void set_rvc_drd(double value, bool trace = true) { ++ setFpuRegisterDouble(rvc_rd_reg(), value); ++ if (trace) TraceRegWr(getFpuRegister(rvc_rd_reg()), DOUBLE); ++ } ++ inline void set_rvc_drd(Float64 value, bool trace = true) { ++ setFpuRegisterDouble(rvc_rd_reg(), value); ++ if (trace) TraceRegWr(getFpuRegister(rvc_rd_reg()), DOUBLE); ++ } ++ inline void set_rvc_frd(Float32 value, bool trace = true) { ++ setFpuRegisterFloat(rvc_rd_reg(), value); ++ if (trace) TraceRegWr(getFpuRegister(rvc_rd_reg()), DOUBLE); ++ } ++ inline void set_rvc_rs2s(sreg_t value, bool trace = true) { ++ setRegister(rvc_rs2s_reg(), value); ++# if JS_CODEGEN_RISCV64 ++ if (trace) TraceRegWr(getRegister(rvc_rs2s_reg()), DWORD); ++# elif JS_CODEGEN_RISCV32 ++ if (trace) TraceRegWr(getRegister(rvc_rs2s_reg()), WORD); ++# endif ++ } ++ inline void set_rvc_drs2s(double value, bool trace = true) { ++ setFpuRegisterDouble(rvc_rs2s_reg(), value); ++ if (trace) TraceRegWr(getFpuRegister(rvc_rs2s_reg()), DOUBLE); ++ } ++ inline void set_rvc_drs2s(Float64 value, bool trace = true) { ++ setFpuRegisterDouble(rvc_rs2s_reg(), value); ++ if (trace) TraceRegWr(getFpuRegister(rvc_rs2s_reg()), DOUBLE); ++ } ++ ++ inline void set_rvc_frs2s(Float32 value, bool trace = true) { ++ setFpuRegisterFloat(rvc_rs2s_reg(), value); ++ if (trace) TraceRegWr(getFpuRegister(rvc_rs2s_reg()), FLOAT); ++ } ++ ++ uint32_t get_dynamic_rounding_mode() { return read_csr_value(csr_frm); } ++ ++ // helper functions to read/write/set/clear CRC values/bits ++ uint32_t read_csr_value(uint32_t csr) { ++ switch (csr) { ++ case csr_fflags: // Floating-Point Accrued Exceptions (RW) ++ return (FCSR_ & kFcsrFlagsMask); ++ case csr_frm: // Floating-Point Dynamic Rounding Mode (RW) ++ return (FCSR_ & kFcsrFrmMask) >> kFcsrFrmShift; ++ case csr_fcsr: // Floating-Point Control and Status Register (RW) ++ return (FCSR_ & kFcsrMask); ++ default: ++ MOZ_CRASH("UNIMPLEMENTED"); ++ } ++ } ++ ++ void write_csr_value(uint32_t csr, reg_t val) { ++ uint32_t value = (uint32_t)val; ++ switch (csr) { ++ case csr_fflags: // Floating-Point Accrued Exceptions (RW) ++ MOZ_ASSERT(value <= ((1 << kFcsrFlagsBits) - 1)); ++ FCSR_ = (FCSR_ & (~kFcsrFlagsMask)) | value; ++ break; ++ case csr_frm: // Floating-Point Dynamic Rounding Mode (RW) ++ MOZ_ASSERT(value <= ((1 << kFcsrFrmBits) - 1)); ++ FCSR_ = (FCSR_ & (~kFcsrFrmMask)) | (value << kFcsrFrmShift); ++ break; ++ case csr_fcsr: // Floating-Point Control and Status Register (RW) ++ MOZ_ASSERT(value <= ((1 << kFcsrBits) - 1)); ++ FCSR_ = (FCSR_ & (~kFcsrMask)) | value; ++ break; ++ default: ++ MOZ_CRASH("UNIMPLEMENTED"); ++ } ++ } ++ ++ void set_csr_bits(uint32_t csr, reg_t val) { ++ uint32_t value = (uint32_t)val; ++ switch (csr) { ++ case csr_fflags: // Floating-Point Accrued Exceptions (RW) ++ MOZ_ASSERT(value <= ((1 << kFcsrFlagsBits) - 1)); ++ FCSR_ = FCSR_ | value; ++ break; ++ case csr_frm: // Floating-Point Dynamic Rounding Mode (RW) ++ MOZ_ASSERT(value <= ((1 << kFcsrFrmBits) - 1)); ++ FCSR_ = FCSR_ | (value << kFcsrFrmShift); ++ break; ++ case csr_fcsr: // Floating-Point Control and Status Register (RW) ++ MOZ_ASSERT(value <= ((1 << kFcsrBits) - 1)); ++ FCSR_ = FCSR_ | value; ++ break; ++ default: ++ MOZ_CRASH("UNIMPLEMENTED"); ++ } ++ } ++ ++ void clear_csr_bits(uint32_t csr, reg_t val) { ++ uint32_t value = (uint32_t)val; ++ switch (csr) { ++ case csr_fflags: // Floating-Point Accrued Exceptions (RW) ++ MOZ_ASSERT(value <= ((1 << kFcsrFlagsBits) - 1)); ++ FCSR_ = FCSR_ & (~value); ++ break; ++ case csr_frm: // Floating-Point Dynamic Rounding Mode (RW) ++ MOZ_ASSERT(value <= ((1 << kFcsrFrmBits) - 1)); ++ FCSR_ = FCSR_ & (~(value << kFcsrFrmShift)); ++ break; ++ case csr_fcsr: // Floating-Point Control and Status Register (RW) ++ MOZ_ASSERT(value <= ((1 << kFcsrBits) - 1)); ++ FCSR_ = FCSR_ & (~value); ++ break; ++ default: ++ MOZ_CRASH("UNIMPLEMENTED"); ++ } ++ } ++ ++ bool test_fflags_bits(uint32_t mask) { ++ return (FCSR_ & kFcsrFlagsMask & mask) != 0; ++ } ++ ++ void set_fflags(uint32_t flags) { set_csr_bits(csr_fflags, flags); } ++ void clear_fflags(int32_t flags) { clear_csr_bits(csr_fflags, flags); } ++ ++ float RoundF2FHelper(float input_val, int rmode); ++ double RoundF2FHelper(double input_val, int rmode); ++ template ++ I_TYPE RoundF2IHelper(F_TYPE original, int rmode); ++ ++ template ++ T FMaxMinHelper(T a, T b, MaxMinKind kind); ++ ++ template ++ bool CompareFHelper(T input1, T input2, FPUCondition cc); ++ ++ template ++ T get_pc_as() const { ++ return reinterpret_cast(get_pc()); ++ } ++ ++ void enable_single_stepping(SingleStepCallback cb, void* arg); ++ void disable_single_stepping(); ++ ++ // Accessor to the internal simulator stack area. ++ uintptr_t stackLimit() const; ++ bool overRecursed(uintptr_t newsp = 0) const; ++ bool overRecursedWithExtra(uint32_t extra) const; ++ ++ // Executes MIPS instructions until the PC reaches end_sim_pc. ++ template ++ void execute(); ++ ++ // Sets up the simulator state and grabs the result on return. ++ int64_t call(uint8_t* entry, int argument_count, ...); ++ ++ // Push an address onto the JS stack. ++ uintptr_t pushAddress(uintptr_t address); ++ ++ // Pop an address from the JS stack. ++ uintptr_t popAddress(); ++ ++ // Debugger input. ++ void setLastDebuggerInput(char* input); ++ char* lastDebuggerInput() { return lastDebuggerInput_; } ++ ++ // Returns true if pc register contains one of the 'SpecialValues' defined ++ // below (bad_ra, end_sim_pc). ++ bool has_bad_pc() const; ++ ++ private: ++ enum SpecialValues { ++ // Known bad pc value to ensure that the simulator does not execute ++ // without being properly setup. ++ bad_ra = -1, ++ // A pc value used to signal the simulator to stop execution. Generally ++ // the ra is set to this value on transition from native C code to ++ // simulated execution, so that the simulator can "return" to the native ++ // C code. ++ end_sim_pc = -2, ++ // Unpredictable value. ++ Unpredictable = 0xbadbeaf ++ }; ++ ++ bool init(); ++ ++ // Unsupported instructions use Format to print an error and stop execution. ++ void format(SimInstruction* instr, const char* format); ++ ++ // Read and write memory. ++ // RISCV Memory read/write methods ++ template ++ T ReadMem(sreg_t addr, Instruction* instr); ++ template ++ void WriteMem(sreg_t addr, T value, Instruction* instr); ++ template ++ T amo(sreg_t addr, OP f, Instruction* instr, TraceType t) { ++ auto lhs = ReadMem(addr, instr); ++ // TODO(RISCV): trace memory read for AMO ++ WriteMem(addr, (T)f(lhs), instr); ++ return lhs; ++ } ++ ++ inline int32_t loadLinkedW(uint64_t addr, SimInstruction* instr); ++ inline int storeConditionalW(uint64_t addr, int32_t value, ++ SimInstruction* instr); ++ ++ inline int64_t loadLinkedD(uint64_t addr, SimInstruction* instr); ++ inline int storeConditionalD(uint64_t addr, int64_t value, ++ SimInstruction* instr); ++ ++ // Used for breakpoints and traps. ++ void SoftwareInterrupt(); ++ ++ // Stop helper functions. ++ bool isWatchpoint(uint32_t code); ++ bool IsTracepoint(uint32_t code); ++ void printWatchpoint(uint32_t code); ++ void handleStop(uint32_t code); ++ bool isStopInstruction(SimInstruction* instr); ++ bool isEnabledStop(uint32_t code); ++ void enableStop(uint32_t code); ++ void disableStop(uint32_t code); ++ void increaseStopCounter(uint32_t code); ++ void printStopInfo(uint32_t code); ++ ++ // Simulator breakpoints. ++ struct Breakpoint { ++ SimInstruction* location; ++ bool enabled; ++ bool is_tbreak; ++ }; ++ std::vector breakpoints_; ++ void SetBreakpoint(SimInstruction* breakpoint, bool is_tbreak); ++ void ListBreakpoints(); ++ void CheckBreakpoints(); ++ ++ JS::ProfilingFrameIterator::RegisterState registerState(); ++ ++ // Handle any wasm faults, returning true if the fault was handled. ++ // This method is rather hot so inline the normal (no-wasm) case. ++ bool MOZ_ALWAYS_INLINE handleWasmSegFault(uint64_t addr, unsigned numBytes) { ++ if (MOZ_LIKELY(!js::wasm::CodeExists)) { ++ return false; ++ } ++ ++ uint8_t* newPC; ++ if (!js::wasm::MemoryAccessTraps(registerState(), (uint8_t*)addr, numBytes, ++ &newPC)) { ++ return false; ++ } ++ ++ LLBit_ = false; ++ set_pc(int64_t(newPC)); ++ return true; ++ } ++ ++ // Executes one instruction. ++ void InstructionDecode(Instruction* instr); ++ ++ // ICache. ++ // static void CheckICache(base::CustomMatcherHashMap* i_cache, ++ // Instruction* instr); ++ // static void FlushOnePage(base::CustomMatcherHashMap* i_cache, intptr_t ++ // start, ++ // size_t size); ++ // static CachePage* GetCachePage(base::CustomMatcherHashMap* i_cache, ++ // void* page); ++ template ++ inline T CanonicalizeFPUOpFMA(Func fn, T dst, T src1, T src2) { ++ static_assert(std::is_floating_point::value); ++ auto alu_out = fn(dst, src1, src2); ++ // if any input or result is NaN, the result is quiet_NaN ++ if (std::isnan(alu_out) || std::isnan(src1) || std::isnan(src2) || ++ std::isnan(dst)) { ++ // signaling_nan sets kInvalidOperation bit ++ if (isSnan(alu_out) || isSnan(src1) || isSnan(src2) || isSnan(dst)) ++ set_fflags(kInvalidOperation); ++ alu_out = std::numeric_limits::quiet_NaN(); ++ } ++ return alu_out; ++ } ++ ++ template ++ inline T CanonicalizeFPUOp3(Func fn) { ++ static_assert(std::is_floating_point::value); ++ T src1 = std::is_same::value ? frs1() : drs1(); ++ T src2 = std::is_same::value ? frs2() : drs2(); ++ T src3 = std::is_same::value ? frs3() : drs3(); ++ auto alu_out = fn(src1, src2, src3); ++ // if any input or result is NaN, the result is quiet_NaN ++ if (std::isnan(alu_out) || std::isnan(src1) || std::isnan(src2) || ++ std::isnan(src3)) { ++ // signaling_nan sets kInvalidOperation bit ++ if (isSnan(alu_out) || isSnan(src1) || isSnan(src2) || isSnan(src3)) ++ set_fflags(kInvalidOperation); ++ alu_out = std::numeric_limits::quiet_NaN(); ++ } ++ return alu_out; ++ } ++ ++ template ++ inline T CanonicalizeFPUOp2(Func fn) { ++ static_assert(std::is_floating_point::value); ++ T src1 = std::is_same::value ? frs1() : drs1(); ++ T src2 = std::is_same::value ? frs2() : drs2(); ++ auto alu_out = fn(src1, src2); ++ // if any input or result is NaN, the result is quiet_NaN ++ if (std::isnan(alu_out) || std::isnan(src1) || std::isnan(src2)) { ++ // signaling_nan sets kInvalidOperation bit ++ if (isSnan(alu_out) || isSnan(src1) || isSnan(src2)) ++ set_fflags(kInvalidOperation); ++ alu_out = std::numeric_limits::quiet_NaN(); ++ } ++ return alu_out; ++ } ++ ++ template ++ inline T CanonicalizeFPUOp1(Func fn) { ++ static_assert(std::is_floating_point::value); ++ T src1 = std::is_same::value ? frs1() : drs1(); ++ auto alu_out = fn(src1); ++ // if any input or result is NaN, the result is quiet_NaN ++ if (std::isnan(alu_out) || std::isnan(src1)) { ++ // signaling_nan sets kInvalidOperation bit ++ if (isSnan(alu_out) || isSnan(src1)) set_fflags(kInvalidOperation); ++ alu_out = std::numeric_limits::quiet_NaN(); ++ } ++ return alu_out; ++ } ++ ++ template ++ inline float CanonicalizeDoubleToFloatOperation(Func fn) { ++ float alu_out = fn(drs1()); ++ if (std::isnan(alu_out) || std::isnan(drs1())) ++ alu_out = std::numeric_limits::quiet_NaN(); ++ return alu_out; ++ } ++ ++ template ++ inline float CanonicalizeDoubleToFloatOperation(Func fn, double frs) { ++ float alu_out = fn(frs); ++ if (std::isnan(alu_out) || std::isnan(drs1())) ++ alu_out = std::numeric_limits::quiet_NaN(); ++ return alu_out; ++ } ++ ++ template ++ inline float CanonicalizeFloatToDoubleOperation(Func fn, float frs) { ++ double alu_out = fn(frs); ++ if (std::isnan(alu_out) || std::isnan(frs1())) ++ alu_out = std::numeric_limits::quiet_NaN(); ++ return alu_out; ++ } ++ ++ template ++ inline float CanonicalizeFloatToDoubleOperation(Func fn) { ++ double alu_out = fn(frs1()); ++ if (std::isnan(alu_out) || std::isnan(frs1())) ++ alu_out = std::numeric_limits::quiet_NaN(); ++ return alu_out; ++ } ++ ++ public: ++ static int64_t StopSimAt; ++ ++ // Runtime call support. ++ static void* RedirectNativeFunction(void* nativeFunction, ++ ABIFunctionType type); ++ ++ private: ++ enum Exception { ++ none, ++ kIntegerOverflow, ++ kIntegerUnderflow, ++ kDivideByZero, ++ kNumExceptions, ++ // RISCV illegual instruction exception ++ kIllegalInstruction, ++ }; ++ int16_t exceptions[kNumExceptions]; ++ ++ // Exceptions. ++ void SignalException(Exception e); ++ ++ // Handle return value for runtime FP functions. ++ void setCallResultDouble(double result); ++ void setCallResultFloat(float result); ++ void setCallResult(int64_t res); ++ void setCallResult(__int128 res); ++ ++ void callInternal(uint8_t* entry); ++ ++ // Architecture state. ++ // Registers. ++ int64_t registers_[kNumSimuRegisters]; ++ // Coprocessor Registers. ++ int64_t FPUregisters_[kNumFPURegisters]; ++ // FPU control register. ++ uint32_t FCSR_; ++ ++ bool LLBit_; ++ uintptr_t LLAddr_; ++ int64_t lastLLValue_; ++ ++ // Simulator support. ++ char* stack_; ++ uintptr_t stackLimit_; ++ bool pc_modified_; ++ int64_t icount_; ++ int64_t break_count_; ++ ++ // Debugger input. ++ char* lastDebuggerInput_; ++ ++ intptr_t* watch_address_ = nullptr; ++ intptr_t watch_value_ = 0; ++ ++ // Registered breakpoints. ++ SimInstruction* break_pc_; ++ Instr break_instr_; ++ EmbeddedVector trace_buf_; ++ ++ // Single-stepping support ++ bool single_stepping_; ++ SingleStepCallback single_step_callback_; ++ void* single_step_callback_arg_; ++ ++ // A stop is watched if its code is less than kNumOfWatchedStops. ++ // Only watched stops support enabling/disabling and the counter feature. ++ static const uint32_t kNumOfWatchedStops = 256; ++ ++ // Stop is disabled if bit 31 is set. ++ static const uint32_t kStopDisabledBit = 1U << 31; ++ ++ // A stop is enabled, meaning the simulator will stop when meeting the ++ // instruction, if bit 31 of watchedStops_[code].count is unset. ++ // The value watchedStops_[code].count & ~(1 << 31) indicates how many times ++ // the breakpoint was hit or gone through. ++ struct StopCountAndDesc { ++ uint32_t count_; ++ char* desc_; ++ }; ++ StopCountAndDesc watchedStops_[kNumOfWatchedStops]; ++}; ++ ++// Process wide simulator state. ++class SimulatorProcess { ++ friend class Redirection; ++ friend class AutoLockSimulatorCache; ++ ++ private: ++ // ICache checking. ++ struct ICacheHasher { ++ typedef void* Key; ++ typedef void* Lookup; ++ static HashNumber hash(const Lookup& l); ++ static bool match(const Key& k, const Lookup& l); ++ }; ++ ++ public: ++ typedef HashMap ICacheMap; ++ ++ static mozilla::Atomic ++ ICacheCheckingDisableCount; ++ static void FlushICache(void* start, size_t size); ++ ++ static void checkICacheLocked(SimInstruction* instr); ++ ++ static bool initialize() { ++ singleton_ = js_new(); ++ return singleton_; ++ } ++ static void destroy() { ++ js_delete(singleton_); ++ singleton_ = nullptr; ++ } ++ ++ SimulatorProcess(); ++ ~SimulatorProcess(); ++ ++ private: ++ static SimulatorProcess* singleton_; ++ ++ // This lock creates a critical section around 'redirection_' and ++ // 'icache_', which are referenced both by the execution engine ++ // and by the off-thread compiler (see Redirection::Get in the cpp file). ++ Mutex cacheLock_ MOZ_UNANNOTATED; ++ ++ Redirection* redirection_; ++ ICacheMap icache_; ++ ++ public: ++ static ICacheMap& icache() { ++ // Technically we need the lock to access the innards of the ++ // icache, not to take its address, but the latter condition ++ // serves as a useful complement to the former. ++ singleton_->cacheLock_.assertOwnedByCurrentThread(); ++ return singleton_->icache_; ++ } ++ ++ static Redirection* redirection() { ++ singleton_->cacheLock_.assertOwnedByCurrentThread(); ++ return singleton_->redirection_; ++ } ++ ++ static void setRedirection(js::jit::Redirection* redirection) { ++ singleton_->cacheLock_.assertOwnedByCurrentThread(); ++ singleton_->redirection_ = redirection; ++ } ++}; ++ ++} // namespace jit ++} // namespace js ++ ++#endif /* JS_SIMULATOR_MIPS64 */ ++ ++#endif /* jit_riscv64_Simulator_riscv64_h */ +diff --git a/js/src/jit/riscv64/Trampoline-riscv64.cpp b/js/src/jit/riscv64/Trampoline-riscv64.cpp +new file mode 100644 +index 0000000000..48fc19acfb +--- /dev/null ++++ b/js/src/jit/riscv64/Trampoline-riscv64.cpp +@@ -0,0 +1,856 @@ ++/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- ++ * vim: set ts=8 sts=2 et sw=2 tw=80: ++ * 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/Bailouts.h" ++#include "jit/BaselineFrame.h" ++#include "jit/CalleeToken.h" ++#include "jit/JitFrames.h" ++#include "jit/JitRuntime.h" ++#ifdef JS_ION_PERF ++# include "jit/PerfSpewer.h" ++#endif ++#include "jit/riscv64/SharedICRegisters-riscv64.h" ++#include "jit/VMFunctions.h" ++#include "vm/JitActivation.h" // js::jit::JitActivation ++#include "vm/JSContext.h" ++ ++#include "jit/MacroAssembler-inl.h" ++ ++using namespace js; ++using namespace js::jit; ++ ++// This file includes stubs for generating the JIT trampolines when there is no ++// JIT backend, and also includes implementations for assorted random things ++// which can't be implemented in headers. ++ ++// All registers to save and restore. This includes the stack pointer, since we ++// use the ability to reference register values on the stack by index. ++static const LiveRegisterSet AllRegs = ++ LiveRegisterSet(GeneralRegisterSet(Registers::AllMask), ++ FloatRegisterSet(FloatRegisters::AllMask)); ++ ++static void PushBailoutFrame(MacroAssembler& masm, Register spArg) { ++ // Push the frameSize_ stored in ra ++ // See: CodeGeneratorLOONG64::generateOutOfLineCode() ++ masm.push(ra); ++ ++ // Push registers such that we can access them from [base + code]. ++ masm.PushRegsInMask(AllRegs); ++ ++ // Put pointer to BailoutStack as first argument to the Bailout() ++ masm.movePtr(StackPointer, spArg); ++} ++ ++struct EnterJITRegs { ++ double fs11; ++ double fs10; ++ double fs9; ++ double fs8; ++ double fs7; ++ double fs6; ++ double fs5; ++ double fs4; ++ double fs3; ++ double fs2; ++ double fs1; ++ double fs0; ++ ++ // uintptr_t align; ++ ++ // non-volatile registers. ++ uint64_t ra; ++ uint64_t sp; ++ uint64_t fp; ++ uint64_t gp; ++ uint64_t s11; ++ uint64_t s10; ++ uint64_t s9; ++ uint64_t s8; ++ uint64_t s7; ++ uint64_t s6; ++ uint64_t s5; ++ uint64_t s4; ++ uint64_t s3; ++ uint64_t s2; ++ uint64_t s1; ++ // Save reg_vp(a7) on stack, use it after call jit code. ++ uint64_t a7; ++}; ++ ++static void GenerateReturn(MacroAssembler& masm, int returnCode) { ++ MOZ_ASSERT(masm.framePushed() == sizeof(EnterJITRegs)); ++ ++ // Restore non-volatile registers ++ masm.ld(s1, StackPointer, offsetof(EnterJITRegs, s1)); ++ masm.ld(s2, StackPointer, offsetof(EnterJITRegs, s2)); ++ masm.ld(s3, StackPointer, offsetof(EnterJITRegs, s3)); ++ masm.ld(s4, StackPointer, offsetof(EnterJITRegs, s4)); ++ masm.ld(s5, StackPointer, offsetof(EnterJITRegs, s5)); ++ masm.ld(s6, StackPointer, offsetof(EnterJITRegs, s6)); ++ masm.ld(s7, StackPointer, offsetof(EnterJITRegs, s7)); ++ masm.ld(s8, StackPointer, offsetof(EnterJITRegs, s8)); ++ masm.ld(s9, StackPointer, offsetof(EnterJITRegs, s9)); ++ masm.ld(s10, StackPointer, offsetof(EnterJITRegs, s10)); ++ masm.ld(s11, StackPointer, offsetof(EnterJITRegs, s11)); ++ masm.ld(gp, StackPointer, offsetof(EnterJITRegs, gp)); ++ masm.ld(fp, StackPointer, offsetof(EnterJITRegs, fp)); ++ masm.ld(sp, StackPointer, offsetof(EnterJITRegs, sp)); ++ masm.ld(ra, StackPointer, offsetof(EnterJITRegs, ra)); ++ ++ // Restore non-volatile floating point registers ++ masm.fld(fs11, StackPointer, offsetof(EnterJITRegs, fs11)); ++ masm.fld(fs10, StackPointer, offsetof(EnterJITRegs, fs10)); ++ masm.fld(fs9, StackPointer, offsetof(EnterJITRegs, fs9)); ++ masm.fld(fs8, StackPointer, offsetof(EnterJITRegs, fs8)); ++ masm.fld(fs7, StackPointer, offsetof(EnterJITRegs, fs7)); ++ masm.fld(fs6, StackPointer, offsetof(EnterJITRegs, fs6)); ++ masm.fld(fs5, StackPointer, offsetof(EnterJITRegs, fs5)); ++ masm.fld(fs4, StackPointer, offsetof(EnterJITRegs, fs4)); ++ masm.fld(fs3, StackPointer, offsetof(EnterJITRegs, fs3)); ++ masm.fld(fs2, StackPointer, offsetof(EnterJITRegs, fs2)); ++ masm.fld(fs1, StackPointer, offsetof(EnterJITRegs, fs1)); ++ masm.fld(fs0, StackPointer, offsetof(EnterJITRegs, fs0)); ++ ++ masm.freeStack(sizeof(EnterJITRegs)); ++ ++ masm.branch(ra); ++} ++ ++static void GeneratePrologue(MacroAssembler& masm) { ++ masm.reserveStack(sizeof(EnterJITRegs)); ++ ++ masm.sd(s1, StackPointer, offsetof(EnterJITRegs, s1)); ++ masm.sd(s2, StackPointer, offsetof(EnterJITRegs, s2)); ++ masm.sd(s3, StackPointer, offsetof(EnterJITRegs, s3)); ++ masm.sd(s4, StackPointer, offsetof(EnterJITRegs, s4)); ++ masm.sd(s5, StackPointer, offsetof(EnterJITRegs, s5)); ++ masm.sd(s6, StackPointer, offsetof(EnterJITRegs, s6)); ++ masm.sd(s7, StackPointer, offsetof(EnterJITRegs, s7)); ++ masm.sd(s8, StackPointer, offsetof(EnterJITRegs, s8)); ++ masm.sd(s9, StackPointer, offsetof(EnterJITRegs, s9)); ++ masm.sd(s10, StackPointer, offsetof(EnterJITRegs, s10)); ++ masm.sd(s11, StackPointer, offsetof(EnterJITRegs, s11)); ++ masm.sd(gp, StackPointer, offsetof(EnterJITRegs, gp)); ++ masm.sd(fp, StackPointer, offsetof(EnterJITRegs, fp)); ++ masm.sd(sp, StackPointer, offsetof(EnterJITRegs, sp)); ++ masm.sd(ra, StackPointer, offsetof(EnterJITRegs, ra)); ++ masm.sd(a7, StackPointer, offsetof(EnterJITRegs, a7)); ++ ++ masm.fsd(fs11, StackPointer, offsetof(EnterJITRegs, fs11)); ++ masm.fsd(fs10, StackPointer, offsetof(EnterJITRegs, fs10)); ++ masm.fsd(fs9, StackPointer, offsetof(EnterJITRegs, fs9)); ++ masm.fsd(fs8, StackPointer, offsetof(EnterJITRegs, fs8)); ++ masm.fsd(fs7, StackPointer, offsetof(EnterJITRegs, fs7)); ++ masm.fsd(fs6, StackPointer, offsetof(EnterJITRegs, fs6)); ++ masm.fsd(fs5, StackPointer, offsetof(EnterJITRegs, fs5)); ++ masm.fsd(fs4, StackPointer, offsetof(EnterJITRegs, fs4)); ++ masm.fsd(fs3, StackPointer, offsetof(EnterJITRegs, fs3)); ++ masm.fsd(fs2, StackPointer, offsetof(EnterJITRegs, fs2)); ++ masm.fsd(fs1, StackPointer, offsetof(EnterJITRegs, fs1)); ++ masm.fsd(fs0, StackPointer, offsetof(EnterJITRegs, fs0)); ++} ++ ++static void GenerateBailoutThunk(MacroAssembler& masm, Label* bailoutTail) { ++ PushBailoutFrame(masm, a0); ++ ++ // Make space for Bailout's bailoutInfo outparam. ++ masm.reserveStack(sizeof(void*)); ++ masm.movePtr(StackPointer, a1); ++ ++ // Call the bailout function. ++ using Fn = bool (*)(BailoutStack * sp, BaselineBailoutInfo * *info); ++ masm.setupUnalignedABICall(a2); ++ masm.passABIArg(a0); ++ masm.passABIArg(a1); ++ masm.callWithABI(MoveOp::GENERAL, ++ CheckUnsafeCallWithABI::DontCheckOther); ++ ++ // Get the bailoutInfo outparam. ++ masm.pop(a2); ++ ++ // Remove both the bailout frame and the topmost Ion frame's stack. ++ masm.moveToStackPtr(FramePointer); ++ ++ // Jump to shared bailout tail. The BailoutInfo pointer has to be in a2. ++ masm.jump(bailoutTail); ++} ++ ++// Generates a trampoline for calling Jit compiled code from a C++ function. ++// The trampoline use the EnterJitCode signature, with the standard x64 fastcall ++// calling convention. ++void JitRuntime::generateEnterJIT(JSContext* cx, MacroAssembler& masm) { ++ AutoCreatedBy acb(masm, "JitRuntime::generateEnterJIT"); ++ ++ enterJITOffset_ = startTrampolineCode(masm); ++ ++ const Register reg_code = IntArgReg0; ++ const Register reg_argc = IntArgReg1; ++ const Register reg_argv = IntArgReg2; ++ const mozilla::DebugOnly reg_frame = IntArgReg3; ++ const Register reg_token = IntArgReg4; ++ const Register reg_chain = IntArgReg5; ++ const Register reg_values = IntArgReg6; ++ const Register reg_vp = IntArgReg7; ++ ++ MOZ_ASSERT(OsrFrameReg == reg_frame); ++ ++ GeneratePrologue(masm); ++ ++ // Save stack pointer as baseline frame. ++ masm.movePtr(StackPointer, FramePointer); ++ ++ // Load the number of actual arguments into s3. ++ masm.unboxInt32(Address(reg_vp, 0), s3); ++ ++ /*************************************************************** ++ Loop over argv vector, push arguments onto stack in reverse order ++ ***************************************************************/ ++ ++ // if we are constructing, that also needs to include newTarget ++ JitSpew(JitSpew_Codegen, "__Line__: %d", __LINE__); ++ { ++ Label noNewTarget; ++ masm.branchTest32(Assembler::Zero, reg_token, ++ Imm32(CalleeToken_FunctionConstructing), &noNewTarget); ++ ++ masm.add32(Imm32(1), reg_argc); ++ ++ masm.bind(&noNewTarget); ++ } ++ JitSpew(JitSpew_Codegen, "__Line__: %d", __LINE__); ++ // Make stack algined ++ masm.ma_and(s2, reg_argc, Imm32(1)); ++ masm.ma_sub64(s1, zero, Imm32(sizeof(Value))); ++ Label no_zero; ++ masm.ma_branch(&no_zero, Assembler::Condition::Equal, s2, Operand(0)); ++ masm.mv(s1, zero); ++ masm.bind(&no_zero); ++ masm.ma_add64(StackPointer, StackPointer, s1); ++ ++ masm.slli(s2, reg_argc, 3); // Value* argv ++ masm.addPtr(reg_argv, s2); // s2 = &argv[argc] ++ JitSpew(JitSpew_Codegen, "__Line__: %d", __LINE__); ++ // Loop over arguments, copying them from an unknown buffer onto the Ion ++ // stack so they can be accessed from JIT'ed code. ++ Label header, footer; ++ // If there aren't any arguments, don't do anything ++ masm.ma_b(s2, reg_argv, &footer, Assembler::BelowOrEqual, ShortJump); ++ { ++ masm.bind(&header); ++ ++ masm.subPtr(Imm32(sizeof(Value)), s2); ++ masm.subPtr(Imm32(sizeof(Value)), StackPointer); ++ ++ ValueOperand value = ValueOperand(s6); ++ masm.loadValue(Address(s2, 0), value); ++ masm.storeValue(value, Address(StackPointer, 0)); ++ ++ masm.ma_b(s2, reg_argv, &header, Assembler::Above, ShortJump); ++ } ++ masm.bind(&footer); ++ JitSpew(JitSpew_Codegen, "__Line__: %d", __LINE__); ++ masm.push(reg_token); ++ masm.pushFrameDescriptorForJitCall(FrameType::CppToJSJit, s3, s3); ++ ++ CodeLabel returnLabel; ++ Label oomReturnLabel; ++ { ++ // Handle Interpreter -> Baseline OSR. ++ AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); ++ MOZ_ASSERT(!regs.has(FramePointer)); ++ regs.take(OsrFrameReg); ++ regs.take(reg_code); ++ MOZ_ASSERT(!regs.has(ReturnReg), "ReturnReg matches reg_code"); ++ ++ Label notOsr; ++ masm.ma_b(OsrFrameReg, OsrFrameReg, ¬Osr, Assembler::Zero, ShortJump); ++ ++ Register numStackValues = reg_values; ++ regs.take(numStackValues); ++ Register scratch = regs.takeAny(); ++ ++ // Push return address. ++ masm.subPtr(Imm32(sizeof(uintptr_t)), StackPointer); ++ masm.ma_li(scratch, &returnLabel); ++ masm.storePtr(scratch, Address(StackPointer, 0)); ++ ++ // Push previous frame pointer. ++ masm.subPtr(Imm32(sizeof(uintptr_t)), StackPointer); ++ masm.storePtr(FramePointer, Address(StackPointer, 0)); ++ ++ // Reserve frame. ++ Register framePtr = FramePointer; ++ masm.movePtr(StackPointer, framePtr); ++ masm.subPtr(Imm32(BaselineFrame::Size()), StackPointer); ++ ++ Register framePtrScratch = regs.takeAny(); ++ masm.movePtr(sp, framePtrScratch); ++ ++ // Reserve space for locals and stack values. ++ masm.slli(scratch, numStackValues, 3); ++ masm.subPtr(scratch, StackPointer); ++ ++ // Enter exit frame. ++ masm.reserveStack(3 * sizeof(uintptr_t)); ++ masm.storePtr( ++ ImmWord(MakeFrameDescriptor(FrameType::BaselineJS)), ++ Address(StackPointer, 2 * sizeof(uintptr_t))); // Frame descriptor ++ masm.storePtr( ++ zero, Address(StackPointer, sizeof(uintptr_t))); // fake return address ++ masm.storePtr(FramePointer, Address(StackPointer, 0)); ++ ++ // No GC things to mark, push a bare token. ++ masm.loadJSContext(scratch); ++ masm.enterFakeExitFrame(scratch, scratch, ExitFrameType::Bare); ++ ++ masm.reserveStack(2 * sizeof(uintptr_t)); ++ masm.storePtr(framePtr, ++ Address(StackPointer, sizeof(uintptr_t))); // BaselineFrame ++ masm.storePtr(reg_code, Address(StackPointer, 0)); // jitcode ++ ++ using Fn = bool (*)(BaselineFrame * frame, InterpreterFrame * interpFrame, ++ uint32_t numStackValues); ++ masm.setupUnalignedABICall(scratch); ++ masm.passABIArg(framePtrScratch); // BaselineFrame ++ masm.passABIArg(OsrFrameReg); // InterpreterFrame ++ masm.passABIArg(numStackValues); ++ masm.callWithABI( ++ MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckHasExitFrame); ++ ++ regs.add(OsrFrameReg); ++ Register jitcode = regs.takeAny(); ++ masm.loadPtr(Address(StackPointer, 0), jitcode); ++ masm.loadPtr(Address(StackPointer, sizeof(uintptr_t)), framePtr); ++ masm.freeStack(2 * sizeof(uintptr_t)); ++ ++ Label error; ++ masm.freeStack(ExitFrameLayout::SizeWithFooter()); ++ masm.branchIfFalseBool(ReturnReg, &error); ++ ++ // If OSR-ing, then emit instrumentation for setting lastProfilerFrame ++ // if profiler instrumentation is enabled. ++ { ++ Label skipProfilingInstrumentation; ++ AbsoluteAddress addressOfEnabled( ++ cx->runtime()->geckoProfiler().addressOfEnabled()); ++ masm.branch32(Assembler::Equal, addressOfEnabled, Imm32(0), ++ &skipProfilingInstrumentation); ++ masm.profilerEnterFrame(framePtr, scratch); ++ masm.bind(&skipProfilingInstrumentation); ++ } ++ ++ masm.jump(jitcode); ++ ++ // OOM: load error value, discard return address and previous frame ++ // pointer and return. ++ masm.bind(&error); ++ masm.movePtr(framePtr, StackPointer); ++ masm.addPtr(Imm32(2 * sizeof(uintptr_t)), StackPointer); ++ masm.moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand); ++ masm.jump(&oomReturnLabel); ++ ++ masm.bind(¬Osr); ++ // Load the scope chain in R1. ++ MOZ_ASSERT(R1.scratchReg() != reg_code); ++ masm.ma_or(R1.scratchReg(), reg_chain, zero); ++ } ++ JitSpew(JitSpew_Codegen, "__Line__: %d", __LINE__); ++ // The call will push the return address and frame pointer on the stack, thus ++ // we check that the stack would be aligned once the call is complete. ++ masm.assertStackAlignment(JitStackAlignment, 2 * sizeof(uintptr_t)); ++ ++ // Call the function with pushing return address to stack. ++ masm.callJitNoProfiler(reg_code); ++ ++ { ++ // Interpreter -> Baseline OSR will return here. ++ masm.bind(&returnLabel); ++ masm.addCodeLabel(returnLabel); ++ masm.bind(&oomReturnLabel); ++ } ++ ++ // Discard arguments and padding. Set sp to the address of the EnterJITRegs ++ // on the stack. ++ masm.mov(FramePointer, StackPointer); ++ ++ // Store the returned value into the vp ++ masm.ld(reg_vp, StackPointer, offsetof(EnterJITRegs, a7)); ++ masm.storeValue(JSReturnOperand, Address(reg_vp, 0)); ++ JitSpew(JitSpew_Codegen, "__Line__: %d", __LINE__); ++ // Restore non-volatile registers and return. ++ GenerateReturn(masm, ShortJump); ++} ++ ++// static ++mozilla::Maybe<::JS::ProfilingFrameIterator::RegisterState> ++JitRuntime::getCppEntryRegisters(JitFrameLayout* frameStackAddress) { ++ return mozilla::Nothing{}; ++} ++ ++void JitRuntime::generateInvalidator(MacroAssembler& masm, Label* bailoutTail) { ++ AutoCreatedBy acb(masm, "JitRuntime::generateInvalidator"); ++ ++ invalidatorOffset_ = startTrampolineCode(masm); ++ ++ // Stack has to be alligned here. If not, we will have to fix it. ++ masm.checkStackAlignment(); ++ ++ // Push registers such that we can access them from [base + code]. ++ masm.PushRegsInMask(AllRegs); ++ ++ // Pass pointer to InvalidationBailoutStack structure. ++ masm.movePtr(StackPointer, a0); ++ ++ // Reserve place for BailoutInfo pointer. Two words to ensure alignment for ++ // setupAlignedABICall. ++ masm.subPtr(Imm32(2 * sizeof(uintptr_t)), StackPointer); ++ // Pass pointer to BailoutInfo ++ masm.movePtr(StackPointer, a1); ++ ++ using Fn = ++ bool (*)(InvalidationBailoutStack * sp, BaselineBailoutInfo * *info); ++ masm.setupAlignedABICall(); ++ masm.passABIArg(a0); ++ masm.passABIArg(a1); ++ masm.callWithABI( ++ MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckOther); ++ ++ masm.pop(a2); ++ ++ // Pop the machine state and the dead frame. ++ masm.moveToStackPtr(FramePointer); ++ ++ // Jump to shared bailout tail. The BailoutInfo pointer has to be in r2. ++ masm.jump(bailoutTail); ++} ++ ++void JitRuntime::generateArgumentsRectifier(MacroAssembler& masm, ++ ArgumentsRectifierKind kind) { ++ // Do not erase the frame pointer in this function. ++ ++ AutoCreatedBy acb(masm, "JitRuntime::generateArgumentsRectifier"); ++ ++ switch (kind) { ++ case ArgumentsRectifierKind::Normal: ++ argumentsRectifierOffset_ = startTrampolineCode(masm); ++ break; ++ case ArgumentsRectifierKind::TrialInlining: ++ trialInliningArgumentsRectifierOffset_ = startTrampolineCode(masm); ++ break; ++ } ++ masm.pushReturnAddress(); ++ // Caller: ++ // [arg2] [arg1] [this] [[argc] [callee] [descr] [raddr]] <- sp ++ ++ // Frame prologue. ++ // ++ // NOTE: if this changes, fix the Baseline bailout code too! ++ // See BaselineStackBuilder::calculatePrevFramePtr and ++ // BaselineStackBuilder::buildRectifierFrame (in BaselineBailouts.cpp). ++ masm.push(FramePointer); ++ masm.mov(StackPointer, FramePointer); ++ ++ // Load argc. ++ masm.loadNumActualArgs(FramePointer, s3); ++ ++ Register numActArgsReg = a6; ++ Register calleeTokenReg = a7; ++ Register numArgsReg = a5; ++ ++ // Load |nformals| into numArgsReg. ++ masm.loadPtr( ++ Address(FramePointer, RectifierFrameLayout::offsetOfCalleeToken()), ++ calleeTokenReg); ++ masm.mov(calleeTokenReg, numArgsReg); ++ masm.andPtr(Imm32(uint32_t(CalleeTokenMask)), numArgsReg); ++ masm.loadFunctionArgCount(numArgsReg, numArgsReg); ++ ++ // Stash another copy in t3, since we are going to do destructive operations ++ // on numArgsReg ++ masm.mov(numArgsReg, t3); ++ ++ static_assert( ++ CalleeToken_FunctionConstructing == 1, ++ "Ensure that we can use the constructing bit to count the value"); ++ masm.mov(calleeTokenReg, t2); ++ masm.ma_and(t2, t2, Imm32(uint32_t(CalleeToken_FunctionConstructing))); ++ ++ // Including |this|, and |new.target|, there are (|nformals| + 1 + ++ // isConstructing) arguments to push to the stack. Then we push a ++ // JitFrameLayout. We compute the padding expressed in the number of extra ++ // |undefined| values to push on the stack. ++ static_assert( ++ sizeof(JitFrameLayout) % JitStackAlignment == 0, ++ "No need to consider the JitFrameLayout for aligning the stack"); ++ static_assert( ++ JitStackAlignment % sizeof(Value) == 0, ++ "Ensure that we can pad the stack by pushing extra UndefinedValue"); ++ ++ MOZ_ASSERT(mozilla::IsPowerOfTwo(JitStackValueAlignment)); ++ masm.add32( ++ Imm32(JitStackValueAlignment - 1 /* for padding */ + 1 /* for |this| */), ++ numArgsReg); ++ masm.add32(t2, numArgsReg); ++ masm.and32(Imm32(~(JitStackValueAlignment - 1)), numArgsReg); ++ ++ // Load the number of |undefined|s to push into t1. Subtract 1 for |this|. ++ masm.ma_sub64(t1, numArgsReg, s3); ++ masm.sub32(Imm32(1), t1); ++ ++ // Caller: ++ // [arg2] [arg1] [this] [ [argc] [callee] [descr] [raddr] ] <- sp ++ // '--- s3 ----' ++ // ++ // Rectifier frame: ++ // [fp'] [undef] [undef] [undef] [arg2] [arg1] [this] [ [argc] [callee] ++ // [descr] [raddr] ] ++ // '-------- t1 ---------' '--- s3 ----' ++ ++ // Copy number of actual arguments into numActArgsReg. ++ masm.mov(s3, numActArgsReg); ++ ++ masm.moveValue(UndefinedValue(), ValueOperand(t0)); ++ ++ // Push undefined. (including the padding) ++ { ++ Label undefLoopTop; ++ ++ masm.bind(&undefLoopTop); ++ masm.sub32(Imm32(1), t1); ++ masm.subPtr(Imm32(sizeof(Value)), StackPointer); ++ masm.storeValue(ValueOperand(t0), Address(StackPointer, 0)); ++ ++ masm.ma_b(t1, t1, &undefLoopTop, Assembler::NonZero, ShortJump); ++ } ++ ++ // Get the topmost argument. ++ static_assert(sizeof(Value) == 8, "TimesEight is used to skip arguments"); ++ ++ // Get the topmost argument. ++ masm.slli(t0, s3, 3); // t0 <- nargs * 8 ++ masm.ma_add64(t1, FramePointer, t0); // t1 <- fp(saved sp) + nargs * 8 ++ masm.addPtr(Imm32(sizeof(RectifierFrameLayout)), t1); ++ ++ // Push arguments, |nargs| + 1 times (to include |this|). ++ masm.addPtr(Imm32(1), s3); ++ { ++ Label copyLoopTop; ++ ++ masm.bind(©LoopTop); ++ masm.sub32(Imm32(1), s3); ++ masm.subPtr(Imm32(sizeof(Value)), StackPointer); ++ masm.loadValue(Address(t1, 0), ValueOperand(t0)); ++ masm.storeValue(ValueOperand(t0), Address(StackPointer, 0)); ++ masm.subPtr(Imm32(sizeof(Value)), t1); ++ ++ masm.ma_b(s3, s3, ©LoopTop, Assembler::NonZero, ShortJump); ++ } ++ ++ // if constructing, copy newTarget ++ { ++ Label notConstructing; ++ ++ masm.branchTest32(Assembler::Zero, calleeTokenReg, ++ Imm32(CalleeToken_FunctionConstructing), ++ ¬Constructing); ++ ++ // thisFrame[numFormals] = prevFrame[argc] ++ ValueOperand newTarget(t0); ++ ++ // Load vp[argc]. Add sizeof(Value) for |this|. ++ BaseIndex newTargetSrc(FramePointer, numActArgsReg, TimesEight, ++ sizeof(RectifierFrameLayout) + sizeof(Value)); ++ masm.loadValue(newTargetSrc, newTarget); ++ ++ // Again, 1 for |this| ++ BaseIndex newTargetDest(StackPointer, t3, TimesEight, sizeof(Value)); ++ masm.storeValue(newTarget, newTargetDest); ++ ++ masm.bind(¬Constructing); ++ } ++ ++ // Caller: ++ // [arg2] [arg1] [this] [ [argc] [callee] [descr] [raddr] ] ++ // ++ // ++ // Rectifier frame: ++ // [fp'] <- fp [undef] [undef] [undef] [arg2] [arg1] [this] <- sp [ [argc] ++ // [callee] [descr] [raddr] ] ++ // ++ ++ // Construct JitFrameLayout. ++ masm.push(calleeTokenReg); ++ masm.pushFrameDescriptorForJitCall(FrameType::Rectifier, numActArgsReg, ++ numActArgsReg); ++ ++ // Call the target function. ++ masm.andPtr(Imm32(uint32_t(CalleeTokenMask)), calleeTokenReg); ++ switch (kind) { ++ case ArgumentsRectifierKind::Normal: ++ masm.loadJitCodeRaw(calleeTokenReg, t1); ++ argumentsRectifierReturnOffset_ = masm.callJitNoProfiler(t1); ++ break; ++ case ArgumentsRectifierKind::TrialInlining: ++ Label noBaselineScript, done; ++ masm.loadBaselineJitCodeRaw(calleeTokenReg, t1, &noBaselineScript); ++ masm.callJitNoProfiler(t1); ++ masm.jump(&done); ++ ++ // See BaselineCacheIRCompiler::emitCallInlinedFunction. ++ masm.bind(&noBaselineScript); ++ masm.loadJitCodeRaw(calleeTokenReg, t1); ++ masm.callJitNoProfiler(t1); ++ masm.bind(&done); ++ break; ++ } ++ ++ masm.mov(FramePointer, StackPointer); ++ masm.pop(FramePointer); ++ masm.ret(); ++} ++ ++void JitRuntime::generateBailoutHandler(MacroAssembler& masm, ++ Label* bailoutTail) { ++ AutoCreatedBy acb(masm, "JitRuntime::generateBailoutHandler"); ++ ++ bailoutHandlerOffset_ = startTrampolineCode(masm); ++ ++ GenerateBailoutThunk(masm, bailoutTail); ++} ++ ++uint32_t JitRuntime::generatePreBarrier(JSContext* cx, MacroAssembler& masm, ++ MIRType type) { ++ AutoCreatedBy acb(masm, "JitRuntime::generatePreBarrier"); ++ ++ uint32_t offset = startTrampolineCode(masm); ++ ++ MOZ_ASSERT(PreBarrierReg == a1); ++ Register temp1 = a0; ++ Register temp2 = a2; ++ Register temp3 = a3; ++ masm.push(temp1); ++ masm.push(temp2); ++ masm.push(temp3); ++ ++ Label noBarrier; ++ masm.emitPreBarrierFastPath(cx->runtime(), type, temp1, temp2, temp3, ++ &noBarrier); ++ ++ // Call into C++ to mark this GC thing. ++ masm.pop(temp3); ++ masm.pop(temp2); ++ masm.pop(temp1); ++ ++ LiveRegisterSet save; ++ save.set() = RegisterSet(GeneralRegisterSet(Registers::VolatileMask), ++ FloatRegisterSet(FloatRegisters::VolatileMask)); ++ masm.push(ra); ++ masm.PushRegsInMask(save); ++ ++ masm.movePtr(ImmPtr(cx->runtime()), a0); ++ ++ masm.setupUnalignedABICall(a2); ++ masm.passABIArg(a0); ++ masm.passABIArg(a1); ++ masm.callWithABI(JitPreWriteBarrier(type)); ++ ++ masm.PopRegsInMask(save); ++ masm.ret(); ++ ++ masm.bind(&noBarrier); ++ masm.pop(temp3); ++ masm.pop(temp2); ++ masm.pop(temp1); ++ masm.abiret(); ++ ++ return offset; ++} ++ ++void JitRuntime::generateBailoutTailStub(MacroAssembler& masm, ++ Label* bailoutTail) { ++ AutoCreatedBy acb(masm, "JitRuntime::generateBailoutTailStub"); ++ ++ masm.bind(bailoutTail); ++ masm.generateBailoutTail(a1, a2); ++} ++ ++bool JitRuntime::generateVMWrapper(JSContext* cx, MacroAssembler& masm, ++ const VMFunctionData& f, DynFn nativeFun, ++ uint32_t* wrapperOffset) { ++ AutoCreatedBy acb(masm, "JitRuntime::generateVMWrapper"); ++ ++ *wrapperOffset = startTrampolineCode(masm); ++ ++ // Avoid conflicts with argument registers while discarding the result after ++ // the function call. ++ AllocatableGeneralRegisterSet regs(Register::Codes::WrapperMask); ++ ++ static_assert( ++ (Register::Codes::VolatileMask & ~Register::Codes::WrapperMask) == 0, ++ "Wrapper register set should be a superset of Volatile register set."); ++ ++ // The context is the first argument; a0 is the first argument register. ++ Register cxreg = a0; ++ regs.take(cxreg); ++ ++ // If it isn't a tail call, then the return address needs to be saved ++ if (f.expectTailCall == NonTailCall) { ++ masm.pushReturnAddress(); ++ } ++ ++ // Push the frame pointer to finish the exit frame, then link it up. ++ masm.Push(FramePointer); ++ masm.loadJSContext(cxreg); ++ masm.enterExitFrame(cxreg, regs.getAny(), &f); ++ ++ // Save the base of the argument set stored on the stack. ++ Register argsBase = InvalidReg; ++ if (f.explicitArgs) { ++ argsBase = t1; // Use temporary register. ++ regs.take(argsBase); ++ masm.ma_add64(argsBase, StackPointer, ++ Imm32(ExitFrameLayout::SizeWithFooter())); ++ } ++ ++ // Reserve space for the outparameter. ++ Register outReg = InvalidReg; ++ switch (f.outParam) { ++ case Type_Value: ++ outReg = regs.takeAny(); ++ masm.reserveStack(sizeof(Value)); ++ masm.movePtr(StackPointer, outReg); ++ break; ++ ++ case Type_Handle: ++ outReg = regs.takeAny(); ++ masm.PushEmptyRooted(f.outParamRootType); ++ masm.movePtr(StackPointer, outReg); ++ break; ++ ++ case Type_Bool: ++ case Type_Int32: ++ outReg = regs.takeAny(); ++ // Reserve 4-byte space to make stack aligned to 8-byte. ++ masm.reserveStack(2 * sizeof(int32_t)); ++ masm.movePtr(StackPointer, outReg); ++ break; ++ ++ case Type_Pointer: ++ outReg = regs.takeAny(); ++ masm.reserveStack(sizeof(uintptr_t)); ++ masm.movePtr(StackPointer, outReg); ++ break; ++ ++ case Type_Double: ++ outReg = regs.takeAny(); ++ masm.reserveStack(sizeof(double)); ++ masm.movePtr(StackPointer, outReg); ++ break; ++ ++ default: ++ MOZ_ASSERT(f.outParam == Type_Void); ++ break; ++ } ++ ++ masm.setupUnalignedABICall(regs.getAny()); ++ masm.passABIArg(cxreg); ++ ++ size_t argDisp = 0; ++ ++ // Copy any arguments. ++ for (uint32_t explicitArg = 0; explicitArg < f.explicitArgs; explicitArg++) { ++ switch (f.argProperties(explicitArg)) { ++ case VMFunctionData::WordByValue: ++ if (f.argPassedInFloatReg(explicitArg)) { ++ masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::DOUBLE); ++ } else { ++ masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::GENERAL); ++ } ++ argDisp += sizeof(void*); ++ break; ++ case VMFunctionData::WordByRef: ++ masm.passABIArg( ++ MoveOperand(argsBase, argDisp, MoveOperand::EFFECTIVE_ADDRESS), ++ MoveOp::GENERAL); ++ argDisp += sizeof(void*); ++ break; ++ case VMFunctionData::DoubleByValue: ++ case VMFunctionData::DoubleByRef: ++ MOZ_CRASH( ++ "NYI: LOONG64 callVM should not be used with 128bits values."); ++ break; ++ } ++ } ++ ++ // Copy the implicit outparam, if any. ++ if (InvalidReg != outReg) { ++ masm.passABIArg(outReg); ++ } ++ ++ masm.callWithABI(nativeFun, MoveOp::GENERAL, ++ CheckUnsafeCallWithABI::DontCheckHasExitFrame); ++ ++ // Test for failure. ++ switch (f.failType()) { ++ case Type_Cell: ++ masm.branchTestPtr(Assembler::Zero, a0, a0, masm.failureLabel()); ++ break; ++ case Type_Bool: ++ // Called functions return bools, which are 0/false and non-zero/true ++ masm.branchIfFalseBool(a0, masm.failureLabel()); ++ break; ++ case Type_Void: ++ break; ++ default: ++ MOZ_CRASH("unknown failure kind"); ++ } ++ ++ // Load the outparam and free any allocated stack. ++ switch (f.outParam) { ++ case Type_Handle: ++ masm.popRooted(f.outParamRootType, ReturnReg, JSReturnOperand); ++ break; ++ ++ case Type_Value: ++ masm.loadValue(Address(StackPointer, 0), JSReturnOperand); ++ masm.freeStack(sizeof(Value)); ++ break; ++ ++ case Type_Int32: ++ masm.load32(Address(StackPointer, 0), ReturnReg); ++ masm.freeStack(2 * sizeof(int32_t)); ++ break; ++ ++ case Type_Pointer: ++ masm.loadPtr(Address(StackPointer, 0), ReturnReg); ++ masm.freeStack(sizeof(uintptr_t)); ++ break; ++ ++ case Type_Bool: ++ masm.load8ZeroExtend(Address(StackPointer, 0), ReturnReg); ++ masm.freeStack(2 * sizeof(int32_t)); ++ break; ++ ++ case Type_Double: ++ masm.fld(ReturnDoubleReg, StackPointer, 0); ++ masm.freeStack(sizeof(double)); ++ break; ++ ++ default: ++ MOZ_ASSERT(f.outParam == Type_Void); ++ break; ++ } ++ ++ // Pop ExitFooterFrame and the frame pointer. ++ masm.leaveExitFrame(sizeof(void*)); ++ ++ // Return. Subtract sizeof(void*) for the frame pointer. ++ masm.retn(Imm32(sizeof(ExitFrameLayout) - sizeof(void*) + ++ f.explicitStackSlots() * sizeof(void*) + ++ f.extraValuesToPop * sizeof(Value))); ++ ++ return true; ++} +diff --git a/js/src/jit/riscv64/constant/Base-constant-riscv.cpp b/js/src/jit/riscv64/constant/Base-constant-riscv.cpp +new file mode 100644 +index 0000000000..9658689775 +--- /dev/null ++++ b/js/src/jit/riscv64/constant/Base-constant-riscv.cpp +@@ -0,0 +1,247 @@ ++// Copyright 2021 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++#include "jit/riscv64/constant/Base-constant-riscv.h" ++ ++#include "mozilla/Assertions.h" ++#include "mozilla/Types.h" ++ ++#include ++ ++#include "jit/riscv64/constant/Constant-riscv-c.h" ++#include "jit/riscv64/constant/Constant-riscv-d.h" ++#include "jit/riscv64/constant/Constant-riscv-f.h" ++#include "jit/riscv64/constant/Constant-riscv-i.h" ++#include "jit/riscv64/constant/Constant-riscv-m.h" ++#include "jit/riscv64/constant/Constant-riscv-v.h" ++#include "jit/riscv64/constant/Constant-riscv-zicsr.h" ++#include "jit/riscv64/constant/Constant-riscv-zifencei.h" ++#include "jit/riscv64/Simulator-riscv64.h" ++namespace js { ++namespace jit { ++ ++int32_t ImmBranchMaxForwardOffset(OffsetSize bits) { ++ return (1 << (bits - 1)) - 1; ++} ++ ++bool InstructionBase::IsShortInstruction() const { ++ uint8_t FirstByte = *reinterpret_cast(this); ++ return (FirstByte & 0x03) <= C2; ++} ++ ++template ++int InstructionGetters::RvcRdValue() const { ++ MOZ_ASSERT(this->IsShortInstruction()); ++ return this->Bits(kRvcRdShift + kRvcRdBits - 1, kRvcRdShift); ++} ++ ++template ++int InstructionGetters::RvcRs2Value() const { ++ MOZ_ASSERT(this->IsShortInstruction()); ++ return this->Bits(kRvcRs2Shift + kRvcRs2Bits - 1, kRvcRs2Shift); ++} ++ ++template ++int InstructionGetters::RvcRs1sValue() const { ++ MOZ_ASSERT(this->IsShortInstruction()); ++ return 0b1000 + this->Bits(kRvcRs1sShift + kRvcRs1sBits - 1, kRvcRs1sShift); ++} ++ ++template ++int InstructionGetters::RvcRs2sValue() const { ++ MOZ_ASSERT(this->IsShortInstruction()); ++ return 0b1000 + this->Bits(kRvcRs2sShift + kRvcRs2sBits - 1, kRvcRs2sShift); ++} ++ ++template ++inline int InstructionGetters::RvcFunct6Value() const { ++ MOZ_ASSERT(this->IsShortInstruction()); ++ return this->Bits(kRvcFunct6Shift + kRvcFunct6Bits - 1, kRvcFunct6Shift); ++} ++ ++template ++inline int InstructionGetters::RvcFunct4Value() const { ++ MOZ_ASSERT(this->IsShortInstruction()); ++ return this->Bits(kRvcFunct4Shift + kRvcFunct4Bits - 1, kRvcFunct4Shift); ++} ++ ++template ++inline int InstructionGetters::RvcFunct3Value() const { ++ MOZ_ASSERT(this->IsShortInstruction()); ++ return this->Bits(kRvcFunct3Shift + kRvcFunct3Bits - 1, kRvcFunct3Shift); ++} ++ ++template ++inline int InstructionGetters::RvcFunct2Value() const { ++ MOZ_ASSERT(this->IsShortInstruction()); ++ return this->Bits(kRvcFunct2Shift + kRvcFunct2Bits - 1, kRvcFunct2Shift); ++} ++ ++template ++inline int InstructionGetters::RvcFunct2BValue() const { ++ MOZ_ASSERT(this->IsShortInstruction()); ++ return this->Bits(kRvcFunct2BShift + kRvcFunct2Bits - 1, kRvcFunct2BShift); ++} ++ ++template ++uint32_t InstructionGetters::Rvvzimm() const { ++ if ((this->InstructionBits() & ++ (kBaseOpcodeMask | kFunct3Mask | 0x80000000)) == RO_V_VSETVLI) { ++ uint32_t Bits = this->InstructionBits(); ++ uint32_t zimm = Bits & kRvvZimmMask; ++ return zimm >> kRvvZimmShift; ++ } else { ++ MOZ_ASSERT((this->InstructionBits() & ++ (kBaseOpcodeMask | kFunct3Mask | 0xC0000000)) == RO_V_VSETIVLI); ++ uint32_t Bits = this->InstructionBits(); ++ uint32_t zimm = Bits & kRvvZimmMask; ++ return (zimm >> kRvvZimmShift) & 0x3FF; ++ } ++} ++ ++template ++uint32_t InstructionGetters::Rvvuimm() const { ++ MOZ_ASSERT((this->InstructionBits() & ++ (kBaseOpcodeMask | kFunct3Mask | 0xC0000000)) == RO_V_VSETIVLI); ++ uint32_t Bits = this->InstructionBits(); ++ uint32_t uimm = Bits & kRvvUimmMask; ++ return uimm >> kRvvUimmShift; ++} ++ ++template class InstructionGetters; ++#ifdef JS_SIMULATOR_RISCV64 ++template class InstructionGetters; ++#endif ++ ++OffsetSize InstructionBase::GetOffsetSize() const { ++ if (IsIllegalInstruction()) { ++ MOZ_CRASH("IllegalInstruction"); ++ } ++ if (IsShortInstruction()) { ++ switch (InstructionBits() & kRvcOpcodeMask) { ++ case RO_C_J: ++ return kOffset11; ++ case RO_C_BEQZ: ++ case RO_C_BNEZ: ++ return kOffset9; ++ default: ++ MOZ_CRASH("IllegalInstruction"); ++ } ++ } else { ++ switch (InstructionBits() & kBaseOpcodeMask) { ++ case BRANCH: ++ return kOffset13; ++ case JAL: ++ return kOffset21; ++ default: ++ MOZ_CRASH("IllegalInstruction"); ++ } ++ } ++} ++ ++InstructionBase::Type InstructionBase::InstructionType() const { ++ if (IsIllegalInstruction()) { ++ return kUnsupported; ++ } ++ // RV64C Instruction ++ if (IsShortInstruction()) { ++ switch (InstructionBits() & kRvcOpcodeMask) { ++ case RO_C_ADDI4SPN: ++ return kCIWType; ++ case RO_C_FLD: ++ case RO_C_LW: ++#ifdef JS_CODEGEN_RISCV64 ++ case RO_C_LD: ++#endif ++ return kCLType; ++ case RO_C_FSD: ++ case RO_C_SW: ++#ifdef JS_CODEGEN_RISCV64 ++ case RO_C_SD: ++#endif ++ return kCSType; ++ case RO_C_NOP_ADDI: ++ case RO_C_LI: ++#ifdef JS_CODEGEN_RISCV64 ++ case RO_C_ADDIW: ++#endif ++ case RO_C_LUI_ADD: ++ return kCIType; ++ case RO_C_MISC_ALU: ++ if (Bits(11, 10) != 0b11) ++ return kCBType; ++ else ++ return kCAType; ++ case RO_C_J: ++ return kCJType; ++ case RO_C_BEQZ: ++ case RO_C_BNEZ: ++ return kCBType; ++ case RO_C_SLLI: ++ case RO_C_FLDSP: ++ case RO_C_LWSP: ++#ifdef JS_CODEGEN_RISCV64 ++ case RO_C_LDSP: ++#endif ++ return kCIType; ++ case RO_C_JR_MV_ADD: ++ return kCRType; ++ case RO_C_FSDSP: ++ case RO_C_SWSP: ++#ifdef JS_CODEGEN_RISCV64 ++ case RO_C_SDSP: ++#endif ++ return kCSSType; ++ default: ++ break; ++ } ++ } else { ++ // RISCV routine ++ switch (InstructionBits() & kBaseOpcodeMask) { ++ case LOAD: ++ return kIType; ++ case LOAD_FP: ++ return kIType; ++ case MISC_MEM: ++ return kIType; ++ case OP_IMM: ++ return kIType; ++ case AUIPC: ++ return kUType; ++ case OP_IMM_32: ++ return kIType; ++ case STORE: ++ return kSType; ++ case STORE_FP: ++ return kSType; ++ case AMO: ++ return kRType; ++ case OP: ++ return kRType; ++ case LUI: ++ return kUType; ++ case OP_32: ++ return kRType; ++ case MADD: ++ case MSUB: ++ case NMSUB: ++ case NMADD: ++ return kR4Type; ++ case OP_FP: ++ return kRType; ++ case BRANCH: ++ return kBType; ++ case JALR: ++ return kIType; ++ case JAL: ++ return kJType; ++ case SYSTEM: ++ return kIType; ++ case OP_V: ++ return kVType; ++ } ++ } ++ return kUnsupported; ++} ++} // namespace jit ++} // namespace js +diff --git a/js/src/jit/riscv64/constant/Base-constant-riscv.h b/js/src/jit/riscv64/constant/Base-constant-riscv.h +new file mode 100644 +index 0000000000..929ccd67b5 +--- /dev/null ++++ b/js/src/jit/riscv64/constant/Base-constant-riscv.h +@@ -0,0 +1,1057 @@ ++// Copyright 2022 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++#ifndef jit_riscv64_constant_Base_constant_riscv__h_ ++#define jit_riscv64_constant_Base_constant_riscv__h_ ++namespace js { ++namespace jit { ++ ++// On RISC-V Simulator breakpoints can have different codes: ++// - Breaks between 0 and kMaxWatchpointCode are treated as simple watchpoints, ++// the simulator will run through them and print the registers. ++// - Breaks between kMaxWatchpointCode and kMaxStopCode are treated as stop() ++// instructions (see Assembler::stop()). ++// - Breaks larger than kMaxStopCode are simple breaks, dropping you into the ++// debugger. ++const uint32_t kMaxTracepointCode = 63; ++const uint32_t kMaxWatchpointCode = 31; ++const uint32_t kMaxStopCode = 127; ++const uint32_t kWasmTrapCode = 6; ++static_assert(kMaxWatchpointCode < kMaxStopCode); ++static_assert(kMaxTracepointCode < kMaxStopCode); ++ ++// Debug parameters. ++// ++// For example: ++// ++// __ Debug(TRACE_ENABLE | LOG_TRACE); ++// starts tracing: set v8_flags.trace-sim is true. ++// __ Debug(TRACE_ENABLE | LOG_REGS); ++// PrintAllregs. ++// __ Debug(TRACE_DISABLE | LOG_TRACE); ++// stops tracing: set v8_flags.trace-sim is false. ++const uint32_t kDebuggerTracingDirectivesMask = 0b111 << 3; ++enum DebugParameters : uint32_t { ++ NO_PARAM = 1 << 5, ++ BREAK = 1 << 0, ++ LOG_TRACE = 1 << 1, ++ LOG_REGS = 1 << 2, ++ LOG_ALL = LOG_TRACE, ++ // Trace control. ++ TRACE_ENABLE = 1 << 3 | NO_PARAM, ++ TRACE_DISABLE = 1 << 4 | NO_PARAM, ++}; ++// On RISCV all instructions are 32 bits, except for RVC. ++using Instr = int32_t; ++using ShortInstr = int16_t; ++typedef unsigned char byte; ++// ----- Fields offset and length. ++// RISCV constants ++const int kBaseOpcodeShift = 0; ++const int kBaseOpcodeBits = 7; ++const int kFunct7Shift = 25; ++const int kFunct7Bits = 7; ++const int kFunct5Shift = 27; ++const int kFunct5Bits = 5; ++const int kFunct3Shift = 12; ++const int kFunct3Bits = 3; ++const int kFunct2Shift = 25; ++const int kFunct2Bits = 2; ++const int kRs1Shift = 15; ++const int kRs1Bits = 5; ++const int kVs1Shift = 15; ++const int kVs1Bits = 5; ++const int kVs2Shift = 20; ++const int kVs2Bits = 5; ++const int kVdShift = 7; ++const int kVdBits = 5; ++const int kRs2Shift = 20; ++const int kRs2Bits = 5; ++const int kRs3Shift = 27; ++const int kRs3Bits = 5; ++const int kRdShift = 7; ++const int kRdBits = 5; ++const int kRlShift = 25; ++const int kAqShift = 26; ++const int kImm12Shift = 20; ++const int kImm12Bits = 12; ++const int kImm11Shift = 2; ++const int kImm11Bits = 11; ++const int kShamtShift = 20; ++const int kShamtBits = 5; ++const int kShamtWShift = 20; ++// FIXME: remove this once we have a proper way to handle the wide shift amount ++const int kShamtWBits = 6; ++const int kArithShiftShift = 30; ++const int kImm20Shift = 12; ++const int kImm20Bits = 20; ++const int kCsrShift = 20; ++const int kCsrBits = 12; ++const int kMemOrderBits = 4; ++const int kPredOrderShift = 24; ++const int kSuccOrderShift = 20; ++ ++// for C extension ++const int kRvcFunct4Shift = 12; ++const int kRvcFunct4Bits = 4; ++const int kRvcFunct3Shift = 13; ++const int kRvcFunct3Bits = 3; ++const int kRvcRs1Shift = 7; ++const int kRvcRs1Bits = 5; ++const int kRvcRs2Shift = 2; ++const int kRvcRs2Bits = 5; ++const int kRvcRdShift = 7; ++const int kRvcRdBits = 5; ++const int kRvcRs1sShift = 7; ++const int kRvcRs1sBits = 3; ++const int kRvcRs2sShift = 2; ++const int kRvcRs2sBits = 3; ++const int kRvcFunct2Shift = 5; ++const int kRvcFunct2BShift = 10; ++const int kRvcFunct2Bits = 2; ++const int kRvcFunct6Shift = 10; ++const int kRvcFunct6Bits = 6; ++ ++const uint32_t kRvcOpcodeMask = ++ 0b11 | (((1 << kRvcFunct3Bits) - 1) << kRvcFunct3Shift); ++const uint32_t kRvcFunct3Mask = ++ (((1 << kRvcFunct3Bits) - 1) << kRvcFunct3Shift); ++const uint32_t kRvcFunct4Mask = ++ (((1 << kRvcFunct4Bits) - 1) << kRvcFunct4Shift); ++const uint32_t kRvcFunct6Mask = ++ (((1 << kRvcFunct6Bits) - 1) << kRvcFunct6Shift); ++const uint32_t kRvcFunct2Mask = ++ (((1 << kRvcFunct2Bits) - 1) << kRvcFunct2Shift); ++const uint32_t kRvcFunct2BMask = ++ (((1 << kRvcFunct2Bits) - 1) << kRvcFunct2BShift); ++const uint32_t kCRTypeMask = kRvcOpcodeMask | kRvcFunct4Mask; ++const uint32_t kCSTypeMask = kRvcOpcodeMask | kRvcFunct6Mask; ++const uint32_t kCATypeMask = kRvcOpcodeMask | kRvcFunct6Mask | kRvcFunct2Mask; ++const uint32_t kRvcBImm8Mask = (((1 << 5) - 1) << 2) | (((1 << 3) - 1) << 10); ++ ++// RISCV Instruction bit masks ++const uint32_t kBaseOpcodeMask = ((1 << kBaseOpcodeBits) - 1) ++ << kBaseOpcodeShift; ++const uint32_t kFunct3Mask = ((1 << kFunct3Bits) - 1) << kFunct3Shift; ++const uint32_t kFunct5Mask = ((1 << kFunct5Bits) - 1) << kFunct5Shift; ++const uint32_t kFunct7Mask = ((1 << kFunct7Bits) - 1) << kFunct7Shift; ++const uint32_t kFunct2Mask = 0b11 << kFunct7Shift; ++const uint32_t kRTypeMask = kBaseOpcodeMask | kFunct3Mask | kFunct7Mask; ++const uint32_t kRATypeMask = kBaseOpcodeMask | kFunct3Mask | kFunct5Mask; ++const uint32_t kRFPTypeMask = kBaseOpcodeMask | kFunct7Mask; ++const uint32_t kR4TypeMask = kBaseOpcodeMask | kFunct3Mask | kFunct2Mask; ++const uint32_t kITypeMask = kBaseOpcodeMask | kFunct3Mask; ++const uint32_t kSTypeMask = kBaseOpcodeMask | kFunct3Mask; ++const uint32_t kBTypeMask = kBaseOpcodeMask | kFunct3Mask; ++const uint32_t kUTypeMask = kBaseOpcodeMask; ++const uint32_t kJTypeMask = kBaseOpcodeMask; ++const uint32_t kRs1FieldMask = ((1 << kRs1Bits) - 1) << kRs1Shift; ++const uint32_t kRs2FieldMask = ((1 << kRs2Bits) - 1) << kRs2Shift; ++const uint32_t kRs3FieldMask = ((1 << kRs3Bits) - 1) << kRs3Shift; ++const uint32_t kRdFieldMask = ((1 << kRdBits) - 1) << kRdShift; ++const uint32_t kBImm12Mask = kFunct7Mask | kRdFieldMask; ++const uint32_t kImm20Mask = ((1 << kImm20Bits) - 1) << kImm20Shift; ++const uint32_t kImm12Mask = ((1 << kImm12Bits) - 1) << kImm12Shift; ++const uint32_t kImm11Mask = ((1 << kImm11Bits) - 1) << kImm11Shift; ++const uint32_t kImm31_12Mask = ((1 << 20) - 1) << 12; ++const uint32_t kImm19_0Mask = ((1 << 20) - 1); ++ ++// for RVV extension ++#define RVV_LMUL(V) \ ++ V(m1) \ ++ V(m2) \ ++ V(m4) \ ++ V(m8) \ ++ V(RESERVERD) \ ++ V(mf8) \ ++ V(mf4) \ ++ V(mf2) ++ ++enum Vlmul { ++#define DEFINE_FLAG(name) name, ++ RVV_LMUL(DEFINE_FLAG) ++#undef DEFINE_FLAG ++}; ++ ++#define RVV_SEW(V) \ ++ V(E8) \ ++ V(E16) \ ++ V(E32) \ ++ V(E64) ++ ++#define DEFINE_FLAG(name) name, ++enum VSew { ++ RVV_SEW(DEFINE_FLAG) ++#undef DEFINE_FLAG ++}; ++ ++constexpr int kRvvELEN = 64; ++constexpr int kRvvVLEN = 128; ++constexpr int kRvvSLEN = kRvvVLEN; ++const int kRvvFunct6Shift = 26; ++const int kRvvFunct6Bits = 6; ++const uint32_t kRvvFunct6Mask = ++ (((1 << kRvvFunct6Bits) - 1) << kRvvFunct6Shift); ++ ++const int kRvvVmBits = 1; ++const int kRvvVmShift = 25; ++const uint32_t kRvvVmMask = (((1 << kRvvVmBits) - 1) << kRvvVmShift); ++ ++const int kRvvVs2Bits = 5; ++const int kRvvVs2Shift = 20; ++const uint32_t kRvvVs2Mask = (((1 << kRvvVs2Bits) - 1) << kRvvVs2Shift); ++ ++const int kRvvVs1Bits = 5; ++const int kRvvVs1Shift = 15; ++const uint32_t kRvvVs1Mask = (((1 << kRvvVs1Bits) - 1) << kRvvVs1Shift); ++ ++const int kRvvRs1Bits = kRvvVs1Bits; ++const int kRvvRs1Shift = kRvvVs1Shift; ++const uint32_t kRvvRs1Mask = (((1 << kRvvRs1Bits) - 1) << kRvvRs1Shift); ++ ++const int kRvvRs2Bits = 5; ++const int kRvvRs2Shift = 20; ++const uint32_t kRvvRs2Mask = (((1 << kRvvRs2Bits) - 1) << kRvvRs2Shift); ++ ++const int kRvvImm5Bits = kRvvVs1Bits; ++const int kRvvImm5Shift = kRvvVs1Shift; ++const uint32_t kRvvImm5Mask = (((1 << kRvvImm5Bits) - 1) << kRvvImm5Shift); ++ ++const int kRvvVdBits = 5; ++const int kRvvVdShift = 7; ++const uint32_t kRvvVdMask = (((1 << kRvvVdBits) - 1) << kRvvVdShift); ++ ++const int kRvvRdBits = kRvvVdBits; ++const int kRvvRdShift = kRvvVdShift; ++const uint32_t kRvvRdMask = (((1 << kRvvRdBits) - 1) << kRvvRdShift); ++ ++const int kRvvZimmBits = 11; ++const int kRvvZimmShift = 20; ++const uint32_t kRvvZimmMask = (((1 << kRvvZimmBits) - 1) << kRvvZimmShift); ++ ++const int kRvvUimmShift = kRvvRs1Shift; ++const int kRvvUimmBits = kRvvRs1Bits; ++const uint32_t kRvvUimmMask = (((1 << kRvvUimmBits) - 1) << kRvvUimmShift); ++ ++const int kRvvWidthBits = 3; ++const int kRvvWidthShift = 12; ++const uint32_t kRvvWidthMask = (((1 << kRvvWidthBits) - 1) << kRvvWidthShift); ++ ++const int kRvvMopBits = 2; ++const int kRvvMopShift = 26; ++const uint32_t kRvvMopMask = (((1 << kRvvMopBits) - 1) << kRvvMopShift); ++ ++const int kRvvMewBits = 1; ++const int kRvvMewShift = 28; ++const uint32_t kRvvMewMask = (((1 << kRvvMewBits) - 1) << kRvvMewShift); ++ ++const int kRvvNfBits = 3; ++const int kRvvNfShift = 29; ++const uint32_t kRvvNfMask = (((1 << kRvvNfBits) - 1) << kRvvNfShift); ++ ++const int kNopByte = 0x00000013; ++ ++enum BaseOpcode : uint32_t { ++ LOAD = 0b0000011, // I form: LB LH LW LBU LHU ++ LOAD_FP = 0b0000111, // I form: FLW FLD FLQ ++ MISC_MEM = 0b0001111, // I special form: FENCE FENCE.I ++ OP_IMM = 0b0010011, // I form: ADDI SLTI SLTIU XORI ORI ANDI SLLI SRLI SRAI ++ // Note: SLLI/SRLI/SRAI I form first, then func3 001/101 => R type ++ AUIPC = 0b0010111, // U form: AUIPC ++ OP_IMM_32 = 0b0011011, // I form: ADDIW SLLIW SRLIW SRAIW ++ // Note: SRLIW SRAIW I form first, then func3 101 special shift encoding ++ STORE = 0b0100011, // S form: SB SH SW SD ++ STORE_FP = 0b0100111, // S form: FSW FSD FSQ ++ AMO = 0b0101111, // R form: All A instructions ++ OP = 0b0110011, // R: ADD SUB SLL SLT SLTU XOR SRL SRA OR AND and 32M set ++ LUI = 0b0110111, // U form: LUI ++ OP_32 = 0b0111011, // R: ADDW SUBW SLLW SRLW SRAW MULW DIVW DIVUW REMW REMUW ++ MADD = 0b1000011, // R4 type: FMADD.S FMADD.D FMADD.Q ++ MSUB = 0b1000111, // R4 type: FMSUB.S FMSUB.D FMSUB.Q ++ NMSUB = 0b1001011, // R4 type: FNMSUB.S FNMSUB.D FNMSUB.Q ++ NMADD = 0b1001111, // R4 type: FNMADD.S FNMADD.D FNMADD.Q ++ OP_FP = 0b1010011, // R type: Q ext ++ BRANCH = 0b1100011, // B form: BEQ BNE, BLT, BGE, BLTU BGEU ++ JALR = 0b1100111, // I form: JALR ++ JAL = 0b1101111, // J form: JAL ++ SYSTEM = 0b1110011, // I form: ECALL EBREAK Zicsr ext ++ OP_V = 0b1010111, // V form: RVV ++ ++ // C extension ++ C0 = 0b00, ++ C1 = 0b01, ++ C2 = 0b10, ++ FUNCT2_0 = 0b00, ++ FUNCT2_1 = 0b01, ++ FUNCT2_2 = 0b10, ++ FUNCT2_3 = 0b11, ++}; ++ ++// ----- Emulated conditions. ++// On RISC-V we use this enum to abstract from conditional branch instructions. ++// The 'U' prefix is used to specify unsigned comparisons. ++// Opposite conditions must be paired as odd/even numbers ++// because 'NegateCondition' function flips LSB to negate condition. ++enum RiscvCondition { // Any value < 0 is considered no_condition. ++ overflow = 0, ++ no_overflow = 1, ++ Uless = 2, ++ Ugreater_equal = 3, ++ Uless_equal = 4, ++ Ugreater = 5, ++ equal = 6, ++ not_equal = 7, // Unordered or Not Equal. ++ less = 8, ++ greater_equal = 9, ++ less_equal = 10, ++ greater = 11, ++ cc_always = 12, ++ ++ // Aliases. ++ eq = equal, ++ ne = not_equal, ++ ge = greater_equal, ++ lt = less, ++ gt = greater, ++ le = less_equal, ++ al = cc_always, ++ ult = Uless, ++ uge = Ugreater_equal, ++ ule = Uless_equal, ++ ugt = Ugreater, ++}; ++ ++// ----- Coprocessor conditions. ++enum FPUCondition { ++ kNoFPUCondition = -1, ++ EQ = 0x02, // Ordered and Equal ++ NE = 0x03, // Unordered or Not Equal ++ LT = 0x04, // Ordered and Less Than ++ GE = 0x05, // Ordered and Greater Than or Equal ++ LE = 0x06, // Ordered and Less Than or Equal ++ GT = 0x07, // Ordered and Greater Than ++}; ++ ++enum CheckForInexactConversion { ++ kCheckForInexactConversion, ++ kDontCheckForInexactConversion ++}; ++ ++enum class MaxMinKind : int { kMin = 0, kMax = 1 }; ++ ++// ---------------------------------------------------------------------------- ++// RISCV flags ++ ++enum ControlStatusReg { ++ csr_fflags = 0x001, // Floating-Point Accrued Exceptions (RW) ++ csr_frm = 0x002, // Floating-Point Dynamic Rounding Mode (RW) ++ csr_fcsr = 0x003, // Floating-Point Control and Status Register (RW) ++ csr_cycle = 0xc00, // Cycle counter for RDCYCLE instruction (RO) ++ csr_time = 0xc01, // Timer for RDTIME instruction (RO) ++ csr_instret = 0xc02, // Insns-retired counter for RDINSTRET instruction (RO) ++ csr_cycleh = 0xc80, // Upper 32 bits of cycle, RV32I only (RO) ++ csr_timeh = 0xc81, // Upper 32 bits of time, RV32I only (RO) ++ csr_instreth = 0xc82 // Upper 32 bits of instret, RV32I only (RO) ++}; ++ ++enum FFlagsMask { ++ kInvalidOperation = 0b10000, // NV: Invalid ++ kDivideByZero = 0b1000, // DZ: Divide by Zero ++ kOverflow = 0b100, // OF: Overflow ++ kUnderflow = 0b10, // UF: Underflow ++ kInexact = 0b1 // NX: Inexact ++}; ++ ++enum FPURoundingMode { ++ RNE = 0b000, // Round to Nearest, ties to Even ++ RTZ = 0b001, // Round towards Zero ++ RDN = 0b010, // Round Down (towards -infinity) ++ RUP = 0b011, // Round Up (towards +infinity) ++ RMM = 0b100, // Round to Nearest, tiest to Max Magnitude ++ DYN = 0b111 // In instruction's rm field, selects dynamic rounding mode; ++ // In Rounding Mode register, Invalid ++}; ++ ++enum MemoryOdering { ++ PSI = 0b1000, // PI or SI ++ PSO = 0b0100, // PO or SO ++ PSR = 0b0010, // PR or SR ++ PSW = 0b0001, // PW or SW ++ PSIORW = PSI | PSO | PSR | PSW ++}; ++ ++const int kFloat32ExponentBias = 127; ++const int kFloat32MantissaBits = 23; ++const int kFloat32ExponentBits = 8; ++const int kFloat64ExponentBias = 1023; ++const int kFloat64MantissaBits = 52; ++const int kFloat64ExponentBits = 11; ++ ++enum FClassFlag { ++ kNegativeInfinity = 1, ++ kNegativeNormalNumber = 1 << 1, ++ kNegativeSubnormalNumber = 1 << 2, ++ kNegativeZero = 1 << 3, ++ kPositiveZero = 1 << 4, ++ kPositiveSubnormalNumber = 1 << 5, ++ kPositiveNormalNumber = 1 << 6, ++ kPositiveInfinity = 1 << 7, ++ kSignalingNaN = 1 << 8, ++ kQuietNaN = 1 << 9 ++}; ++ ++enum OffsetSize : uint32_t { ++ kOffset21 = 21, // RISCV jal ++ kOffset12 = 12, // RISCV imm12 ++ kOffset20 = 20, // RISCV imm20 ++ kOffset13 = 13, // RISCV branch ++ kOffset32 = 32, // RISCV auipc + instr_I ++ kOffset11 = 11, // RISCV C_J ++ kOffset9 = 9, // RISCV compressed branch ++}; ++ ++// The classes of immediate branch ranges, in order of increasing range. ++// Note that CondBranchType and CompareBranchType have the same range. ++enum ImmBranchRangeType { ++ CondBranchRangeType, // ++ UncondBranchRangeType, // ++ UnknownBranchRangeType, ++ ++ // Number of 'short-range' branch range types. ++ // We don't consider unconditional branches 'short-range'. ++ NumShortBranchRangeTypes = UnknownBranchRangeType ++}; ++ ++inline ImmBranchRangeType OffsetSizeToImmBranchRangeType(OffsetSize bits) { ++ switch (bits) { ++ case kOffset21: ++ return UncondBranchRangeType; ++ case kOffset13: ++ return CondBranchRangeType; ++ default: ++ MOZ_CRASH("Unimplement"); ++ } ++} ++ ++inline OffsetSize ImmBranchRangeTypeToOffsetSize(ImmBranchRangeType type) { ++ switch (type) { ++ case CondBranchRangeType: ++ return kOffset13; ++ case UncondBranchRangeType: ++ return kOffset21; ++ default: ++ MOZ_CRASH("Unimplement"); ++ } ++} ++ ++int32_t ImmBranchMaxForwardOffset(OffsetSize bits); ++ ++inline int32_t ImmBranchMaxForwardOffset(ImmBranchRangeType type) { ++ return ImmBranchMaxForwardOffset(ImmBranchRangeTypeToOffsetSize(type)); ++} ++// ----------------------------------------------------------------------------- ++// Specific instructions, constants, and masks. ++// These constants are declared in assembler-riscv64.cc, as they use named ++// registers and other constants. ++ ++// An Illegal instruction ++const Instr kIllegalInstr = 0; // All other bits are 0s (i.e., ecall) ++// An ECALL instruction, used for redirected real time call ++const Instr rtCallRedirInstr = SYSTEM; // All other bits are 0s (i.e., ecall) ++// An EBreak instruction, used for debugging and semi-hosting ++const Instr kBreakInstr = SYSTEM | 1 << kImm12Shift; // ebreak ++ ++constexpr uint8_t kInstrSize = 4; ++constexpr uint8_t kShortInstrSize = 2; ++constexpr uint8_t kInstrSizeLog2 = 2; ++ ++class InstructionBase { ++ public: ++ enum { ++ // On RISC-V, PC cannot actually be directly accessed. We behave as if PC ++ // was always the value of the current instruction being executed. ++ kPCReadOffset = 0 ++ }; ++ ++ // Instruction type. ++ enum Type { ++ kRType, ++ kR4Type, // Special R4 for Q extension ++ kIType, ++ kSType, ++ kBType, ++ kUType, ++ kJType, ++ // C extension ++ kCRType, ++ kCIType, ++ kCSSType, ++ kCIWType, ++ kCLType, ++ kCSType, ++ kCAType, ++ kCBType, ++ kCJType, ++ // V extension ++ kVType, ++ kVLType, ++ kVSType, ++ kVAMOType, ++ kVIVVType, ++ kVFVVType, ++ kVMVVType, ++ kVIVIType, ++ kVIVXType, ++ kVFVFType, ++ kVMVXType, ++ kVSETType, ++ kUnsupported = -1 ++ }; ++ ++ inline bool IsIllegalInstruction() const { ++ uint16_t FirstHalfWord = *reinterpret_cast(this); ++ return FirstHalfWord == 0; ++ } ++ ++ bool IsShortInstruction() const; ++ ++ inline uint8_t InstructionSize() const { ++ return (this->IsShortInstruction()) ? kShortInstrSize : kInstrSize; ++ } ++ ++ // Get the raw instruction bits. ++ inline Instr InstructionBits() const { ++ if (this->IsShortInstruction()) { ++ return 0x0000FFFF & (*reinterpret_cast(this)); ++ } ++ return *reinterpret_cast(this); ++ } ++ ++ // Set the raw instruction bits to value. ++ inline void SetInstructionBits(Instr value) { ++ *reinterpret_cast(this) = value; ++ } ++ ++ // Read one particular bit out of the instruction bits. ++ inline int Bit(int nr) const { return (InstructionBits() >> nr) & 1; } ++ ++ // Read a bit field out of the instruction bits. ++ inline int Bits(int hi, int lo) const { ++ return (InstructionBits() >> lo) & ((2U << (hi - lo)) - 1); ++ } ++ ++ // Accessors for the different named fields used in the RISC-V encoding. ++ inline BaseOpcode BaseOpcodeValue() const { ++ return static_cast( ++ Bits(kBaseOpcodeShift + kBaseOpcodeBits - 1, kBaseOpcodeShift)); ++ } ++ ++ // Return the fields at their original place in the instruction encoding. ++ inline BaseOpcode BaseOpcodeFieldRaw() const { ++ return static_cast(InstructionBits() & kBaseOpcodeMask); ++ } ++ ++ // Safe to call within R-type instructions ++ inline int Funct7FieldRaw() const { return InstructionBits() & kFunct7Mask; } ++ ++ // Safe to call within R-, I-, S-, or B-type instructions ++ inline int Funct3FieldRaw() const { return InstructionBits() & kFunct3Mask; } ++ ++ // Safe to call within R-, I-, S-, or B-type instructions ++ inline int Rs1FieldRawNoAssert() const { ++ return InstructionBits() & kRs1FieldMask; ++ } ++ ++ // Safe to call within R-, S-, or B-type instructions ++ inline int Rs2FieldRawNoAssert() const { ++ return InstructionBits() & kRs2FieldMask; ++ } ++ ++ // Safe to call within R4-type instructions ++ inline int Rs3FieldRawNoAssert() const { ++ return InstructionBits() & kRs3FieldMask; ++ } ++ ++ inline int32_t ITypeBits() const { return InstructionBits() & kITypeMask; } ++ ++ inline int32_t InstructionOpcodeType() const { ++ if (IsShortInstruction()) { ++ return InstructionBits() & kRvcOpcodeMask; ++ } else { ++ return InstructionBits() & kBaseOpcodeMask; ++ } ++ } ++ ++ // Get the encoding type of the instruction. ++ Type InstructionType() const; ++ OffsetSize GetOffsetSize() const; ++ inline ImmBranchRangeType GetImmBranchRangeType() const { ++ return OffsetSizeToImmBranchRangeType(GetOffsetSize()); ++ } ++ ++ protected: ++ InstructionBase() {} ++}; ++ ++template ++class InstructionGetters : public T { ++ public: ++ // Say if the instruction is a break or a trap. ++ bool IsTrap() const; ++ ++ inline int BaseOpcode() const { ++ return this->InstructionBits() & kBaseOpcodeMask; ++ } ++ ++ inline int RvcOpcode() const { ++ MOZ_ASSERT(this->IsShortInstruction()); ++ return this->InstructionBits() & kRvcOpcodeMask; ++ } ++ ++ inline int Rs1Value() const { ++ MOZ_ASSERT(this->InstructionType() == InstructionBase::kRType || ++ this->InstructionType() == InstructionBase::kR4Type || ++ this->InstructionType() == InstructionBase::kIType || ++ this->InstructionType() == InstructionBase::kSType || ++ this->InstructionType() == InstructionBase::kBType || ++ this->InstructionType() == InstructionBase::kIType || ++ this->InstructionType() == InstructionBase::kVType); ++ return this->Bits(kRs1Shift + kRs1Bits - 1, kRs1Shift); ++ } ++ ++ inline int Rs2Value() const { ++ MOZ_ASSERT(this->InstructionType() == InstructionBase::kRType || ++ this->InstructionType() == InstructionBase::kR4Type || ++ this->InstructionType() == InstructionBase::kSType || ++ this->InstructionType() == InstructionBase::kBType || ++ this->InstructionType() == InstructionBase::kIType || ++ this->InstructionType() == InstructionBase::kVType); ++ return this->Bits(kRs2Shift + kRs2Bits - 1, kRs2Shift); ++ } ++ ++ inline int Rs3Value() const { ++ MOZ_ASSERT(this->InstructionType() == InstructionBase::kR4Type); ++ return this->Bits(kRs3Shift + kRs3Bits - 1, kRs3Shift); ++ } ++ ++ inline int Vs1Value() const { ++ MOZ_ASSERT(this->InstructionType() == InstructionBase::kVType || ++ this->InstructionType() == InstructionBase::kIType || ++ this->InstructionType() == InstructionBase::kSType); ++ return this->Bits(kVs1Shift + kVs1Bits - 1, kVs1Shift); ++ } ++ ++ inline int Vs2Value() const { ++ MOZ_ASSERT(this->InstructionType() == InstructionBase::kVType || ++ this->InstructionType() == InstructionBase::kIType || ++ this->InstructionType() == InstructionBase::kSType); ++ return this->Bits(kVs2Shift + kVs2Bits - 1, kVs2Shift); ++ } ++ ++ inline int VdValue() const { ++ MOZ_ASSERT(this->InstructionType() == InstructionBase::kVType || ++ this->InstructionType() == InstructionBase::kIType || ++ this->InstructionType() == InstructionBase::kSType); ++ return this->Bits(kVdShift + kVdBits - 1, kVdShift); ++ } ++ ++ inline int RdValue() const { ++ MOZ_ASSERT(this->InstructionType() == InstructionBase::kRType || ++ this->InstructionType() == InstructionBase::kR4Type || ++ this->InstructionType() == InstructionBase::kIType || ++ this->InstructionType() == InstructionBase::kSType || ++ this->InstructionType() == InstructionBase::kUType || ++ this->InstructionType() == InstructionBase::kJType || ++ this->InstructionType() == InstructionBase::kVType); ++ return this->Bits(kRdShift + kRdBits - 1, kRdShift); ++ } ++ ++ inline int RvcRs1Value() const { return this->RvcRdValue(); } ++ ++ int RvcRdValue() const; ++ ++ int RvcRs2Value() const; ++ ++ int RvcRs1sValue() const; ++ ++ int RvcRs2sValue() const; ++ ++ int Funct7Value() const; ++ ++ inline int Funct3Value() const { ++ MOZ_ASSERT(this->InstructionType() == InstructionBase::kRType || ++ this->InstructionType() == InstructionBase::kIType || ++ this->InstructionType() == InstructionBase::kSType || ++ this->InstructionType() == InstructionBase::kBType); ++ return this->Bits(kFunct3Shift + kFunct3Bits - 1, kFunct3Shift); ++ } ++ ++ inline int Funct5Value() const { ++ MOZ_ASSERT(this->InstructionType() == InstructionBase::kRType && ++ this->BaseOpcode() == OP_FP); ++ return this->Bits(kFunct5Shift + kFunct5Bits - 1, kFunct5Shift); ++ } ++ ++ int RvcFunct6Value() const; ++ ++ int RvcFunct4Value() const; ++ ++ int RvcFunct3Value() const; ++ ++ int RvcFunct2Value() const; ++ ++ int RvcFunct2BValue() const; ++ ++ inline int CsrValue() const { ++ MOZ_ASSERT(this->InstructionType() == InstructionBase::kIType && ++ this->BaseOpcode() == SYSTEM); ++ return (this->Bits(kCsrShift + kCsrBits - 1, kCsrShift)); ++ } ++ ++ inline int RoundMode() const { ++ MOZ_ASSERT((this->InstructionType() == InstructionBase::kRType || ++ this->InstructionType() == InstructionBase::kR4Type) && ++ this->BaseOpcode() == OP_FP); ++ return this->Bits(kFunct3Shift + kFunct3Bits - 1, kFunct3Shift); ++ } ++ ++ inline int MemoryOrder(bool is_pred) const { ++ MOZ_ASSERT((this->InstructionType() == InstructionBase::kIType && ++ this->BaseOpcode() == MISC_MEM)); ++ if (is_pred) { ++ return this->Bits(kPredOrderShift + kMemOrderBits - 1, kPredOrderShift); ++ } else { ++ return this->Bits(kSuccOrderShift + kMemOrderBits - 1, kSuccOrderShift); ++ } ++ } ++ ++ inline int Imm12Value() const { ++ MOZ_ASSERT(this->InstructionType() == InstructionBase::kIType); ++ int Value = this->Bits(kImm12Shift + kImm12Bits - 1, kImm12Shift); ++ return Value << 20 >> 20; ++ } ++ ++ inline int32_t Imm12SExtValue() const { ++ int32_t Value = this->Imm12Value() << 20 >> 20; ++ return Value; ++ } ++ ++ inline int BranchOffset() const { ++ MOZ_ASSERT(this->InstructionType() == InstructionBase::kBType); ++ // | imm[12|10:5] | rs2 | rs1 | funct3 | imm[4:1|11] | opcode | ++ // 31 25 11 7 ++ uint32_t Bits = this->InstructionBits(); ++ int16_t imm13 = ((Bits & 0xf00) >> 7) | ((Bits & 0x7e000000) >> 20) | ++ ((Bits & 0x80) << 4) | ((Bits & 0x80000000) >> 19); ++ return imm13 << 19 >> 19; ++ } ++ ++ inline int StoreOffset() const { ++ MOZ_ASSERT(this->InstructionType() == InstructionBase::kSType); ++ // | imm[11:5] | rs2 | rs1 | funct3 | imm[4:0] | opcode | ++ // 31 25 11 7 ++ uint32_t Bits = this->InstructionBits(); ++ int16_t imm12 = ((Bits & 0xf80) >> 7) | ((Bits & 0xfe000000) >> 20); ++ return imm12 << 20 >> 20; ++ } ++ ++ inline int Imm20UValue() const { ++ MOZ_ASSERT(this->InstructionType() == InstructionBase::kUType); ++ // | imm[31:12] | rd | opcode | ++ // 31 12 ++ int32_t Bits = this->InstructionBits(); ++ return Bits >> 12; ++ } ++ ++ inline int Imm20JValue() const { ++ MOZ_ASSERT(this->InstructionType() == InstructionBase::kJType); ++ // | imm[20|10:1|11|19:12] | rd | opcode | ++ // 31 12 ++ uint32_t Bits = this->InstructionBits(); ++ int32_t imm20 = ((Bits & 0x7fe00000) >> 20) | ((Bits & 0x100000) >> 9) | ++ (Bits & 0xff000) | ((Bits & 0x80000000) >> 11); ++ return imm20 << 11 >> 11; ++ } ++ ++ inline bool IsArithShift() const { ++ // Valid only for right shift operations ++ MOZ_ASSERT((this->BaseOpcode() == OP || this->BaseOpcode() == OP_32 || ++ this->BaseOpcode() == OP_IMM || ++ this->BaseOpcode() == OP_IMM_32) && ++ this->Funct3Value() == 0b101); ++ return this->InstructionBits() & 0x40000000; ++ } ++ ++ inline int Shamt() const { ++ // Valid only for shift instructions (SLLI, SRLI, SRAI) ++ MOZ_ASSERT((this->InstructionBits() & kBaseOpcodeMask) == OP_IMM && ++ (this->Funct3Value() == 0b001 || this->Funct3Value() == 0b101)); ++ // | 0A0000 | shamt | rs1 | funct3 | rd | opcode | ++ // 31 25 20 ++ return this->Bits(kImm12Shift + 5, kImm12Shift); ++ } ++ ++ inline int Shamt32() const { ++ // Valid only for shift instructions (SLLIW, SRLIW, SRAIW) ++ MOZ_ASSERT((this->InstructionBits() & kBaseOpcodeMask) == OP_IMM_32 && ++ (this->Funct3Value() == 0b001 || this->Funct3Value() == 0b101)); ++ // | 0A00000 | shamt | rs1 | funct3 | rd | opcode | ++ // 31 24 20 ++ return this->Bits(kImm12Shift + 4, kImm12Shift); ++ } ++ ++ inline int RvcImm6Value() const { ++ MOZ_ASSERT(this->IsShortInstruction()); ++ // | funct3 | imm[5] | rs1/rd | imm[4:0] | opcode | ++ // 15 12 6 2 ++ uint32_t Bits = this->InstructionBits(); ++ int32_t imm6 = ((Bits & 0x1000) >> 7) | ((Bits & 0x7c) >> 2); ++ return imm6 << 26 >> 26; ++ } ++ ++ inline int RvcImm6Addi16spValue() const { ++ MOZ_ASSERT(this->IsShortInstruction()); ++ // | funct3 | nzimm[9] | 2 | nzimm[4|6|8:7|5] | opcode | ++ // 15 12 6 2 ++ uint32_t Bits = this->InstructionBits(); ++ int32_t imm10 = ((Bits & 0x1000) >> 3) | ((Bits & 0x40) >> 2) | ++ ((Bits & 0x20) << 1) | ((Bits & 0x18) << 4) | ++ ((Bits & 0x4) << 3); ++ MOZ_ASSERT(imm10 != 0); ++ return imm10 << 22 >> 22; ++ } ++ ++ inline int RvcImm8Addi4spnValue() const { ++ MOZ_ASSERT(this->IsShortInstruction()); ++ // | funct3 | nzimm[11] | rd' | opcode | ++ // 15 13 5 2 ++ uint32_t Bits = this->InstructionBits(); ++ int32_t uimm10 = ((Bits & 0x20) >> 2) | ((Bits & 0x40) >> 4) | ++ ((Bits & 0x780) >> 1) | ((Bits & 0x1800) >> 7); ++ MOZ_ASSERT(uimm10 != 0); ++ return uimm10; ++ } ++ ++ inline int RvcShamt6() const { ++ MOZ_ASSERT(this->IsShortInstruction()); ++ // | funct3 | nzuimm[5] | rs1/rd | nzuimm[4:0] | opcode | ++ // 15 12 6 2 ++ int32_t imm6 = this->RvcImm6Value(); ++ return imm6 & 0x3f; ++ } ++ ++ inline int RvcImm6LwspValue() const { ++ MOZ_ASSERT(this->IsShortInstruction()); ++ // | funct3 | uimm[5] | rs1 | uimm[4:2|7:6] | opcode | ++ // 15 12 6 2 ++ uint32_t Bits = this->InstructionBits(); ++ int32_t imm8 = ++ ((Bits & 0x1000) >> 7) | ((Bits & 0x70) >> 2) | ((Bits & 0xc) << 4); ++ return imm8; ++ } ++ ++ inline int RvcImm6LdspValue() const { ++ MOZ_ASSERT(this->IsShortInstruction()); ++ // | funct3 | uimm[5] | rs1 | uimm[4:3|8:6] | opcode | ++ // 15 12 6 2 ++ uint32_t Bits = this->InstructionBits(); ++ int32_t imm9 = ++ ((Bits & 0x1000) >> 7) | ((Bits & 0x60) >> 2) | ((Bits & 0x1c) << 4); ++ return imm9; ++ } ++ ++ inline int RvcImm6SwspValue() const { ++ MOZ_ASSERT(this->IsShortInstruction()); ++ // | funct3 | uimm[5:2|7:6] | rs2 | opcode | ++ // 15 12 7 ++ uint32_t Bits = this->InstructionBits(); ++ int32_t imm8 = ((Bits & 0x1e00) >> 7) | ((Bits & 0x180) >> 1); ++ return imm8; ++ } ++ ++ inline int RvcImm6SdspValue() const { ++ MOZ_ASSERT(this->IsShortInstruction()); ++ // | funct3 | uimm[5:3|8:6] | rs2 | opcode | ++ // 15 12 7 ++ uint32_t Bits = this->InstructionBits(); ++ int32_t imm9 = ((Bits & 0x1c00) >> 7) | ((Bits & 0x380) >> 1); ++ return imm9; ++ } ++ ++ inline int RvcImm5WValue() const { ++ MOZ_ASSERT(this->IsShortInstruction()); ++ // | funct3 | imm[5:3] | rs1 | imm[2|6] | rd | opcode | ++ // 15 12 10 6 4 2 ++ uint32_t Bits = this->InstructionBits(); ++ int32_t imm7 = ++ ((Bits & 0x1c00) >> 7) | ((Bits & 0x40) >> 4) | ((Bits & 0x20) << 1); ++ return imm7; ++ } ++ ++ inline int RvcImm5DValue() const { ++ MOZ_ASSERT(this->IsShortInstruction()); ++ // | funct3 | imm[5:3] | rs1 | imm[7:6] | rd | opcode | ++ // 15 12 10 6 4 2 ++ uint32_t Bits = this->InstructionBits(); ++ int32_t imm8 = ((Bits & 0x1c00) >> 7) | ((Bits & 0x60) << 1); ++ return imm8; ++ } ++ ++ inline int RvcImm11CJValue() const { ++ MOZ_ASSERT(this->IsShortInstruction()); ++ // | funct3 | [11|4|9:8|10|6|7|3:1|5] | opcode | ++ // 15 12 2 ++ uint32_t Bits = this->InstructionBits(); ++ int32_t imm12 = ((Bits & 0x4) << 3) | ((Bits & 0x38) >> 2) | ++ ((Bits & 0x40) << 1) | ((Bits & 0x80) >> 1) | ++ ((Bits & 0x100) << 2) | ((Bits & 0x600) >> 1) | ++ ((Bits & 0x800) >> 7) | ((Bits & 0x1000) >> 1); ++ return imm12 << 20 >> 20; ++ } ++ ++ inline int RvcImm8BValue() const { ++ MOZ_ASSERT(this->IsShortInstruction()); ++ // | funct3 | imm[8|4:3] | rs1` | imm[7:6|2:1|5] | opcode | ++ // 15 12 10 7 2 ++ uint32_t Bits = this->InstructionBits(); ++ int32_t imm9 = ((Bits & 0x4) << 3) | ((Bits & 0x18) >> 2) | ++ ((Bits & 0x60) << 1) | ((Bits & 0xc00) >> 7) | ++ ((Bits & 0x1000) >> 4); ++ return imm9 << 23 >> 23; ++ } ++ ++ inline int vl_vs_width() { ++ int width = 0; ++ if ((this->InstructionBits() & kBaseOpcodeMask) != LOAD_FP && ++ (this->InstructionBits() & kBaseOpcodeMask) != STORE_FP) ++ return -1; ++ switch (this->InstructionBits() & (kRvvWidthMask | kRvvMewMask)) { ++ case 0x0: ++ width = 8; ++ break; ++ case 0x00005000: ++ width = 16; ++ break; ++ case 0x00006000: ++ width = 32; ++ break; ++ case 0x00007000: ++ width = 64; ++ break; ++ case 0x10000000: ++ width = 128; ++ break; ++ case 0x10005000: ++ width = 256; ++ break; ++ case 0x10006000: ++ width = 512; ++ break; ++ case 0x10007000: ++ width = 1024; ++ break; ++ default: ++ width = -1; ++ break; ++ } ++ return width; ++ } ++ ++ uint32_t Rvvzimm() const; ++ ++ uint32_t Rvvuimm() const; ++ ++ inline uint32_t RvvVsew() const { ++ uint32_t zimm = this->Rvvzimm(); ++ uint32_t vsew = (zimm >> 3) & 0x7; ++ return vsew; ++ } ++ ++ inline uint32_t RvvVlmul() const { ++ uint32_t zimm = this->Rvvzimm(); ++ uint32_t vlmul = zimm & 0x7; ++ return vlmul; ++ } ++ ++ inline uint8_t RvvVM() const { ++ MOZ_ASSERT(this->InstructionType() == InstructionBase::kVType || ++ this->InstructionType() == InstructionBase::kIType || ++ this->InstructionType() == InstructionBase::kSType); ++ return this->Bits(kRvvVmShift + kRvvVmBits - 1, kRvvVmShift); ++ } ++ ++ inline const char* RvvSEW() const { ++ uint32_t vsew = this->RvvVsew(); ++ switch (vsew) { ++#define CAST_VSEW(name) \ ++ case name: \ ++ return #name; ++ RVV_SEW(CAST_VSEW) ++ default: ++ return "unknown"; ++#undef CAST_VSEW ++ } ++ } ++ ++ inline const char* RvvLMUL() const { ++ uint32_t vlmul = this->RvvVlmul(); ++ switch (vlmul) { ++#define CAST_VLMUL(name) \ ++ case name: \ ++ return #name; ++ RVV_LMUL(CAST_VLMUL) ++ default: ++ return "unknown"; ++#undef CAST_VLMUL ++ } ++ } ++ ++#define sext(x, len) (((int32_t)(x) << (32 - len)) >> (32 - len)) ++#define zext(x, len) (((uint32_t)(x) << (32 - len)) >> (32 - len)) ++ ++ inline int32_t RvvSimm5() const { ++ MOZ_ASSERT(this->InstructionType() == InstructionBase::kVType); ++ return sext(this->Bits(kRvvImm5Shift + kRvvImm5Bits - 1, kRvvImm5Shift), ++ kRvvImm5Bits); ++ } ++ ++ inline uint32_t RvvUimm5() const { ++ MOZ_ASSERT(this->InstructionType() == InstructionBase::kVType); ++ uint32_t imm = this->Bits(kRvvImm5Shift + kRvvImm5Bits - 1, kRvvImm5Shift); ++ return zext(imm, kRvvImm5Bits); ++ } ++#undef sext ++#undef zext ++ inline bool AqValue() const { return this->Bits(kAqShift, kAqShift); } ++ ++ inline bool RlValue() const { return this->Bits(kRlShift, kRlShift); } ++}; ++ ++class Instruction : public InstructionGetters { ++ public: ++ // Instructions are read of out a code stream. The only way to get a ++ // reference to an instruction is to convert a pointer. There is no way ++ // to allocate or create instances of class Instruction. ++ // Use the At(pc) function to create references to Instruction. ++ static Instruction* At(byte* pc) { ++ return reinterpret_cast(pc); ++ } ++ ++ private: ++ // We need to prevent the creation of instances of class Instruction. ++ Instruction() = delete; ++ Instruction(const Instruction&) = delete; ++ Instruction& operator=(const Instruction&) = delete; ++}; ++ ++// ----------------------------------------------------------------------------- ++// Instructions. ++ ++template ++bool InstructionGetters

::IsTrap() const { ++ return (this->InstructionBits() == kBreakInstr); ++} ++ ++} // namespace jit ++} // namespace js ++#endif // jit_riscv64_constant_Base_constant_riscv__h_ +diff --git a/js/src/jit/riscv64/constant/Constant-riscv-a.h b/js/src/jit/riscv64/constant/Constant-riscv-a.h +new file mode 100644 +index 0000000000..718e607240 +--- /dev/null ++++ b/js/src/jit/riscv64/constant/Constant-riscv-a.h +@@ -0,0 +1,43 @@ ++// Copyright 2022 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++#ifndef jit_riscv64_constant_Constant_riscv64_a_h_ ++#define jit_riscv64_constant_Constant_riscv64_a_h_ ++ ++#include "jit/riscv64/constant/Base-constant-riscv.h" ++namespace js { ++namespace jit { ++ ++enum OpcodeRISCVA : uint32_t { ++ // RV32A Standard Extension ++ RO_LR_W = AMO | (0b010 << kFunct3Shift) | (0b00010 << kFunct5Shift), ++ RO_SC_W = AMO | (0b010 << kFunct3Shift) | (0b00011 << kFunct5Shift), ++ RO_AMOSWAP_W = AMO | (0b010 << kFunct3Shift) | (0b00001 << kFunct5Shift), ++ RO_AMOADD_W = AMO | (0b010 << kFunct3Shift) | (0b00000 << kFunct5Shift), ++ RO_AMOXOR_W = AMO | (0b010 << kFunct3Shift) | (0b00100 << kFunct5Shift), ++ RO_AMOAND_W = AMO | (0b010 << kFunct3Shift) | (0b01100 << kFunct5Shift), ++ RO_AMOOR_W = AMO | (0b010 << kFunct3Shift) | (0b01000 << kFunct5Shift), ++ RO_AMOMIN_W = AMO | (0b010 << kFunct3Shift) | (0b10000 << kFunct5Shift), ++ RO_AMOMAX_W = AMO | (0b010 << kFunct3Shift) | (0b10100 << kFunct5Shift), ++ RO_AMOMINU_W = AMO | (0b010 << kFunct3Shift) | (0b11000 << kFunct5Shift), ++ RO_AMOMAXU_W = AMO | (0b010 << kFunct3Shift) | (0b11100 << kFunct5Shift), ++ ++#ifdef JS_CODEGEN_RISCV64 ++ // RV64A Standard Extension (in addition to RV32A) ++ RO_LR_D = AMO | (0b011 << kFunct3Shift) | (0b00010 << kFunct5Shift), ++ RO_SC_D = AMO | (0b011 << kFunct3Shift) | (0b00011 << kFunct5Shift), ++ RO_AMOSWAP_D = AMO | (0b011 << kFunct3Shift) | (0b00001 << kFunct5Shift), ++ RO_AMOADD_D = AMO | (0b011 << kFunct3Shift) | (0b00000 << kFunct5Shift), ++ RO_AMOXOR_D = AMO | (0b011 << kFunct3Shift) | (0b00100 << kFunct5Shift), ++ RO_AMOAND_D = AMO | (0b011 << kFunct3Shift) | (0b01100 << kFunct5Shift), ++ RO_AMOOR_D = AMO | (0b011 << kFunct3Shift) | (0b01000 << kFunct5Shift), ++ RO_AMOMIN_D = AMO | (0b011 << kFunct3Shift) | (0b10000 << kFunct5Shift), ++ RO_AMOMAX_D = AMO | (0b011 << kFunct3Shift) | (0b10100 << kFunct5Shift), ++ RO_AMOMINU_D = AMO | (0b011 << kFunct3Shift) | (0b11000 << kFunct5Shift), ++ RO_AMOMAXU_D = AMO | (0b011 << kFunct3Shift) | (0b11100 << kFunct5Shift), ++#endif // JS_CODEGEN_RISCV64 ++}; ++} // namespace jit ++} // namespace js ++ ++#endif // jit_riscv64_constant_Constant_riscv64_a_h_ +diff --git a/js/src/jit/riscv64/constant/Constant-riscv-c.h b/js/src/jit/riscv64/constant/Constant-riscv-c.h +new file mode 100644 +index 0000000000..a7d4792f5f +--- /dev/null ++++ b/js/src/jit/riscv64/constant/Constant-riscv-c.h +@@ -0,0 +1,61 @@ ++// Copyright 2022 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++#ifndef jit_riscv64_constant_Constant_riscv64_c_h_ ++#define jit_riscv64_constant_Constant_riscv64_c_h_ ++ ++#include "jit/riscv64/constant/Base-constant-riscv.h" ++namespace js { ++namespace jit { ++enum OpcodeRISCVC : uint32_t { ++ ++ RO_C_ADDI4SPN = C0 | (0b000 << kRvcFunct3Shift), ++ RO_C_ADDI16SP = C1 | (0b011 << kRvcFunct3Shift), ++ RO_C_LW = C0 | (0b010 << kRvcFunct3Shift), ++ RO_C_SW = C0 | (0b110 << kRvcFunct3Shift), ++ RO_C_NOP_ADDI = C1 | (0b000 << kRvcFunct3Shift), ++ RO_C_LI = C1 | (0b010 << kRvcFunct3Shift), ++ RO_C_SUB = C1 | (0b100011 << kRvcFunct6Shift) | (FUNCT2_0 << kRvcFunct2Shift), ++ RO_C_XOR = C1 | (0b100011 << kRvcFunct6Shift) | (FUNCT2_1 << kRvcFunct2Shift), ++ RO_C_OR = C1 | (0b100011 << kRvcFunct6Shift) | (FUNCT2_2 << kRvcFunct2Shift), ++ RO_C_AND = C1 | (0b100011 << kRvcFunct6Shift) | (FUNCT2_3 << kRvcFunct2Shift), ++ RO_C_LUI_ADD = C1 | (0b011 << kRvcFunct3Shift), ++ RO_C_MISC_ALU = C1 | (0b100 << kRvcFunct3Shift), ++ RO_C_J = C1 | (0b101 << kRvcFunct3Shift), ++ RO_C_BEQZ = C1 | (0b110 << kRvcFunct3Shift), ++ RO_C_BNEZ = C1 | (0b111 << kRvcFunct3Shift), ++ RO_C_SLLI = C2 | (0b000 << kRvcFunct3Shift), ++ RO_C_LWSP = C2 | (0b010 << kRvcFunct3Shift), ++ RO_C_JR_MV_ADD = C2 | (0b100 << kRvcFunct3Shift), ++ RO_C_JR = C2 | (0b1000 << kRvcFunct4Shift), ++ RO_C_MV = C2 | (0b1000 << kRvcFunct4Shift), ++ RO_C_EBREAK = C2 | (0b1001 << kRvcFunct4Shift), ++ RO_C_JALR = C2 | (0b1001 << kRvcFunct4Shift), ++ RO_C_ADD = C2 | (0b1001 << kRvcFunct4Shift), ++ RO_C_SWSP = C2 | (0b110 << kRvcFunct3Shift), ++ ++ RO_C_FSD = C0 | (0b101 << kRvcFunct3Shift), ++ RO_C_FLD = C0 | (0b001 << kRvcFunct3Shift), ++ RO_C_FLDSP = C2 | (0b001 << kRvcFunct3Shift), ++ RO_C_FSDSP = C2 | (0b101 << kRvcFunct3Shift), ++#ifdef JS_CODEGEN_RISCV64 ++ RO_C_LD = C0 | (0b011 << kRvcFunct3Shift), ++ RO_C_SD = C0 | (0b111 << kRvcFunct3Shift), ++ RO_C_LDSP = C2 | (0b011 << kRvcFunct3Shift), ++ RO_C_SDSP = C2 | (0b111 << kRvcFunct3Shift), ++ RO_C_ADDIW = C1 | (0b001 << kRvcFunct3Shift), ++ RO_C_SUBW = ++ C1 | (0b100111 << kRvcFunct6Shift) | (FUNCT2_0 << kRvcFunct2Shift), ++ RO_C_ADDW = ++ C1 | (0b100111 << kRvcFunct6Shift) | (FUNCT2_1 << kRvcFunct2Shift), ++#endif ++#ifdef JS_CODEGEN_RISCV32 ++ RO_C_FLWSP = C2 | (0b011 << kRvcFunct3Shift), ++ RO_C_FSWSP = C2 | (0b111 << kRvcFunct3Shift), ++ RO_C_FLW = C0 | (0b011 << kRvcFunct3Shift), ++ RO_C_FSW = C0 | (0b111 << kRvcFunct3Shift), ++#endif ++}; ++} // namespace jit ++} // namespace js ++#endif // jit_riscv64_constant_Constant_riscv64_c_h_ +diff --git a/js/src/jit/riscv64/constant/Constant-riscv-d.h b/js/src/jit/riscv64/constant/Constant-riscv-d.h +new file mode 100644 +index 0000000000..d97e44ffe5 +--- /dev/null ++++ b/js/src/jit/riscv64/constant/Constant-riscv-d.h +@@ -0,0 +1,55 @@ ++// Copyright 2022 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++#ifndef jit_riscv64_constant_Constant_riscv64_d_h_ ++#define jit_riscv64_constant_Constant_riscv64_d_h_ ++#include "jit/riscv64/constant/Base-constant-riscv.h" ++namespace js { ++namespace jit { ++ ++enum OpcodeRISCVD : uint32_t { ++ // RV32D Standard Extension ++ RO_FLD = LOAD_FP | (0b011 << kFunct3Shift), ++ RO_FSD = STORE_FP | (0b011 << kFunct3Shift), ++ RO_FMADD_D = MADD | (0b01 << kFunct2Shift), ++ RO_FMSUB_D = MSUB | (0b01 << kFunct2Shift), ++ RO_FNMSUB_D = NMSUB | (0b01 << kFunct2Shift), ++ RO_FNMADD_D = NMADD | (0b01 << kFunct2Shift), ++ RO_FADD_D = OP_FP | (0b0000001 << kFunct7Shift), ++ RO_FSUB_D = OP_FP | (0b0000101 << kFunct7Shift), ++ RO_FMUL_D = OP_FP | (0b0001001 << kFunct7Shift), ++ RO_FDIV_D = OP_FP | (0b0001101 << kFunct7Shift), ++ RO_FSQRT_D = OP_FP | (0b0101101 << kFunct7Shift) | (0b00000 << kRs2Shift), ++ RO_FSGNJ_D = OP_FP | (0b000 << kFunct3Shift) | (0b0010001 << kFunct7Shift), ++ RO_FSGNJN_D = OP_FP | (0b001 << kFunct3Shift) | (0b0010001 << kFunct7Shift), ++ RO_FSQNJX_D = OP_FP | (0b010 << kFunct3Shift) | (0b0010001 << kFunct7Shift), ++ RO_FMIN_D = OP_FP | (0b000 << kFunct3Shift) | (0b0010101 << kFunct7Shift), ++ RO_FMAX_D = OP_FP | (0b001 << kFunct3Shift) | (0b0010101 << kFunct7Shift), ++ RO_FCVT_S_D = OP_FP | (0b0100000 << kFunct7Shift) | (0b00001 << kRs2Shift), ++ RO_FCVT_D_S = OP_FP | (0b0100001 << kFunct7Shift) | (0b00000 << kRs2Shift), ++ RO_FEQ_D = OP_FP | (0b010 << kFunct3Shift) | (0b1010001 << kFunct7Shift), ++ RO_FLT_D = OP_FP | (0b001 << kFunct3Shift) | (0b1010001 << kFunct7Shift), ++ RO_FLE_D = OP_FP | (0b000 << kFunct3Shift) | (0b1010001 << kFunct7Shift), ++ RO_FCLASS_D = OP_FP | (0b001 << kFunct3Shift) | (0b1110001 << kFunct7Shift) | ++ (0b00000 << kRs2Shift), ++ RO_FCVT_W_D = OP_FP | (0b1100001 << kFunct7Shift) | (0b00000 << kRs2Shift), ++ RO_FCVT_WU_D = OP_FP | (0b1100001 << kFunct7Shift) | (0b00001 << kRs2Shift), ++ RO_FCVT_D_W = OP_FP | (0b1101001 << kFunct7Shift) | (0b00000 << kRs2Shift), ++ RO_FCVT_D_WU = OP_FP | (0b1101001 << kFunct7Shift) | (0b00001 << kRs2Shift), ++ ++#ifdef JS_CODEGEN_RISCV64 ++ // RV64D Standard Extension (in addition to RV32D) ++ RO_FCVT_L_D = OP_FP | (0b1100001 << kFunct7Shift) | (0b00010 << kRs2Shift), ++ RO_FCVT_LU_D = OP_FP | (0b1100001 << kFunct7Shift) | (0b00011 << kRs2Shift), ++ RO_FMV_X_D = OP_FP | (0b000 << kFunct3Shift) | (0b1110001 << kFunct7Shift) | ++ (0b00000 << kRs2Shift), ++ RO_FCVT_D_L = OP_FP | (0b1101001 << kFunct7Shift) | (0b00010 << kRs2Shift), ++ RO_FCVT_D_LU = OP_FP | (0b1101001 << kFunct7Shift) | (0b00011 << kRs2Shift), ++ RO_FMV_D_X = OP_FP | (0b000 << kFunct3Shift) | (0b1111001 << kFunct7Shift) | ++ (0b00000 << kRs2Shift), ++#endif ++}; ++} // namespace jit ++} // namespace js ++ ++#endif // jit_riscv64_constant_Constant_riscv64_a_h_ +diff --git a/js/src/jit/riscv64/constant/Constant-riscv-f.h b/js/src/jit/riscv64/constant/Constant-riscv-f.h +new file mode 100644 +index 0000000000..28c96394e2 +--- /dev/null ++++ b/js/src/jit/riscv64/constant/Constant-riscv-f.h +@@ -0,0 +1,51 @@ ++// Copyright 2022 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++#ifndef jit_riscv64_constant_Constant_riscv64_f_h_ ++#define jit_riscv64_constant_Constant_riscv64_f_h_ ++#include "jit/riscv64/constant/Base-constant-riscv.h" ++namespace js { ++namespace jit { ++ ++enum OpcodeRISCVF : uint32_t { ++ // RV32F Standard Extension ++ RO_FLW = LOAD_FP | (0b010 << kFunct3Shift), ++ RO_FSW = STORE_FP | (0b010 << kFunct3Shift), ++ RO_FMADD_S = MADD | (0b00 << kFunct2Shift), ++ RO_FMSUB_S = MSUB | (0b00 << kFunct2Shift), ++ RO_FNMSUB_S = NMSUB | (0b00 << kFunct2Shift), ++ RO_FNMADD_S = NMADD | (0b00 << kFunct2Shift), ++ RO_FADD_S = OP_FP | (0b0000000 << kFunct7Shift), ++ RO_FSUB_S = OP_FP | (0b0000100 << kFunct7Shift), ++ RO_FMUL_S = OP_FP | (0b0001000 << kFunct7Shift), ++ RO_FDIV_S = OP_FP | (0b0001100 << kFunct7Shift), ++ RO_FSQRT_S = OP_FP | (0b0101100 << kFunct7Shift) | (0b00000 << kRs2Shift), ++ RO_FSGNJ_S = OP_FP | (0b000 << kFunct3Shift) | (0b0010000 << kFunct7Shift), ++ RO_FSGNJN_S = OP_FP | (0b001 << kFunct3Shift) | (0b0010000 << kFunct7Shift), ++ RO_FSQNJX_S = OP_FP | (0b010 << kFunct3Shift) | (0b0010000 << kFunct7Shift), ++ RO_FMIN_S = OP_FP | (0b000 << kFunct3Shift) | (0b0010100 << kFunct7Shift), ++ RO_FMAX_S = OP_FP | (0b001 << kFunct3Shift) | (0b0010100 << kFunct7Shift), ++ RO_FCVT_W_S = OP_FP | (0b1100000 << kFunct7Shift) | (0b00000 << kRs2Shift), ++ RO_FCVT_WU_S = OP_FP | (0b1100000 << kFunct7Shift) | (0b00001 << kRs2Shift), ++ RO_FMV = OP_FP | (0b1110000 << kFunct7Shift) | (0b000 << kFunct3Shift) | ++ (0b00000 << kRs2Shift), ++ RO_FEQ_S = OP_FP | (0b010 << kFunct3Shift) | (0b1010000 << kFunct7Shift), ++ RO_FLT_S = OP_FP | (0b001 << kFunct3Shift) | (0b1010000 << kFunct7Shift), ++ RO_FLE_S = OP_FP | (0b000 << kFunct3Shift) | (0b1010000 << kFunct7Shift), ++ RO_FCLASS_S = OP_FP | (0b001 << kFunct3Shift) | (0b1110000 << kFunct7Shift), ++ RO_FCVT_S_W = OP_FP | (0b1101000 << kFunct7Shift) | (0b00000 << kRs2Shift), ++ RO_FCVT_S_WU = OP_FP | (0b1101000 << kFunct7Shift) | (0b00001 << kRs2Shift), ++ RO_FMV_W_X = OP_FP | (0b000 << kFunct3Shift) | (0b1111000 << kFunct7Shift), ++ ++#ifdef JS_CODEGEN_RISCV64 ++ // RV64F Standard Extension (in addition to RV32F) ++ RO_FCVT_L_S = OP_FP | (0b1100000 << kFunct7Shift) | (0b00010 << kRs2Shift), ++ RO_FCVT_LU_S = OP_FP | (0b1100000 << kFunct7Shift) | (0b00011 << kRs2Shift), ++ RO_FCVT_S_L = OP_FP | (0b1101000 << kFunct7Shift) | (0b00010 << kRs2Shift), ++ RO_FCVT_S_LU = OP_FP | (0b1101000 << kFunct7Shift) | (0b00011 << kRs2Shift), ++#endif // JS_CODEGEN_RISCV64 ++}; ++} // namespace jit ++} // namespace js ++ ++#endif // jit_riscv64_constant_Constant_riscv64_f_h_ +diff --git a/js/src/jit/riscv64/constant/Constant-riscv-i.h b/js/src/jit/riscv64/constant/Constant-riscv-i.h +new file mode 100644 +index 0000000000..586ffd8a14 +--- /dev/null ++++ b/js/src/jit/riscv64/constant/Constant-riscv-i.h +@@ -0,0 +1,73 @@ ++// Copyright 2022 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++#ifndef jit_riscv64_constant_Constant_riscv64_i_h_ ++#define jit_riscv64_constant_Constant_riscv64_i_h_ ++#include "jit/riscv64/constant/Base-constant-riscv.h" ++namespace js { ++namespace jit { ++ ++enum OpcodeRISCV32I : uint32_t { ++ // Note use RO (RiscV Opcode) prefix ++ // RV32I Base Instruction Set ++ RO_LUI = LUI, ++ RO_AUIPC = AUIPC, ++ RO_JAL = JAL, ++ RO_JALR = JALR | (0b000 << kFunct3Shift), ++ RO_BEQ = BRANCH | (0b000 << kFunct3Shift), ++ RO_BNE = BRANCH | (0b001 << kFunct3Shift), ++ RO_BLT = BRANCH | (0b100 << kFunct3Shift), ++ RO_BGE = BRANCH | (0b101 << kFunct3Shift), ++ RO_BLTU = BRANCH | (0b110 << kFunct3Shift), ++ RO_BGEU = BRANCH | (0b111 << kFunct3Shift), ++ RO_LB = LOAD | (0b000 << kFunct3Shift), ++ RO_LH = LOAD | (0b001 << kFunct3Shift), ++ RO_LW = LOAD | (0b010 << kFunct3Shift), ++ RO_LBU = LOAD | (0b100 << kFunct3Shift), ++ RO_LHU = LOAD | (0b101 << kFunct3Shift), ++ RO_SB = STORE | (0b000 << kFunct3Shift), ++ RO_SH = STORE | (0b001 << kFunct3Shift), ++ RO_SW = STORE | (0b010 << kFunct3Shift), ++ RO_ADDI = OP_IMM | (0b000 << kFunct3Shift), ++ RO_SLTI = OP_IMM | (0b010 << kFunct3Shift), ++ RO_SLTIU = OP_IMM | (0b011 << kFunct3Shift), ++ RO_XORI = OP_IMM | (0b100 << kFunct3Shift), ++ RO_ORI = OP_IMM | (0b110 << kFunct3Shift), ++ RO_ANDI = OP_IMM | (0b111 << kFunct3Shift), ++ RO_SLLI = OP_IMM | (0b001 << kFunct3Shift), ++ RO_SRLI = OP_IMM | (0b101 << kFunct3Shift), ++ // RO_SRAI = OP_IMM | (0b101 << kFunct3Shift), // Same as SRLI, use func7 ++ RO_ADD = OP | (0b000 << kFunct3Shift) | (0b0000000 << kFunct7Shift), ++ RO_SUB = OP | (0b000 << kFunct3Shift) | (0b0100000 << kFunct7Shift), ++ RO_SLL = OP | (0b001 << kFunct3Shift) | (0b0000000 << kFunct7Shift), ++ RO_SLT = OP | (0b010 << kFunct3Shift) | (0b0000000 << kFunct7Shift), ++ RO_SLTU = OP | (0b011 << kFunct3Shift) | (0b0000000 << kFunct7Shift), ++ RO_XOR = OP | (0b100 << kFunct3Shift) | (0b0000000 << kFunct7Shift), ++ RO_SRL = OP | (0b101 << kFunct3Shift) | (0b0000000 << kFunct7Shift), ++ RO_SRA = OP | (0b101 << kFunct3Shift) | (0b0100000 << kFunct7Shift), ++ RO_OR = OP | (0b110 << kFunct3Shift) | (0b0000000 << kFunct7Shift), ++ RO_AND = OP | (0b111 << kFunct3Shift) | (0b0000000 << kFunct7Shift), ++ RO_FENCE = MISC_MEM | (0b000 << kFunct3Shift), ++ RO_ECALL = SYSTEM | (0b000 << kFunct3Shift), ++// RO_EBREAK = SYSTEM | (0b000 << kFunct3Shift), // Same as ECALL, use imm12 ++ ++#if JS_CODEGEN_RISCV64 ++ // RV64I Base Instruction Set (in addition to RV32I) ++ RO_LWU = LOAD | (0b110 << kFunct3Shift), ++ RO_LD = LOAD | (0b011 << kFunct3Shift), ++ RO_SD = STORE | (0b011 << kFunct3Shift), ++ RO_ADDIW = OP_IMM_32 | (0b000 << kFunct3Shift), ++ RO_SLLIW = OP_IMM_32 | (0b001 << kFunct3Shift), ++ RO_SRLIW = OP_IMM_32 | (0b101 << kFunct3Shift), ++ // RO_SRAIW = OP_IMM_32 | (0b101 << kFunct3Shift), // Same as SRLIW, use func7 ++ RO_ADDW = OP_32 | (0b000 << kFunct3Shift) | (0b0000000 << kFunct7Shift), ++ RO_SUBW = OP_32 | (0b000 << kFunct3Shift) | (0b0100000 << kFunct7Shift), ++ RO_SLLW = OP_32 | (0b001 << kFunct3Shift) | (0b0000000 << kFunct7Shift), ++ RO_SRLW = OP_32 | (0b101 << kFunct3Shift) | (0b0000000 << kFunct7Shift), ++ RO_SRAW = OP_32 | (0b101 << kFunct3Shift) | (0b0100000 << kFunct7Shift), ++#endif ++}; ++} // namespace jit ++} // namespace js ++ ++#endif // jit_riscv64_constant_Constant_riscv64_i_h_ +diff --git a/js/src/jit/riscv64/constant/Constant-riscv-m.h b/js/src/jit/riscv64/constant/Constant-riscv-m.h +new file mode 100644 +index 0000000000..81a69dab41 +--- /dev/null ++++ b/js/src/jit/riscv64/constant/Constant-riscv-m.h +@@ -0,0 +1,34 @@ ++// Copyright 2022 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++#ifndef jit_riscv64_constant_Constant_riscv64_m_h_ ++#define jit_riscv64_constant_Constant_riscv64_m_h_ ++ ++#include "jit/riscv64/constant/Base-constant-riscv.h" ++namespace js { ++namespace jit { ++ ++enum OpcodeRISCVM : uint32_t { ++ // RV32M Standard Extension ++ RO_MUL = OP | (0b000 << kFunct3Shift) | (0b0000001 << kFunct7Shift), ++ RO_MULH = OP | (0b001 << kFunct3Shift) | (0b0000001 << kFunct7Shift), ++ RO_MULHSU = OP | (0b010 << kFunct3Shift) | (0b0000001 << kFunct7Shift), ++ RO_MULHU = OP | (0b011 << kFunct3Shift) | (0b0000001 << kFunct7Shift), ++ RO_DIV = OP | (0b100 << kFunct3Shift) | (0b0000001 << kFunct7Shift), ++ RO_DIVU = OP | (0b101 << kFunct3Shift) | (0b0000001 << kFunct7Shift), ++ RO_REM = OP | (0b110 << kFunct3Shift) | (0b0000001 << kFunct7Shift), ++ RO_REMU = OP | (0b111 << kFunct3Shift) | (0b0000001 << kFunct7Shift), ++ ++#ifdef JS_CODEGEN_RISCV64 ++ // RV64M Standard Extension (in addition to RV32M) ++ RO_MULW = OP_32 | (0b000 << kFunct3Shift) | (0b0000001 << kFunct7Shift), ++ RO_DIVW = OP_32 | (0b100 << kFunct3Shift) | (0b0000001 << kFunct7Shift), ++ RO_DIVUW = OP_32 | (0b101 << kFunct3Shift) | (0b0000001 << kFunct7Shift), ++ RO_REMW = OP_32 | (0b110 << kFunct3Shift) | (0b0000001 << kFunct7Shift), ++ RO_REMUW = OP_32 | (0b111 << kFunct3Shift) | (0b0000001 << kFunct7Shift), ++#endif ++}; ++} // namespace jit ++} // namespace js ++ ++#endif // jit_riscv64_extension_CONSTANT_RISCV_M_h_ +diff --git a/js/src/jit/riscv64/constant/Constant-riscv-v.h b/js/src/jit/riscv64/constant/Constant-riscv-v.h +new file mode 100644 +index 0000000000..cca3540efd +--- /dev/null ++++ b/js/src/jit/riscv64/constant/Constant-riscv-v.h +@@ -0,0 +1,508 @@ ++// Copyright 2022 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++#ifndef jit_riscv64_constant_Constant_riscv64_v_h_ ++#define jit_riscv64_constant_Constant_riscv64_v_h_ ++#include "jit/riscv64/constant/Base-constant-riscv.h" ++namespace js { ++namespace jit { ++ ++namespace RVV { ++enum TailAgnosticType { ++ ta = 0x1, // Tail agnostic ++ tu = 0x0, // Tail undisturbed ++}; ++ ++enum MaskAgnosticType { ++ ma = 0x1, // Mask agnostic ++ mu = 0x0, // Mask undisturbed ++}; ++enum MaskType { ++ Mask = 0x0, // use the mask ++ NoMask = 0x1, ++}; ++} // namespace RVV ++ ++enum OpcodeRISCVV : uint32_t { ++ // RVV Extension ++ OP_IVV = OP_V | (0b000 << kFunct3Shift), ++ OP_FVV = OP_V | (0b001 << kFunct3Shift), ++ OP_MVV = OP_V | (0b010 << kFunct3Shift), ++ OP_IVI = OP_V | (0b011 << kFunct3Shift), ++ OP_IVX = OP_V | (0b100 << kFunct3Shift), ++ OP_FVF = OP_V | (0b101 << kFunct3Shift), ++ OP_MVX = OP_V | (0b110 << kFunct3Shift), ++ ++ RO_V_VSETVLI = OP_V | (0b111 << kFunct3Shift) | 0b0 << 31, ++ RO_V_VSETIVLI = OP_V | (0b111 << kFunct3Shift) | 0b11 << 30, ++ RO_V_VSETVL = OP_V | (0b111 << kFunct3Shift) | 0b1 << 31, ++ ++ // RVV LOAD/STORE ++ RO_V_VL = LOAD_FP | (0b00 << kRvvMopShift) | (0b000 << kRvvNfShift), ++ RO_V_VLS = LOAD_FP | (0b10 << kRvvMopShift) | (0b000 << kRvvNfShift), ++ RO_V_VLX = LOAD_FP | (0b11 << kRvvMopShift) | (0b000 << kRvvNfShift), ++ ++ RO_V_VS = STORE_FP | (0b00 << kRvvMopShift) | (0b000 << kRvvNfShift), ++ RO_V_VSS = STORE_FP | (0b10 << kRvvMopShift) | (0b000 << kRvvNfShift), ++ RO_V_VSX = STORE_FP | (0b11 << kRvvMopShift) | (0b000 << kRvvNfShift), ++ RO_V_VSU = STORE_FP | (0b01 << kRvvMopShift) | (0b000 << kRvvNfShift), ++ // THE kFunct6Shift is mop ++ RO_V_VLSEG2 = LOAD_FP | (0b00 << kRvvMopShift) | (0b001 << kRvvNfShift), ++ RO_V_VLSEG3 = LOAD_FP | (0b00 << kRvvMopShift) | (0b010 << kRvvNfShift), ++ RO_V_VLSEG4 = LOAD_FP | (0b00 << kRvvMopShift) | (0b011 << kRvvNfShift), ++ RO_V_VLSEG5 = LOAD_FP | (0b00 << kRvvMopShift) | (0b100 << kRvvNfShift), ++ RO_V_VLSEG6 = LOAD_FP | (0b00 << kRvvMopShift) | (0b101 << kRvvNfShift), ++ RO_V_VLSEG7 = LOAD_FP | (0b00 << kRvvMopShift) | (0b110 << kRvvNfShift), ++ RO_V_VLSEG8 = LOAD_FP | (0b00 << kRvvMopShift) | (0b111 << kRvvNfShift), ++ ++ RO_V_VSSEG2 = STORE_FP | (0b00 << kRvvMopShift) | (0b001 << kRvvNfShift), ++ RO_V_VSSEG3 = STORE_FP | (0b00 << kRvvMopShift) | (0b010 << kRvvNfShift), ++ RO_V_VSSEG4 = STORE_FP | (0b00 << kRvvMopShift) | (0b011 << kRvvNfShift), ++ RO_V_VSSEG5 = STORE_FP | (0b00 << kRvvMopShift) | (0b100 << kRvvNfShift), ++ RO_V_VSSEG6 = STORE_FP | (0b00 << kRvvMopShift) | (0b101 << kRvvNfShift), ++ RO_V_VSSEG7 = STORE_FP | (0b00 << kRvvMopShift) | (0b110 << kRvvNfShift), ++ RO_V_VSSEG8 = STORE_FP | (0b00 << kRvvMopShift) | (0b111 << kRvvNfShift), ++ ++ RO_V_VLSSEG2 = LOAD_FP | (0b10 << kRvvMopShift) | (0b001 << kRvvNfShift), ++ RO_V_VLSSEG3 = LOAD_FP | (0b10 << kRvvMopShift) | (0b010 << kRvvNfShift), ++ RO_V_VLSSEG4 = LOAD_FP | (0b10 << kRvvMopShift) | (0b011 << kRvvNfShift), ++ RO_V_VLSSEG5 = LOAD_FP | (0b10 << kRvvMopShift) | (0b100 << kRvvNfShift), ++ RO_V_VLSSEG6 = LOAD_FP | (0b10 << kRvvMopShift) | (0b101 << kRvvNfShift), ++ RO_V_VLSSEG7 = LOAD_FP | (0b10 << kRvvMopShift) | (0b110 << kRvvNfShift), ++ RO_V_VLSSEG8 = LOAD_FP | (0b10 << kRvvMopShift) | (0b111 << kRvvNfShift), ++ ++ RO_V_VSSSEG2 = STORE_FP | (0b10 << kRvvMopShift) | (0b001 << kRvvNfShift), ++ RO_V_VSSSEG3 = STORE_FP | (0b10 << kRvvMopShift) | (0b010 << kRvvNfShift), ++ RO_V_VSSSEG4 = STORE_FP | (0b10 << kRvvMopShift) | (0b011 << kRvvNfShift), ++ RO_V_VSSSEG5 = STORE_FP | (0b10 << kRvvMopShift) | (0b100 << kRvvNfShift), ++ RO_V_VSSSEG6 = STORE_FP | (0b10 << kRvvMopShift) | (0b101 << kRvvNfShift), ++ RO_V_VSSSEG7 = STORE_FP | (0b10 << kRvvMopShift) | (0b110 << kRvvNfShift), ++ RO_V_VSSSEG8 = STORE_FP | (0b10 << kRvvMopShift) | (0b111 << kRvvNfShift), ++ ++ RO_V_VLXSEG2 = LOAD_FP | (0b11 << kRvvMopShift) | (0b001 << kRvvNfShift), ++ RO_V_VLXSEG3 = LOAD_FP | (0b11 << kRvvMopShift) | (0b010 << kRvvNfShift), ++ RO_V_VLXSEG4 = LOAD_FP | (0b11 << kRvvMopShift) | (0b011 << kRvvNfShift), ++ RO_V_VLXSEG5 = LOAD_FP | (0b11 << kRvvMopShift) | (0b100 << kRvvNfShift), ++ RO_V_VLXSEG6 = LOAD_FP | (0b11 << kRvvMopShift) | (0b101 << kRvvNfShift), ++ RO_V_VLXSEG7 = LOAD_FP | (0b11 << kRvvMopShift) | (0b110 << kRvvNfShift), ++ RO_V_VLXSEG8 = LOAD_FP | (0b11 << kRvvMopShift) | (0b111 << kRvvNfShift), ++ ++ RO_V_VSXSEG2 = STORE_FP | (0b11 << kRvvMopShift) | (0b001 << kRvvNfShift), ++ RO_V_VSXSEG3 = STORE_FP | (0b11 << kRvvMopShift) | (0b010 << kRvvNfShift), ++ RO_V_VSXSEG4 = STORE_FP | (0b11 << kRvvMopShift) | (0b011 << kRvvNfShift), ++ RO_V_VSXSEG5 = STORE_FP | (0b11 << kRvvMopShift) | (0b100 << kRvvNfShift), ++ RO_V_VSXSEG6 = STORE_FP | (0b11 << kRvvMopShift) | (0b101 << kRvvNfShift), ++ RO_V_VSXSEG7 = STORE_FP | (0b11 << kRvvMopShift) | (0b110 << kRvvNfShift), ++ RO_V_VSXSEG8 = STORE_FP | (0b11 << kRvvMopShift) | (0b111 << kRvvNfShift), ++ ++ // RVV Vector Arithmetic Instruction ++ VADD_FUNCT6 = 0b000000, ++ RO_V_VADD_VI = OP_IVI | (VADD_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VADD_VV = OP_IVV | (VADD_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VADD_VX = OP_IVX | (VADD_FUNCT6 << kRvvFunct6Shift), ++ ++ VSUB_FUNCT6 = 0b000010, ++ RO_V_VSUB_VX = OP_IVX | (VSUB_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VSUB_VV = OP_IVV | (VSUB_FUNCT6 << kRvvFunct6Shift), ++ ++ VDIVU_FUNCT6 = 0b100000, ++ RO_V_VDIVU_VX = OP_MVX | (VDIVU_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VDIVU_VV = OP_MVV | (VDIVU_FUNCT6 << kRvvFunct6Shift), ++ ++ VDIV_FUNCT6 = 0b100001, ++ RO_V_VDIV_VX = OP_MVX | (VDIV_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VDIV_VV = OP_MVV | (VDIV_FUNCT6 << kRvvFunct6Shift), ++ ++ VREMU_FUNCT6 = 0b100010, ++ RO_V_VREMU_VX = OP_MVX | (VREMU_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VREMU_VV = OP_MVV | (VREMU_FUNCT6 << kRvvFunct6Shift), ++ ++ VREM_FUNCT6 = 0b100011, ++ RO_V_VREM_VX = OP_MVX | (VREM_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VREM_VV = OP_MVV | (VREM_FUNCT6 << kRvvFunct6Shift), ++ ++ VMULHU_FUNCT6 = 0b100100, ++ RO_V_VMULHU_VX = OP_MVX | (VMULHU_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VMULHU_VV = OP_MVV | (VMULHU_FUNCT6 << kRvvFunct6Shift), ++ ++ VMUL_FUNCT6 = 0b100101, ++ RO_V_VMUL_VX = OP_MVX | (VMUL_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VMUL_VV = OP_MVV | (VMUL_FUNCT6 << kRvvFunct6Shift), ++ ++ VWMUL_FUNCT6 = 0b111011, ++ RO_V_VWMUL_VX = OP_MVX | (VWMUL_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VWMUL_VV = OP_MVV | (VWMUL_FUNCT6 << kRvvFunct6Shift), ++ ++ VWMULU_FUNCT6 = 0b111000, ++ RO_V_VWMULU_VX = OP_MVX | (VWMULU_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VWMULU_VV = OP_MVV | (VWMULU_FUNCT6 << kRvvFunct6Shift), ++ ++ VMULHSU_FUNCT6 = 0b100110, ++ RO_V_VMULHSU_VX = OP_MVX | (VMULHSU_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VMULHSU_VV = OP_MVV | (VMULHSU_FUNCT6 << kRvvFunct6Shift), ++ ++ VMULH_FUNCT6 = 0b100111, ++ RO_V_VMULH_VX = OP_MVX | (VMULH_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VMULH_VV = OP_MVV | (VMULH_FUNCT6 << kRvvFunct6Shift), ++ ++ VWADD_FUNCT6 = 0b110001, ++ RO_V_VWADD_VV = OP_MVV | (VWADD_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VWADD_VX = OP_MVX | (VWADD_FUNCT6 << kRvvFunct6Shift), ++ ++ VWADDU_FUNCT6 = 0b110000, ++ RO_V_VWADDU_VV = OP_MVV | (VWADDU_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VWADDU_VX = OP_MVX | (VWADDU_FUNCT6 << kRvvFunct6Shift), ++ ++ VWADDUW_FUNCT6 = 0b110101, ++ RO_V_VWADDUW_VX = OP_MVX | (VWADDUW_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VWADDUW_VV = OP_MVV | (VWADDUW_FUNCT6 << kRvvFunct6Shift), ++ ++ VCOMPRESS_FUNCT6 = 0b010111, ++ RO_V_VCOMPRESS_VV = OP_MVV | (VCOMPRESS_FUNCT6 << kRvvFunct6Shift), ++ ++ VSADDU_FUNCT6 = 0b100000, ++ RO_V_VSADDU_VI = OP_IVI | (VSADDU_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VSADDU_VV = OP_IVV | (VSADDU_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VSADDU_VX = OP_IVX | (VSADDU_FUNCT6 << kRvvFunct6Shift), ++ ++ VSADD_FUNCT6 = 0b100001, ++ RO_V_VSADD_VI = OP_IVI | (VSADD_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VSADD_VV = OP_IVV | (VSADD_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VSADD_VX = OP_IVX | (VSADD_FUNCT6 << kRvvFunct6Shift), ++ ++ VSSUB_FUNCT6 = 0b100011, ++ RO_V_VSSUB_VV = OP_IVV | (VSSUB_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VSSUB_VX = OP_IVX | (VSSUB_FUNCT6 << kRvvFunct6Shift), ++ ++ VSSUBU_FUNCT6 = 0b100010, ++ RO_V_VSSUBU_VV = OP_IVV | (VSSUBU_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VSSUBU_VX = OP_IVX | (VSSUBU_FUNCT6 << kRvvFunct6Shift), ++ ++ VRSUB_FUNCT6 = 0b000011, ++ RO_V_VRSUB_VX = OP_IVX | (VRSUB_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VRSUB_VI = OP_IVI | (VRSUB_FUNCT6 << kRvvFunct6Shift), ++ ++ VMINU_FUNCT6 = 0b000100, ++ RO_V_VMINU_VX = OP_IVX | (VMINU_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VMINU_VV = OP_IVV | (VMINU_FUNCT6 << kRvvFunct6Shift), ++ ++ VMIN_FUNCT6 = 0b000101, ++ RO_V_VMIN_VX = OP_IVX | (VMIN_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VMIN_VV = OP_IVV | (VMIN_FUNCT6 << kRvvFunct6Shift), ++ ++ VMAXU_FUNCT6 = 0b000110, ++ RO_V_VMAXU_VX = OP_IVX | (VMAXU_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VMAXU_VV = OP_IVV | (VMAXU_FUNCT6 << kRvvFunct6Shift), ++ ++ VMAX_FUNCT6 = 0b000111, ++ RO_V_VMAX_VX = OP_IVX | (VMAX_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VMAX_VV = OP_IVV | (VMAX_FUNCT6 << kRvvFunct6Shift), ++ ++ VAND_FUNCT6 = 0b001001, ++ RO_V_VAND_VI = OP_IVI | (VAND_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VAND_VV = OP_IVV | (VAND_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VAND_VX = OP_IVX | (VAND_FUNCT6 << kRvvFunct6Shift), ++ ++ VOR_FUNCT6 = 0b001010, ++ RO_V_VOR_VI = OP_IVI | (VOR_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VOR_VV = OP_IVV | (VOR_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VOR_VX = OP_IVX | (VOR_FUNCT6 << kRvvFunct6Shift), ++ ++ VXOR_FUNCT6 = 0b001011, ++ RO_V_VXOR_VI = OP_IVI | (VXOR_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VXOR_VV = OP_IVV | (VXOR_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VXOR_VX = OP_IVX | (VXOR_FUNCT6 << kRvvFunct6Shift), ++ ++ VRGATHER_FUNCT6 = 0b001100, ++ RO_V_VRGATHER_VI = OP_IVI | (VRGATHER_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VRGATHER_VV = OP_IVV | (VRGATHER_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VRGATHER_VX = OP_IVX | (VRGATHER_FUNCT6 << kRvvFunct6Shift), ++ ++ VMV_FUNCT6 = 0b010111, ++ RO_V_VMV_VI = OP_IVI | (VMV_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VMV_VV = OP_IVV | (VMV_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VMV_VX = OP_IVX | (VMV_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VFMV_VF = OP_FVF | (VMV_FUNCT6 << kRvvFunct6Shift), ++ ++ RO_V_VMERGE_VI = RO_V_VMV_VI, ++ RO_V_VMERGE_VV = RO_V_VMV_VV, ++ RO_V_VMERGE_VX = RO_V_VMV_VX, ++ ++ VMSEQ_FUNCT6 = 0b011000, ++ RO_V_VMSEQ_VI = OP_IVI | (VMSEQ_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VMSEQ_VV = OP_IVV | (VMSEQ_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VMSEQ_VX = OP_IVX | (VMSEQ_FUNCT6 << kRvvFunct6Shift), ++ ++ VMSNE_FUNCT6 = 0b011001, ++ RO_V_VMSNE_VI = OP_IVI | (VMSNE_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VMSNE_VV = OP_IVV | (VMSNE_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VMSNE_VX = OP_IVX | (VMSNE_FUNCT6 << kRvvFunct6Shift), ++ ++ VMSLTU_FUNCT6 = 0b011010, ++ RO_V_VMSLTU_VV = OP_IVV | (VMSLTU_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VMSLTU_VX = OP_IVX | (VMSLTU_FUNCT6 << kRvvFunct6Shift), ++ ++ VMSLT_FUNCT6 = 0b011011, ++ RO_V_VMSLT_VV = OP_IVV | (VMSLT_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VMSLT_VX = OP_IVX | (VMSLT_FUNCT6 << kRvvFunct6Shift), ++ ++ VMSLE_FUNCT6 = 0b011101, ++ RO_V_VMSLE_VI = OP_IVI | (VMSLE_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VMSLE_VV = OP_IVV | (VMSLE_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VMSLE_VX = OP_IVX | (VMSLE_FUNCT6 << kRvvFunct6Shift), ++ ++ VMSLEU_FUNCT6 = 0b011100, ++ RO_V_VMSLEU_VI = OP_IVI | (VMSLEU_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VMSLEU_VV = OP_IVV | (VMSLEU_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VMSLEU_VX = OP_IVX | (VMSLEU_FUNCT6 << kRvvFunct6Shift), ++ ++ VMSGTU_FUNCT6 = 0b011110, ++ RO_V_VMSGTU_VI = OP_IVI | (VMSGTU_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VMSGTU_VX = OP_IVX | (VMSGTU_FUNCT6 << kRvvFunct6Shift), ++ ++ VMSGT_FUNCT6 = 0b011111, ++ RO_V_VMSGT_VI = OP_IVI | (VMSGT_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VMSGT_VX = OP_IVX | (VMSGT_FUNCT6 << kRvvFunct6Shift), ++ ++ VSLIDEUP_FUNCT6 = 0b001110, ++ RO_V_VSLIDEUP_VI = OP_IVI | (VSLIDEUP_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VSLIDEUP_VX = OP_IVX | (VSLIDEUP_FUNCT6 << kRvvFunct6Shift), ++ ++ VSLIDEDOWN_FUNCT6 = 0b001111, ++ RO_V_VSLIDEDOWN_VI = OP_IVI | (VSLIDEDOWN_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VSLIDEDOWN_VX = OP_IVX | (VSLIDEDOWN_FUNCT6 << kRvvFunct6Shift), ++ ++ VSRL_FUNCT6 = 0b101000, ++ RO_V_VSRL_VI = OP_IVI | (VSRL_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VSRL_VV = OP_IVV | (VSRL_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VSRL_VX = OP_IVX | (VSRL_FUNCT6 << kRvvFunct6Shift), ++ ++ VSRA_FUNCT6 = 0b101001, ++ RO_V_VSRA_VI = OP_IVI | (VSRA_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VSRA_VV = OP_IVV | (VSRA_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VSRA_VX = OP_IVX | (VSRA_FUNCT6 << kRvvFunct6Shift), ++ ++ VSLL_FUNCT6 = 0b100101, ++ RO_V_VSLL_VI = OP_IVI | (VSLL_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VSLL_VV = OP_IVV | (VSLL_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VSLL_VX = OP_IVX | (VSLL_FUNCT6 << kRvvFunct6Shift), ++ ++ VSMUL_FUNCT6 = 0b100111, ++ RO_V_VSMUL_VV = OP_IVV | (VSMUL_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VSMUL_VX = OP_IVX | (VSMUL_FUNCT6 << kRvvFunct6Shift), ++ ++ VADC_FUNCT6 = 0b010000, ++ RO_V_VADC_VI = OP_IVI | (VADC_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VADC_VV = OP_IVV | (VADC_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VADC_VX = OP_IVX | (VADC_FUNCT6 << kRvvFunct6Shift), ++ ++ VMADC_FUNCT6 = 0b010001, ++ RO_V_VMADC_VI = OP_IVI | (VMADC_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VMADC_VV = OP_IVV | (VMADC_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VMADC_VX = OP_IVX | (VMADC_FUNCT6 << kRvvFunct6Shift), ++ ++ VWXUNARY0_FUNCT6 = 0b010000, ++ VRXUNARY0_FUNCT6 = 0b010000, ++ VMUNARY0_FUNCT6 = 0b010100, ++ ++ RO_V_VWXUNARY0 = OP_MVV | (VWXUNARY0_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VRXUNARY0 = OP_MVX | (VRXUNARY0_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VMUNARY0 = OP_MVV | (VMUNARY0_FUNCT6 << kRvvFunct6Shift), ++ ++ VID_V = 0b10001, ++ ++ VXUNARY0_FUNCT6 = 0b010010, ++ RO_V_VXUNARY0 = OP_MVV | (VXUNARY0_FUNCT6 << kRvvFunct6Shift), ++ ++ VWFUNARY0_FUNCT6 = 0b010000, ++ RO_V_VFMV_FS = OP_FVV | (VWFUNARY0_FUNCT6 << kRvvFunct6Shift), ++ ++ VRFUNARY0_FUNCT6 = 0b010000, ++ RO_V_VFMV_SF = OP_FVF | (VRFUNARY0_FUNCT6 << kRvvFunct6Shift), ++ ++ VREDMAXU_FUNCT6 = 0b000110, ++ RO_V_VREDMAXU = OP_MVV | (VREDMAXU_FUNCT6 << kRvvFunct6Shift), ++ VREDMAX_FUNCT6 = 0b000111, ++ RO_V_VREDMAX = OP_MVV | (VREDMAX_FUNCT6 << kRvvFunct6Shift), ++ ++ VREDMINU_FUNCT6 = 0b000100, ++ RO_V_VREDMINU = OP_MVV | (VREDMINU_FUNCT6 << kRvvFunct6Shift), ++ VREDMIN_FUNCT6 = 0b000101, ++ RO_V_VREDMIN = OP_MVV | (VREDMIN_FUNCT6 << kRvvFunct6Shift), ++ ++ VFUNARY0_FUNCT6 = 0b010010, ++ RO_V_VFUNARY0 = OP_FVV | (VFUNARY0_FUNCT6 << kRvvFunct6Shift), ++ VFUNARY1_FUNCT6 = 0b010011, ++ RO_V_VFUNARY1 = OP_FVV | (VFUNARY1_FUNCT6 << kRvvFunct6Shift), ++ ++ VFCVT_XU_F_V = 0b00000, ++ VFCVT_X_F_V = 0b00001, ++ VFCVT_F_XU_V = 0b00010, ++ VFCVT_F_X_V = 0b00011, ++ VFWCVT_XU_F_V = 0b01000, ++ VFWCVT_X_F_V = 0b01001, ++ VFWCVT_F_XU_V = 0b01010, ++ VFWCVT_F_X_V = 0b01011, ++ VFWCVT_F_F_V = 0b01100, ++ VFNCVT_F_F_W = 0b10100, ++ VFNCVT_X_F_W = 0b10001, ++ VFNCVT_XU_F_W = 0b10000, ++ ++ VFCLASS_V = 0b10000, ++ VFSQRT_V = 0b00000, ++ VFRSQRT7_V = 0b00100, ++ VFREC7_V = 0b00101, ++ ++ VFADD_FUNCT6 = 0b000000, ++ RO_V_VFADD_VV = OP_FVV | (VFADD_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VFADD_VF = OP_FVF | (VFADD_FUNCT6 << kRvvFunct6Shift), ++ ++ VFSUB_FUNCT6 = 0b000010, ++ RO_V_VFSUB_VV = OP_FVV | (VFSUB_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VFSUB_VF = OP_FVF | (VFSUB_FUNCT6 << kRvvFunct6Shift), ++ ++ VFDIV_FUNCT6 = 0b100000, ++ RO_V_VFDIV_VV = OP_FVV | (VFDIV_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VFDIV_VF = OP_FVF | (VFDIV_FUNCT6 << kRvvFunct6Shift), ++ ++ VFMUL_FUNCT6 = 0b100100, ++ RO_V_VFMUL_VV = OP_FVV | (VFMUL_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VFMUL_VF = OP_FVF | (VFMUL_FUNCT6 << kRvvFunct6Shift), ++ ++ // Vector Widening Floating-Point Add/Subtract Instructions ++ VFWADD_FUNCT6 = 0b110000, ++ RO_V_VFWADD_VV = OP_FVV | (VFWADD_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VFWADD_VF = OP_FVF | (VFWADD_FUNCT6 << kRvvFunct6Shift), ++ ++ VFWSUB_FUNCT6 = 0b110010, ++ RO_V_VFWSUB_VV = OP_FVV | (VFWSUB_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VFWSUB_VF = OP_FVF | (VFWSUB_FUNCT6 << kRvvFunct6Shift), ++ ++ VFWADD_W_FUNCT6 = 0b110100, ++ RO_V_VFWADD_W_VV = OP_FVV | (VFWADD_W_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VFWADD_W_VF = OP_FVF | (VFWADD_W_FUNCT6 << kRvvFunct6Shift), ++ ++ VFWSUB_W_FUNCT6 = 0b110110, ++ RO_V_VFWSUB_W_VV = OP_FVV | (VFWSUB_W_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VFWSUB_W_VF = OP_FVF | (VFWSUB_W_FUNCT6 << kRvvFunct6Shift), ++ ++ // Vector Widening Floating-Point Reduction Instructions ++ VFWREDUSUM_FUNCT6 = 0b110001, ++ RO_V_VFWREDUSUM_VV = OP_FVV | (VFWREDUSUM_FUNCT6 << kRvvFunct6Shift), ++ ++ VFWREDOSUM_FUNCT6 = 0b110011, ++ RO_V_VFWREDOSUM_VV = OP_FVV | (VFWREDOSUM_FUNCT6 << kRvvFunct6Shift), ++ ++ // Vector Widening Floating-Point Multiply ++ VFWMUL_FUNCT6 = 0b111000, ++ RO_V_VFWMUL_VV = OP_FVV | (VFWMUL_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VFWMUL_VF = OP_FVF | (VFWMUL_FUNCT6 << kRvvFunct6Shift), ++ ++ VMFEQ_FUNCT6 = 0b011000, ++ RO_V_VMFEQ_VV = OP_FVV | (VMFEQ_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VMFEQ_VF = OP_FVF | (VMFEQ_FUNCT6 << kRvvFunct6Shift), ++ ++ VMFNE_FUNCT6 = 0b011100, ++ RO_V_VMFNE_VV = OP_FVV | (VMFNE_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VMFNE_VF = OP_FVF | (VMFNE_FUNCT6 << kRvvFunct6Shift), ++ ++ VMFLT_FUNCT6 = 0b011011, ++ RO_V_VMFLT_VV = OP_FVV | (VMFLT_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VMFLT_VF = OP_FVF | (VMFLT_FUNCT6 << kRvvFunct6Shift), ++ ++ VMFLE_FUNCT6 = 0b011001, ++ RO_V_VMFLE_VV = OP_FVV | (VMFLE_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VMFLE_VF = OP_FVF | (VMFLE_FUNCT6 << kRvvFunct6Shift), ++ ++ VMFGE_FUNCT6 = 0b011111, ++ RO_V_VMFGE_VF = OP_FVF | (VMFGE_FUNCT6 << kRvvFunct6Shift), ++ ++ VMFGT_FUNCT6 = 0b011101, ++ RO_V_VMFGT_VF = OP_FVF | (VMFGT_FUNCT6 << kRvvFunct6Shift), ++ ++ VFMAX_FUNCT6 = 0b000110, ++ RO_V_VFMAX_VV = OP_FVV | (VFMAX_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VFMAX_VF = OP_FVF | (VFMAX_FUNCT6 << kRvvFunct6Shift), ++ ++ VFREDMAX_FUNCT6 = 0b0001111, ++ RO_V_VFREDMAX_VV = OP_FVV | (VFREDMAX_FUNCT6 << kRvvFunct6Shift), ++ ++ VFMIN_FUNCT6 = 0b000100, ++ RO_V_VFMIN_VV = OP_FVV | (VFMIN_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VFMIN_VF = OP_FVF | (VFMIN_FUNCT6 << kRvvFunct6Shift), ++ ++ VFSGNJ_FUNCT6 = 0b001000, ++ RO_V_VFSGNJ_VV = OP_FVV | (VFSGNJ_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VFSGNJ_VF = OP_FVF | (VFSGNJ_FUNCT6 << kRvvFunct6Shift), ++ ++ VFSGNJN_FUNCT6 = 0b001001, ++ RO_V_VFSGNJN_VV = OP_FVV | (VFSGNJN_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VFSGNJN_VF = OP_FVF | (VFSGNJN_FUNCT6 << kRvvFunct6Shift), ++ ++ VFSGNJX_FUNCT6 = 0b001010, ++ RO_V_VFSGNJX_VV = OP_FVV | (VFSGNJX_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VFSGNJX_VF = OP_FVF | (VFSGNJX_FUNCT6 << kRvvFunct6Shift), ++ ++ VFMADD_FUNCT6 = 0b101000, ++ RO_V_VFMADD_VV = OP_FVV | (VFMADD_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VFMADD_VF = OP_FVF | (VFMADD_FUNCT6 << kRvvFunct6Shift), ++ ++ VFNMADD_FUNCT6 = 0b101001, ++ RO_V_VFNMADD_VV = OP_FVV | (VFNMADD_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VFNMADD_VF = OP_FVF | (VFNMADD_FUNCT6 << kRvvFunct6Shift), ++ ++ VFMSUB_FUNCT6 = 0b101010, ++ RO_V_VFMSUB_VV = OP_FVV | (VFMSUB_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VFMSUB_VF = OP_FVF | (VFMSUB_FUNCT6 << kRvvFunct6Shift), ++ ++ VFNMSUB_FUNCT6 = 0b101011, ++ RO_V_VFNMSUB_VV = OP_FVV | (VFNMSUB_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VFNMSUB_VF = OP_FVF | (VFNMSUB_FUNCT6 << kRvvFunct6Shift), ++ ++ VFMACC_FUNCT6 = 0b101100, ++ RO_V_VFMACC_VV = OP_FVV | (VFMACC_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VFMACC_VF = OP_FVF | (VFMACC_FUNCT6 << kRvvFunct6Shift), ++ ++ VFNMACC_FUNCT6 = 0b101101, ++ RO_V_VFNMACC_VV = OP_FVV | (VFNMACC_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VFNMACC_VF = OP_FVF | (VFNMACC_FUNCT6 << kRvvFunct6Shift), ++ ++ VFMSAC_FUNCT6 = 0b101110, ++ RO_V_VFMSAC_VV = OP_FVV | (VFMSAC_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VFMSAC_VF = OP_FVF | (VFMSAC_FUNCT6 << kRvvFunct6Shift), ++ ++ VFNMSAC_FUNCT6 = 0b101111, ++ RO_V_VFNMSAC_VV = OP_FVV | (VFNMSAC_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VFNMSAC_VF = OP_FVF | (VFNMSAC_FUNCT6 << kRvvFunct6Shift), ++ ++ // Vector Widening Floating-Point Fused Multiply-Add Instructions ++ VFWMACC_FUNCT6 = 0b111100, ++ RO_V_VFWMACC_VV = OP_FVV | (VFWMACC_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VFWMACC_VF = OP_FVF | (VFWMACC_FUNCT6 << kRvvFunct6Shift), ++ ++ VFWNMACC_FUNCT6 = 0b111101, ++ RO_V_VFWNMACC_VV = OP_FVV | (VFWNMACC_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VFWNMACC_VF = OP_FVF | (VFWNMACC_FUNCT6 << kRvvFunct6Shift), ++ ++ VFWMSAC_FUNCT6 = 0b111110, ++ RO_V_VFWMSAC_VV = OP_FVV | (VFWMSAC_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VFWMSAC_VF = OP_FVF | (VFWMSAC_FUNCT6 << kRvvFunct6Shift), ++ ++ VFWNMSAC_FUNCT6 = 0b111111, ++ RO_V_VFWNMSAC_VV = OP_FVV | (VFWNMSAC_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VFWNMSAC_VF = OP_FVF | (VFWNMSAC_FUNCT6 << kRvvFunct6Shift), ++ ++ VNCLIP_FUNCT6 = 0b101111, ++ RO_V_VNCLIP_WV = OP_IVV | (VNCLIP_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VNCLIP_WX = OP_IVX | (VNCLIP_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VNCLIP_WI = OP_IVI | (VNCLIP_FUNCT6 << kRvvFunct6Shift), ++ ++ VNCLIPU_FUNCT6 = 0b101110, ++ RO_V_VNCLIPU_WV = OP_IVV | (VNCLIPU_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VNCLIPU_WX = OP_IVX | (VNCLIPU_FUNCT6 << kRvvFunct6Shift), ++ RO_V_VNCLIPU_WI = OP_IVI | (VNCLIPU_FUNCT6 << kRvvFunct6Shift), ++}; ++} // namespace jit ++} // namespace js ++ ++#endif // jit_riscv64_constant_Constant_riscv64_v_h_ +diff --git a/js/src/jit/riscv64/constant/Constant-riscv-zicsr.h b/js/src/jit/riscv64/constant/Constant-riscv-zicsr.h +new file mode 100644 +index 0000000000..6fecfa3d92 +--- /dev/null ++++ b/js/src/jit/riscv64/constant/Constant-riscv-zicsr.h +@@ -0,0 +1,30 @@ ++// Copyright 2022 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++#ifndef jit_riscv64_constant_Constant_riscv64_zicsr_h_ ++#define jit_riscv64_constant_Constant_riscv64_zicsr_h_ ++ ++#include "jit/riscv64/constant/Base-constant-riscv.h" ++namespace js { ++namespace jit { ++// RISCV CSR related bit mask and shift ++const int kFcsrFlagsBits = 5; ++const uint32_t kFcsrFlagsMask = (1 << kFcsrFlagsBits) - 1; ++const int kFcsrFrmBits = 3; ++const int kFcsrFrmShift = kFcsrFlagsBits; ++const uint32_t kFcsrFrmMask = ((1 << kFcsrFrmBits) - 1) << kFcsrFrmShift; ++const int kFcsrBits = kFcsrFlagsBits + kFcsrFrmBits; ++const uint32_t kFcsrMask = kFcsrFlagsMask | kFcsrFrmMask; ++ ++enum OpcodeRISCVZICSR : uint32_t { ++ // RV32/RV64 Zicsr Standard Extension ++ RO_CSRRW = SYSTEM | (0b001 << kFunct3Shift), ++ RO_CSRRS = SYSTEM | (0b010 << kFunct3Shift), ++ RO_CSRRC = SYSTEM | (0b011 << kFunct3Shift), ++ RO_CSRRWI = SYSTEM | (0b101 << kFunct3Shift), ++ RO_CSRRSI = SYSTEM | (0b110 << kFunct3Shift), ++ RO_CSRRCI = SYSTEM | (0b111 << kFunct3Shift), ++}; ++} // namespace jit ++} // namespace js ++#endif // jit_riscv64_constant_Constant_riscv64_zicsr_h_ +diff --git a/js/src/jit/riscv64/constant/Constant-riscv-zifencei.h b/js/src/jit/riscv64/constant/Constant-riscv-zifencei.h +new file mode 100644 +index 0000000000..be01cd0ae0 +--- /dev/null ++++ b/js/src/jit/riscv64/constant/Constant-riscv-zifencei.h +@@ -0,0 +1,15 @@ ++// Copyright 2022 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++#ifndef jit_riscv64_constant_Constant_riscv64_zifencei_h_ ++#define jit_riscv64_constant_Constant_riscv64_zifencei_h_ ++ ++#include "jit/riscv64/constant/Base-constant-riscv.h" ++namespace js { ++namespace jit { ++enum OpcodeRISCVIFENCEI : uint32_t { ++ RO_FENCE_I = MISC_MEM | (0b001 << kFunct3Shift), ++}; ++} ++} // namespace js ++#endif // jit_riscv64_constant_Constant_riscv64_zifencei_h_ +diff --git a/js/src/jit/riscv64/constant/Constant-riscv64.h b/js/src/jit/riscv64/constant/Constant-riscv64.h +new file mode 100644 +index 0000000000..b9b1f894e7 +--- /dev/null ++++ b/js/src/jit/riscv64/constant/Constant-riscv64.h +@@ -0,0 +1,68 @@ ++/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- ++ * vim: set ts=8 sts=2 et sw=2 tw=80: ++ * 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_riscv64_constant_Constant_riscv64_h ++#define jit_riscv64_constant_Constant_riscv64_h ++#include "mozilla/Assertions.h" ++#include "mozilla/Types.h" ++ ++#include ++ ++#include "jit/riscv64/constant/Base-constant-riscv.h" ++#include "jit/riscv64/constant/Constant-riscv-a.h" ++#include "jit/riscv64/constant/Constant-riscv-c.h" ++#include "jit/riscv64/constant/Constant-riscv-d.h" ++#include "jit/riscv64/constant/Constant-riscv-f.h" ++#include "jit/riscv64/constant/Constant-riscv-i.h" ++#include "jit/riscv64/constant/Constant-riscv-m.h" ++#include "jit/riscv64/constant/Constant-riscv-v.h" ++#include "jit/riscv64/constant/Constant-riscv-zicsr.h" ++#include "jit/riscv64/constant/Constant-riscv-zifencei.h" ++ ++namespace js { ++namespace jit { ++ ++// A reasonable (ie, safe) buffer size for the disassembly of a single ++// instruction. ++const int ReasonableBufferSize = 256; ++ ++// Difference between address of current opcode and value read from pc ++// register. ++static constexpr int kPcLoadDelta = 4; ++ ++// Bits available for offset field in branches ++static constexpr int kBranchOffsetBits = 13; ++ ++// Bits available for offset field in jump ++static constexpr int kJumpOffsetBits = 21; ++ ++// Bits available for offset field in compresed jump ++static constexpr int kCJalOffsetBits = 12; ++ ++// Bits available for offset field in 4 branch ++static constexpr int kCBranchOffsetBits = 9; ++ ++// Max offset for b instructions with 12-bit offset field (multiple of 2) ++static constexpr int kMaxBranchOffset = (1 << (kBranchOffsetBits - 1)) - 1; ++ ++static constexpr int kCBranchOffset = (1 << (kCBranchOffsetBits - 1)) - 1; ++// Max offset for jal instruction with 20-bit offset field (multiple of 2) ++static constexpr int kMaxJumpOffset = (1 << (kJumpOffsetBits - 1)) - 1; ++ ++static constexpr int kCJumpOffset = (1 << (kCJalOffsetBits - 1)) - 1; ++ ++static constexpr int kTrampolineSlotsSize = 2 * kInstrSize; ++ ++static_assert(kCJalOffsetBits == kOffset12); ++static_assert(kCBranchOffsetBits == kOffset9); ++static_assert(kJumpOffsetBits == kOffset21); ++static_assert(kBranchOffsetBits == kOffset13); ++// Vector as used by the original code to allow for minimal modification. ++// Functions exactly like a character array with helper methods. ++} // namespace jit ++} // namespace js ++ ++#endif // jit_riscv64_constant_Constant_riscv64_h +diff --git a/js/src/jit/riscv64/constant/util-riscv64.h b/js/src/jit/riscv64/constant/util-riscv64.h +new file mode 100644 +index 0000000000..5f145d9ded +--- /dev/null ++++ b/js/src/jit/riscv64/constant/util-riscv64.h +@@ -0,0 +1,79 @@ ++ ++// Copyright 2022 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++#ifndef jit_riscv64_constant_util_riscv64__h_ ++#define jit_riscv64_constant_util_riscv64__h_ ++namespace js { ++namespace jit { ++template ++class V8Vector { ++ public: ++ V8Vector() : start_(nullptr), length_(0) {} ++ V8Vector(T* data, int length) : start_(data), length_(length) { ++ MOZ_ASSERT(length == 0 || (length > 0 && data != nullptr)); ++ } ++ ++ // Returns the length of the vector. ++ int length() const { return length_; } ++ ++ // Returns the pointer to the start of the data in the vector. ++ T* start() const { return start_; } ++ ++ // Access individual vector elements - checks bounds in debug mode. ++ T& operator[](int index) const { ++ MOZ_ASSERT(0 <= index && index < length_); ++ return start_[index]; ++ } ++ ++ inline V8Vector operator+(int offset) { ++ MOZ_ASSERT(offset < length_); ++ return V8Vector(start_ + offset, length_ - offset); ++ } ++ ++ private: ++ T* start_; ++ int length_; ++}; ++ ++template ++class EmbeddedVector : public V8Vector { ++ public: ++ EmbeddedVector() : V8Vector(buffer_, kSize) {} ++ ++ explicit EmbeddedVector(T initial_value) : V8Vector(buffer_, kSize) { ++ for (int i = 0; i < kSize; ++i) { ++ buffer_[i] = initial_value; ++ } ++ } ++ ++ // When copying, make underlying Vector to reference our buffer. ++ EmbeddedVector(const EmbeddedVector& rhs) : V8Vector(rhs) { ++ MemCopy(buffer_, rhs.buffer_, sizeof(T) * kSize); ++ this->set_start(buffer_); ++ } ++ ++ EmbeddedVector& operator=(const EmbeddedVector& rhs) { ++ if (this == &rhs) return *this; ++ V8Vector::operator=(rhs); ++ MemCopy(buffer_, rhs.buffer_, sizeof(T) * kSize); ++ this->set_start(buffer_); ++ return *this; ++ } ++ ++ private: ++ T buffer_[kSize]; ++}; ++ ++// Helper function for printing to a Vector. ++static inline int MOZ_FORMAT_PRINTF(2, 3) ++ SNPrintF(V8Vector str, const char* format, ...) { ++ va_list args; ++ va_start(args, format); ++ int result = vsnprintf(str.start(), str.length(), format, args); ++ va_end(args); ++ return result; ++} ++} // namespace jit ++} // namespace js ++#endif +diff --git a/js/src/jit/riscv64/disasm/Disasm-riscv64.cpp b/js/src/jit/riscv64/disasm/Disasm-riscv64.cpp +new file mode 100644 +index 0000000000..bd9770d074 +--- /dev/null ++++ b/js/src/jit/riscv64/disasm/Disasm-riscv64.cpp +@@ -0,0 +1,2155 @@ ++/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- ++ * vim: set ts=8 sts=2 et sw=2 tw=80: ++ */ ++// Copyright 2011 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++// A Disassembler object is used to disassemble a block of code instruction by ++// instruction. The default implementation of the NameConverter object can be ++// overriden to modify register names or to do symbol lookup on addresses. ++// ++// The example below will disassemble a block of code and print it to stdout. ++// ++// disasm::NameConverter converter; ++// disasm::Disassembler d(converter); ++// for (uint8_t* pc = begin; pc < end;) { ++// disasm::EmbeddedVector buffer; ++// uint8_t* prev_pc = pc; ++// pc += d.InstructionDecode(buffer, pc); ++// printf("%p %08x %s\n", ++// prev_pc, *reinterpret_cast(prev_pc), buffer); ++// } ++// ++// The Disassembler class also has a convenience method to disassemble a block ++// of code into a FILE*, meaning that the above functionality could also be ++// achieved by just calling Disassembler::Disassemble(stdout, begin, end); ++ ++#include "jit/riscv64/disasm/Disasm-riscv64.h" ++ ++#include ++#include ++#include ++ ++#include "jit/riscv64/Assembler-riscv64.h" ++ ++namespace js { ++namespace jit { ++namespace disasm { ++ ++#define UNSUPPORTED_RISCV() printf("Unsupported instruction %d.\n", __LINE__) ++//------------------------------------------------------------------------------ ++ ++// Decoder decodes and disassembles instructions into an output buffer. ++// It uses the converter to convert register names and call destinations into ++// more informative description. ++class Decoder { ++ public: ++ Decoder(const disasm::NameConverter& converter, V8Vector out_buffer) ++ : converter_(converter), out_buffer_(out_buffer), out_buffer_pos_(0) { ++ out_buffer_[out_buffer_pos_] = '\0'; ++ } ++ ++ ~Decoder() {} ++ ++ // Writes one disassembled instruction into 'buffer' (0-terminated). ++ // Returns the length of the disassembled machine instruction in bytes. ++ int InstructionDecode(uint8_t* instruction); ++ ++ static bool IsConstantPoolAt(uint8_t* instr_ptr); ++ static int ConstantPoolSizeAt(uint8_t* instr_ptr); ++ ++ private: ++ // Bottleneck functions to print into the out_buffer. ++ void PrintChar(const char ch); ++ void Print(const char* str); ++ ++ // Printing of common values. ++ void PrintRegister(int reg); ++ void PrintFPURegister(int freg); ++ void PrintVRegister(int reg); ++ void PrintFPUStatusRegister(int freg); ++ void PrintRs1(Instruction* instr); ++ void PrintRs2(Instruction* instr); ++ void PrintRd(Instruction* instr); ++ void PrintUimm(Instruction* instr); ++ void PrintVs1(Instruction* instr); ++ void PrintVs2(Instruction* instr); ++ void PrintVd(Instruction* instr); ++ void PrintFRs1(Instruction* instr); ++ void PrintFRs2(Instruction* instr); ++ void PrintFRs3(Instruction* instr); ++ void PrintFRd(Instruction* instr); ++ void PrintImm12(Instruction* instr); ++ void PrintImm12X(Instruction* instr); ++ void PrintImm20U(Instruction* instr); ++ void PrintImm20J(Instruction* instr); ++ void PrintShamt(Instruction* instr); ++ void PrintShamt32(Instruction* instr); ++ void PrintRvcImm6(Instruction* instr); ++ void PrintRvcImm6U(Instruction* instr); ++ void PrintRvcImm6Addi16sp(Instruction* instr); ++ void PrintRvcShamt(Instruction* instr); ++ void PrintRvcImm6Ldsp(Instruction* instr); ++ void PrintRvcImm6Lwsp(Instruction* instr); ++ void PrintRvcImm6Sdsp(Instruction* instr); ++ void PrintRvcImm6Swsp(Instruction* instr); ++ void PrintRvcImm5W(Instruction* instr); ++ void PrintRvcImm5D(Instruction* instr); ++ void PrintRvcImm8Addi4spn(Instruction* instr); ++ void PrintRvcImm11CJ(Instruction* instr); ++ void PrintRvcImm8B(Instruction* instr); ++ void PrintRvvVm(Instruction* instr); ++ void PrintAcquireRelease(Instruction* instr); ++ void PrintBranchOffset(Instruction* instr); ++ void PrintStoreOffset(Instruction* instr); ++ void PrintCSRReg(Instruction* instr); ++ void PrintRvvSEW(Instruction* instr); ++ void PrintRvvLMUL(Instruction* instr); ++ void PrintRvvSimm5(Instruction* instr); ++ void PrintRvvUimm5(Instruction* instr); ++ void PrintRoundingMode(Instruction* instr); ++ void PrintMemoryOrder(Instruction* instr, bool is_pred); ++ ++ // Each of these functions decodes one particular instruction type. ++ void DecodeRType(Instruction* instr); ++ void DecodeR4Type(Instruction* instr); ++ void DecodeRAType(Instruction* instr); ++ void DecodeRFPType(Instruction* instr); ++ void DecodeIType(Instruction* instr); ++ void DecodeSType(Instruction* instr); ++ void DecodeBType(Instruction* instr); ++ void DecodeUType(Instruction* instr); ++ void DecodeJType(Instruction* instr); ++ void DecodeCRType(Instruction* instr); ++ void DecodeCAType(Instruction* instr); ++ void DecodeCIType(Instruction* instr); ++ void DecodeCIWType(Instruction* instr); ++ void DecodeCSSType(Instruction* instr); ++ void DecodeCLType(Instruction* instr); ++ void DecodeCSType(Instruction* instr); ++ void DecodeCJType(Instruction* instr); ++ void DecodeCBType(Instruction* instr); ++ ++ // Printing of instruction name. ++ void PrintInstructionName(Instruction* instr); ++ void PrintTarget(Instruction* instr); ++ ++ // Handle formatting of instructions and their options. ++ int FormatRegister(Instruction* instr, const char* option); ++ int FormatFPURegisterOrRoundMode(Instruction* instr, const char* option); ++ int FormatRvcRegister(Instruction* instr, const char* option); ++ int FormatRvcImm(Instruction* instr, const char* option); ++ int FormatOption(Instruction* instr, const char* option); ++ void Format(Instruction* instr, const char* format); ++ void Unknown(Instruction* instr); ++ ++ int switch_sew(Instruction* instr); ++ int switch_nf(Instruction* instr); ++ ++ const disasm::NameConverter& converter_; ++ V8Vector out_buffer_; ++ int out_buffer_pos_; ++ ++ // Disallow copy and assign. ++ Decoder(const Decoder&) = delete; ++ void operator=(const Decoder&) = delete; ++}; ++ ++// Support for assertions in the Decoder formatting functions. ++#define STRING_STARTS_WITH(string, compare_string) \ ++ (strncmp(string, compare_string, strlen(compare_string)) == 0) ++ ++// Append the ch to the output buffer. ++void Decoder::PrintChar(const char ch) { out_buffer_[out_buffer_pos_++] = ch; } ++ ++// Append the str to the output buffer. ++void Decoder::Print(const char* str) { ++ char cur = *str++; ++ while (cur != '\0' && (out_buffer_pos_ < int(out_buffer_.length() - 1))) { ++ PrintChar(cur); ++ cur = *str++; ++ } ++ out_buffer_[out_buffer_pos_] = 0; ++} ++ ++int Decoder::switch_nf(Instruction* instr) { ++ int nf = 0; ++ switch (instr->InstructionBits() & kRvvNfMask) { ++ case 0x20000000: ++ nf = 2; ++ break; ++ case 0x40000000: ++ nf = 3; ++ break; ++ case 0x60000000: ++ nf = 4; ++ break; ++ case 0x80000000: ++ nf = 5; ++ break; ++ case 0xa0000000: ++ nf = 6; ++ break; ++ case 0xc0000000: ++ nf = 7; ++ break; ++ case 0xe0000000: ++ nf = 8; ++ break; ++ } ++ return nf; ++} ++ ++int Decoder::switch_sew(Instruction* instr) { ++ int width = 0; ++ if ((instr->InstructionBits() & kBaseOpcodeMask) != LOAD_FP && ++ (instr->InstructionBits() & kBaseOpcodeMask) != STORE_FP) ++ return -1; ++ switch (instr->InstructionBits() & (kRvvWidthMask | kRvvMewMask)) { ++ case 0x0: ++ width = 8; ++ break; ++ case 0x00005000: ++ width = 16; ++ break; ++ case 0x00006000: ++ width = 32; ++ break; ++ case 0x00007000: ++ width = 64; ++ break; ++ case 0x10000000: ++ width = 128; ++ break; ++ case 0x10005000: ++ width = 256; ++ break; ++ case 0x10006000: ++ width = 512; ++ break; ++ case 0x10007000: ++ width = 1024; ++ break; ++ default: ++ width = -1; ++ break; ++ } ++ return width; ++} ++ ++// Handle all register based formatting in this function to reduce the ++// complexity of FormatOption. ++int Decoder::FormatRegister(Instruction* instr, const char* format) { ++ MOZ_ASSERT(format[0] == 'r'); ++ if (format[1] == 's') { // 'rs[12]: Rs register. ++ if (format[2] == '1') { ++ int reg = instr->Rs1Value(); ++ PrintRegister(reg); ++ return 3; ++ } else if (format[2] == '2') { ++ int reg = instr->Rs2Value(); ++ PrintRegister(reg); ++ return 3; ++ } ++ MOZ_CRASH(); ++ } else if (format[1] == 'd') { // 'rd: rd register. ++ int reg = instr->RdValue(); ++ PrintRegister(reg); ++ return 2; ++ } ++ MOZ_CRASH(); ++} ++ ++// Handle all FPUregister based formatting in this function to reduce the ++// complexity of FormatOption. ++int Decoder::FormatFPURegisterOrRoundMode(Instruction* instr, ++ const char* format) { ++ MOZ_ASSERT(format[0] == 'f'); ++ if (format[1] == 's') { // 'fs[1-3]: Rs register. ++ if (format[2] == '1') { ++ int reg = instr->Rs1Value(); ++ PrintFPURegister(reg); ++ return 3; ++ } else if (format[2] == '2') { ++ int reg = instr->Rs2Value(); ++ PrintFPURegister(reg); ++ return 3; ++ } else if (format[2] == '3') { ++ int reg = instr->Rs3Value(); ++ PrintFPURegister(reg); ++ return 3; ++ } ++ MOZ_CRASH(); ++ } else if (format[1] == 'd') { // 'fd: fd register. ++ int reg = instr->RdValue(); ++ PrintFPURegister(reg); ++ return 2; ++ } else if (format[1] == 'r') { // 'frm ++ MOZ_ASSERT(STRING_STARTS_WITH(format, "frm")); ++ PrintRoundingMode(instr); ++ return 3; ++ } ++ MOZ_CRASH(); ++} ++ ++// Handle all C extension register based formatting in this function to reduce ++// the complexity of FormatOption. ++int Decoder::FormatRvcRegister(Instruction* instr, const char* format) { ++ MOZ_ASSERT(format[0] == 'C'); ++ MOZ_ASSERT(format[1] == 'r' || format[1] == 'f'); ++ if (format[2] == 's') { // 'Crs[12]: Rs register. ++ if (format[3] == '1') { ++ if (format[4] == 's') { // 'Crs1s: 3-bits register ++ int reg = instr->RvcRs1sValue(); ++ if (format[1] == 'r') { ++ PrintRegister(reg); ++ } else if (format[1] == 'f') { ++ PrintFPURegister(reg); ++ } ++ return 5; ++ } ++ int reg = instr->RvcRs1Value(); ++ if (format[1] == 'r') { ++ PrintRegister(reg); ++ } else if (format[1] == 'f') { ++ PrintFPURegister(reg); ++ } ++ return 4; ++ } else if (format[3] == '2') { ++ if (format[4] == 's') { // 'Crs2s: 3-bits register ++ int reg = instr->RvcRs2sValue(); ++ if (format[1] == 'r') { ++ PrintRegister(reg); ++ } else if (format[1] == 'f') { ++ PrintFPURegister(reg); ++ } ++ return 5; ++ } ++ int reg = instr->RvcRs2Value(); ++ if (format[1] == 'r') { ++ PrintRegister(reg); ++ } else if (format[1] == 'f') { ++ PrintFPURegister(reg); ++ } ++ return 4; ++ } ++ MOZ_CRASH(); ++ } else if (format[2] == 'd') { // 'Crd: rd register. ++ int reg = instr->RvcRdValue(); ++ if (format[1] == 'r') { ++ PrintRegister(reg); ++ } else if (format[1] == 'f') { ++ PrintFPURegister(reg); ++ } ++ return 3; ++ } ++ MOZ_CRASH(); ++} ++ ++// Handle all C extension immediates based formatting in this function to reduce ++// the complexity of FormatOption. ++int Decoder::FormatRvcImm(Instruction* instr, const char* format) { ++ // TODO(riscv): add other rvc imm format ++ MOZ_ASSERT(STRING_STARTS_WITH(format, "Cimm")); ++ if (format[4] == '6') { ++ if (format[5] == 'U') { ++ MOZ_ASSERT(STRING_STARTS_WITH(format, "Cimm6U")); ++ PrintRvcImm6U(instr); ++ return 6; ++ } else if (format[5] == 'A') { ++ if (format[9] == '1' && format[10] == '6') { ++ MOZ_ASSERT(STRING_STARTS_WITH(format, "Cimm6Addi16sp")); ++ PrintRvcImm6Addi16sp(instr); ++ return 13; ++ } ++ MOZ_CRASH(); ++ } else if (format[5] == 'L') { ++ if (format[6] == 'd') { ++ if (format[7] == 's') { ++ MOZ_ASSERT(STRING_STARTS_WITH(format, "Cimm6Ldsp")); ++ PrintRvcImm6Ldsp(instr); ++ return 9; ++ } ++ } else if (format[6] == 'w') { ++ if (format[7] == 's') { ++ MOZ_ASSERT(STRING_STARTS_WITH(format, "Cimm6Lwsp")); ++ PrintRvcImm6Lwsp(instr); ++ return 9; ++ } ++ } ++ MOZ_CRASH(); ++ } else if (format[5] == 'S') { ++ if (format[6] == 'w') { ++ MOZ_ASSERT(STRING_STARTS_WITH(format, "Cimm6Swsp")); ++ PrintRvcImm6Swsp(instr); ++ return 9; ++ } else if (format[6] == 'd') { ++ MOZ_ASSERT(STRING_STARTS_WITH(format, "Cimm6Sdsp")); ++ PrintRvcImm6Sdsp(instr); ++ return 9; ++ } ++ MOZ_CRASH(); ++ } ++ PrintRvcImm6(instr); ++ return 5; ++ } else if (format[4] == '5') { ++ MOZ_ASSERT(STRING_STARTS_WITH(format, "Cimm5")); ++ if (format[5] == 'W') { ++ MOZ_ASSERT(STRING_STARTS_WITH(format, "Cimm5W")); ++ PrintRvcImm5W(instr); ++ return 6; ++ } else if (format[5] == 'D') { ++ MOZ_ASSERT(STRING_STARTS_WITH(format, "Cimm5D")); ++ PrintRvcImm5D(instr); ++ return 6; ++ } ++ MOZ_CRASH(); ++ } else if (format[4] == '8') { ++ MOZ_ASSERT(STRING_STARTS_WITH(format, "Cimm8")); ++ if (format[5] == 'A') { ++ MOZ_ASSERT(STRING_STARTS_WITH(format, "Cimm8Addi4spn")); ++ PrintRvcImm8Addi4spn(instr); ++ return 13; ++ } else if (format[5] == 'B') { ++ MOZ_ASSERT(STRING_STARTS_WITH(format, "Cimm8B")); ++ PrintRvcImm8B(instr); ++ return 6; ++ } ++ MOZ_CRASH(); ++ } else if (format[4] == '1') { ++ MOZ_ASSERT(STRING_STARTS_WITH(format, "Cimm1")); ++ if (format[5] == '1') { ++ MOZ_ASSERT(STRING_STARTS_WITH(format, "Cimm11CJ")); ++ PrintRvcImm11CJ(instr); ++ return 8; ++ } ++ MOZ_CRASH(); ++ } ++ MOZ_CRASH(); ++} ++ ++// FormatOption takes a formatting string and interprets it based on ++// the current instructions. The format string points to the first ++// character of the option string (the option escape has already been ++// consumed by the caller.) FormatOption returns the number of ++// characters that were consumed from the formatting string. ++int Decoder::FormatOption(Instruction* instr, const char* format) { ++ switch (format[0]) { ++ case 'C': { // `C extension ++ if (format[1] == 'r' || format[1] == 'f') { ++ return FormatRvcRegister(instr, format); ++ } else if (format[1] == 'i') { ++ return FormatRvcImm(instr, format); ++ } else if (format[1] == 's') { ++ MOZ_ASSERT(STRING_STARTS_WITH(format, "Cshamt")); ++ PrintRvcShamt(instr); ++ return 6; ++ } ++ MOZ_CRASH(); ++ } ++ case 'c': { // `csr: CSR registers ++ if (format[1] == 's') { ++ if (format[2] == 'r') { ++ PrintCSRReg(instr); ++ return 3; ++ } ++ } ++ MOZ_CRASH(); ++ } ++ case 'i': { // 'imm12, 'imm12x, 'imm20U, or 'imm20J: Immediates. ++ if (format[3] == '1') { ++ if (format[4] == '2') { ++ MOZ_ASSERT(STRING_STARTS_WITH(format, "imm12")); ++ if (format[5] == 'x') { ++ PrintImm12X(instr); ++ return 6; ++ } ++ PrintImm12(instr); ++ return 5; ++ } ++ } else if (format[3] == '2' && format[4] == '0') { ++ MOZ_ASSERT(STRING_STARTS_WITH(format, "imm20")); ++ switch (format[5]) { ++ case 'U': ++ MOZ_ASSERT(STRING_STARTS_WITH(format, "imm20U")); ++ PrintImm20U(instr); ++ break; ++ case 'J': ++ MOZ_ASSERT(STRING_STARTS_WITH(format, "imm20J")); ++ PrintImm20J(instr); ++ break; ++ } ++ return 6; ++ } ++ MOZ_CRASH(); ++ } ++ case 'o': { // 'offB or 'offS: Offsets. ++ if (format[3] == 'B') { ++ MOZ_ASSERT(STRING_STARTS_WITH(format, "offB")); ++ PrintBranchOffset(instr); ++ return 4; ++ } else if (format[3] == 'S') { ++ MOZ_ASSERT(STRING_STARTS_WITH(format, "offS")); ++ PrintStoreOffset(instr); ++ return 4; ++ } ++ MOZ_CRASH(); ++ } ++ case 'r': { // 'r: registers. ++ return FormatRegister(instr, format); ++ } ++ case 'f': { // 'f: FPUregisters or `frm ++ return FormatFPURegisterOrRoundMode(instr, format); ++ } ++ case 'a': { // 'a: Atomic acquire and release. ++ PrintAcquireRelease(instr); ++ return 1; ++ } ++ case 'p': { // `pre ++ MOZ_ASSERT(STRING_STARTS_WITH(format, "pre")); ++ PrintMemoryOrder(instr, true); ++ return 3; ++ } ++ case 's': { // 's32 or 's64: Shift amount. ++ if (format[1] == '3') { ++ MOZ_ASSERT(STRING_STARTS_WITH(format, "s32")); ++ PrintShamt32(instr); ++ return 3; ++ } else if (format[1] == '6') { ++ MOZ_ASSERT(STRING_STARTS_WITH(format, "s64")); ++ PrintShamt(instr); ++ return 3; ++ } else if (format[1] == 'u') { ++ MOZ_ASSERT(STRING_STARTS_WITH(format, "suc")); ++ PrintMemoryOrder(instr, false); ++ return 3; ++ } else if (format[1] == 'e') { ++ MOZ_ASSERT(STRING_STARTS_WITH(format, "sew")); ++ PrintRvvSEW(instr); ++ return 3; ++ } else if (format[1] == 'i') { ++ MOZ_ASSERT(STRING_STARTS_WITH(format, "simm5")); ++ PrintRvvSimm5(instr); ++ return 5; ++ } ++ MOZ_CRASH(); ++ } ++ case 'v': { ++ if (format[1] == 'd') { ++ MOZ_ASSERT(STRING_STARTS_WITH(format, "vd")); ++ PrintVd(instr); ++ return 2; ++ } else if (format[2] == '1') { ++ MOZ_ASSERT(STRING_STARTS_WITH(format, "vs1")); ++ PrintVs1(instr); ++ return 3; ++ } else if (format[2] == '2') { ++ MOZ_ASSERT(STRING_STARTS_WITH(format, "vs2")); ++ PrintVs2(instr); ++ return 3; ++ } else { ++ MOZ_ASSERT(STRING_STARTS_WITH(format, "vm")); ++ PrintRvvVm(instr); ++ return 2; ++ } ++ } ++ case 'l': { ++ MOZ_ASSERT(STRING_STARTS_WITH(format, "lmul")); ++ PrintRvvLMUL(instr); ++ return 4; ++ } ++ case 'u': { ++ if (STRING_STARTS_WITH(format, "uimm5")) { ++ PrintRvvUimm5(instr); ++ return 5; ++ } else { ++ MOZ_ASSERT(STRING_STARTS_WITH(format, "uimm")); ++ PrintUimm(instr); ++ return 4; ++ } ++ } ++ case 't': { // 'target: target of branch instructions' ++ MOZ_ASSERT(STRING_STARTS_WITH(format, "target")); ++ PrintTarget(instr); ++ return 6; ++ } ++ } ++ MOZ_CRASH(); ++} ++ ++// Format takes a formatting string for a whole instruction and prints it into ++// the output buffer. All escaped options are handed to FormatOption to be ++// parsed further. ++void Decoder::Format(Instruction* instr, const char* format) { ++ char cur = *format++; ++ while ((cur != 0) && (out_buffer_pos_ < (out_buffer_.length() - 1))) { ++ if (cur == '\'') { // Single quote is used as the formatting escape. ++ format += FormatOption(instr, format); ++ } else { ++ out_buffer_[out_buffer_pos_++] = cur; ++ } ++ cur = *format++; ++ } ++ out_buffer_[out_buffer_pos_] = '\0'; ++} ++ ++// The disassembler may end up decoding data inlined in the code. We do not want ++// it to crash if the data does not ressemble any known instruction. ++#define VERIFY(condition) \ ++ if (!(condition)) { \ ++ Unknown(instr); \ ++ return; \ ++ } ++ ++// For currently unimplemented decodings the disassembler calls Unknown(instr) ++// which will just print "unknown" of the instruction bits. ++void Decoder::Unknown(Instruction* instr) { Format(instr, "unknown"); } ++ ++// Print the register name according to the active name converter. ++void Decoder::PrintRegister(int reg) { ++ Print(converter_.NameOfCPURegister(reg)); ++} ++ ++void Decoder::PrintVRegister(int reg) { UNSUPPORTED_RISCV(); } ++ ++void Decoder::PrintRs1(Instruction* instr) { ++ int reg = instr->Rs1Value(); ++ PrintRegister(reg); ++} ++ ++void Decoder::PrintRs2(Instruction* instr) { ++ int reg = instr->Rs2Value(); ++ PrintRegister(reg); ++} ++ ++void Decoder::PrintRd(Instruction* instr) { ++ int reg = instr->RdValue(); ++ PrintRegister(reg); ++} ++ ++void Decoder::PrintUimm(Instruction* instr) { ++ int val = instr->Rs1Value(); ++ out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "0x%x", val); ++} ++ ++void Decoder::PrintVs1(Instruction* instr) { ++ int reg = instr->Vs1Value(); ++ PrintVRegister(reg); ++} ++ ++void Decoder::PrintVs2(Instruction* instr) { ++ int reg = instr->Vs2Value(); ++ PrintVRegister(reg); ++} ++ ++void Decoder::PrintVd(Instruction* instr) { ++ int reg = instr->VdValue(); ++ PrintVRegister(reg); ++} ++ ++// Print the FPUregister name according to the active name converter. ++void Decoder::PrintFPURegister(int freg) { ++ Print(converter_.NameOfXMMRegister(freg)); ++} ++ ++void Decoder::PrintFRs1(Instruction* instr) { ++ int reg = instr->Rs1Value(); ++ PrintFPURegister(reg); ++} ++ ++void Decoder::PrintFRs2(Instruction* instr) { ++ int reg = instr->Rs2Value(); ++ PrintFPURegister(reg); ++} ++ ++void Decoder::PrintFRs3(Instruction* instr) { ++ int reg = instr->Rs3Value(); ++ PrintFPURegister(reg); ++} ++ ++void Decoder::PrintFRd(Instruction* instr) { ++ int reg = instr->RdValue(); ++ PrintFPURegister(reg); ++} ++ ++void Decoder::PrintImm12X(Instruction* instr) { ++ int32_t imm = instr->Imm12Value(); ++ out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "0x%x", imm); ++} ++ ++void Decoder::PrintImm12(Instruction* instr) { ++ int32_t imm = instr->Imm12Value(); ++ out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm); ++} ++ ++void Decoder::PrintTarget(Instruction* instr) { ++ // if (Assembler::IsJalr(instr->InstructionBits())) { ++ // if (Assembler::IsAuipc((instr - 4)->InstructionBits()) && ++ // (instr - 4)->RdValue() == instr->Rs1Value()) { ++ // int32_t imm = Assembler::BrachlongOffset((instr - ++ // 4)->InstructionBits(), ++ // instr->InstructionBits()); ++ // const char* target = ++ // converter_.NameOfAddress(reinterpret_cast(instr - 4) + imm); ++ // out_buffer_pos_ += ++ // SNPrintF(out_buffer_ + out_buffer_pos_, " -> %s", target); ++ // return; ++ // } ++ // } ++} ++ ++void Decoder::PrintBranchOffset(Instruction* instr) { ++ int32_t imm = instr->BranchOffset(); ++ const char* target = ++ converter_.NameOfAddress(reinterpret_cast(instr) + imm); ++ out_buffer_pos_ += ++ SNPrintF(out_buffer_ + out_buffer_pos_, "%d -> %s", imm, target); ++} ++ ++void Decoder::PrintStoreOffset(Instruction* instr) { ++ int32_t imm = instr->StoreOffset(); ++ out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm); ++} ++ ++void Decoder::PrintRvvSEW(Instruction* instr) { ++ const char* sew = instr->RvvSEW(); ++ out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%s", sew); ++} ++ ++void Decoder::PrintRvvLMUL(Instruction* instr) { ++ const char* lmul = instr->RvvLMUL(); ++ out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%s", lmul); ++} ++ ++void Decoder::PrintRvvSimm5(Instruction* instr) { ++ const int simm5 = instr->RvvSimm5(); ++ out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", simm5); ++} ++ ++void Decoder::PrintRvvUimm5(Instruction* instr) { ++ const uint32_t uimm5 = instr->RvvUimm5(); ++ out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%u", uimm5); ++} ++ ++void Decoder::PrintImm20U(Instruction* instr) { ++ int32_t imm = instr->Imm20UValue(); ++ out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "0x%x", imm); ++} ++ ++void Decoder::PrintImm20J(Instruction* instr) { ++ int32_t imm = instr->Imm20JValue(); ++ const char* target = ++ converter_.NameOfAddress(reinterpret_cast(instr) + imm); ++ out_buffer_pos_ += ++ SNPrintF(out_buffer_ + out_buffer_pos_, "%d -> %s", imm, target); ++} ++ ++void Decoder::PrintShamt(Instruction* instr) { ++ int32_t imm = instr->Shamt(); ++ out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm); ++} ++ ++void Decoder::PrintShamt32(Instruction* instr) { ++ int32_t imm = instr->Shamt32(); ++ out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm); ++} ++ ++void Decoder::PrintRvcImm6(Instruction* instr) { ++ int32_t imm = instr->RvcImm6Value(); ++ out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm); ++} ++ ++void Decoder::PrintRvcImm6U(Instruction* instr) { ++ int32_t imm = instr->RvcImm6Value() & 0xFFFFF; ++ out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "0x%x", imm); ++} ++ ++void Decoder::PrintRvcImm6Addi16sp(Instruction* instr) { ++ int32_t imm = instr->RvcImm6Addi16spValue(); ++ out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm); ++} ++ ++void Decoder::PrintRvcShamt(Instruction* instr) { ++ int32_t imm = instr->RvcShamt6(); ++ out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm); ++} ++ ++void Decoder::PrintRvcImm6Ldsp(Instruction* instr) { ++ int32_t imm = instr->RvcImm6LdspValue(); ++ out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm); ++} ++ ++void Decoder::PrintRvcImm6Lwsp(Instruction* instr) { ++ int32_t imm = instr->RvcImm6LwspValue(); ++ out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm); ++} ++ ++void Decoder::PrintRvcImm6Swsp(Instruction* instr) { ++ int32_t imm = instr->RvcImm6SwspValue(); ++ out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm); ++} ++ ++void Decoder::PrintRvcImm6Sdsp(Instruction* instr) { ++ int32_t imm = instr->RvcImm6SdspValue(); ++ out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm); ++} ++ ++void Decoder::PrintRvcImm5W(Instruction* instr) { ++ int32_t imm = instr->RvcImm5WValue(); ++ out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm); ++} ++ ++void Decoder::PrintRvcImm5D(Instruction* instr) { ++ int32_t imm = instr->RvcImm5DValue(); ++ out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm); ++} ++ ++void Decoder::PrintRvcImm8Addi4spn(Instruction* instr) { ++ int32_t imm = instr->RvcImm8Addi4spnValue(); ++ out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm); ++} ++ ++void Decoder::PrintRvcImm11CJ(Instruction* instr) { ++ int32_t imm = instr->RvcImm11CJValue(); ++ out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm); ++} ++ ++void Decoder::PrintRvcImm8B(Instruction* instr) { ++ int32_t imm = instr->RvcImm8BValue(); ++ out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm); ++} ++ ++void Decoder::PrintRvvVm(Instruction* instr) { ++ uint8_t imm = instr->RvvVM(); ++ if (imm == 0) { ++ out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, " v0.t"); ++ } ++} ++ ++void Decoder::PrintAcquireRelease(Instruction* instr) { ++ bool aq = instr->AqValue(); ++ bool rl = instr->RlValue(); ++ if (aq || rl) { ++ out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "."); ++ } ++ if (aq) { ++ out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "aq"); ++ } ++ if (rl) { ++ out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "rl"); ++ } ++} ++ ++void Decoder::PrintCSRReg(Instruction* instr) { ++ int32_t csr_reg = instr->CsrValue(); ++ std::string s; ++ switch (csr_reg) { ++ case csr_fflags: // Floating-Point Accrued Exceptions (RW) ++ s = "csr_fflags"; ++ break; ++ case csr_frm: // Floating-Point Dynamic Rounding Mode (RW) ++ s = "csr_frm"; ++ break; ++ case csr_fcsr: // Floating-Point Control and Status Register (RW) ++ s = "csr_fcsr"; ++ break; ++ case csr_cycle: ++ s = "csr_cycle"; ++ break; ++ case csr_time: ++ s = "csr_time"; ++ break; ++ case csr_instret: ++ s = "csr_instret"; ++ break; ++ case csr_cycleh: ++ s = "csr_cycleh"; ++ break; ++ case csr_timeh: ++ s = "csr_timeh"; ++ break; ++ case csr_instreth: ++ s = "csr_instreth"; ++ break; ++ default: ++ MOZ_CRASH(); ++ } ++ out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%s", s.c_str()); ++} ++ ++void Decoder::PrintRoundingMode(Instruction* instr) { ++ int frm = instr->RoundMode(); ++ std::string s; ++ switch (frm) { ++ case RNE: ++ s = "RNE"; ++ break; ++ case RTZ: ++ s = "RTZ"; ++ break; ++ case RDN: ++ s = "RDN"; ++ break; ++ case RUP: ++ s = "RUP"; ++ break; ++ case RMM: ++ s = "RMM"; ++ break; ++ case DYN: ++ s = "DYN"; ++ break; ++ default: ++ MOZ_CRASH(); ++ } ++ out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%s", s.c_str()); ++} ++ ++void Decoder::PrintMemoryOrder(Instruction* instr, bool is_pred) { ++ int memOrder = instr->MemoryOrder(is_pred); ++ std::string s; ++ if ((memOrder & PSI) == PSI) { ++ s += "i"; ++ } ++ if ((memOrder & PSO) == PSO) { ++ s += "o"; ++ } ++ if ((memOrder & PSR) == PSR) { ++ s += "r"; ++ } ++ if ((memOrder & PSW) == PSW) { ++ s += "w"; ++ } ++ out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%s", s.c_str()); ++} ++ ++// Printing of instruction name. ++void Decoder::PrintInstructionName(Instruction* instr) {} ++ ++// RISCV Instruction Decode Routine ++void Decoder::DecodeRType(Instruction* instr) { ++ switch (instr->InstructionBits() & kRTypeMask) { ++ case RO_ADD: ++ Format(instr, "add 'rd, 'rs1, 'rs2"); ++ break; ++ case RO_SUB: ++ if (instr->Rs1Value() == zero.code()) ++ Format(instr, "neg 'rd, 'rs2"); ++ else ++ Format(instr, "sub 'rd, 'rs1, 'rs2"); ++ break; ++ case RO_SLL: ++ Format(instr, "sll 'rd, 'rs1, 'rs2"); ++ break; ++ case RO_SLT: ++ if (instr->Rs2Value() == zero.code()) ++ Format(instr, "sltz 'rd, 'rs1"); ++ else if (instr->Rs1Value() == zero.code()) ++ Format(instr, "sgtz 'rd, 'rs2"); ++ else ++ Format(instr, "slt 'rd, 'rs1, 'rs2"); ++ break; ++ case RO_SLTU: ++ if (instr->Rs1Value() == zero.code()) ++ Format(instr, "snez 'rd, 'rs2"); ++ else ++ Format(instr, "sltu 'rd, 'rs1, 'rs2"); ++ break; ++ case RO_XOR: ++ Format(instr, "xor 'rd, 'rs1, 'rs2"); ++ break; ++ case RO_SRL: ++ Format(instr, "srl 'rd, 'rs1, 'rs2"); ++ break; ++ case RO_SRA: ++ Format(instr, "sra 'rd, 'rs1, 'rs2"); ++ break; ++ case RO_OR: ++ Format(instr, "or 'rd, 'rs1, 'rs2"); ++ break; ++ case RO_AND: ++ Format(instr, "and 'rd, 'rs1, 'rs2"); ++ break; ++#ifdef JS_CODEGEN_RISCV64 ++ case RO_ADDW: ++ Format(instr, "addw 'rd, 'rs1, 'rs2"); ++ break; ++ case RO_SUBW: ++ if (instr->Rs1Value() == zero.code()) ++ Format(instr, "negw 'rd, 'rs2"); ++ else ++ Format(instr, "subw 'rd, 'rs1, 'rs2"); ++ break; ++ case RO_SLLW: ++ Format(instr, "sllw 'rd, 'rs1, 'rs2"); ++ break; ++ case RO_SRLW: ++ Format(instr, "srlw 'rd, 'rs1, 'rs2"); ++ break; ++ case RO_SRAW: ++ Format(instr, "sraw 'rd, 'rs1, 'rs2"); ++ break; ++#endif /* JS_CODEGEN_RISCV64 */ ++ // TODO(riscv): Add RISCV M extension macro ++ case RO_MUL: ++ Format(instr, "mul 'rd, 'rs1, 'rs2"); ++ break; ++ case RO_MULH: ++ Format(instr, "mulh 'rd, 'rs1, 'rs2"); ++ break; ++ case RO_MULHSU: ++ Format(instr, "mulhsu 'rd, 'rs1, 'rs2"); ++ break; ++ case RO_MULHU: ++ Format(instr, "mulhu 'rd, 'rs1, 'rs2"); ++ break; ++ case RO_DIV: ++ Format(instr, "div 'rd, 'rs1, 'rs2"); ++ break; ++ case RO_DIVU: ++ Format(instr, "divu 'rd, 'rs1, 'rs2"); ++ break; ++ case RO_REM: ++ Format(instr, "rem 'rd, 'rs1, 'rs2"); ++ break; ++ case RO_REMU: ++ Format(instr, "remu 'rd, 'rs1, 'rs2"); ++ break; ++#ifdef JS_CODEGEN_RISCV64 ++ case RO_MULW: ++ Format(instr, "mulw 'rd, 'rs1, 'rs2"); ++ break; ++ case RO_DIVW: ++ Format(instr, "divw 'rd, 'rs1, 'rs2"); ++ break; ++ case RO_DIVUW: ++ Format(instr, "divuw 'rd, 'rs1, 'rs2"); ++ break; ++ case RO_REMW: ++ Format(instr, "remw 'rd, 'rs1, 'rs2"); ++ break; ++ case RO_REMUW: ++ Format(instr, "remuw 'rd, 'rs1, 'rs2"); ++ break; ++#endif /*JS_CODEGEN_RISCV64*/ ++ // TODO(riscv): End Add RISCV M extension macro ++ default: { ++ switch (instr->BaseOpcode()) { ++ case AMO: ++ DecodeRAType(instr); ++ break; ++ case OP_FP: ++ DecodeRFPType(instr); ++ break; ++ default: ++ UNSUPPORTED_RISCV(); ++ } ++ } ++ } ++} ++ ++void Decoder::DecodeRAType(Instruction* instr) { ++ // TODO(riscv): Add macro for RISCV A extension ++ // Special handling for A extension instructions because it uses func5 ++ // For all A extension instruction, V8 simulator is pure sequential. No ++ // Memory address lock or other synchronizaiton behaviors. ++ switch (instr->InstructionBits() & kRATypeMask) { ++ case RO_LR_W: ++ Format(instr, "lr.w'a 'rd, ('rs1)"); ++ break; ++ case RO_SC_W: ++ Format(instr, "sc.w'a 'rd, 'rs2, ('rs1)"); ++ break; ++ case RO_AMOSWAP_W: ++ Format(instr, "amoswap.w'a 'rd, 'rs2, ('rs1)"); ++ break; ++ case RO_AMOADD_W: ++ Format(instr, "amoadd.w'a 'rd, 'rs2, ('rs1)"); ++ break; ++ case RO_AMOXOR_W: ++ Format(instr, "amoxor.w'a 'rd, 'rs2, ('rs1)"); ++ break; ++ case RO_AMOAND_W: ++ Format(instr, "amoand.w'a 'rd, 'rs2, ('rs1)"); ++ break; ++ case RO_AMOOR_W: ++ Format(instr, "amoor.w'a 'rd, 'rs2, ('rs1)"); ++ break; ++ case RO_AMOMIN_W: ++ Format(instr, "amomin.w'a 'rd, 'rs2, ('rs1)"); ++ break; ++ case RO_AMOMAX_W: ++ Format(instr, "amomax.w'a 'rd, 'rs2, ('rs1)"); ++ break; ++ case RO_AMOMINU_W: ++ Format(instr, "amominu.w'a 'rd, 'rs2, ('rs1)"); ++ break; ++ case RO_AMOMAXU_W: ++ Format(instr, "amomaxu.w'a 'rd, 'rs2, ('rs1)"); ++ break; ++#ifdef JS_CODEGEN_RISCV64 ++ case RO_LR_D: ++ Format(instr, "lr.d'a 'rd, ('rs1)"); ++ break; ++ case RO_SC_D: ++ Format(instr, "sc.d'a 'rd, 'rs2, ('rs1)"); ++ break; ++ case RO_AMOSWAP_D: ++ Format(instr, "amoswap.d'a 'rd, 'rs2, ('rs1)"); ++ break; ++ case RO_AMOADD_D: ++ Format(instr, "amoadd.d'a 'rd, 'rs2, ('rs1)"); ++ break; ++ case RO_AMOXOR_D: ++ Format(instr, "amoxor.d'a 'rd, 'rs2, ('rs1)"); ++ break; ++ case RO_AMOAND_D: ++ Format(instr, "amoand.d'a 'rd, 'rs2, ('rs1)"); ++ break; ++ case RO_AMOOR_D: ++ Format(instr, "amoor.d'a 'rd, 'rs2, ('rs1)"); ++ break; ++ case RO_AMOMIN_D: ++ Format(instr, "amomin.d'a 'rd, 'rs2, ('rs1)"); ++ break; ++ case RO_AMOMAX_D: ++ Format(instr, "amoswap.d'a 'rd, 'rs2, ('rs1)"); ++ break; ++ case RO_AMOMINU_D: ++ Format(instr, "amominu.d'a 'rd, 'rs2, ('rs1)"); ++ break; ++ case RO_AMOMAXU_D: ++ Format(instr, "amomaxu.d'a 'rd, 'rs2, ('rs1)"); ++ break; ++#endif /*JS_CODEGEN_RISCV64*/ ++ // TODO(riscv): End Add macro for RISCV A extension ++ default: { ++ UNSUPPORTED_RISCV(); ++ } ++ } ++} ++ ++void Decoder::DecodeRFPType(Instruction* instr) { ++ // OP_FP instructions (F/D) uses func7 first. Some further uses fun3 and rs2() ++ ++ // kRATypeMask is only for func7 ++ switch (instr->InstructionBits() & kRFPTypeMask) { ++ // TODO(riscv): Add macro for RISCV F extension ++ case RO_FADD_S: ++ Format(instr, "fadd.s 'fd, 'fs1, 'fs2"); ++ break; ++ case RO_FSUB_S: ++ Format(instr, "fsub.s 'fd, 'fs1, 'fs2"); ++ break; ++ case RO_FMUL_S: ++ Format(instr, "fmul.s 'fd, 'fs1, 'fs2"); ++ break; ++ case RO_FDIV_S: ++ Format(instr, "fdiv.s 'fd, 'fs1, 'fs2"); ++ break; ++ case RO_FSQRT_S: ++ Format(instr, "fsqrt.s 'fd, 'fs1"); ++ break; ++ case RO_FSGNJ_S: { // RO_FSGNJN_S RO_FSGNJX_S ++ switch (instr->Funct3Value()) { ++ case 0b000: // RO_FSGNJ_S ++ if (instr->Rs1Value() == instr->Rs2Value()) ++ Format(instr, "fmv.s 'fd, 'fs1"); ++ else ++ Format(instr, "fsgnj.s 'fd, 'fs1, 'fs2"); ++ break; ++ case 0b001: // RO_FSGNJN_S ++ if (instr->Rs1Value() == instr->Rs2Value()) ++ Format(instr, "fneg.s 'fd, 'fs1"); ++ else ++ Format(instr, "fsgnjn.s 'fd, 'fs1, 'fs2"); ++ break; ++ case 0b010: // RO_FSGNJX_S ++ if (instr->Rs1Value() == instr->Rs2Value()) ++ Format(instr, "fabs.s 'fd, 'fs1"); ++ else ++ Format(instr, "fsgnjx.s 'fd, 'fs1, 'fs2"); ++ break; ++ default: ++ UNSUPPORTED_RISCV(); ++ } ++ break; ++ } ++ case RO_FMIN_S: { // RO_FMAX_S ++ switch (instr->Funct3Value()) { ++ case 0b000: // RO_FMIN_S ++ Format(instr, "fmin.s 'fd, 'fs1, 'fs2"); ++ break; ++ case 0b001: // RO_FMAX_S ++ Format(instr, "fmax.s 'fd, 'fs1, 'fs2"); ++ break; ++ default: ++ UNSUPPORTED_RISCV(); ++ } ++ break; ++ } ++ case RO_FCVT_W_S: { // RO_FCVT_WU_S , 64F RO_FCVT_L_S RO_FCVT_LU_S ++ switch (instr->Rs2Value()) { ++ case 0b00000: // RO_FCVT_W_S ++ Format(instr, "fcvt.w.s ['frm] 'rd, 'fs1"); ++ break; ++ case 0b00001: // RO_FCVT_WU_S ++ Format(instr, "fcvt.wu.s ['frm] 'rd, 'fs1"); ++ break; ++#ifdef JS_CODEGEN_RISCV64 ++ case 0b00010: // RO_FCVT_L_S ++ Format(instr, "fcvt.l.s ['frm] 'rd, 'fs1"); ++ break; ++ case 0b00011: // RO_FCVT_LU_S ++ Format(instr, "fcvt.lu.s ['frm] 'rd, 'fs1"); ++ break; ++#endif /* JS_CODEGEN_RISCV64 */ ++ default: ++ UNSUPPORTED_RISCV(); ++ } ++ break; ++ } ++ case RO_FMV: { // RO_FCLASS_S ++ if (instr->Rs2Value() != 0b00000) { ++ UNSUPPORTED_RISCV(); ++ } ++ switch (instr->Funct3Value()) { ++ case 0b000: // RO_FMV_X_W ++ Format(instr, "fmv.x.w 'rd, 'fs1"); ++ break; ++ case 0b001: // RO_FCLASS_S ++ Format(instr, "fclass.s 'rd, 'fs1"); ++ break; ++ default: ++ UNSUPPORTED_RISCV(); ++ } ++ break; ++ } ++ case RO_FLE_S: { // RO_FEQ_S RO_FLT_S RO_FLE_S ++ switch (instr->Funct3Value()) { ++ case 0b010: // RO_FEQ_S ++ Format(instr, "feq.s 'rd, 'fs1, 'fs2"); ++ break; ++ case 0b001: // RO_FLT_S ++ Format(instr, "flt.s 'rd, 'fs1, 'fs2"); ++ break; ++ case 0b000: // RO_FLE_S ++ Format(instr, "fle.s 'rd, 'fs1, 'fs2"); ++ break; ++ default: ++ UNSUPPORTED_RISCV(); ++ } ++ break; ++ } ++ case RO_FCVT_S_W: { // RO_FCVT_S_WU , 64F RO_FCVT_S_L RO_FCVT_S_LU ++ switch (instr->Rs2Value()) { ++ case 0b00000: // RO_FCVT_S_W ++ Format(instr, "fcvt.s.w 'fd, 'rs1"); ++ break; ++ case 0b00001: // RO_FCVT_S_WU ++ Format(instr, "fcvt.s.wu 'fd, 'rs1"); ++ break; ++#ifdef JS_CODEGEN_RISCV64 ++ case 0b00010: // RO_FCVT_S_L ++ Format(instr, "fcvt.s.l 'fd, 'rs1"); ++ break; ++ case 0b00011: // RO_FCVT_S_LU ++ Format(instr, "fcvt.s.lu 'fd, 'rs1"); ++ break; ++#endif /* JS_CODEGEN_RISCV64 */ ++ default: { ++ UNSUPPORTED_RISCV(); ++ } ++ } ++ break; ++ } ++ case RO_FMV_W_X: { ++ if (instr->Funct3Value() == 0b000) { ++ Format(instr, "fmv.w.x 'fd, 'rs1"); ++ } else { ++ UNSUPPORTED_RISCV(); ++ } ++ break; ++ } ++ // TODO(riscv): Add macro for RISCV D extension ++ case RO_FADD_D: ++ Format(instr, "fadd.d 'fd, 'fs1, 'fs2"); ++ break; ++ case RO_FSUB_D: ++ Format(instr, "fsub.d 'fd, 'fs1, 'fs2"); ++ break; ++ case RO_FMUL_D: ++ Format(instr, "fmul.d 'fd, 'fs1, 'fs2"); ++ break; ++ case RO_FDIV_D: ++ Format(instr, "fdiv.d 'fd, 'fs1, 'fs2"); ++ break; ++ case RO_FSQRT_D: { ++ if (instr->Rs2Value() == 0b00000) { ++ Format(instr, "fsqrt.d 'fd, 'fs1"); ++ } else { ++ UNSUPPORTED_RISCV(); ++ } ++ break; ++ } ++ case RO_FSGNJ_D: { // RO_FSGNJN_D RO_FSGNJX_D ++ switch (instr->Funct3Value()) { ++ case 0b000: // RO_FSGNJ_D ++ if (instr->Rs1Value() == instr->Rs2Value()) ++ Format(instr, "fmv.d 'fd, 'fs1"); ++ else ++ Format(instr, "fsgnj.d 'fd, 'fs1, 'fs2"); ++ break; ++ case 0b001: // RO_FSGNJN_D ++ if (instr->Rs1Value() == instr->Rs2Value()) ++ Format(instr, "fneg.d 'fd, 'fs1"); ++ else ++ Format(instr, "fsgnjn.d 'fd, 'fs1, 'fs2"); ++ break; ++ case 0b010: // RO_FSGNJX_D ++ if (instr->Rs1Value() == instr->Rs2Value()) ++ Format(instr, "fabs.d 'fd, 'fs1"); ++ else ++ Format(instr, "fsgnjx.d 'fd, 'fs1, 'fs2"); ++ break; ++ default: ++ UNSUPPORTED_RISCV(); ++ } ++ break; ++ } ++ case RO_FMIN_D: { // RO_FMAX_D ++ switch (instr->Funct3Value()) { ++ case 0b000: // RO_FMIN_D ++ Format(instr, "fmin.d 'fd, 'fs1, 'fs2"); ++ break; ++ case 0b001: // RO_FMAX_D ++ Format(instr, "fmax.d 'fd, 'fs1, 'fs2"); ++ break; ++ default: ++ UNSUPPORTED_RISCV(); ++ } ++ break; ++ } ++ case (RO_FCVT_S_D & kRFPTypeMask): { ++ if (instr->Rs2Value() == 0b00001) { ++ Format(instr, "fcvt.s.d ['frm] 'fd, 'fs1"); ++ } else { ++ UNSUPPORTED_RISCV(); ++ } ++ break; ++ } ++ case RO_FCVT_D_S: { ++ if (instr->Rs2Value() == 0b00000) { ++ Format(instr, "fcvt.d.s 'fd, 'fs1"); ++ } else { ++ UNSUPPORTED_RISCV(); ++ } ++ break; ++ } ++ case RO_FLE_D: { // RO_FEQ_D RO_FLT_D RO_FLE_D ++ switch (instr->Funct3Value()) { ++ case 0b010: // RO_FEQ_S ++ Format(instr, "feq.d 'rd, 'fs1, 'fs2"); ++ break; ++ case 0b001: // RO_FLT_D ++ Format(instr, "flt.d 'rd, 'fs1, 'fs2"); ++ break; ++ case 0b000: // RO_FLE_D ++ Format(instr, "fle.d 'rd, 'fs1, 'fs2"); ++ break; ++ default: ++ UNSUPPORTED_RISCV(); ++ } ++ break; ++ } ++ case (RO_FCLASS_D & kRFPTypeMask): { // RO_FCLASS_D , 64D RO_FMV_X_D ++ if (instr->Rs2Value() != 0b00000) { ++ UNSUPPORTED_RISCV(); ++ break; ++ } ++ switch (instr->Funct3Value()) { ++ case 0b001: // RO_FCLASS_D ++ Format(instr, "fclass.d 'rd, 'fs1"); ++ break; ++#ifdef JS_CODEGEN_RISCV64 ++ case 0b000: // RO_FMV_X_D ++ Format(instr, "fmv.x.d 'rd, 'fs1"); ++ break; ++#endif /* JS_CODEGEN_RISCV64 */ ++ default: ++ UNSUPPORTED_RISCV(); ++ } ++ break; ++ } ++ case RO_FCVT_W_D: { // RO_FCVT_WU_D , 64F RO_FCVT_L_D RO_FCVT_LU_D ++ switch (instr->Rs2Value()) { ++ case 0b00000: // RO_FCVT_W_D ++ Format(instr, "fcvt.w.d ['frm] 'rd, 'fs1"); ++ break; ++ case 0b00001: // RO_FCVT_WU_D ++ Format(instr, "fcvt.wu.d ['frm] 'rd, 'fs1"); ++ break; ++#ifdef JS_CODEGEN_RISCV64 ++ case 0b00010: // RO_FCVT_L_D ++ Format(instr, "fcvt.l.d ['frm] 'rd, 'fs1"); ++ break; ++ case 0b00011: // RO_FCVT_LU_D ++ Format(instr, "fcvt.lu.d ['frm] 'rd, 'fs1"); ++ break; ++#endif /* JS_CODEGEN_RISCV64 */ ++ default: ++ UNSUPPORTED_RISCV(); ++ } ++ break; ++ } ++ case RO_FCVT_D_W: { // RO_FCVT_D_WU , 64F RO_FCVT_D_L RO_FCVT_D_LU ++ switch (instr->Rs2Value()) { ++ case 0b00000: // RO_FCVT_D_W ++ Format(instr, "fcvt.d.w 'fd, 'rs1"); ++ break; ++ case 0b00001: // RO_FCVT_D_WU ++ Format(instr, "fcvt.d.wu 'fd, 'rs1"); ++ break; ++#ifdef JS_CODEGEN_RISCV64 ++ case 0b00010: // RO_FCVT_D_L ++ Format(instr, "fcvt.d.l 'fd, 'rs1"); ++ break; ++ case 0b00011: // RO_FCVT_D_LU ++ Format(instr, "fcvt.d.lu 'fd, 'rs1"); ++ break; ++#endif /* JS_CODEGEN_RISCV64 */ ++ default: ++ UNSUPPORTED_RISCV(); ++ } ++ break; ++ } ++#ifdef JS_CODEGEN_RISCV64 ++ case RO_FMV_D_X: { ++ if (instr->Funct3Value() == 0b000 && instr->Rs2Value() == 0b00000) { ++ Format(instr, "fmv.d.x 'fd, 'rs1"); ++ } else { ++ UNSUPPORTED_RISCV(); ++ } ++ break; ++ } ++#endif /* JS_CODEGEN_RISCV64 */ ++ default: { ++ UNSUPPORTED_RISCV(); ++ } ++ } ++} ++ ++void Decoder::DecodeR4Type(Instruction* instr) { ++ switch (instr->InstructionBits() & kR4TypeMask) { ++ // TODO(riscv): use F Extension macro block ++ case RO_FMADD_S: ++ Format(instr, "fmadd.s 'fd, 'fs1, 'fs2, 'fs3"); ++ break; ++ case RO_FMSUB_S: ++ Format(instr, "fmsub.s 'fd, 'fs1, 'fs2, 'fs3"); ++ break; ++ case RO_FNMSUB_S: ++ Format(instr, "fnmsub.s 'fd, 'fs1, 'fs2, 'fs3"); ++ break; ++ case RO_FNMADD_S: ++ Format(instr, "fnmadd.s 'fd, 'fs1, 'fs2, 'fs3"); ++ break; ++ // TODO(riscv): use F Extension macro block ++ case RO_FMADD_D: ++ Format(instr, "fmadd.d 'fd, 'fs1, 'fs2, 'fs3"); ++ break; ++ case RO_FMSUB_D: ++ Format(instr, "fmsub.d 'fd, 'fs1, 'fs2, 'fs3"); ++ break; ++ case RO_FNMSUB_D: ++ Format(instr, "fnmsub.d 'fd, 'fs1, 'fs2, 'fs3"); ++ break; ++ case RO_FNMADD_D: ++ Format(instr, "fnmadd.d 'fd, 'fs1, 'fs2, 'fs3"); ++ break; ++ default: ++ UNSUPPORTED_RISCV(); ++ } ++} ++ ++void Decoder::DecodeIType(Instruction* instr) { ++ switch (instr->InstructionBits() & kITypeMask) { ++ case RO_JALR: ++ if (instr->RdValue() == zero.code() && instr->Rs1Value() == ra.code() && ++ instr->Imm12Value() == 0) ++ Format(instr, "ret"); ++ else if (instr->RdValue() == zero.code() && instr->Imm12Value() == 0) ++ Format(instr, "jr 'rs1"); ++ else if (instr->RdValue() == ra.code() && instr->Imm12Value() == 0) ++ Format(instr, "jalr 'rs1"); ++ else ++ Format(instr, "jalr 'rd, 'imm12('rs1)"); ++ break; ++ case RO_LB: ++ Format(instr, "lb 'rd, 'imm12('rs1)"); ++ break; ++ case RO_LH: ++ Format(instr, "lh 'rd, 'imm12('rs1)"); ++ break; ++ case RO_LW: ++ Format(instr, "lw 'rd, 'imm12('rs1)"); ++ break; ++ case RO_LBU: ++ Format(instr, "lbu 'rd, 'imm12('rs1)"); ++ break; ++ case RO_LHU: ++ Format(instr, "lhu 'rd, 'imm12('rs1)"); ++ break; ++#ifdef JS_CODEGEN_RISCV64 ++ case RO_LWU: ++ Format(instr, "lwu 'rd, 'imm12('rs1)"); ++ break; ++ case RO_LD: ++ Format(instr, "ld 'rd, 'imm12('rs1)"); ++ break; ++#endif /*JS_CODEGEN_RISCV64*/ ++ case RO_ADDI: ++ if (instr->Imm12Value() == 0) { ++ if (instr->RdValue() == zero.code() && instr->Rs1Value() == zero.code()) ++ Format(instr, "nop"); ++ else ++ Format(instr, "mv 'rd, 'rs1"); ++ } else if (instr->Rs1Value() == zero.code()) { ++ Format(instr, "li 'rd, 'imm12"); ++ } else { ++ Format(instr, "addi 'rd, 'rs1, 'imm12"); ++ } ++ break; ++ case RO_SLTI: ++ Format(instr, "slti 'rd, 'rs1, 'imm12"); ++ break; ++ case RO_SLTIU: ++ if (instr->Imm12Value() == 1) ++ Format(instr, "seqz 'rd, 'rs1"); ++ else ++ Format(instr, "sltiu 'rd, 'rs1, 'imm12"); ++ break; ++ case RO_XORI: ++ if (instr->Imm12Value() == -1) ++ Format(instr, "not 'rd, 'rs1"); ++ else ++ Format(instr, "xori 'rd, 'rs1, 'imm12x"); ++ break; ++ case RO_ORI: ++ Format(instr, "ori 'rd, 'rs1, 'imm12x"); ++ break; ++ case RO_ANDI: ++ Format(instr, "andi 'rd, 'rs1, 'imm12x"); ++ break; ++ case RO_SLLI: ++ Format(instr, "slli 'rd, 'rs1, 's64"); ++ break; ++ case RO_SRLI: { // RO_SRAI ++ if (!instr->IsArithShift()) { ++ Format(instr, "srli 'rd, 'rs1, 's64"); ++ } else { ++ Format(instr, "srai 'rd, 'rs1, 's64"); ++ } ++ break; ++ } ++#ifdef JS_CODEGEN_RISCV64 ++ case RO_ADDIW: ++ if (instr->Imm12Value() == 0) ++ Format(instr, "sext.w 'rd, 'rs1"); ++ else ++ Format(instr, "addiw 'rd, 'rs1, 'imm12"); ++ break; ++ case RO_SLLIW: ++ Format(instr, "slliw 'rd, 'rs1, 's32"); ++ break; ++ case RO_SRLIW: { // RO_SRAIW ++ if (!instr->IsArithShift()) { ++ Format(instr, "srliw 'rd, 'rs1, 's32"); ++ } else { ++ Format(instr, "sraiw 'rd, 'rs1, 's32"); ++ } ++ break; ++ } ++#endif /*JS_CODEGEN_RISCV64*/ ++ case RO_FENCE: ++ if (instr->MemoryOrder(true) == PSIORW && ++ instr->MemoryOrder(false) == PSIORW) ++ Format(instr, "fence"); ++ else ++ Format(instr, "fence 'pre, 'suc"); ++ break; ++ case RO_ECALL: { // RO_EBREAK ++ if (instr->Imm12Value() == 0) { // ECALL ++ Format(instr, "ecall"); ++ } else if (instr->Imm12Value() == 1) { // EBREAK ++ Format(instr, "ebreak"); ++ } else { ++ UNSUPPORTED_RISCV(); ++ } ++ break; ++ } ++ // TODO(riscv): use Zifencei Standard Extension macro block ++ case RO_FENCE_I: ++ Format(instr, "fence.i"); ++ break; ++ // TODO(riscv): use Zicsr Standard Extension macro block ++ // FIXME(RISC-V): Add special formatting for CSR registers ++ case RO_CSRRW: ++ if (instr->CsrValue() == csr_fcsr) { ++ if (instr->RdValue() == zero.code()) ++ Format(instr, "fscsr 'rs1"); ++ else ++ Format(instr, "fscsr 'rd, 'rs1"); ++ } else if (instr->CsrValue() == csr_frm) { ++ if (instr->RdValue() == zero.code()) ++ Format(instr, "fsrm 'rs1"); ++ else ++ Format(instr, "fsrm 'rd, 'rs1"); ++ } else if (instr->CsrValue() == csr_fflags) { ++ if (instr->RdValue() == zero.code()) ++ Format(instr, "fsflags 'rs1"); ++ else ++ Format(instr, "fsflags 'rd, 'rs1"); ++ } else if (instr->RdValue() == zero.code()) { ++ Format(instr, "csrw 'csr, 'rs1"); ++ } else { ++ Format(instr, "csrrw 'rd, 'csr, 'rs1"); ++ } ++ break; ++ case RO_CSRRS: ++ if (instr->Rs1Value() == zero.code()) { ++ switch (instr->CsrValue()) { ++ case csr_instret: ++ Format(instr, "rdinstret 'rd"); ++ break; ++ case csr_instreth: ++ Format(instr, "rdinstreth 'rd"); ++ break; ++ case csr_time: ++ Format(instr, "rdtime 'rd"); ++ break; ++ case csr_timeh: ++ Format(instr, "rdtimeh 'rd"); ++ break; ++ case csr_cycle: ++ Format(instr, "rdcycle 'rd"); ++ break; ++ case csr_cycleh: ++ Format(instr, "rdcycleh 'rd"); ++ break; ++ case csr_fflags: ++ Format(instr, "frflags 'rd"); ++ break; ++ case csr_frm: ++ Format(instr, "frrm 'rd"); ++ break; ++ case csr_fcsr: ++ Format(instr, "frcsr 'rd"); ++ break; ++ default: ++ MOZ_CRASH(); ++ } ++ } else if (instr->Rs1Value() == zero.code()) { ++ Format(instr, "csrr 'rd, 'csr"); ++ } else if (instr->RdValue() == zero.code()) { ++ Format(instr, "csrs 'csr, 'rs1"); ++ } else { ++ Format(instr, "csrrs 'rd, 'csr, 'rs1"); ++ } ++ break; ++ case RO_CSRRC: ++ if (instr->RdValue() == zero.code()) ++ Format(instr, "csrc 'csr, 'rs1"); ++ else ++ Format(instr, "csrrc 'rd, 'csr, 'rs1"); ++ break; ++ case RO_CSRRWI: ++ if (instr->RdValue() == zero.code()) ++ Format(instr, "csrwi 'csr, 'uimm"); ++ else ++ Format(instr, "csrrwi 'rd, 'csr, 'uimm"); ++ break; ++ case RO_CSRRSI: ++ if (instr->RdValue() == zero.code()) ++ Format(instr, "csrsi 'csr, 'uimm"); ++ else ++ Format(instr, "csrrsi 'rd, 'csr, 'uimm"); ++ break; ++ case RO_CSRRCI: ++ if (instr->RdValue() == zero.code()) ++ Format(instr, "csrci 'csr, 'uimm"); ++ else ++ Format(instr, "csrrci 'rd, 'csr, 'uimm"); ++ break; ++ // TODO(riscv): use F Extension macro block ++ case RO_FLW: ++ Format(instr, "flw 'fd, 'imm12('rs1)"); ++ break; ++ // TODO(riscv): use D Extension macro block ++ case RO_FLD: ++ Format(instr, "fld 'fd, 'imm12('rs1)"); ++ break; ++ default: ++#ifdef CAN_USE_RVV_INSTRUCTIONS ++ if (instr->vl_vs_width() != -1) { ++ DecodeRvvVL(instr); ++ } else { ++ UNSUPPORTED_RISCV(); ++ } ++ break; ++#else ++ UNSUPPORTED_RISCV(); ++#endif ++ } ++} ++ ++void Decoder::DecodeSType(Instruction* instr) { ++ switch (instr->InstructionBits() & kSTypeMask) { ++ case RO_SB: ++ Format(instr, "sb 'rs2, 'offS('rs1)"); ++ break; ++ case RO_SH: ++ Format(instr, "sh 'rs2, 'offS('rs1)"); ++ break; ++ case RO_SW: ++ Format(instr, "sw 'rs2, 'offS('rs1)"); ++ break; ++#ifdef JS_CODEGEN_RISCV64 ++ case RO_SD: ++ Format(instr, "sd 'rs2, 'offS('rs1)"); ++ break; ++#endif /*JS_CODEGEN_RISCV64*/ ++ // TODO(riscv): use F Extension macro block ++ case RO_FSW: ++ Format(instr, "fsw 'fs2, 'offS('rs1)"); ++ break; ++ // TODO(riscv): use D Extension macro block ++ case RO_FSD: ++ Format(instr, "fsd 'fs2, 'offS('rs1)"); ++ break; ++ default: ++#ifdef CAN_USE_RVV_INSTRUCTIONS ++ if (instr->vl_vs_width() != -1) { ++ DecodeRvvVS(instr); ++ } else { ++ UNSUPPORTED_RISCV(); ++ } ++ break; ++#else ++ UNSUPPORTED_RISCV(); ++#endif ++ } ++} ++ ++void Decoder::DecodeBType(Instruction* instr) { ++ switch (instr->InstructionBits() & kBTypeMask) { ++ case RO_BEQ: ++ Format(instr, "beq 'rs1, 'rs2, 'offB"); ++ break; ++ case RO_BNE: ++ Format(instr, "bne 'rs1, 'rs2, 'offB"); ++ break; ++ case RO_BLT: ++ Format(instr, "blt 'rs1, 'rs2, 'offB"); ++ break; ++ case RO_BGE: ++ Format(instr, "bge 'rs1, 'rs2, 'offB"); ++ break; ++ case RO_BLTU: ++ Format(instr, "bltu 'rs1, 'rs2, 'offB"); ++ break; ++ case RO_BGEU: ++ Format(instr, "bgeu 'rs1, 'rs2, 'offB"); ++ break; ++ default: ++ UNSUPPORTED_RISCV(); ++ } ++} ++void Decoder::DecodeUType(Instruction* instr) { ++ // U Type doesn't have additional mask ++ switch (instr->BaseOpcodeFieldRaw()) { ++ case LUI: ++ Format(instr, "lui 'rd, 'imm20U"); ++ break; ++ case AUIPC: ++ Format(instr, "auipc 'rd, 'imm20U"); ++ break; ++ default: ++ UNSUPPORTED_RISCV(); ++ } ++} ++// namespace jit ++void Decoder::DecodeJType(Instruction* instr) { ++ // J Type doesn't have additional mask ++ switch (instr->BaseOpcodeValue()) { ++ case JAL: ++ if (instr->RdValue() == zero.code()) ++ Format(instr, "j 'imm20J"); ++ else if (instr->RdValue() == ra.code()) ++ Format(instr, "jal 'imm20J"); ++ else ++ Format(instr, "jal 'rd, 'imm20J"); ++ break; ++ default: ++ UNSUPPORTED_RISCV(); ++ } ++} ++ ++void Decoder::DecodeCRType(Instruction* instr) { ++ switch (instr->RvcFunct4Value()) { ++ case 0b1000: ++ if (instr->RvcRs1Value() != 0 && instr->RvcRs2Value() == 0) ++ Format(instr, "jr 'Crs1"); ++ else if (instr->RvcRdValue() != 0 && instr->RvcRs2Value() != 0) ++ Format(instr, "mv 'Crd, 'Crs2"); ++ else ++ UNSUPPORTED_RISCV(); ++ break; ++ case 0b1001: ++ if (instr->RvcRs1Value() == 0 && instr->RvcRs2Value() == 0) ++ Format(instr, "ebreak"); ++ else if (instr->RvcRdValue() != 0 && instr->RvcRs2Value() == 0) ++ Format(instr, "jalr 'Crs1"); ++ else if (instr->RvcRdValue() != 0 && instr->RvcRs2Value() != 0) ++ Format(instr, "add 'Crd, 'Crd, 'Crs2"); ++ else ++ UNSUPPORTED_RISCV(); ++ break; ++ default: ++ UNSUPPORTED_RISCV(); ++ } ++} ++ ++void Decoder::DecodeCAType(Instruction* instr) { ++ switch (instr->InstructionBits() & kCATypeMask) { ++ case RO_C_SUB: ++ Format(instr, "sub 'Crs1s, 'Crs1s, 'Crs2s"); ++ break; ++ case RO_C_XOR: ++ Format(instr, "xor 'Crs1s, 'Crs1s, 'Crs2s"); ++ break; ++ case RO_C_OR: ++ Format(instr, "or 'Crs1s, 'Crs1s, 'Crs2s"); ++ break; ++ case RO_C_AND: ++ Format(instr, "and 'Crs1s, 'Crs1s, 'Crs2s"); ++ break; ++#ifdef JS_CODEGEN_RISCV64 ++ case RO_C_SUBW: ++ Format(instr, "subw 'Crs1s, 'Crs1s, 'Crs2s"); ++ break; ++ case RO_C_ADDW: ++ Format(instr, "addw 'Crs1s, 'Crs1s, 'Crs2s"); ++ break; ++#endif ++ default: ++ UNSUPPORTED_RISCV(); ++ } ++} ++ ++void Decoder::DecodeCIType(Instruction* instr) { ++ switch (instr->RvcOpcode()) { ++ case RO_C_NOP_ADDI: ++ if (instr->RvcRdValue() == 0) ++ Format(instr, "nop"); ++ else ++ Format(instr, "addi 'Crd, 'Crd, 'Cimm6"); ++ break; ++#ifdef JS_CODEGEN_RISCV64 ++ case RO_C_ADDIW: ++ Format(instr, "addiw 'Crd, 'Crd, 'Cimm6"); ++ break; ++#endif ++ case RO_C_LI: ++ Format(instr, "li 'Crd, 'Cimm6"); ++ break; ++ case RO_C_LUI_ADD: ++ if (instr->RvcRdValue() == 2) ++ Format(instr, "addi sp, sp, 'Cimm6Addi16sp"); ++ else if (instr->RvcRdValue() != 0 && instr->RvcRdValue() != 2) ++ Format(instr, "lui 'Crd, 'Cimm6U"); ++ else ++ UNSUPPORTED_RISCV(); ++ break; ++ case RO_C_SLLI: ++ Format(instr, "slli 'Crd, 'Crd, 'Cshamt"); ++ break; ++ case RO_C_FLDSP: ++ Format(instr, "fld 'Cfd, 'Cimm6Ldsp(sp)"); ++ break; ++ case RO_C_LWSP: ++ Format(instr, "lw 'Crd, 'Cimm6Lwsp(sp)"); ++ break; ++#ifdef JS_CODEGEN_RISCV64 ++ case RO_C_LDSP: ++ Format(instr, "ld 'Crd, 'Cimm6Ldsp(sp)"); ++ break; ++#elif defined(JS_CODEGEN_RISCV32) ++ case RO_C_FLWSP: ++ Format(instr, "flw 'Cfd, 'Cimm6Ldsp(sp)"); ++ break; ++#endif ++ default: ++ UNSUPPORTED_RISCV(); ++ } ++} ++ ++void Decoder::DecodeCIWType(Instruction* instr) { ++ switch (instr->RvcOpcode()) { ++ case RO_C_ADDI4SPN: ++ Format(instr, "addi 'Crs2s, sp, 'Cimm8Addi4spn"); ++ break; ++ default: ++ UNSUPPORTED_RISCV(); ++ } ++} ++ ++void Decoder::DecodeCSSType(Instruction* instr) { ++ switch (instr->RvcOpcode()) { ++ case RO_C_SWSP: ++ Format(instr, "sw 'Crs2, 'Cimm6Swsp(sp)"); ++ break; ++#ifdef JS_CODEGEN_RISCV64 ++ case RO_C_SDSP: ++ Format(instr, "sd 'Crs2, 'Cimm6Sdsp(sp)"); ++ break; ++#elif defined(JS_CODEGEN_RISCV32) ++ case RO_C_FSWSP: ++ Format(instr, "fsw 'Cfs2, 'Cimm6Sdsp(sp)"); ++ break; ++#endif ++ case RO_C_FSDSP: ++ Format(instr, "fsd 'Cfs2, 'Cimm6Sdsp(sp)"); ++ break; ++ default: ++ UNSUPPORTED_RISCV(); ++ } ++} ++ ++void Decoder::DecodeCLType(Instruction* instr) { ++ switch (instr->RvcOpcode()) { ++ case RO_C_FLD: ++ Format(instr, "fld 'Cfs2s, 'Cimm5D('Crs1s)"); ++ break; ++ case RO_C_LW: ++ Format(instr, "lw 'Crs2s, 'Cimm5W('Crs1s)"); ++ break; ++#ifdef JS_CODEGEN_RISCV64 ++ case RO_C_LD: ++ Format(instr, "ld 'Crs2s, 'Cimm5D('Crs1s)"); ++ break; ++#elif defined(JS_CODEGEN_RISCV32) ++ case RO_C_FLW: ++ Format(instr, "fld 'Cfs2s, 'Cimm5D('Crs1s)"); ++ break; ++#endif ++ ++ default: ++ UNSUPPORTED_RISCV(); ++ } ++} ++ ++void Decoder::DecodeCSType(Instruction* instr) { ++ switch (instr->RvcOpcode()) { ++ case RO_C_FSD: ++ Format(instr, "fsd 'Cfs2s, 'Cimm5D('Crs1s)"); ++ break; ++ case RO_C_SW: ++ Format(instr, "sw 'Crs2s, 'Cimm5W('Crs1s)"); ++ break; ++#ifdef JS_CODEGEN_RISCV64 ++ case RO_C_SD: ++ Format(instr, "sd 'Crs2s, 'Cimm5D('Crs1s)"); ++ break; ++#elif defined(JS_CODEGEN_RISCV32) ++ case RO_C_FSW: ++ Format(instr, "fsw 'Cfs2s, 'Cimm5D('Crs1s)"); ++ break; ++#endif ++ default: ++ UNSUPPORTED_RISCV(); ++ } ++} ++ ++void Decoder::DecodeCJType(Instruction* instr) { ++ switch (instr->RvcOpcode()) { ++ case RO_C_J: ++ Format(instr, "j 'Cimm11CJ"); ++ break; ++ default: ++ UNSUPPORTED_RISCV(); ++ } ++} ++ ++void Decoder::DecodeCBType(Instruction* instr) { ++ switch (instr->RvcOpcode()) { ++ case RO_C_BNEZ: ++ Format(instr, "bnez 'Crs1s, x0, 'Cimm8B"); ++ break; ++ case RO_C_BEQZ: ++ Format(instr, "beqz 'Crs1s, x0, 'Cimm8B"); ++ break; ++ case RO_C_MISC_ALU: ++ if (instr->RvcFunct2BValue() == 0b00) ++ Format(instr, "srli 'Crs1s, 'Crs1s, 'Cshamt"); ++ else if (instr->RvcFunct2BValue() == 0b01) ++ Format(instr, "srai 'Crs1s, 'Crs1s, 'Cshamt"); ++ else if (instr->RvcFunct2BValue() == 0b10) ++ Format(instr, "andi 'Crs1s, 'Crs1s, 'Cimm6"); ++ else ++ UNSUPPORTED_RISCV(); ++ break; ++ default: ++ UNSUPPORTED_RISCV(); ++ } ++} ++ ++#undef VERIFIY ++ ++bool Decoder::IsConstantPoolAt(uint8_t* instr_ptr) { ++ UNSUPPORTED_RISCV(); ++ MOZ_CRASH(); ++} ++ ++int Decoder::ConstantPoolSizeAt(uint8_t* instr_ptr) { ++ UNSUPPORTED_RISCV(); ++ MOZ_CRASH(); ++} ++ ++// Disassemble the instruction at *instr_ptr into the output buffer. ++int Decoder::InstructionDecode(byte* instr_ptr) { ++ Instruction* instr = Instruction::At(instr_ptr); ++ // Print raw instruction bytes. ++ out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%08x ", ++ instr->InstructionBits()); ++ switch (instr->InstructionType()) { ++ case Instruction::kRType: ++ DecodeRType(instr); ++ break; ++ case Instruction::kR4Type: ++ DecodeR4Type(instr); ++ break; ++ case Instruction::kIType: ++ DecodeIType(instr); ++ break; ++ case Instruction::kSType: ++ DecodeSType(instr); ++ break; ++ case Instruction::kBType: ++ DecodeBType(instr); ++ break; ++ case Instruction::kUType: ++ DecodeUType(instr); ++ break; ++ case Instruction::kJType: ++ DecodeJType(instr); ++ break; ++ case Instruction::kCRType: ++ DecodeCRType(instr); ++ break; ++ case Instruction::kCAType: ++ DecodeCAType(instr); ++ break; ++ case Instruction::kCJType: ++ DecodeCJType(instr); ++ break; ++ case Instruction::kCIType: ++ DecodeCIType(instr); ++ break; ++ case Instruction::kCIWType: ++ DecodeCIWType(instr); ++ break; ++ case Instruction::kCSSType: ++ DecodeCSSType(instr); ++ break; ++ case Instruction::kCLType: ++ DecodeCLType(instr); ++ break; ++ case Instruction::kCSType: ++ DecodeCSType(instr); ++ break; ++ case Instruction::kCBType: ++ DecodeCBType(instr); ++ break; ++#ifdef CAN_USE_RVV_INSTRUCTIONS ++ case Instruction::kVType: ++ DecodeVType(instr); ++ break; ++#endif ++ default: ++ Format(instr, "UNSUPPORTED"); ++ UNSUPPORTED_RISCV(); ++ } ++ return instr->InstructionSize(); ++} ++ ++} // namespace disasm ++ ++#undef STRING_STARTS_WITH ++#undef VERIFY ++ ++//------------------------------------------------------------------------------ ++ ++namespace disasm { ++ ++const char* NameConverter::NameOfAddress(uint8_t* addr) const { ++ SNPrintF(tmp_buffer_, "%p", addr); ++ return tmp_buffer_.start(); ++} ++ ++const char* NameConverter::NameOfConstant(uint8_t* addr) const { ++ return NameOfAddress(addr); ++} ++ ++const char* NameConverter::NameOfCPURegister(int reg) const { ++ return Registers::GetName(reg); ++} ++ ++const char* NameConverter::NameOfByteCPURegister(int reg) const { ++ MOZ_CRASH(" RISC-V does not have the concept of a byte register."); ++} ++ ++const char* NameConverter::NameOfXMMRegister(int reg) const { ++ return FloatRegisters::GetName(reg); ++} ++ ++const char* NameConverter::NameInCode(uint8_t* addr) const { ++ // The default name converter is called for unknown code. So we will not try ++ // to access any memory. ++ return ""; ++} ++ ++//------------------------------------------------------------------------------ ++ ++Disassembler::Disassembler(const NameConverter& converter) ++ : converter_(converter) {} ++ ++Disassembler::~Disassembler() {} ++ ++int Disassembler::InstructionDecode(V8Vector buffer, ++ uint8_t* instruction) { ++ Decoder d(converter_, buffer); ++ return d.InstructionDecode(instruction); ++} ++ ++int Disassembler::ConstantPoolSizeAt(uint8_t* instruction) { ++ return Decoder::ConstantPoolSizeAt(instruction); ++} ++ ++void Disassembler::Disassemble(FILE* f, uint8_t* begin, uint8_t* end) { ++ NameConverter converter; ++ Disassembler d(converter); ++ for (uint8_t* pc = begin; pc < end;) { ++ EmbeddedVector buffer; ++ buffer[0] = '\0'; ++ uint8_t* prev_pc = pc; ++ pc += d.InstructionDecode(buffer, pc); ++ fprintf(f, "%p %08x %s\n", prev_pc, ++ *reinterpret_cast(prev_pc), buffer.start()); ++ } ++} ++ ++} // namespace disasm ++} // namespace jit ++} // namespace js +diff --git a/js/src/jit/riscv64/disasm/Disasm-riscv64.h b/js/src/jit/riscv64/disasm/Disasm-riscv64.h +new file mode 100644 +index 0000000000..0548523f6b +--- /dev/null ++++ b/js/src/jit/riscv64/disasm/Disasm-riscv64.h +@@ -0,0 +1,74 @@ ++/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- ++ * vim: set ts=8 sts=2 et sw=2 tw=80: ++ */ ++// Copyright 2007-2008 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef jit_riscv64_disasm_Disasm_riscv64_h ++#define jit_riscv64_disasm_Disasm_riscv64_h ++ ++#include "mozilla/Assertions.h" ++#include "mozilla/Types.h" ++ ++#include ++ ++#include "jit/riscv64/constant/Constant-riscv64.h" ++#include "jit/riscv64/constant/util-riscv64.h" ++namespace js { ++namespace jit { ++namespace disasm { ++ ++typedef unsigned char byte; ++ ++// Interface and default implementation for converting addresses and ++// register-numbers to text. The default implementation is machine ++// specific. ++class NameConverter { ++ public: ++ virtual ~NameConverter() {} ++ virtual const char* NameOfCPURegister(int reg) const; ++ virtual const char* NameOfByteCPURegister(int reg) const; ++ virtual const char* NameOfXMMRegister(int reg) const; ++ virtual const char* NameOfAddress(byte* addr) const; ++ virtual const char* NameOfConstant(byte* addr) const; ++ virtual const char* NameInCode(byte* addr) const; ++ ++ protected: ++ EmbeddedVector tmp_buffer_; ++}; ++ ++// A generic Disassembler interface ++class Disassembler { ++ public: ++ // Caller deallocates converter. ++ explicit Disassembler(const NameConverter& converter); ++ ++ virtual ~Disassembler(); ++ ++ // Writes one disassembled instruction into 'buffer' (0-terminated). ++ // Returns the length of the disassembled machine instruction in bytes. ++ int InstructionDecode(V8Vector buffer, uint8_t* instruction); ++ ++ // Returns -1 if instruction does not mark the beginning of a constant pool, ++ // or the number of entries in the constant pool beginning here. ++ int ConstantPoolSizeAt(byte* instruction); ++ ++ // Write disassembly into specified file 'f' using specified NameConverter ++ // (see constructor). ++ static void Disassemble(FILE* f, uint8_t* begin, uint8_t* end); ++ ++ private: ++ const NameConverter& converter_; ++ ++ // Disallow implicit constructors. ++ Disassembler() = delete; ++ Disassembler(const Disassembler&) = delete; ++ void operator=(const Disassembler&) = delete; ++}; ++ ++} // namespace disasm ++} // namespace jit ++} // namespace js ++ ++#endif // jit_riscv64_disasm_Disasm_riscv64_h +diff --git a/js/src/jit/riscv64/extension/base-assembler-riscv.cc b/js/src/jit/riscv64/extension/base-assembler-riscv.cc +new file mode 100644 +index 0000000000..a64cc818b3 +--- /dev/null ++++ b/js/src/jit/riscv64/extension/base-assembler-riscv.cc +@@ -0,0 +1,517 @@ ++// Copyright (c) 1994-2006 Sun Microsystems Inc. ++// All Rights Reserved. ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions are ++// met: ++// ++// - Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// ++// - Redistribution in binary form must reproduce the above copyright ++// notice, this list of conditions and the following disclaimer in the ++// documentation and/or other materials provided with the distribution. ++// ++// - Neither the name of Sun Microsystems or the names of contributors may ++// be used to endorse or promote products derived from this software without ++// specific prior written permission. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ ++// The original source code covered by the above license above has been ++// modified significantly by Google Inc. ++// Copyright 2021 the V8 project authors. All rights reserved. ++ ++#include "jit/riscv64/extension/base-assembler-riscv.h" ++#include "jit/riscv64/Architecture-riscv64.h" ++ ++namespace js { ++namespace jit { ++ ++int ToNumber(Register reg) { ++ MOZ_ASSERT(reg.code() < Registers::Total && reg.code() >= 0); ++ const int kNumbers[] = { ++ 0, // zero_reg ++ 1, // ra ++ 2, // sp ++ 3, // gp ++ 4, // tp ++ 5, // t0 ++ 6, // t1 ++ 7, // t2 ++ 8, // s0/fp ++ 9, // s1 ++ 10, // a0 ++ 11, // a1 ++ 12, // a2 ++ 13, // a3 ++ 14, // a4 ++ 15, // a5 ++ 16, // a6 ++ 17, // a7 ++ 18, // s2 ++ 19, // s3 ++ 20, // s4 ++ 21, // s5 ++ 22, // s6 ++ 23, // s7 ++ 24, // s8 ++ 25, // s9 ++ 26, // s10 ++ 27, // s11 ++ 28, // t3 ++ 29, // t4 ++ 30, // t5 ++ 31, // t6 ++ }; ++ return kNumbers[reg.code()]; ++} ++ ++Register ToRegister(uint32_t num) { ++ MOZ_ASSERT(num >= 0 && num < Registers::Total); ++ const Register kRegisters[] = { ++ zero_reg, ra, sp, gp, tp, t0, t1, t2, fp, s1, a0, a1, a2, a3, a4, a5, ++ a6, a7, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, t3, t4, t5, t6}; ++ return kRegisters[num]; ++} ++ ++// ----- Top-level instruction formats match those in the ISA manual ++// (R, I, S, B, U, J). These match the formats defined in the compiler ++void AssemblerRiscvBase::GenInstrR(uint8_t funct7, uint8_t funct3, ++ BaseOpcode opcode, Register rd, Register rs1, ++ Register rs2) { ++ MOZ_ASSERT(is_uint7(funct7) && is_uint3(funct3)); ++ Instr instr = opcode | (rd.code() << kRdShift) | (funct3 << kFunct3Shift) | ++ (rs1.code() << kRs1Shift) | (rs2.code() << kRs2Shift) | ++ (funct7 << kFunct7Shift); ++ emit(instr); ++} ++ ++void AssemblerRiscvBase::GenInstrR(uint8_t funct7, uint8_t funct3, ++ BaseOpcode opcode, FPURegister rd, ++ FPURegister rs1, FPURegister rs2) { ++ MOZ_ASSERT(is_uint7(funct7) && is_uint3(funct3)); ++ Instr instr = opcode | (rd.code() << kRdShift) | (funct3 << kFunct3Shift) | ++ (rs1.code() << kRs1Shift) | (rs2.code() << kRs2Shift) | ++ (funct7 << kFunct7Shift); ++ emit(instr); ++} ++ ++void AssemblerRiscvBase::GenInstrR(uint8_t funct7, uint8_t funct3, ++ BaseOpcode opcode, Register rd, ++ FPURegister rs1, Register rs2) { ++ MOZ_ASSERT(is_uint7(funct7) && is_uint3(funct3)); ++ Instr instr = opcode | (rd.code() << kRdShift) | (funct3 << kFunct3Shift) | ++ (rs1.code() << kRs1Shift) | (rs2.code() << kRs2Shift) | ++ (funct7 << kFunct7Shift); ++ emit(instr); ++} ++ ++void AssemblerRiscvBase::GenInstrR(uint8_t funct7, uint8_t funct3, ++ BaseOpcode opcode, FPURegister rd, ++ Register rs1, Register rs2) { ++ MOZ_ASSERT(is_uint7(funct7) && is_uint3(funct3)); ++ Instr instr = opcode | (rd.code() << kRdShift) | (funct3 << kFunct3Shift) | ++ (rs1.code() << kRs1Shift) | (rs2.code() << kRs2Shift) | ++ (funct7 << kFunct7Shift); ++ emit(instr); ++} ++ ++void AssemblerRiscvBase::GenInstrR(uint8_t funct7, uint8_t funct3, ++ BaseOpcode opcode, FPURegister rd, ++ FPURegister rs1, Register rs2) { ++ MOZ_ASSERT(is_uint7(funct7) && is_uint3(funct3)); ++ Instr instr = opcode | (rd.code() << kRdShift) | (funct3 << kFunct3Shift) | ++ (rs1.code() << kRs1Shift) | (rs2.code() << kRs2Shift) | ++ (funct7 << kFunct7Shift); ++ emit(instr); ++} ++ ++void AssemblerRiscvBase::GenInstrR(uint8_t funct7, uint8_t funct3, ++ BaseOpcode opcode, Register rd, ++ FPURegister rs1, FPURegister rs2) { ++ MOZ_ASSERT(is_uint7(funct7) && is_uint3(funct3)); ++ Instr instr = opcode | (rd.code() << kRdShift) | (funct3 << kFunct3Shift) | ++ (rs1.code() << kRs1Shift) | (rs2.code() << kRs2Shift) | ++ (funct7 << kFunct7Shift); ++ emit(instr); ++} ++ ++void AssemblerRiscvBase::GenInstrR4(uint8_t funct2, BaseOpcode opcode, ++ Register rd, Register rs1, Register rs2, ++ Register rs3, FPURoundingMode frm) { ++ MOZ_ASSERT(is_uint2(funct2) && is_uint3(frm)); ++ Instr instr = opcode | (rd.code() << kRdShift) | (frm << kFunct3Shift) | ++ (rs1.code() << kRs1Shift) | (rs2.code() << kRs2Shift) | ++ (funct2 << kFunct2Shift) | (rs3.code() << kRs3Shift); ++ emit(instr); ++} ++ ++void AssemblerRiscvBase::GenInstrR4(uint8_t funct2, BaseOpcode opcode, ++ FPURegister rd, FPURegister rs1, ++ FPURegister rs2, FPURegister rs3, ++ FPURoundingMode frm) { ++ MOZ_ASSERT(is_uint2(funct2) && is_uint3(frm)); ++ Instr instr = opcode | (rd.code() << kRdShift) | (frm << kFunct3Shift) | ++ (rs1.code() << kRs1Shift) | (rs2.code() << kRs2Shift) | ++ (funct2 << kFunct2Shift) | (rs3.code() << kRs3Shift); ++ emit(instr); ++} ++ ++void AssemblerRiscvBase::GenInstrRAtomic(uint8_t funct5, bool aq, bool rl, ++ uint8_t funct3, Register rd, ++ Register rs1, Register rs2) { ++ MOZ_ASSERT(is_uint5(funct5) && is_uint3(funct3)); ++ Instr instr = AMO | (rd.code() << kRdShift) | (funct3 << kFunct3Shift) | ++ (rs1.code() << kRs1Shift) | (rs2.code() << kRs2Shift) | ++ (rl << kRlShift) | (aq << kAqShift) | (funct5 << kFunct5Shift); ++ emit(instr); ++} ++ ++void AssemblerRiscvBase::GenInstrRFrm(uint8_t funct7, BaseOpcode opcode, ++ Register rd, Register rs1, Register rs2, ++ FPURoundingMode frm) { ++ MOZ_ASSERT(is_uint3(frm)); ++ Instr instr = opcode | (rd.code() << kRdShift) | (frm << kFunct3Shift) | ++ (rs1.code() << kRs1Shift) | (rs2.code() << kRs2Shift) | ++ (funct7 << kFunct7Shift); ++ emit(instr); ++} ++ ++void AssemblerRiscvBase::GenInstrI(uint8_t funct3, BaseOpcode opcode, ++ Register rd, Register rs1, int16_t imm12) { ++ MOZ_ASSERT(is_uint3(funct3) && (is_uint12(imm12) || is_int12(imm12))); ++ Instr instr = opcode | (rd.code() << kRdShift) | (funct3 << kFunct3Shift) | ++ (rs1.code() << kRs1Shift) | (imm12 << kImm12Shift); ++ emit(instr); ++} ++ ++void AssemblerRiscvBase::GenInstrI(uint8_t funct3, BaseOpcode opcode, ++ FPURegister rd, Register rs1, ++ int16_t imm12) { ++ MOZ_ASSERT(is_uint3(funct3) && (is_uint12(imm12) || is_int12(imm12))); ++ Instr instr = opcode | (rd.code() << kRdShift) | (funct3 << kFunct3Shift) | ++ (rs1.code() << kRs1Shift) | (imm12 << kImm12Shift); ++ emit(instr); ++} ++ ++void AssemblerRiscvBase::GenInstrIShift(bool arithshift, uint8_t funct3, ++ BaseOpcode opcode, Register rd, ++ Register rs1, uint8_t shamt) { ++ MOZ_ASSERT(is_uint3(funct3) && is_uint6(shamt)); ++ Instr instr = opcode | (rd.code() << kRdShift) | (funct3 << kFunct3Shift) | ++ (rs1.code() << kRs1Shift) | (shamt << kShamtShift) | ++ (arithshift << kArithShiftShift); ++ emit(instr); ++} ++ ++void AssemblerRiscvBase::GenInstrIShiftW(bool arithshift, uint8_t funct3, ++ BaseOpcode opcode, Register rd, ++ Register rs1, uint8_t shamt) { ++ MOZ_ASSERT(is_uint3(funct3) && is_uint5(shamt)); ++ Instr instr = opcode | (rd.code() << kRdShift) | (funct3 << kFunct3Shift) | ++ (rs1.code() << kRs1Shift) | (shamt << kShamtWShift) | ++ (arithshift << kArithShiftShift); ++ emit(instr); ++} ++ ++void AssemblerRiscvBase::GenInstrS(uint8_t funct3, BaseOpcode opcode, ++ Register rs1, Register rs2, int16_t imm12) { ++ MOZ_ASSERT(is_uint3(funct3) && is_int12(imm12)); ++ Instr instr = opcode | ((imm12 & 0x1f) << 7) | // bits 4-0 ++ (funct3 << kFunct3Shift) | (rs1.code() << kRs1Shift) | ++ (rs2.code() << kRs2Shift) | ++ ((imm12 & 0xfe0) << 20); // bits 11-5 ++ emit(instr); ++} ++ ++void AssemblerRiscvBase::GenInstrS(uint8_t funct3, BaseOpcode opcode, ++ Register rs1, FPURegister rs2, ++ int16_t imm12) { ++ MOZ_ASSERT(is_uint3(funct3) && is_int12(imm12)); ++ Instr instr = opcode | ((imm12 & 0x1f) << 7) | // bits 4-0 ++ (funct3 << kFunct3Shift) | (rs1.code() << kRs1Shift) | ++ (rs2.code() << kRs2Shift) | ++ ((imm12 & 0xfe0) << 20); // bits 11-5 ++ emit(instr); ++} ++ ++void AssemblerRiscvBase::GenInstrB(uint8_t funct3, BaseOpcode opcode, ++ Register rs1, Register rs2, int16_t imm13) { ++ MOZ_ASSERT(is_uint3(funct3) && is_int13(imm13) && ((imm13 & 1) == 0)); ++ Instr instr = opcode | ((imm13 & 0x800) >> 4) | // bit 11 ++ ((imm13 & 0x1e) << 7) | // bits 4-1 ++ (funct3 << kFunct3Shift) | (rs1.code() << kRs1Shift) | ++ (rs2.code() << kRs2Shift) | ++ ((imm13 & 0x7e0) << 20) | // bits 10-5 ++ ((imm13 & 0x1000) << 19); // bit 12 ++ emit(instr); ++} ++ ++void AssemblerRiscvBase::GenInstrU(BaseOpcode opcode, Register rd, ++ int32_t imm20) { ++ MOZ_ASSERT((is_int20(imm20) || is_uint20(imm20))); ++ Instr instr = opcode | (rd.code() << kRdShift) | (imm20 << kImm20Shift); ++ emit(instr); ++} ++ ++void AssemblerRiscvBase::GenInstrJ(BaseOpcode opcode, Register rd, ++ int32_t imm21) { ++ MOZ_ASSERT(is_int21(imm21) && ((imm21 & 1) == 0)); ++ Instr instr = opcode | (rd.code() << kRdShift) | ++ (imm21 & 0xff000) | // bits 19-12 ++ ((imm21 & 0x800) << 9) | // bit 11 ++ ((imm21 & 0x7fe) << 20) | // bits 10-1 ++ ((imm21 & 0x100000) << 11); // bit 20 ++ emit(instr); ++} ++ ++void AssemblerRiscvBase::GenInstrCR(uint8_t funct4, BaseOpcode opcode, ++ Register rd, Register rs2) { ++ MOZ_ASSERT(is_uint4(funct4)); ++ ShortInstr instr = opcode | (rs2.code() << kRvcRs2Shift) | ++ (rd.code() << kRvcRdShift) | (funct4 << kRvcFunct4Shift); ++ emit(instr); ++} ++ ++void AssemblerRiscvBase::GenInstrCA(uint8_t funct6, BaseOpcode opcode, ++ Register rd, uint8_t funct, Register rs2) { ++ MOZ_ASSERT(is_uint6(funct6) && is_uint2(funct)); ++ ShortInstr instr = opcode | ((rs2.code() & 0x7) << kRvcRs2sShift) | ++ ((rd.code() & 0x7) << kRvcRs1sShift) | ++ (funct6 << kRvcFunct6Shift) | (funct << kRvcFunct2Shift); ++ emit(instr); ++} ++ ++void AssemblerRiscvBase::GenInstrCI(uint8_t funct3, BaseOpcode opcode, ++ Register rd, int8_t imm6) { ++ MOZ_ASSERT(is_uint3(funct3) && is_int6(imm6)); ++ ShortInstr instr = opcode | ((imm6 & 0x1f) << 2) | ++ (rd.code() << kRvcRdShift) | ((imm6 & 0x20) << 7) | ++ (funct3 << kRvcFunct3Shift); ++ emit(instr); ++} ++ ++void AssemblerRiscvBase::GenInstrCIU(uint8_t funct3, BaseOpcode opcode, ++ Register rd, uint8_t uimm6) { ++ MOZ_ASSERT(is_uint3(funct3) && is_uint6(uimm6)); ++ ShortInstr instr = opcode | ((uimm6 & 0x1f) << 2) | ++ (rd.code() << kRvcRdShift) | ((uimm6 & 0x20) << 7) | ++ (funct3 << kRvcFunct3Shift); ++ emit(instr); ++} ++ ++void AssemblerRiscvBase::GenInstrCIU(uint8_t funct3, BaseOpcode opcode, ++ FPURegister rd, uint8_t uimm6) { ++ MOZ_ASSERT(is_uint3(funct3) && is_uint6(uimm6)); ++ ShortInstr instr = opcode | ((uimm6 & 0x1f) << 2) | ++ (rd.code() << kRvcRdShift) | ((uimm6 & 0x20) << 7) | ++ (funct3 << kRvcFunct3Shift); ++ emit(instr); ++} ++ ++void AssemblerRiscvBase::GenInstrCIW(uint8_t funct3, BaseOpcode opcode, ++ Register rd, uint8_t uimm8) { ++ MOZ_ASSERT(is_uint3(funct3) && is_uint8(uimm8)); ++ ShortInstr instr = opcode | ((uimm8) << 5) | ++ ((rd.code() & 0x7) << kRvcRs2sShift) | ++ (funct3 << kRvcFunct3Shift); ++ emit(instr); ++} ++ ++void AssemblerRiscvBase::GenInstrCSS(uint8_t funct3, BaseOpcode opcode, ++ Register rs2, uint8_t uimm6) { ++ MOZ_ASSERT(is_uint3(funct3) && is_uint6(uimm6)); ++ ShortInstr instr = opcode | (uimm6 << 7) | (rs2.code() << kRvcRs2Shift) | ++ (funct3 << kRvcFunct3Shift); ++ emit(instr); ++} ++ ++void AssemblerRiscvBase::GenInstrCSS(uint8_t funct3, BaseOpcode opcode, ++ FPURegister rs2, uint8_t uimm6) { ++ MOZ_ASSERT(is_uint3(funct3) && is_uint6(uimm6)); ++ ShortInstr instr = opcode | (uimm6 << 7) | (rs2.code() << kRvcRs2Shift) | ++ (funct3 << kRvcFunct3Shift); ++ emit(instr); ++} ++ ++void AssemblerRiscvBase::GenInstrCL(uint8_t funct3, BaseOpcode opcode, ++ Register rd, Register rs1, uint8_t uimm5) { ++ MOZ_ASSERT(is_uint3(funct3) && is_uint5(uimm5)); ++ ShortInstr instr = opcode | ((uimm5 & 0x3) << 5) | ++ ((rd.code() & 0x7) << kRvcRs2sShift) | ++ ((uimm5 & 0x1c) << 8) | (funct3 << kRvcFunct3Shift) | ++ ((rs1.code() & 0x7) << kRvcRs1sShift); ++ emit(instr); ++} ++ ++void AssemblerRiscvBase::GenInstrCL(uint8_t funct3, BaseOpcode opcode, ++ FPURegister rd, Register rs1, ++ uint8_t uimm5) { ++ MOZ_ASSERT(is_uint3(funct3) && is_uint5(uimm5)); ++ ShortInstr instr = opcode | ((uimm5 & 0x3) << 5) | ++ ((rd.code() & 0x7) << kRvcRs2sShift) | ++ ((uimm5 & 0x1c) << 8) | (funct3 << kRvcFunct3Shift) | ++ ((rs1.code() & 0x7) << kRvcRs1sShift); ++ emit(instr); ++} ++void AssemblerRiscvBase::GenInstrCJ(uint8_t funct3, BaseOpcode opcode, ++ uint16_t uint11) { ++ MOZ_ASSERT(is_uint11(uint11)); ++ ShortInstr instr = opcode | (funct3 << kRvcFunct3Shift) | (uint11 << 2); ++ emit(instr); ++} ++ ++void AssemblerRiscvBase::GenInstrCS(uint8_t funct3, BaseOpcode opcode, ++ Register rs2, Register rs1, uint8_t uimm5) { ++ MOZ_ASSERT(is_uint3(funct3) && is_uint5(uimm5)); ++ ShortInstr instr = opcode | ((uimm5 & 0x3) << 5) | ++ ((rs2.code() & 0x7) << kRvcRs2sShift) | ++ ((uimm5 & 0x1c) << 8) | (funct3 << kRvcFunct3Shift) | ++ ((rs1.code() & 0x7) << kRvcRs1sShift); ++ emit(instr); ++} ++ ++void AssemblerRiscvBase::GenInstrCS(uint8_t funct3, BaseOpcode opcode, ++ FPURegister rs2, Register rs1, ++ uint8_t uimm5) { ++ MOZ_ASSERT(is_uint3(funct3) && is_uint5(uimm5)); ++ ShortInstr instr = opcode | ((uimm5 & 0x3) << 5) | ++ ((rs2.code() & 0x7) << kRvcRs2sShift) | ++ ((uimm5 & 0x1c) << 8) | (funct3 << kRvcFunct3Shift) | ++ ((rs1.code() & 0x7) << kRvcRs1sShift); ++ emit(instr); ++} ++ ++void AssemblerRiscvBase::GenInstrCB(uint8_t funct3, BaseOpcode opcode, ++ Register rs1, uint8_t uimm8) { ++ MOZ_ASSERT(is_uint3(funct3) && is_uint8(uimm8)); ++ ShortInstr instr = opcode | ((uimm8 & 0x1f) << 2) | ((uimm8 & 0xe0) << 5) | ++ ((rs1.code() & 0x7) << kRvcRs1sShift) | ++ (funct3 << kRvcFunct3Shift); ++ emit(instr); ++} ++ ++void AssemblerRiscvBase::GenInstrCBA(uint8_t funct3, uint8_t funct2, ++ BaseOpcode opcode, Register rs1, ++ int8_t imm6) { ++ MOZ_ASSERT(is_uint3(funct3) && is_uint2(funct2) && is_int6(imm6)); ++ ShortInstr instr = opcode | ((imm6 & 0x1f) << 2) | ((imm6 & 0x20) << 7) | ++ ((rs1.code() & 0x7) << kRvcRs1sShift) | ++ (funct3 << kRvcFunct3Shift) | (funct2 << 10); ++ emit(instr); ++} ++// ----- Instruction class templates match those in the compiler ++ ++void AssemblerRiscvBase::GenInstrBranchCC_rri(uint8_t funct3, Register rs1, ++ Register rs2, int16_t imm13) { ++ GenInstrB(funct3, BRANCH, rs1, rs2, imm13); ++} ++ ++void AssemblerRiscvBase::GenInstrLoad_ri(uint8_t funct3, Register rd, ++ Register rs1, int16_t imm12) { ++ GenInstrI(funct3, LOAD, rd, rs1, imm12); ++} ++ ++void AssemblerRiscvBase::GenInstrStore_rri(uint8_t funct3, Register rs1, ++ Register rs2, int16_t imm12) { ++ GenInstrS(funct3, STORE, rs1, rs2, imm12); ++} ++ ++void AssemblerRiscvBase::GenInstrALU_ri(uint8_t funct3, Register rd, ++ Register rs1, int16_t imm12) { ++ GenInstrI(funct3, OP_IMM, rd, rs1, imm12); ++} ++ ++void AssemblerRiscvBase::GenInstrShift_ri(bool arithshift, uint8_t funct3, ++ Register rd, Register rs1, ++ uint8_t shamt) { ++ MOZ_ASSERT(is_uint6(shamt)); ++ GenInstrI(funct3, OP_IMM, rd, rs1, (arithshift << 10) | shamt); ++} ++ ++void AssemblerRiscvBase::GenInstrALU_rr(uint8_t funct7, uint8_t funct3, ++ Register rd, Register rs1, ++ Register rs2) { ++ GenInstrR(funct7, funct3, OP, rd, rs1, rs2); ++} ++ ++void AssemblerRiscvBase::GenInstrCSR_ir(uint8_t funct3, Register rd, ++ ControlStatusReg csr, Register rs1) { ++ GenInstrI(funct3, SYSTEM, rd, rs1, csr); ++} ++ ++void AssemblerRiscvBase::GenInstrCSR_ii(uint8_t funct3, Register rd, ++ ControlStatusReg csr, uint8_t imm5) { ++ GenInstrI(funct3, SYSTEM, rd, ToRegister(imm5), csr); ++} ++ ++void AssemblerRiscvBase::GenInstrShiftW_ri(bool arithshift, uint8_t funct3, ++ Register rd, Register rs1, ++ uint8_t shamt) { ++ GenInstrIShiftW(arithshift, funct3, OP_IMM_32, rd, rs1, shamt); ++} ++ ++void AssemblerRiscvBase::GenInstrALUW_rr(uint8_t funct7, uint8_t funct3, ++ Register rd, Register rs1, ++ Register rs2) { ++ GenInstrR(funct7, funct3, OP_32, rd, rs1, rs2); ++} ++ ++void AssemblerRiscvBase::GenInstrPriv(uint8_t funct7, Register rs1, ++ Register rs2) { ++ GenInstrR(funct7, 0b000, SYSTEM, ToRegister(0UL), rs1, rs2); ++} ++ ++void AssemblerRiscvBase::GenInstrLoadFP_ri(uint8_t funct3, FPURegister rd, ++ Register rs1, int16_t imm12) { ++ GenInstrI(funct3, LOAD_FP, rd, rs1, imm12); ++} ++ ++void AssemblerRiscvBase::GenInstrStoreFP_rri(uint8_t funct3, Register rs1, ++ FPURegister rs2, int16_t imm12) { ++ GenInstrS(funct3, STORE_FP, rs1, rs2, imm12); ++} ++ ++void AssemblerRiscvBase::GenInstrALUFP_rr(uint8_t funct7, uint8_t funct3, ++ FPURegister rd, FPURegister rs1, ++ FPURegister rs2) { ++ GenInstrR(funct7, funct3, OP_FP, rd, rs1, rs2); ++} ++ ++void AssemblerRiscvBase::GenInstrALUFP_rr(uint8_t funct7, uint8_t funct3, ++ FPURegister rd, Register rs1, ++ Register rs2) { ++ GenInstrR(funct7, funct3, OP_FP, rd, rs1, rs2); ++} ++ ++void AssemblerRiscvBase::GenInstrALUFP_rr(uint8_t funct7, uint8_t funct3, ++ FPURegister rd, FPURegister rs1, ++ Register rs2) { ++ GenInstrR(funct7, funct3, OP_FP, rd, rs1, rs2); ++} ++ ++void AssemblerRiscvBase::GenInstrALUFP_rr(uint8_t funct7, uint8_t funct3, ++ Register rd, FPURegister rs1, ++ Register rs2) { ++ GenInstrR(funct7, funct3, OP_FP, rd, rs1, rs2); ++} ++ ++void AssemblerRiscvBase::GenInstrALUFP_rr(uint8_t funct7, uint8_t funct3, ++ Register rd, FPURegister rs1, ++ FPURegister rs2) { ++ GenInstrR(funct7, funct3, OP_FP, rd, rs1, rs2); ++} ++ ++} // namespace jit ++} // namespace js +diff --git a/js/src/jit/riscv64/extension/base-assembler-riscv.h b/js/src/jit/riscv64/extension/base-assembler-riscv.h +new file mode 100644 +index 0000000000..36f8d62b9a +--- /dev/null ++++ b/js/src/jit/riscv64/extension/base-assembler-riscv.h +@@ -0,0 +1,219 @@ ++// Copyright (c) 1994-2006 Sun Microsystems Inc. ++// All Rights Reserved. ++// ++// Redistribution and use in source and binary forms, with or without ++// modification, are permitted provided that the following conditions are ++// met: ++// ++// - Redistributions of source code must retain the above copyright notice, ++// this list of conditions and the following disclaimer. ++// ++// - Redistribution in binary form must reproduce the above copyright ++// notice, this list of conditions and the following disclaimer in the ++// documentation and/or other materials provided with the distribution. ++// ++// - Neither the name of Sun Microsystems or the names of contributors may ++// be used to endorse or promote products derived from this software without ++// specific prior written permission. ++// ++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ ++// The original source code covered by the above license above has been ++// modified significantly by Google Inc. ++// Copyright 2021 the V8 project authors. All rights reserved. ++ ++#ifndef jit_riscv64_extension_Base_assembler_riscv_h ++#define jit_riscv64_extension_Base_assembler_riscv_h ++ ++#include ++#include ++#include ++ ++#include "jit/Label.h" ++#include "jit/riscv64/Architecture-riscv64.h" ++#include "jit/riscv64/constant/Constant-riscv64.h" ++#include "jit/riscv64/Register-riscv64.h" ++ ++#define xlen (uint8_t(sizeof(void*) * 8)) ++ ++#define kBitsPerByte 8UL ++// Check number width. ++inline constexpr bool is_intn(int64_t x, unsigned n) { ++ MOZ_ASSERT((0 < n) && (n < 64)); ++ int64_t limit = static_cast(1) << (n - 1); ++ return (-limit <= x) && (x < limit); ++} ++ ++inline constexpr bool is_uintn(int64_t x, unsigned n) { ++ MOZ_ASSERT((0 < n) && (n < (sizeof(x) * kBitsPerByte))); ++ return !(x >> n); ++} ++#undef kBitsPerByte ++// clang-format off ++#define INT_1_TO_63_LIST(V) \ ++ V(1) V(2) V(3) V(4) V(5) V(6) V(7) V(8) V(9) V(10) \ ++ V(11) V(12) V(13) V(14) V(15) V(16) V(17) V(18) V(19) V(20) \ ++ V(21) V(22) V(23) V(24) V(25) V(26) V(27) V(28) V(29) V(30) \ ++ V(31) V(32) V(33) V(34) V(35) V(36) V(37) V(38) V(39) V(40) \ ++ V(41) V(42) V(43) V(44) V(45) V(46) V(47) V(48) V(49) V(50) \ ++ V(51) V(52) V(53) V(54) V(55) V(56) V(57) V(58) V(59) V(60) \ ++ V(61) V(62) V(63) ++// clang-format on ++ ++#define DECLARE_IS_INT_N(N) \ ++ inline constexpr bool is_int##N(int64_t x) { return is_intn(x, N); } ++ ++#define DECLARE_IS_UINT_N(N) \ ++ template \ ++ inline constexpr bool is_uint##N(T x) { \ ++ return is_uintn(x, N); \ ++ } ++INT_1_TO_63_LIST(DECLARE_IS_INT_N) ++INT_1_TO_63_LIST(DECLARE_IS_UINT_N) ++ ++#undef DECLARE_IS_INT_N ++#undef INT_1_TO_63_LIST ++ ++namespace js { ++namespace jit { ++ ++typedef FloatRegister FPURegister; ++#define zero_reg zero ++ ++#define DEBUG_PRINTF(...) \ ++ if (FLAG_riscv_debug) { \ ++ printf(__VA_ARGS__); \ ++ } ++ ++int ToNumber(Register reg); ++Register ToRegister(uint32_t num); ++ ++class AssemblerRiscvBase { ++ protected: ++ virtual int32_t branch_offset_helper(Label* L, OffsetSize bits) = 0; ++ ++ virtual void emit(Instr x) = 0; ++ virtual void emit(ShortInstr x) = 0; ++ virtual void emit(uint64_t x) = 0; ++ virtual uint32_t currentOffset() = 0; ++ // Instruction generation. ++ ++ // ----- Top-level instruction formats match those in the ISA manual ++ // (R, I, S, B, U, J). These match the formats defined in LLVM's ++ // RISCVInstrFormats.td. ++ void GenInstrR(uint8_t funct7, uint8_t funct3, BaseOpcode opcode, Register rd, ++ Register rs1, Register rs2); ++ void GenInstrR(uint8_t funct7, uint8_t funct3, BaseOpcode opcode, ++ FPURegister rd, FPURegister rs1, FPURegister rs2); ++ void GenInstrR(uint8_t funct7, uint8_t funct3, BaseOpcode opcode, Register rd, ++ FPURegister rs1, Register rs2); ++ void GenInstrR(uint8_t funct7, uint8_t funct3, BaseOpcode opcode, ++ FPURegister rd, Register rs1, Register rs2); ++ void GenInstrR(uint8_t funct7, uint8_t funct3, BaseOpcode opcode, ++ FPURegister rd, FPURegister rs1, Register rs2); ++ void GenInstrR(uint8_t funct7, uint8_t funct3, BaseOpcode opcode, Register rd, ++ FPURegister rs1, FPURegister rs2); ++ void GenInstrR4(uint8_t funct2, BaseOpcode opcode, Register rd, Register rs1, ++ Register rs2, Register rs3, FPURoundingMode frm); ++ void GenInstrR4(uint8_t funct2, BaseOpcode opcode, FPURegister rd, ++ FPURegister rs1, FPURegister rs2, FPURegister rs3, ++ FPURoundingMode frm); ++ void GenInstrRAtomic(uint8_t funct5, bool aq, bool rl, uint8_t funct3, ++ Register rd, Register rs1, Register rs2); ++ void GenInstrRFrm(uint8_t funct7, BaseOpcode opcode, Register rd, ++ Register rs1, Register rs2, FPURoundingMode frm); ++ void GenInstrI(uint8_t funct3, BaseOpcode opcode, Register rd, Register rs1, ++ int16_t imm12); ++ void GenInstrI(uint8_t funct3, BaseOpcode opcode, FPURegister rd, ++ Register rs1, int16_t imm12); ++ void GenInstrIShift(bool arithshift, uint8_t funct3, BaseOpcode opcode, ++ Register rd, Register rs1, uint8_t shamt); ++ void GenInstrIShiftW(bool arithshift, uint8_t funct3, BaseOpcode opcode, ++ Register rd, Register rs1, uint8_t shamt); ++ void GenInstrS(uint8_t funct3, BaseOpcode opcode, Register rs1, Register rs2, ++ int16_t imm12); ++ void GenInstrS(uint8_t funct3, BaseOpcode opcode, Register rs1, ++ FPURegister rs2, int16_t imm12); ++ void GenInstrB(uint8_t funct3, BaseOpcode opcode, Register rs1, Register rs2, ++ int16_t imm12); ++ void GenInstrU(BaseOpcode opcode, Register rd, int32_t imm20); ++ void GenInstrJ(BaseOpcode opcode, Register rd, int32_t imm20); ++ void GenInstrCR(uint8_t funct4, BaseOpcode opcode, Register rd, Register rs2); ++ void GenInstrCA(uint8_t funct6, BaseOpcode opcode, Register rd, uint8_t funct, ++ Register rs2); ++ void GenInstrCI(uint8_t funct3, BaseOpcode opcode, Register rd, int8_t imm6); ++ void GenInstrCIU(uint8_t funct3, BaseOpcode opcode, Register rd, ++ uint8_t uimm6); ++ void GenInstrCIU(uint8_t funct3, BaseOpcode opcode, FPURegister rd, ++ uint8_t uimm6); ++ void GenInstrCIW(uint8_t funct3, BaseOpcode opcode, Register rd, ++ uint8_t uimm8); ++ void GenInstrCSS(uint8_t funct3, BaseOpcode opcode, FPURegister rs2, ++ uint8_t uimm6); ++ void GenInstrCSS(uint8_t funct3, BaseOpcode opcode, Register rs2, ++ uint8_t uimm6); ++ void GenInstrCL(uint8_t funct3, BaseOpcode opcode, Register rd, Register rs1, ++ uint8_t uimm5); ++ void GenInstrCL(uint8_t funct3, BaseOpcode opcode, FPURegister rd, ++ Register rs1, uint8_t uimm5); ++ void GenInstrCS(uint8_t funct3, BaseOpcode opcode, Register rs2, Register rs1, ++ uint8_t uimm5); ++ void GenInstrCS(uint8_t funct3, BaseOpcode opcode, FPURegister rs2, ++ Register rs1, uint8_t uimm5); ++ void GenInstrCJ(uint8_t funct3, BaseOpcode opcode, uint16_t uint11); ++ void GenInstrCB(uint8_t funct3, BaseOpcode opcode, Register rs1, ++ uint8_t uimm8); ++ void GenInstrCBA(uint8_t funct3, uint8_t funct2, BaseOpcode opcode, ++ Register rs1, int8_t imm6); ++ ++ // ----- Instruction class templates match those in LLVM's RISCVInstrInfo.td ++ void GenInstrBranchCC_rri(uint8_t funct3, Register rs1, Register rs2, ++ int16_t imm12); ++ void GenInstrLoad_ri(uint8_t funct3, Register rd, Register rs1, ++ int16_t imm12); ++ void GenInstrStore_rri(uint8_t funct3, Register rs1, Register rs2, ++ int16_t imm12); ++ void GenInstrALU_ri(uint8_t funct3, Register rd, Register rs1, int16_t imm12); ++ void GenInstrShift_ri(bool arithshift, uint8_t funct3, Register rd, ++ Register rs1, uint8_t shamt); ++ void GenInstrALU_rr(uint8_t funct7, uint8_t funct3, Register rd, Register rs1, ++ Register rs2); ++ void GenInstrCSR_ir(uint8_t funct3, Register rd, ControlStatusReg csr, ++ Register rs1); ++ void GenInstrCSR_ii(uint8_t funct3, Register rd, ControlStatusReg csr, ++ uint8_t rs1); ++ void GenInstrShiftW_ri(bool arithshift, uint8_t funct3, Register rd, ++ Register rs1, uint8_t shamt); ++ void GenInstrALUW_rr(uint8_t funct7, uint8_t funct3, Register rd, ++ Register rs1, Register rs2); ++ void GenInstrPriv(uint8_t funct7, Register rs1, Register rs2); ++ void GenInstrLoadFP_ri(uint8_t funct3, FPURegister rd, Register rs1, ++ int16_t imm12); ++ void GenInstrStoreFP_rri(uint8_t funct3, Register rs1, FPURegister rs2, ++ int16_t imm12); ++ void GenInstrALUFP_rr(uint8_t funct7, uint8_t funct3, FPURegister rd, ++ FPURegister rs1, FPURegister rs2); ++ void GenInstrALUFP_rr(uint8_t funct7, uint8_t funct3, FPURegister rd, ++ Register rs1, Register rs2); ++ void GenInstrALUFP_rr(uint8_t funct7, uint8_t funct3, FPURegister rd, ++ FPURegister rs1, Register rs2); ++ void GenInstrALUFP_rr(uint8_t funct7, uint8_t funct3, Register rd, ++ FPURegister rs1, Register rs2); ++ void GenInstrALUFP_rr(uint8_t funct7, uint8_t funct3, Register rd, ++ FPURegister rs1, FPURegister rs2); ++}; ++ ++} // namespace jit ++} // namespace js ++ ++#endif // jit_riscv64_extension_Base_assembler_riscv_h +diff --git a/js/src/jit/riscv64/extension/base-riscv-i.cc b/js/src/jit/riscv64/extension/base-riscv-i.cc +new file mode 100644 +index 0000000000..2ee8877eb1 +--- /dev/null ++++ b/js/src/jit/riscv64/extension/base-riscv-i.cc +@@ -0,0 +1,351 @@ ++// Copyright 2022 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++#include "jit/riscv64/extension/base-riscv-i.h" ++#include "jit/riscv64/constant/Constant-riscv64.h" ++#include "jit/riscv64/Assembler-riscv64.h" ++#include "jit/riscv64/Architecture-riscv64.h" ++namespace js { ++namespace jit { ++ ++void AssemblerRISCVI::lui(Register rd, int32_t imm20) { ++ GenInstrU(LUI, rd, imm20); ++} ++ ++void AssemblerRISCVI::auipc(Register rd, int32_t imm20) { ++ GenInstrU(AUIPC, rd, imm20); ++} ++ ++// Jumps ++ ++void AssemblerRISCVI::jal(Register rd, int32_t imm21) { ++ GenInstrJ(JAL, rd, imm21); ++} ++ ++void AssemblerRISCVI::jalr(Register rd, Register rs1, int16_t imm12) { ++ GenInstrI(0b000, JALR, rd, rs1, imm12); ++} ++ ++// Branches ++ ++void AssemblerRISCVI::beq(Register rs1, Register rs2, int16_t imm13) { ++ GenInstrBranchCC_rri(0b000, rs1, rs2, imm13); ++} ++ ++void AssemblerRISCVI::bne(Register rs1, Register rs2, int16_t imm13) { ++ GenInstrBranchCC_rri(0b001, rs1, rs2, imm13); ++} ++ ++void AssemblerRISCVI::blt(Register rs1, Register rs2, int16_t imm13) { ++ GenInstrBranchCC_rri(0b100, rs1, rs2, imm13); ++} ++ ++void AssemblerRISCVI::bge(Register rs1, Register rs2, int16_t imm13) { ++ GenInstrBranchCC_rri(0b101, rs1, rs2, imm13); ++} ++ ++void AssemblerRISCVI::bltu(Register rs1, Register rs2, int16_t imm13) { ++ GenInstrBranchCC_rri(0b110, rs1, rs2, imm13); ++} ++ ++void AssemblerRISCVI::bgeu(Register rs1, Register rs2, int16_t imm13) { ++ GenInstrBranchCC_rri(0b111, rs1, rs2, imm13); ++} ++ ++// Loads ++ ++void AssemblerRISCVI::lb(Register rd, Register rs1, int16_t imm12) { ++ GenInstrLoad_ri(0b000, rd, rs1, imm12); ++} ++ ++void AssemblerRISCVI::lh(Register rd, Register rs1, int16_t imm12) { ++ GenInstrLoad_ri(0b001, rd, rs1, imm12); ++} ++ ++void AssemblerRISCVI::lw(Register rd, Register rs1, int16_t imm12) { ++ GenInstrLoad_ri(0b010, rd, rs1, imm12); ++} ++ ++void AssemblerRISCVI::lbu(Register rd, Register rs1, int16_t imm12) { ++ GenInstrLoad_ri(0b100, rd, rs1, imm12); ++} ++ ++void AssemblerRISCVI::lhu(Register rd, Register rs1, int16_t imm12) { ++ GenInstrLoad_ri(0b101, rd, rs1, imm12); ++} ++ ++// Stores ++ ++void AssemblerRISCVI::sb(Register source, Register base, int16_t imm12) { ++ GenInstrStore_rri(0b000, base, source, imm12); ++} ++ ++void AssemblerRISCVI::sh(Register source, Register base, int16_t imm12) { ++ GenInstrStore_rri(0b001, base, source, imm12); ++} ++ ++void AssemblerRISCVI::sw(Register source, Register base, int16_t imm12) { ++ GenInstrStore_rri(0b010, base, source, imm12); ++} ++ ++// Arithmetic with immediate ++ ++void AssemblerRISCVI::addi(Register rd, Register rs1, int16_t imm12) { ++ GenInstrALU_ri(0b000, rd, rs1, imm12); ++} ++ ++void AssemblerRISCVI::slti(Register rd, Register rs1, int16_t imm12) { ++ GenInstrALU_ri(0b010, rd, rs1, imm12); ++} ++ ++void AssemblerRISCVI::sltiu(Register rd, Register rs1, int16_t imm12) { ++ GenInstrALU_ri(0b011, rd, rs1, imm12); ++} ++ ++void AssemblerRISCVI::xori(Register rd, Register rs1, int16_t imm12) { ++ GenInstrALU_ri(0b100, rd, rs1, imm12); ++} ++ ++void AssemblerRISCVI::ori(Register rd, Register rs1, int16_t imm12) { ++ GenInstrALU_ri(0b110, rd, rs1, imm12); ++} ++ ++void AssemblerRISCVI::andi(Register rd, Register rs1, int16_t imm12) { ++ GenInstrALU_ri(0b111, rd, rs1, imm12); ++} ++ ++void AssemblerRISCVI::slli(Register rd, Register rs1, uint8_t shamt) { ++ GenInstrShift_ri(0, 0b001, rd, rs1, shamt & 0x3f); ++} ++ ++void AssemblerRISCVI::srli(Register rd, Register rs1, uint8_t shamt) { ++ GenInstrShift_ri(0, 0b101, rd, rs1, shamt & 0x3f); ++} ++ ++void AssemblerRISCVI::srai(Register rd, Register rs1, uint8_t shamt) { ++ GenInstrShift_ri(1, 0b101, rd, rs1, shamt & 0x3f); ++} ++ ++// Arithmetic ++ ++void AssemblerRISCVI::add(Register rd, Register rs1, Register rs2) { ++ GenInstrALU_rr(0b0000000, 0b000, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVI::sub(Register rd, Register rs1, Register rs2) { ++ GenInstrALU_rr(0b0100000, 0b000, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVI::sll(Register rd, Register rs1, Register rs2) { ++ GenInstrALU_rr(0b0000000, 0b001, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVI::slt(Register rd, Register rs1, Register rs2) { ++ GenInstrALU_rr(0b0000000, 0b010, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVI::sltu(Register rd, Register rs1, Register rs2) { ++ GenInstrALU_rr(0b0000000, 0b011, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVI::xor_(Register rd, Register rs1, Register rs2) { ++ GenInstrALU_rr(0b0000000, 0b100, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVI::srl(Register rd, Register rs1, Register rs2) { ++ GenInstrALU_rr(0b0000000, 0b101, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVI::sra(Register rd, Register rs1, Register rs2) { ++ GenInstrALU_rr(0b0100000, 0b101, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVI::or_(Register rd, Register rs1, Register rs2) { ++ GenInstrALU_rr(0b0000000, 0b110, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVI::and_(Register rd, Register rs1, Register rs2) { ++ GenInstrALU_rr(0b0000000, 0b111, rd, rs1, rs2); ++} ++ ++// Memory fences ++ ++void AssemblerRISCVI::fence(uint8_t pred, uint8_t succ) { ++ MOZ_ASSERT(is_uint4(pred) && is_uint4(succ)); ++ uint16_t imm12 = succ | (pred << 4) | (0b0000 << 8); ++ GenInstrI(0b000, MISC_MEM, ToRegister(0UL), ToRegister(0UL), imm12); ++} ++ ++void AssemblerRISCVI::fence_tso() { ++ uint16_t imm12 = (0b0011) | (0b0011 << 4) | (0b1000 << 8); ++ GenInstrI(0b000, MISC_MEM, ToRegister(0UL), ToRegister(0UL), imm12); ++} ++ ++// Environment call / break ++ ++void AssemblerRISCVI::ecall() { ++ GenInstrI(0b000, SYSTEM, ToRegister(0UL), ToRegister(0UL), 0); ++} ++ ++void AssemblerRISCVI::ebreak() { ++ GenInstrI(0b000, SYSTEM, ToRegister(0UL), ToRegister(0UL), 1); ++} ++ ++// This is a de facto standard (as set by GNU binutils) 32-bit unimplemented ++// instruction (i.e., it should always trap, if your implementation has invalid ++// instruction traps). ++void AssemblerRISCVI::unimp() { ++ GenInstrI(0b001, SYSTEM, ToRegister(0), ToRegister(0), 0b110000000000); ++} ++ ++bool AssemblerRISCVI::IsBranch(Instr instr) { ++ return (instr & kBaseOpcodeMask) == BRANCH; ++} ++ ++bool AssemblerRISCVI::IsJump(Instr instr) { ++ int Op = instr & kBaseOpcodeMask; ++ return Op == JAL || Op == JALR; ++} ++ ++bool AssemblerRISCVI::IsNop(Instr instr) { return instr == kNopByte; } ++ ++bool AssemblerRISCVI::IsJal(Instr instr) { ++ return (instr & kBaseOpcodeMask) == JAL; ++} ++ ++bool AssemblerRISCVI::IsJalr(Instr instr) { ++ return (instr & kBaseOpcodeMask) == JALR; ++} ++ ++bool AssemblerRISCVI::IsLui(Instr instr) { ++ return (instr & kBaseOpcodeMask) == LUI; ++} ++bool AssemblerRISCVI::IsAuipc(Instr instr) { ++ return (instr & kBaseOpcodeMask) == AUIPC; ++} ++bool AssemblerRISCVI::IsAddi(Instr instr) { ++ return (instr & (kBaseOpcodeMask | kFunct3Mask)) == RO_ADDI; ++} ++bool AssemblerRISCVI::IsOri(Instr instr) { ++ return (instr & (kBaseOpcodeMask | kFunct3Mask)) == RO_ORI; ++} ++bool AssemblerRISCVI::IsSlli(Instr instr) { ++ return (instr & (kBaseOpcodeMask | kFunct3Mask)) == RO_SLLI; ++} ++ ++int AssemblerRISCVI::JumpOffset(Instr instr) { ++ int32_t imm21 = ((instr & 0x7fe00000) >> 20) | ((instr & 0x100000) >> 9) | ++ (instr & 0xff000) | ((instr & 0x80000000) >> 11); ++ imm21 = imm21 << 11 >> 11; ++ return imm21; ++} ++ ++int AssemblerRISCVI::JalrOffset(Instr instr) { ++ MOZ_ASSERT(IsJalr(instr)); ++ int32_t imm12 = static_cast(instr & kImm12Mask) >> 20; ++ return imm12; ++} ++ ++int AssemblerRISCVI::AuipcOffset(Instr instr) { ++ MOZ_ASSERT(IsAuipc(instr)); ++ int32_t imm20 = static_cast(instr & kImm20Mask); ++ return imm20; ++} ++ ++bool AssemblerRISCVI::IsLw(Instr instr) { ++ return (instr & (kBaseOpcodeMask | kFunct3Mask)) == RO_LW; ++} ++ ++int AssemblerRISCVI::LoadOffset(Instr instr) { ++#if JS_CODEGEN_RISCV64 ++ MOZ_ASSERT(IsLd(instr)); ++#elif V8_TARGET_ARCH_RISCV32 ++ MOZ_ASSERT(IsLw(instr)); ++#endif ++ int32_t imm12 = static_cast(instr & kImm12Mask) >> 20; ++ return imm12; ++} ++ ++#ifdef JS_CODEGEN_RISCV64 ++ ++bool AssemblerRISCVI::IsAddiw(Instr instr) { ++ return (instr & (kBaseOpcodeMask | kFunct3Mask)) == RO_ADDIW; ++} ++ ++bool AssemblerRISCVI::IsLd(Instr instr) { ++ return (instr & (kBaseOpcodeMask | kFunct3Mask)) == RO_LD; ++} ++ ++void AssemblerRISCVI::lwu(Register rd, Register rs1, int16_t imm12) { ++ GenInstrLoad_ri(0b110, rd, rs1, imm12); ++} ++ ++void AssemblerRISCVI::ld(Register rd, Register rs1, int16_t imm12) { ++ GenInstrLoad_ri(0b011, rd, rs1, imm12); ++} ++ ++void AssemblerRISCVI::sd(Register source, Register base, int16_t imm12) { ++ GenInstrStore_rri(0b011, base, source, imm12); ++} ++ ++void AssemblerRISCVI::addiw(Register rd, Register rs1, int16_t imm12) { ++ GenInstrI(0b000, OP_IMM_32, rd, rs1, imm12); ++} ++ ++void AssemblerRISCVI::slliw(Register rd, Register rs1, uint8_t shamt) { ++ GenInstrShiftW_ri(0, 0b001, rd, rs1, shamt & 0x1f); ++} ++ ++void AssemblerRISCVI::srliw(Register rd, Register rs1, uint8_t shamt) { ++ GenInstrShiftW_ri(0, 0b101, rd, rs1, shamt & 0x1f); ++} ++ ++void AssemblerRISCVI::sraiw(Register rd, Register rs1, uint8_t shamt) { ++ GenInstrShiftW_ri(1, 0b101, rd, rs1, shamt & 0x1f); ++} ++ ++void AssemblerRISCVI::addw(Register rd, Register rs1, Register rs2) { ++ GenInstrALUW_rr(0b0000000, 0b000, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVI::subw(Register rd, Register rs1, Register rs2) { ++ GenInstrALUW_rr(0b0100000, 0b000, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVI::sllw(Register rd, Register rs1, Register rs2) { ++ GenInstrALUW_rr(0b0000000, 0b001, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVI::srlw(Register rd, Register rs1, Register rs2) { ++ GenInstrALUW_rr(0b0000000, 0b101, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVI::sraw(Register rd, Register rs1, Register rs2) { ++ GenInstrALUW_rr(0b0100000, 0b101, rd, rs1, rs2); ++} ++ ++#endif ++ ++int AssemblerRISCVI::BranchOffset(Instr instr) { ++ // | imm[12] | imm[10:5] | rs2 | rs1 | funct3 | imm[4:1|11] | opcode | ++ // 31 25 11 7 ++ int32_t imm13 = ((instr & 0xf00) >> 7) | ((instr & 0x7e000000) >> 20) | ++ ((instr & 0x80) << 4) | ((instr & 0x80000000) >> 19); ++ imm13 = imm13 << 19 >> 19; ++ return imm13; ++} ++ ++int AssemblerRISCVI::BrachlongOffset(Instr auipc, Instr instr_I) { ++ MOZ_ASSERT(reinterpret_cast(&instr_I)->InstructionType() == ++ InstructionBase::kIType); ++ MOZ_ASSERT(IsAuipc(auipc)); ++ MOZ_ASSERT(((auipc & kRdFieldMask) >> kRdShift) == ++ ((instr_I & kRs1FieldMask) >> kRs1Shift)); ++ int32_t imm_auipc = AuipcOffset(auipc); ++ int32_t imm12 = static_cast(instr_I & kImm12Mask) >> 20; ++ int32_t offset = imm12 + imm_auipc; ++ return offset; ++} ++ ++} // namespace jit ++} // namespace js +diff --git a/js/src/jit/riscv64/extension/base-riscv-i.h b/js/src/jit/riscv64/extension/base-riscv-i.h +new file mode 100644 +index 0000000000..cca342c960 +--- /dev/null ++++ b/js/src/jit/riscv64/extension/base-riscv-i.h +@@ -0,0 +1,273 @@ ++// Copyright 2022 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++#ifndef jit_riscv64_extension_Base_riscv_i_h_ ++#define jit_riscv64_extension_Base_riscv_i_h_ ++#include "mozilla/Assertions.h" ++ ++#include ++ ++#include "jit/riscv64/constant/Constant-riscv64.h" ++#include "jit/riscv64/extension/base-assembler-riscv.h" ++namespace js { ++namespace jit { ++ ++class AssemblerRISCVI : public AssemblerRiscvBase { ++ public: ++ void lui(Register rd, int32_t imm20); ++ void auipc(Register rd, int32_t imm20); ++ ++ // Jumps ++ void jal(Register rd, int32_t imm20); ++ void jalr(Register rd, Register rs1, int16_t imm12); ++ ++ // Branches ++ void beq(Register rs1, Register rs2, int16_t imm12); ++ void bne(Register rs1, Register rs2, int16_t imm12); ++ void blt(Register rs1, Register rs2, int16_t imm12); ++ void bge(Register rs1, Register rs2, int16_t imm12); ++ void bltu(Register rs1, Register rs2, int16_t imm12); ++ void bgeu(Register rs1, Register rs2, int16_t imm12); ++ // Loads ++ void lb(Register rd, Register rs1, int16_t imm12); ++ void lh(Register rd, Register rs1, int16_t imm12); ++ void lw(Register rd, Register rs1, int16_t imm12); ++ void lbu(Register rd, Register rs1, int16_t imm12); ++ void lhu(Register rd, Register rs1, int16_t imm12); ++ ++ // Stores ++ void sb(Register source, Register base, int16_t imm12); ++ void sh(Register source, Register base, int16_t imm12); ++ void sw(Register source, Register base, int16_t imm12); ++ ++ // Arithmetic with immediate ++ void addi(Register rd, Register rs1, int16_t imm12); ++ void slti(Register rd, Register rs1, int16_t imm12); ++ void sltiu(Register rd, Register rs1, int16_t imm12); ++ void xori(Register rd, Register rs1, int16_t imm12); ++ void ori(Register rd, Register rs1, int16_t imm12); ++ void andi(Register rd, Register rs1, int16_t imm12); ++ void slli(Register rd, Register rs1, uint8_t shamt); ++ void srli(Register rd, Register rs1, uint8_t shamt); ++ void srai(Register rd, Register rs1, uint8_t shamt); ++ ++ // Arithmetic ++ void add(Register rd, Register rs1, Register rs2); ++ void sub(Register rd, Register rs1, Register rs2); ++ void sll(Register rd, Register rs1, Register rs2); ++ void slt(Register rd, Register rs1, Register rs2); ++ void sltu(Register rd, Register rs1, Register rs2); ++ void xor_(Register rd, Register rs1, Register rs2); ++ void srl(Register rd, Register rs1, Register rs2); ++ void sra(Register rd, Register rs1, Register rs2); ++ void or_(Register rd, Register rs1, Register rs2); ++ void and_(Register rd, Register rs1, Register rs2); ++ ++ // Other pseudo instructions that are not part of RISCV pseudo assemly ++ void nor(Register rd, Register rs, Register rt) { ++ or_(rd, rs, rt); ++ not_(rd, rd); ++ } ++ ++ // Memory fences ++ void fence(uint8_t pred, uint8_t succ); ++ void fence_tso(); ++ ++ // Environment call / break ++ void ecall(); ++ void ebreak(); ++ ++ void sync() { fence(0b1111, 0b1111); } ++ ++ // This is a de facto standard (as set by GNU binutils) 32-bit unimplemented ++ // instruction (i.e., it should always trap, if your implementation has ++ // invalid instruction traps). ++ void unimp(); ++ ++ static int JumpOffset(Instr instr); ++ static int AuipcOffset(Instr instr); ++ static int JalrOffset(Instr instr); ++ static int LoadOffset(Instr instr); ++ static int BranchOffset(Instr instr); ++ static int BrachlongOffset(Instr auipc, Instr instr_I); ++ static inline Instr SetBranchOffset(int32_t pos, int32_t target_pos, ++ Instr instr) { ++ int32_t imm = target_pos - pos; ++ MOZ_ASSERT((imm & 1) == 0); ++ MOZ_ASSERT(is_intn(imm, kBranchOffsetBits)); ++ ++ instr &= ~kBImm12Mask; ++ int32_t imm12 = ((imm & 0x800) >> 4) | // bit 11 ++ ((imm & 0x1e) << 7) | // bits 4-1 ++ ((imm & 0x7e0) << 20) | // bits 10-5 ++ ((imm & 0x1000) << 19); // bit 12 ++ ++ return instr | (imm12 & kBImm12Mask); ++ } ++ ++ static inline Instr SetJalOffset(int32_t pos, int32_t target_pos, ++ Instr instr) { ++ MOZ_ASSERT(IsJal(instr)); ++ int32_t imm = target_pos - pos; ++ MOZ_ASSERT((imm & 1) == 0); ++ MOZ_ASSERT(is_intn(imm, kJumpOffsetBits)); ++ ++ instr &= ~kImm20Mask; ++ int32_t imm20 = (imm & 0xff000) | // bits 19-12 ++ ((imm & 0x800) << 9) | // bit 11 ++ ((imm & 0x7fe) << 20) | // bits 10-1 ++ ((imm & 0x100000) << 11); // bit 20 ++ ++ return instr | (imm20 & kImm20Mask); ++ } ++ ++ static inline Instr SetJalrOffset(int32_t offset, Instr instr) { ++ MOZ_ASSERT(IsJalr(instr)); ++ MOZ_ASSERT(is_int12(offset)); ++ instr &= ~kImm12Mask; ++ int32_t imm12 = offset << kImm12Shift; ++ MOZ_ASSERT(IsJalr(instr | (imm12 & kImm12Mask))); ++ MOZ_ASSERT(JalrOffset(instr | (imm12 & kImm12Mask)) == offset); ++ return instr | (imm12 & kImm12Mask); ++ } ++ ++ static inline Instr SetLoadOffset(int32_t offset, Instr instr) { ++#if JS_CODEGEN_RISCV64 ++ MOZ_ASSERT(IsLd(instr)); ++#elif JS_CODEGEN_RISCV32 ++ MOZ_ASSERT(IsLw(instr)); ++#endif ++ MOZ_ASSERT(is_int12(offset)); ++ instr &= ~kImm12Mask; ++ int32_t imm12 = offset << kImm12Shift; ++ return instr | (imm12 & kImm12Mask); ++ } ++ ++ static inline Instr SetAuipcOffset(int32_t offset, Instr instr) { ++ MOZ_ASSERT(IsAuipc(instr)); ++ MOZ_ASSERT(is_int20(offset)); ++ instr = (instr & ~kImm31_12Mask) | ((offset & kImm19_0Mask) << 12); ++ return instr; ++ } ++ ++ // Check if an instruction is a branch of some kind. ++ static bool IsBranch(Instr instr); ++ static bool IsNop(Instr instr); ++ static bool IsJump(Instr instr); ++ static bool IsJal(Instr instr); ++ static bool IsJalr(Instr instr); ++ static bool IsLui(Instr instr); ++ static bool IsAuipc(Instr instr); ++ static bool IsAddi(Instr instr); ++ static bool IsOri(Instr instr); ++ static bool IsSlli(Instr instr); ++ static bool IsLw(Instr instr); ++ ++ inline int32_t branch_offset(Label* L) { ++ return branch_offset_helper(L, OffsetSize::kOffset13); ++ } ++ inline int32_t jump_offset(Label* L) { ++ return branch_offset_helper(L, OffsetSize::kOffset21); ++ } ++ ++ // Branches ++ void beq(Register rs1, Register rs2, Label* L) { ++ beq(rs1, rs2, branch_offset(L)); ++ } ++ void bne(Register rs1, Register rs2, Label* L) { ++ bne(rs1, rs2, branch_offset(L)); ++ } ++ void blt(Register rs1, Register rs2, Label* L) { ++ blt(rs1, rs2, branch_offset(L)); ++ } ++ void bge(Register rs1, Register rs2, Label* L) { ++ bge(rs1, rs2, branch_offset(L)); ++ } ++ void bltu(Register rs1, Register rs2, Label* L) { ++ bltu(rs1, rs2, branch_offset(L)); ++ } ++ void bgeu(Register rs1, Register rs2, Label* L) { ++ bgeu(rs1, rs2, branch_offset(L)); ++ } ++ ++ void beqz(Register rs, int16_t imm13) { beq(rs, zero_reg, imm13); } ++ void beqz(Register rs1, Label* L) { beqz(rs1, branch_offset(L)); } ++ void bnez(Register rs, int16_t imm13) { bne(rs, zero_reg, imm13); } ++ void bnez(Register rs1, Label* L) { bnez(rs1, branch_offset(L)); } ++ void blez(Register rs, int16_t imm13) { bge(zero_reg, rs, imm13); } ++ void blez(Register rs1, Label* L) { blez(rs1, branch_offset(L)); } ++ void bgez(Register rs, int16_t imm13) { bge(rs, zero_reg, imm13); } ++ void bgez(Register rs1, Label* L) { bgez(rs1, branch_offset(L)); } ++ void bltz(Register rs, int16_t imm13) { blt(rs, zero_reg, imm13); } ++ void bltz(Register rs1, Label* L) { bltz(rs1, branch_offset(L)); } ++ void bgtz(Register rs, int16_t imm13) { blt(zero_reg, rs, imm13); } ++ ++ void bgtz(Register rs1, Label* L) { bgtz(rs1, branch_offset(L)); } ++ void bgt(Register rs1, Register rs2, int16_t imm13) { blt(rs2, rs1, imm13); } ++ void bgt(Register rs1, Register rs2, Label* L) { ++ bgt(rs1, rs2, branch_offset(L)); ++ } ++ void ble(Register rs1, Register rs2, int16_t imm13) { bge(rs2, rs1, imm13); } ++ void ble(Register rs1, Register rs2, Label* L) { ++ ble(rs1, rs2, branch_offset(L)); ++ } ++ void bgtu(Register rs1, Register rs2, int16_t imm13) { ++ bltu(rs2, rs1, imm13); ++ } ++ void bgtu(Register rs1, Register rs2, Label* L) { ++ bgtu(rs1, rs2, branch_offset(L)); ++ } ++ void bleu(Register rs1, Register rs2, int16_t imm13) { ++ bgeu(rs2, rs1, imm13); ++ } ++ void bleu(Register rs1, Register rs2, Label* L) { ++ bleu(rs1, rs2, branch_offset(L)); ++ } ++ ++ void j(int32_t imm21) { jal(zero_reg, imm21); } ++ void j(Label* L) { j(jump_offset(L)); } ++ void b(Label* L) { j(L); } ++ void jal(int32_t imm21) { jal(ra, imm21); } ++ void jal(Label* L) { jal(jump_offset(L)); } ++ void jr(Register rs) { jalr(zero_reg, rs, 0); } ++ void jr(Register rs, int32_t imm12) { jalr(zero_reg, rs, imm12); } ++ void jalr(Register rs, int32_t imm12) { jalr(ra, rs, imm12); } ++ void jalr(Register rs) { jalr(ra, rs, 0); } ++ void call(int32_t offset) { ++ auipc(ra, (offset >> 12) + ((offset & 0x800) >> 11)); ++ jalr(ra, ra, offset << 20 >> 20); ++ } ++ ++ void mv(Register rd, Register rs) { addi(rd, rs, 0); } ++ void not_(Register rd, Register rs) { xori(rd, rs, -1); } ++ void neg(Register rd, Register rs) { sub(rd, zero_reg, rs); } ++ void seqz(Register rd, Register rs) { sltiu(rd, rs, 1); } ++ void snez(Register rd, Register rs) { sltu(rd, zero_reg, rs); } ++ void sltz(Register rd, Register rs) { slt(rd, rs, zero_reg); } ++ void sgtz(Register rd, Register rs) { slt(rd, zero_reg, rs); } ++ ++#if JS_CODEGEN_RISCV64 ++ void lwu(Register rd, Register rs1, int16_t imm12); ++ void ld(Register rd, Register rs1, int16_t imm12); ++ void sd(Register source, Register base, int16_t imm12); ++ void addiw(Register rd, Register rs1, int16_t imm12); ++ void slliw(Register rd, Register rs1, uint8_t shamt); ++ void srliw(Register rd, Register rs1, uint8_t shamt); ++ void sraiw(Register rd, Register rs1, uint8_t shamt); ++ void addw(Register rd, Register rs1, Register rs2); ++ void subw(Register rd, Register rs1, Register rs2); ++ void sllw(Register rd, Register rs1, Register rs2); ++ void srlw(Register rd, Register rs1, Register rs2); ++ void sraw(Register rd, Register rs1, Register rs2); ++ void negw(Register rd, Register rs) { subw(rd, zero_reg, rs); } ++ void sext_w(Register rd, Register rs) { addiw(rd, rs, 0); } ++ ++ static bool IsAddiw(Instr instr); ++ static bool IsLd(Instr instr); ++#endif ++}; ++ ++} // namespace jit ++} // namespace js ++ ++#endif // jit_riscv64_extension_Base_riscv_I_h_ +diff --git a/js/src/jit/riscv64/extension/extension-riscv-a.cc b/js/src/jit/riscv64/extension/extension-riscv-a.cc +new file mode 100644 +index 0000000000..ead355fc0a +--- /dev/null ++++ b/js/src/jit/riscv64/extension/extension-riscv-a.cc +@@ -0,0 +1,123 @@ ++// Copyright 2022 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++#include "jit/riscv64/extension/extension-riscv-a.h" ++#include "jit/riscv64/Assembler-riscv64.h" ++#include "jit/riscv64/constant/Constant-riscv64.h" ++#include "jit/riscv64/Architecture-riscv64.h" ++namespace js { ++namespace jit { ++ ++// RV32A Standard Extension ++void AssemblerRISCVA::lr_w(bool aq, bool rl, Register rd, Register rs1) { ++ GenInstrRAtomic(0b00010, aq, rl, 0b010, rd, rs1, zero_reg); ++} ++ ++void AssemblerRISCVA::sc_w(bool aq, bool rl, Register rd, Register rs1, ++ Register rs2) { ++ GenInstrRAtomic(0b00011, aq, rl, 0b010, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVA::amoswap_w(bool aq, bool rl, Register rd, Register rs1, ++ Register rs2) { ++ GenInstrRAtomic(0b00001, aq, rl, 0b010, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVA::amoadd_w(bool aq, bool rl, Register rd, Register rs1, ++ Register rs2) { ++ GenInstrRAtomic(0b00000, aq, rl, 0b010, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVA::amoxor_w(bool aq, bool rl, Register rd, Register rs1, ++ Register rs2) { ++ GenInstrRAtomic(0b00100, aq, rl, 0b010, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVA::amoand_w(bool aq, bool rl, Register rd, Register rs1, ++ Register rs2) { ++ GenInstrRAtomic(0b01100, aq, rl, 0b010, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVA::amoor_w(bool aq, bool rl, Register rd, Register rs1, ++ Register rs2) { ++ GenInstrRAtomic(0b01000, aq, rl, 0b010, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVA::amomin_w(bool aq, bool rl, Register rd, Register rs1, ++ Register rs2) { ++ GenInstrRAtomic(0b10000, aq, rl, 0b010, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVA::amomax_w(bool aq, bool rl, Register rd, Register rs1, ++ Register rs2) { ++ GenInstrRAtomic(0b10100, aq, rl, 0b010, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVA::amominu_w(bool aq, bool rl, Register rd, Register rs1, ++ Register rs2) { ++ GenInstrRAtomic(0b11000, aq, rl, 0b010, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVA::amomaxu_w(bool aq, bool rl, Register rd, Register rs1, ++ Register rs2) { ++ GenInstrRAtomic(0b11100, aq, rl, 0b010, rd, rs1, rs2); ++} ++ ++// RV64A Standard Extension (in addition to RV32A) ++#ifdef JS_CODEGEN_RISCV64 ++void AssemblerRISCVA::lr_d(bool aq, bool rl, Register rd, Register rs1) { ++ GenInstrRAtomic(0b00010, aq, rl, 0b011, rd, rs1, zero_reg); ++} ++ ++void AssemblerRISCVA::sc_d(bool aq, bool rl, Register rd, Register rs1, ++ Register rs2) { ++ GenInstrRAtomic(0b00011, aq, rl, 0b011, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVA::amoswap_d(bool aq, bool rl, Register rd, Register rs1, ++ Register rs2) { ++ GenInstrRAtomic(0b00001, aq, rl, 0b011, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVA::amoadd_d(bool aq, bool rl, Register rd, Register rs1, ++ Register rs2) { ++ GenInstrRAtomic(0b00000, aq, rl, 0b011, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVA::amoxor_d(bool aq, bool rl, Register rd, Register rs1, ++ Register rs2) { ++ GenInstrRAtomic(0b00100, aq, rl, 0b011, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVA::amoand_d(bool aq, bool rl, Register rd, Register rs1, ++ Register rs2) { ++ GenInstrRAtomic(0b01100, aq, rl, 0b011, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVA::amoor_d(bool aq, bool rl, Register rd, Register rs1, ++ Register rs2) { ++ GenInstrRAtomic(0b01000, aq, rl, 0b011, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVA::amomin_d(bool aq, bool rl, Register rd, Register rs1, ++ Register rs2) { ++ GenInstrRAtomic(0b10000, aq, rl, 0b011, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVA::amomax_d(bool aq, bool rl, Register rd, Register rs1, ++ Register rs2) { ++ GenInstrRAtomic(0b10100, aq, rl, 0b011, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVA::amominu_d(bool aq, bool rl, Register rd, Register rs1, ++ Register rs2) { ++ GenInstrRAtomic(0b11000, aq, rl, 0b011, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVA::amomaxu_d(bool aq, bool rl, Register rd, Register rs1, ++ Register rs2) { ++ GenInstrRAtomic(0b11100, aq, rl, 0b011, rd, rs1, rs2); ++} ++#endif ++} // namespace jit ++} // namespace js +diff --git a/js/src/jit/riscv64/extension/extension-riscv-a.h b/js/src/jit/riscv64/extension/extension-riscv-a.h +new file mode 100644 +index 0000000000..442a4f5bba +--- /dev/null ++++ b/js/src/jit/riscv64/extension/extension-riscv-a.h +@@ -0,0 +1,46 @@ ++// Copyright 2022 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file." ++#ifndef jit_riscv64_extension_Extension_riscv_a_h_ ++#define jit_riscv64_extension_Extension_riscv_a_h_ ++#include "mozilla/Assertions.h" ++ ++#include ++ ++#include "jit/riscv64/extension/base-assembler-riscv.h" ++#include "jit/riscv64/Register-riscv64.h" ++namespace js { ++namespace jit { ++class AssemblerRISCVA : public AssemblerRiscvBase { ++ // RV32A Standard Extension ++ public: ++ void lr_w(bool aq, bool rl, Register rd, Register rs1); ++ void sc_w(bool aq, bool rl, Register rd, Register rs1, Register rs2); ++ void amoswap_w(bool aq, bool rl, Register rd, Register rs1, Register rs2); ++ void amoadd_w(bool aq, bool rl, Register rd, Register rs1, Register rs2); ++ void amoxor_w(bool aq, bool rl, Register rd, Register rs1, Register rs2); ++ void amoand_w(bool aq, bool rl, Register rd, Register rs1, Register rs2); ++ void amoor_w(bool aq, bool rl, Register rd, Register rs1, Register rs2); ++ void amomin_w(bool aq, bool rl, Register rd, Register rs1, Register rs2); ++ void amomax_w(bool aq, bool rl, Register rd, Register rs1, Register rs2); ++ void amominu_w(bool aq, bool rl, Register rd, Register rs1, Register rs2); ++ void amomaxu_w(bool aq, bool rl, Register rd, Register rs1, Register rs2); ++ ++#ifdef JS_CODEGEN_RISCV64 ++ // RV64A Standard Extension (in addition to RV32A) ++ void lr_d(bool aq, bool rl, Register rd, Register rs1); ++ void sc_d(bool aq, bool rl, Register rd, Register rs1, Register rs2); ++ void amoswap_d(bool aq, bool rl, Register rd, Register rs1, Register rs2); ++ void amoadd_d(bool aq, bool rl, Register rd, Register rs1, Register rs2); ++ void amoxor_d(bool aq, bool rl, Register rd, Register rs1, Register rs2); ++ void amoand_d(bool aq, bool rl, Register rd, Register rs1, Register rs2); ++ void amoor_d(bool aq, bool rl, Register rd, Register rs1, Register rs2); ++ void amomin_d(bool aq, bool rl, Register rd, Register rs1, Register rs2); ++ void amomax_d(bool aq, bool rl, Register rd, Register rs1, Register rs2); ++ void amominu_d(bool aq, bool rl, Register rd, Register rs1, Register rs2); ++ void amomaxu_d(bool aq, bool rl, Register rd, Register rs1, Register rs2); ++#endif ++}; ++} // namespace jit ++} // namespace js ++#endif // jit_riscv64_extension_Extension_riscv_A_h_ +diff --git a/js/src/jit/riscv64/extension/extension-riscv-c.cc b/js/src/jit/riscv64/extension/extension-riscv-c.cc +new file mode 100644 +index 0000000000..714753a0e0 +--- /dev/null ++++ b/js/src/jit/riscv64/extension/extension-riscv-c.cc +@@ -0,0 +1,275 @@ ++// Copyright 2022 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++#include "jit/riscv64/extension/extension-riscv-c.h" ++#include "jit/riscv64/Assembler-riscv64.h" ++#include "jit/riscv64/constant/Constant-riscv64.h" ++#include "jit/riscv64/Architecture-riscv64.h" ++namespace js { ++namespace jit { ++// RV64C Standard Extension ++void AssemblerRISCVC::c_nop() { GenInstrCI(0b000, C1, zero_reg, 0); } ++ ++void AssemblerRISCVC::c_addi(Register rd, int8_t imm6) { ++ MOZ_ASSERT(rd != zero_reg && imm6 != 0); ++ GenInstrCI(0b000, C1, rd, imm6); ++} ++ ++#ifdef JS_CODEGEN_RISCV64 ++void AssemblerRISCVC::c_addiw(Register rd, int8_t imm6) { ++ MOZ_ASSERT(rd != zero_reg); ++ GenInstrCI(0b001, C1, rd, imm6); ++} ++#endif ++ ++void AssemblerRISCVC::c_addi16sp(int16_t imm10) { ++ MOZ_ASSERT(is_int10(imm10) && (imm10 & 0xf) == 0); ++ uint8_t uimm6 = ((imm10 & 0x200) >> 4) | (imm10 & 0x10) | ++ ((imm10 & 0x40) >> 3) | ((imm10 & 0x180) >> 6) | ++ ((imm10 & 0x20) >> 5); ++ GenInstrCIU(0b011, C1, sp, uimm6); ++} ++ ++void AssemblerRISCVC::c_addi4spn(Register rd, int16_t uimm10) { ++ MOZ_ASSERT(is_uint10(uimm10) && (uimm10 != 0)); ++ uint8_t uimm8 = ((uimm10 & 0x4) >> 1) | ((uimm10 & 0x8) >> 3) | ++ ((uimm10 & 0x30) << 2) | ((uimm10 & 0x3c0) >> 4); ++ GenInstrCIW(0b000, C0, rd, uimm8); ++} ++ ++void AssemblerRISCVC::c_li(Register rd, int8_t imm6) { ++ MOZ_ASSERT(rd != zero_reg); ++ GenInstrCI(0b010, C1, rd, imm6); ++} ++ ++void AssemblerRISCVC::c_lui(Register rd, int8_t imm6) { ++ MOZ_ASSERT(rd != zero_reg && rd != sp && imm6 != 0); ++ GenInstrCI(0b011, C1, rd, imm6); ++} ++ ++void AssemblerRISCVC::c_slli(Register rd, uint8_t shamt6) { ++ MOZ_ASSERT(rd != zero_reg && shamt6 != 0); ++ GenInstrCIU(0b000, C2, rd, shamt6); ++} ++ ++void AssemblerRISCVC::c_fldsp(FPURegister rd, uint16_t uimm9) { ++ MOZ_ASSERT(is_uint9(uimm9) && (uimm9 & 0x7) == 0); ++ uint8_t uimm6 = (uimm9 & 0x38) | ((uimm9 & 0x1c0) >> 6); ++ GenInstrCIU(0b001, C2, rd, uimm6); ++} ++ ++#ifdef JS_CODEGEN_RISCV64 ++void AssemblerRISCVC::c_ldsp(Register rd, uint16_t uimm9) { ++ MOZ_ASSERT(rd != zero_reg && is_uint9(uimm9) && (uimm9 & 0x7) == 0); ++ uint8_t uimm6 = (uimm9 & 0x38) | ((uimm9 & 0x1c0) >> 6); ++ GenInstrCIU(0b011, C2, rd, uimm6); ++} ++#endif ++ ++void AssemblerRISCVC::c_lwsp(Register rd, uint16_t uimm8) { ++ MOZ_ASSERT(rd != zero_reg && is_uint8(uimm8) && (uimm8 & 0x3) == 0); ++ uint8_t uimm6 = (uimm8 & 0x3c) | ((uimm8 & 0xc0) >> 6); ++ GenInstrCIU(0b010, C2, rd, uimm6); ++} ++ ++void AssemblerRISCVC::c_jr(Register rs1) { ++ MOZ_ASSERT(rs1 != zero_reg); ++ GenInstrCR(0b1000, C2, rs1, zero_reg); ++} ++ ++void AssemblerRISCVC::c_mv(Register rd, Register rs2) { ++ MOZ_ASSERT(rd != zero_reg && rs2 != zero_reg); ++ GenInstrCR(0b1000, C2, rd, rs2); ++} ++ ++void AssemblerRISCVC::c_ebreak() { GenInstrCR(0b1001, C2, zero_reg, zero_reg); } ++ ++void AssemblerRISCVC::c_jalr(Register rs1) { ++ MOZ_ASSERT(rs1 != zero_reg); ++ GenInstrCR(0b1001, C2, rs1, zero_reg); ++} ++ ++void AssemblerRISCVC::c_add(Register rd, Register rs2) { ++ MOZ_ASSERT(rd != zero_reg && rs2 != zero_reg); ++ GenInstrCR(0b1001, C2, rd, rs2); ++} ++ ++// CA Instructions ++void AssemblerRISCVC::c_sub(Register rd, Register rs2) { ++ MOZ_ASSERT(((rd.code() & 0b11000) == 0b01000) && ++ ((rs2.code() & 0b11000) == 0b01000)); ++ GenInstrCA(0b100011, C1, rd, 0b00, rs2); ++} ++ ++void AssemblerRISCVC::c_xor(Register rd, Register rs2) { ++ MOZ_ASSERT(((rd.code() & 0b11000) == 0b01000) && ++ ((rs2.code() & 0b11000) == 0b01000)); ++ GenInstrCA(0b100011, C1, rd, 0b01, rs2); ++} ++ ++void AssemblerRISCVC::c_or(Register rd, Register rs2) { ++ MOZ_ASSERT(((rd.code() & 0b11000) == 0b01000) && ++ ((rs2.code() & 0b11000) == 0b01000)); ++ GenInstrCA(0b100011, C1, rd, 0b10, rs2); ++} ++ ++void AssemblerRISCVC::c_and(Register rd, Register rs2) { ++ MOZ_ASSERT(((rd.code() & 0b11000) == 0b01000) && ++ ((rs2.code() & 0b11000) == 0b01000)); ++ GenInstrCA(0b100011, C1, rd, 0b11, rs2); ++} ++ ++#ifdef JS_CODEGEN_RISCV64 ++void AssemblerRISCVC::c_subw(Register rd, Register rs2) { ++ MOZ_ASSERT(((rd.code() & 0b11000) == 0b01000) && ++ ((rs2.code() & 0b11000) == 0b01000)); ++ GenInstrCA(0b100111, C1, rd, 0b00, rs2); ++} ++ ++void AssemblerRISCVC::c_addw(Register rd, Register rs2) { ++ MOZ_ASSERT(((rd.code() & 0b11000) == 0b01000) && ++ ((rs2.code() & 0b11000) == 0b01000)); ++ GenInstrCA(0b100111, C1, rd, 0b01, rs2); ++} ++#endif ++ ++void AssemblerRISCVC::c_swsp(Register rs2, uint16_t uimm8) { ++ MOZ_ASSERT(is_uint8(uimm8) && (uimm8 & 0x3) == 0); ++ uint8_t uimm6 = (uimm8 & 0x3c) | ((uimm8 & 0xc0) >> 6); ++ GenInstrCSS(0b110, C2, rs2, uimm6); ++} ++ ++#ifdef JS_CODEGEN_RISCV64 ++void AssemblerRISCVC::c_sdsp(Register rs2, uint16_t uimm9) { ++ MOZ_ASSERT(is_uint9(uimm9) && (uimm9 & 0x7) == 0); ++ uint8_t uimm6 = (uimm9 & 0x38) | ((uimm9 & 0x1c0) >> 6); ++ GenInstrCSS(0b111, C2, rs2, uimm6); ++} ++#endif ++ ++void AssemblerRISCVC::c_fsdsp(FPURegister rs2, uint16_t uimm9) { ++ MOZ_ASSERT(is_uint9(uimm9) && (uimm9 & 0x7) == 0); ++ uint8_t uimm6 = (uimm9 & 0x38) | ((uimm9 & 0x1c0) >> 6); ++ GenInstrCSS(0b101, C2, rs2, uimm6); ++} ++ ++// CL Instructions ++ ++void AssemblerRISCVC::c_lw(Register rd, Register rs1, uint16_t uimm7) { ++ MOZ_ASSERT(((rd.code() & 0b11000) == 0b01000) && ++ ((rs1.code() & 0b11000) == 0b01000) && is_uint7(uimm7) && ++ ((uimm7 & 0x3) == 0)); ++ uint8_t uimm5 = ++ ((uimm7 & 0x4) >> 1) | ((uimm7 & 0x40) >> 6) | ((uimm7 & 0x38) >> 1); ++ GenInstrCL(0b010, C0, rd, rs1, uimm5); ++} ++ ++#ifdef JS_CODEGEN_RISCV64 ++void AssemblerRISCVC::c_ld(Register rd, Register rs1, uint16_t uimm8) { ++ MOZ_ASSERT(((rd.code() & 0b11000) == 0b01000) && ++ ((rs1.code() & 0b11000) == 0b01000) && is_uint8(uimm8) && ++ ((uimm8 & 0x7) == 0)); ++ uint8_t uimm5 = ((uimm8 & 0x38) >> 1) | ((uimm8 & 0xc0) >> 6); ++ GenInstrCL(0b011, C0, rd, rs1, uimm5); ++} ++#endif ++ ++void AssemblerRISCVC::c_fld(FPURegister rd, Register rs1, uint16_t uimm8) { ++ MOZ_ASSERT(((rd.code() & 0b11000) == 0b01000) && ++ ((rs1.code() & 0b11000) == 0b01000) && is_uint8(uimm8) && ++ ((uimm8 & 0x7) == 0)); ++ uint8_t uimm5 = ((uimm8 & 0x38) >> 1) | ((uimm8 & 0xc0) >> 6); ++ GenInstrCL(0b001, C0, rd, rs1, uimm5); ++} ++ ++// CS Instructions ++ ++void AssemblerRISCVC::c_sw(Register rs2, Register rs1, uint16_t uimm7) { ++ MOZ_ASSERT(((rs2.code() & 0b11000) == 0b01000) && ++ ((rs1.code() & 0b11000) == 0b01000) && is_uint7(uimm7) && ++ ((uimm7 & 0x3) == 0)); ++ uint8_t uimm5 = ++ ((uimm7 & 0x4) >> 1) | ((uimm7 & 0x40) >> 6) | ((uimm7 & 0x38) >> 1); ++ GenInstrCS(0b110, C0, rs2, rs1, uimm5); ++} ++ ++#ifdef JS_CODEGEN_RISCV64 ++void AssemblerRISCVC::c_sd(Register rs2, Register rs1, uint16_t uimm8) { ++ MOZ_ASSERT(((rs2.code() & 0b11000) == 0b01000) && ++ ((rs1.code() & 0b11000) == 0b01000) && is_uint8(uimm8) && ++ ((uimm8 & 0x7) == 0)); ++ uint8_t uimm5 = ((uimm8 & 0x38) >> 1) | ((uimm8 & 0xc0) >> 6); ++ GenInstrCS(0b111, C0, rs2, rs1, uimm5); ++} ++#endif ++ ++void AssemblerRISCVC::c_fsd(FPURegister rs2, Register rs1, uint16_t uimm8) { ++ MOZ_ASSERT(((rs2.code() & 0b11000) == 0b01000) && ++ ((rs1.code() & 0b11000) == 0b01000) && is_uint8(uimm8) && ++ ((uimm8 & 0x7) == 0)); ++ uint8_t uimm5 = ((uimm8 & 0x38) >> 1) | ((uimm8 & 0xc0) >> 6); ++ GenInstrCS(0b101, C0, rs2, rs1, uimm5); ++} ++ ++// CJ Instructions ++ ++void AssemblerRISCVC::c_j(int16_t imm12) { ++ MOZ_ASSERT(is_int12(imm12)); ++ int16_t uimm11 = ((imm12 & 0x800) >> 1) | ((imm12 & 0x400) >> 4) | ++ ((imm12 & 0x300) >> 1) | ((imm12 & 0x80) >> 3) | ++ ((imm12 & 0x40) >> 1) | ((imm12 & 0x20) >> 5) | ++ ((imm12 & 0x10) << 5) | (imm12 & 0xe); ++ GenInstrCJ(0b101, C1, uimm11); ++} ++ ++// CB Instructions ++ ++void AssemblerRISCVC::c_bnez(Register rs1, int16_t imm9) { ++ MOZ_ASSERT(((rs1.code() & 0b11000) == 0b01000) && is_int9(imm9)); ++ uint8_t uimm8 = ((imm9 & 0x20) >> 5) | ((imm9 & 0x6)) | ((imm9 & 0xc0) >> 3) | ++ ((imm9 & 0x18) << 2) | ((imm9 & 0x100) >> 1); ++ GenInstrCB(0b111, C1, rs1, uimm8); ++} ++ ++void AssemblerRISCVC::c_beqz(Register rs1, int16_t imm9) { ++ MOZ_ASSERT(((rs1.code() & 0b11000) == 0b01000) && is_int9(imm9)); ++ uint8_t uimm8 = ((imm9 & 0x20) >> 5) | ((imm9 & 0x6)) | ((imm9 & 0xc0) >> 3) | ++ ((imm9 & 0x18) << 2) | ((imm9 & 0x100) >> 1); ++ GenInstrCB(0b110, C1, rs1, uimm8); ++} ++ ++void AssemblerRISCVC::c_srli(Register rs1, int8_t shamt6) { ++ MOZ_ASSERT(((rs1.code() & 0b11000) == 0b01000) && is_int6(shamt6)); ++ GenInstrCBA(0b100, 0b00, C1, rs1, shamt6); ++} ++ ++void AssemblerRISCVC::c_srai(Register rs1, int8_t shamt6) { ++ MOZ_ASSERT(((rs1.code() & 0b11000) == 0b01000) && is_int6(shamt6)); ++ GenInstrCBA(0b100, 0b01, C1, rs1, shamt6); ++} ++ ++void AssemblerRISCVC::c_andi(Register rs1, int8_t imm6) { ++ MOZ_ASSERT(((rs1.code() & 0b11000) == 0b01000) && is_int6(imm6)); ++ GenInstrCBA(0b100, 0b10, C1, rs1, imm6); ++} ++ ++bool AssemblerRISCVC::IsCJal(Instr instr) { ++ return (instr & kRvcOpcodeMask) == RO_C_J; ++} ++ ++bool AssemblerRISCVC::IsCBranch(Instr instr) { ++ int Op = instr & kRvcOpcodeMask; ++ return Op == RO_C_BNEZ || Op == RO_C_BEQZ; ++} ++ ++int AssemblerRISCVC::CJumpOffset(Instr instr) { ++ int32_t imm12 = ((instr & 0x4) << 3) | ((instr & 0x38) >> 2) | ++ ((instr & 0x40) << 1) | ((instr & 0x80) >> 1) | ++ ((instr & 0x100) << 2) | ((instr & 0x600) >> 1) | ++ ((instr & 0x800) >> 7) | ((instr & 0x1000) >> 1); ++ imm12 = imm12 << 20 >> 20; ++ return imm12; ++} ++ ++} // namespace jit ++} // namespace js +diff --git a/js/src/jit/riscv64/extension/extension-riscv-c.h b/js/src/jit/riscv64/extension/extension-riscv-c.h +new file mode 100644 +index 0000000000..655141cb30 +--- /dev/null ++++ b/js/src/jit/riscv64/extension/extension-riscv-c.h +@@ -0,0 +1,77 @@ ++// Copyright 2022 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++#ifndef jit_riscv64_extension_Extension_riscv_c_h_ ++#define jit_riscv64_extension_Extension_riscv_c_h_ ++#include "mozilla/Assertions.h" ++ ++#include ++ ++#include "jit/riscv64/extension/base-assembler-riscv.h" ++#include "jit/riscv64/Register-riscv64.h" ++namespace js { ++namespace jit { ++class AssemblerRISCVC : public AssemblerRiscvBase { ++ // RV64C Standard Extension ++ public: ++ void c_nop(); ++ void c_addi(Register rd, int8_t imm6); ++ ++ void c_addi16sp(int16_t imm10); ++ void c_addi4spn(Register rd, int16_t uimm10); ++ void c_li(Register rd, int8_t imm6); ++ void c_lui(Register rd, int8_t imm6); ++ void c_slli(Register rd, uint8_t shamt6); ++ void c_lwsp(Register rd, uint16_t uimm8); ++ void c_jr(Register rs1); ++ void c_mv(Register rd, Register rs2); ++ void c_ebreak(); ++ void c_jalr(Register rs1); ++ void c_j(int16_t imm12); ++ void c_add(Register rd, Register rs2); ++ void c_sub(Register rd, Register rs2); ++ void c_and(Register rd, Register rs2); ++ void c_xor(Register rd, Register rs2); ++ void c_or(Register rd, Register rs2); ++ void c_swsp(Register rs2, uint16_t uimm8); ++ void c_lw(Register rd, Register rs1, uint16_t uimm7); ++ void c_sw(Register rs2, Register rs1, uint16_t uimm7); ++ void c_bnez(Register rs1, int16_t imm9); ++ void c_beqz(Register rs1, int16_t imm9); ++ void c_srli(Register rs1, int8_t shamt6); ++ void c_srai(Register rs1, int8_t shamt6); ++ void c_andi(Register rs1, int8_t imm6); ++ ++ void c_fld(FPURegister rd, Register rs1, uint16_t uimm8); ++ void c_fsd(FPURegister rs2, Register rs1, uint16_t uimm8); ++ void c_fldsp(FPURegister rd, uint16_t uimm9); ++ void c_fsdsp(FPURegister rs2, uint16_t uimm9); ++#ifdef JS_CODEGEN_RISCV64 ++ void c_ld(Register rd, Register rs1, uint16_t uimm8); ++ void c_sd(Register rs2, Register rs1, uint16_t uimm8); ++ void c_subw(Register rd, Register rs2); ++ void c_addw(Register rd, Register rs2); ++ void c_addiw(Register rd, int8_t imm6); ++ void c_ldsp(Register rd, uint16_t uimm9); ++ void c_sdsp(Register rs2, uint16_t uimm9); ++#endif ++ ++ int CJumpOffset(Instr instr); ++ ++ static bool IsCBranch(Instr instr); ++ static bool IsCJal(Instr instr); ++ ++ inline int16_t cjump_offset(Label* L) { ++ return (int16_t)branch_offset_helper(L, OffsetSize::kOffset11); ++ } ++ inline int32_t cbranch_offset(Label* L) { ++ return branch_offset_helper(L, OffsetSize::kOffset9); ++ } ++ ++ void c_j(Label* L) { c_j(cjump_offset(L)); } ++ void c_bnez(Register rs1, Label* L) { c_bnez(rs1, cbranch_offset(L)); } ++ void c_beqz(Register rs1, Label* L) { c_beqz(rs1, cbranch_offset(L)); } ++}; ++} // namespace jit ++} // namespace js ++#endif // jit_riscv64_extension_Extension_riscv_C_h_ +diff --git a/js/src/jit/riscv64/extension/extension-riscv-d.cc b/js/src/jit/riscv64/extension/extension-riscv-d.cc +new file mode 100644 +index 0000000000..cb728baf12 +--- /dev/null ++++ b/js/src/jit/riscv64/extension/extension-riscv-d.cc +@@ -0,0 +1,167 @@ ++// Copyright 2022 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++#include "jit/riscv64/extension/extension-riscv-d.h" ++#include "jit/riscv64/Assembler-riscv64.h" ++#include "jit/riscv64/constant/Constant-riscv64.h" ++#include "jit/riscv64/Architecture-riscv64.h" ++namespace js { ++namespace jit { ++// RV32D Standard Extension ++ ++void AssemblerRISCVD::fld(FPURegister rd, Register rs1, int16_t imm12) { ++ GenInstrLoadFP_ri(0b011, rd, rs1, imm12); ++} ++ ++void AssemblerRISCVD::fsd(FPURegister source, Register base, int16_t imm12) { ++ GenInstrStoreFP_rri(0b011, base, source, imm12); ++} ++ ++void AssemblerRISCVD::fmadd_d(FPURegister rd, FPURegister rs1, FPURegister rs2, ++ FPURegister rs3, FPURoundingMode frm) { ++ GenInstrR4(0b01, MADD, rd, rs1, rs2, rs3, frm); ++} ++ ++void AssemblerRISCVD::fmsub_d(FPURegister rd, FPURegister rs1, FPURegister rs2, ++ FPURegister rs3, FPURoundingMode frm) { ++ GenInstrR4(0b01, MSUB, rd, rs1, rs2, rs3, frm); ++} ++ ++void AssemblerRISCVD::fnmsub_d(FPURegister rd, FPURegister rs1, FPURegister rs2, ++ FPURegister rs3, FPURoundingMode frm) { ++ GenInstrR4(0b01, NMSUB, rd, rs1, rs2, rs3, frm); ++} ++ ++void AssemblerRISCVD::fnmadd_d(FPURegister rd, FPURegister rs1, FPURegister rs2, ++ FPURegister rs3, FPURoundingMode frm) { ++ GenInstrR4(0b01, NMADD, rd, rs1, rs2, rs3, frm); ++} ++ ++void AssemblerRISCVD::fadd_d(FPURegister rd, FPURegister rs1, FPURegister rs2, ++ FPURoundingMode frm) { ++ GenInstrALUFP_rr(0b0000001, frm, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVD::fsub_d(FPURegister rd, FPURegister rs1, FPURegister rs2, ++ FPURoundingMode frm) { ++ GenInstrALUFP_rr(0b0000101, frm, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVD::fmul_d(FPURegister rd, FPURegister rs1, FPURegister rs2, ++ FPURoundingMode frm) { ++ GenInstrALUFP_rr(0b0001001, frm, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVD::fdiv_d(FPURegister rd, FPURegister rs1, FPURegister rs2, ++ FPURoundingMode frm) { ++ GenInstrALUFP_rr(0b0001101, frm, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVD::fsqrt_d(FPURegister rd, FPURegister rs1, ++ FPURoundingMode frm) { ++ GenInstrALUFP_rr(0b0101101, frm, rd, rs1, zero_reg); ++} ++ ++void AssemblerRISCVD::fsgnj_d(FPURegister rd, FPURegister rs1, ++ FPURegister rs2) { ++ GenInstrALUFP_rr(0b0010001, 0b000, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVD::fsgnjn_d(FPURegister rd, FPURegister rs1, ++ FPURegister rs2) { ++ GenInstrALUFP_rr(0b0010001, 0b001, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVD::fsgnjx_d(FPURegister rd, FPURegister rs1, ++ FPURegister rs2) { ++ GenInstrALUFP_rr(0b0010001, 0b010, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVD::fmin_d(FPURegister rd, FPURegister rs1, FPURegister rs2) { ++ GenInstrALUFP_rr(0b0010101, 0b000, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVD::fmax_d(FPURegister rd, FPURegister rs1, FPURegister rs2) { ++ GenInstrALUFP_rr(0b0010101, 0b001, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVD::fcvt_s_d(FPURegister rd, FPURegister rs1, ++ FPURoundingMode frm) { ++ GenInstrALUFP_rr(0b0100000, frm, rd, rs1, ToRegister(1)); ++} ++ ++void AssemblerRISCVD::fcvt_d_s(FPURegister rd, FPURegister rs1, ++ FPURoundingMode frm) { ++ GenInstrALUFP_rr(0b0100001, frm, rd, rs1, zero_reg); ++} ++ ++void AssemblerRISCVD::feq_d(Register rd, FPURegister rs1, FPURegister rs2) { ++ GenInstrALUFP_rr(0b1010001, 0b010, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVD::flt_d(Register rd, FPURegister rs1, FPURegister rs2) { ++ GenInstrALUFP_rr(0b1010001, 0b001, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVD::fle_d(Register rd, FPURegister rs1, FPURegister rs2) { ++ GenInstrALUFP_rr(0b1010001, 0b000, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVD::fclass_d(Register rd, FPURegister rs1) { ++ GenInstrALUFP_rr(0b1110001, 0b001, rd, rs1, zero_reg); ++} ++ ++void AssemblerRISCVD::fcvt_w_d(Register rd, FPURegister rs1, ++ FPURoundingMode frm) { ++ GenInstrALUFP_rr(0b1100001, frm, rd, rs1, zero_reg); ++} ++ ++void AssemblerRISCVD::fcvt_wu_d(Register rd, FPURegister rs1, ++ FPURoundingMode frm) { ++ GenInstrALUFP_rr(0b1100001, frm, rd, rs1, ToRegister(1)); ++} ++ ++void AssemblerRISCVD::fcvt_d_w(FPURegister rd, Register rs1, ++ FPURoundingMode frm) { ++ GenInstrALUFP_rr(0b1101001, frm, rd, rs1, zero_reg); ++} ++ ++void AssemblerRISCVD::fcvt_d_wu(FPURegister rd, Register rs1, ++ FPURoundingMode frm) { ++ GenInstrALUFP_rr(0b1101001, frm, rd, rs1, ToRegister(1)); ++} ++ ++#ifdef JS_CODEGEN_RISCV64 ++// RV64D Standard Extension (in addition to RV32D) ++ ++void AssemblerRISCVD::fcvt_l_d(Register rd, FPURegister rs1, ++ FPURoundingMode frm) { ++ GenInstrALUFP_rr(0b1100001, frm, rd, rs1, ToRegister(2)); ++} ++ ++void AssemblerRISCVD::fcvt_lu_d(Register rd, FPURegister rs1, ++ FPURoundingMode frm) { ++ GenInstrALUFP_rr(0b1100001, frm, rd, rs1, ToRegister(3)); ++} ++ ++void AssemblerRISCVD::fmv_x_d(Register rd, FPURegister rs1) { ++ GenInstrALUFP_rr(0b1110001, 0b000, rd, rs1, zero_reg); ++} ++ ++void AssemblerRISCVD::fcvt_d_l(FPURegister rd, Register rs1, ++ FPURoundingMode frm) { ++ GenInstrALUFP_rr(0b1101001, frm, rd, rs1, ToRegister(2)); ++} ++ ++void AssemblerRISCVD::fcvt_d_lu(FPURegister rd, Register rs1, ++ FPURoundingMode frm) { ++ GenInstrALUFP_rr(0b1101001, frm, rd, rs1, ToRegister(3)); ++} ++ ++void AssemblerRISCVD::fmv_d_x(FPURegister rd, Register rs1) { ++ GenInstrALUFP_rr(0b1111001, 0b000, rd, rs1, zero_reg); ++} ++#endif ++ ++} // namespace jit ++} // namespace js +diff --git a/js/src/jit/riscv64/extension/extension-riscv-d.h b/js/src/jit/riscv64/extension/extension-riscv-d.h +new file mode 100644 +index 0000000000..8497c0ca63 +--- /dev/null ++++ b/js/src/jit/riscv64/extension/extension-riscv-d.h +@@ -0,0 +1,68 @@ ++// Copyright 2022 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++#ifndef jit_riscv64_extension_Extension_riscv_d_h_ ++#define jit_riscv64_extension_Extension_riscv_d_h_ ++#include "mozilla/Assertions.h" ++ ++#include ++ ++#include "jit/riscv64/extension/base-assembler-riscv.h" ++#include "jit/riscv64/Register-riscv64.h" ++namespace js { ++namespace jit { ++class AssemblerRISCVD : public AssemblerRiscvBase { ++ // RV32D Standard Extension ++ public: ++ void fld(FPURegister rd, Register rs1, int16_t imm12); ++ void fsd(FPURegister source, Register base, int16_t imm12); ++ void fmadd_d(FPURegister rd, FPURegister rs1, FPURegister rs2, ++ FPURegister rs3, FPURoundingMode frm = RNE); ++ void fmsub_d(FPURegister rd, FPURegister rs1, FPURegister rs2, ++ FPURegister rs3, FPURoundingMode frm = RNE); ++ void fnmsub_d(FPURegister rd, FPURegister rs1, FPURegister rs2, ++ FPURegister rs3, FPURoundingMode frm = RNE); ++ void fnmadd_d(FPURegister rd, FPURegister rs1, FPURegister rs2, ++ FPURegister rs3, FPURoundingMode frm = RNE); ++ void fadd_d(FPURegister rd, FPURegister rs1, FPURegister rs2, ++ FPURoundingMode frm = RNE); ++ void fsub_d(FPURegister rd, FPURegister rs1, FPURegister rs2, ++ FPURoundingMode frm = RNE); ++ void fmul_d(FPURegister rd, FPURegister rs1, FPURegister rs2, ++ FPURoundingMode frm = RNE); ++ void fdiv_d(FPURegister rd, FPURegister rs1, FPURegister rs2, ++ FPURoundingMode frm = RNE); ++ void fsqrt_d(FPURegister rd, FPURegister rs1, FPURoundingMode frm = RNE); ++ void fsgnj_d(FPURegister rd, FPURegister rs1, FPURegister rs2); ++ void fsgnjn_d(FPURegister rd, FPURegister rs1, FPURegister rs2); ++ void fsgnjx_d(FPURegister rd, FPURegister rs1, FPURegister rs2); ++ void fmin_d(FPURegister rd, FPURegister rs1, FPURegister rs2); ++ void fmax_d(FPURegister rd, FPURegister rs1, FPURegister rs2); ++ void fcvt_s_d(FPURegister rd, FPURegister rs1, FPURoundingMode frm = RNE); ++ void fcvt_d_s(FPURegister rd, FPURegister rs1, FPURoundingMode frm = RNE); ++ void feq_d(Register rd, FPURegister rs1, FPURegister rs2); ++ void flt_d(Register rd, FPURegister rs1, FPURegister rs2); ++ void fle_d(Register rd, FPURegister rs1, FPURegister rs2); ++ void fclass_d(Register rd, FPURegister rs1); ++ void fcvt_w_d(Register rd, FPURegister rs1, FPURoundingMode frm = RNE); ++ void fcvt_wu_d(Register rd, FPURegister rs1, FPURoundingMode frm = RNE); ++ void fcvt_d_w(FPURegister rd, Register rs1, FPURoundingMode frm = RNE); ++ void fcvt_d_wu(FPURegister rd, Register rs1, FPURoundingMode frm = RNE); ++ ++#ifdef JS_CODEGEN_RISCV64 ++ // RV64D Standard Extension (in addition to RV32D) ++ void fcvt_l_d(Register rd, FPURegister rs1, FPURoundingMode frm = RNE); ++ void fcvt_lu_d(Register rd, FPURegister rs1, FPURoundingMode frm = RNE); ++ void fmv_x_d(Register rd, FPURegister rs1); ++ void fcvt_d_l(FPURegister rd, Register rs1, FPURoundingMode frm = RNE); ++ void fcvt_d_lu(FPURegister rd, Register rs1, FPURoundingMode frm = RNE); ++ void fmv_d_x(FPURegister rd, Register rs1); ++#endif ++ ++ void fmv_d(FPURegister rd, FPURegister rs) { fsgnj_d(rd, rs, rs); } ++ void fabs_d(FPURegister rd, FPURegister rs) { fsgnjx_d(rd, rs, rs); } ++ void fneg_d(FPURegister rd, FPURegister rs) { fsgnjn_d(rd, rs, rs); } ++}; ++} // namespace jit ++} // namespace js ++#endif // jit_riscv64_extension_Extension_riscv_D_h_ +diff --git a/js/src/jit/riscv64/extension/extension-riscv-f.cc b/js/src/jit/riscv64/extension/extension-riscv-f.cc +new file mode 100644 +index 0000000000..44e1fdc495 +--- /dev/null ++++ b/js/src/jit/riscv64/extension/extension-riscv-f.cc +@@ -0,0 +1,158 @@ ++// Copyright 2022 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++#include "jit/riscv64/extension/extension-riscv-f.h" ++#include "jit/riscv64/Assembler-riscv64.h" ++#include "jit/riscv64/constant/Constant-riscv64.h" ++#include "jit/riscv64/Architecture-riscv64.h" ++namespace js { ++namespace jit { ++ ++// RV32F Standard Extension ++ ++void AssemblerRISCVF::flw(FPURegister rd, Register rs1, int16_t imm12) { ++ GenInstrLoadFP_ri(0b010, rd, rs1, imm12); ++} ++ ++void AssemblerRISCVF::fsw(FPURegister source, Register base, int16_t imm12) { ++ GenInstrStoreFP_rri(0b010, base, source, imm12); ++} ++ ++void AssemblerRISCVF::fmadd_s(FPURegister rd, FPURegister rs1, FPURegister rs2, ++ FPURegister rs3, FPURoundingMode frm) { ++ GenInstrR4(0b00, MADD, rd, rs1, rs2, rs3, frm); ++} ++ ++void AssemblerRISCVF::fmsub_s(FPURegister rd, FPURegister rs1, FPURegister rs2, ++ FPURegister rs3, FPURoundingMode frm) { ++ GenInstrR4(0b00, MSUB, rd, rs1, rs2, rs3, frm); ++} ++ ++void AssemblerRISCVF::fnmsub_s(FPURegister rd, FPURegister rs1, FPURegister rs2, ++ FPURegister rs3, FPURoundingMode frm) { ++ GenInstrR4(0b00, NMSUB, rd, rs1, rs2, rs3, frm); ++} ++ ++void AssemblerRISCVF::fnmadd_s(FPURegister rd, FPURegister rs1, FPURegister rs2, ++ FPURegister rs3, FPURoundingMode frm) { ++ GenInstrR4(0b00, NMADD, rd, rs1, rs2, rs3, frm); ++} ++ ++void AssemblerRISCVF::fadd_s(FPURegister rd, FPURegister rs1, FPURegister rs2, ++ FPURoundingMode frm) { ++ GenInstrALUFP_rr(0b0000000, frm, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVF::fsub_s(FPURegister rd, FPURegister rs1, FPURegister rs2, ++ FPURoundingMode frm) { ++ GenInstrALUFP_rr(0b0000100, frm, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVF::fmul_s(FPURegister rd, FPURegister rs1, FPURegister rs2, ++ FPURoundingMode frm) { ++ GenInstrALUFP_rr(0b0001000, frm, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVF::fdiv_s(FPURegister rd, FPURegister rs1, FPURegister rs2, ++ FPURoundingMode frm) { ++ GenInstrALUFP_rr(0b0001100, frm, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVF::fsqrt_s(FPURegister rd, FPURegister rs1, ++ FPURoundingMode frm) { ++ GenInstrALUFP_rr(0b0101100, frm, rd, rs1, zero_reg); ++} ++ ++void AssemblerRISCVF::fsgnj_s(FPURegister rd, FPURegister rs1, ++ FPURegister rs2) { ++ GenInstrALUFP_rr(0b0010000, 0b000, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVF::fsgnjn_s(FPURegister rd, FPURegister rs1, ++ FPURegister rs2) { ++ GenInstrALUFP_rr(0b0010000, 0b001, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVF::fsgnjx_s(FPURegister rd, FPURegister rs1, ++ FPURegister rs2) { ++ GenInstrALUFP_rr(0b0010000, 0b010, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVF::fmin_s(FPURegister rd, FPURegister rs1, FPURegister rs2) { ++ GenInstrALUFP_rr(0b0010100, 0b000, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVF::fmax_s(FPURegister rd, FPURegister rs1, FPURegister rs2) { ++ GenInstrALUFP_rr(0b0010100, 0b001, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVF::fcvt_w_s(Register rd, FPURegister rs1, ++ FPURoundingMode frm) { ++ GenInstrALUFP_rr(0b1100000, frm, rd, rs1, zero_reg); ++} ++ ++void AssemblerRISCVF::fcvt_wu_s(Register rd, FPURegister rs1, ++ FPURoundingMode frm) { ++ GenInstrALUFP_rr(0b1100000, frm, rd, rs1, ToRegister(1)); ++} ++ ++void AssemblerRISCVF::fmv_x_w(Register rd, FPURegister rs1) { ++ GenInstrALUFP_rr(0b1110000, 0b000, rd, rs1, zero_reg); ++} ++ ++void AssemblerRISCVF::feq_s(Register rd, FPURegister rs1, FPURegister rs2) { ++ GenInstrALUFP_rr(0b1010000, 0b010, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVF::flt_s(Register rd, FPURegister rs1, FPURegister rs2) { ++ GenInstrALUFP_rr(0b1010000, 0b001, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVF::fle_s(Register rd, FPURegister rs1, FPURegister rs2) { ++ GenInstrALUFP_rr(0b1010000, 0b000, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVF::fclass_s(Register rd, FPURegister rs1) { ++ GenInstrALUFP_rr(0b1110000, 0b001, rd, rs1, zero_reg); ++} ++ ++void AssemblerRISCVF::fcvt_s_w(FPURegister rd, Register rs1, ++ FPURoundingMode frm) { ++ GenInstrALUFP_rr(0b1101000, frm, rd, rs1, zero_reg); ++} ++ ++void AssemblerRISCVF::fcvt_s_wu(FPURegister rd, Register rs1, ++ FPURoundingMode frm) { ++ GenInstrALUFP_rr(0b1101000, frm, rd, rs1, ToRegister(1)); ++} ++ ++void AssemblerRISCVF::fmv_w_x(FPURegister rd, Register rs1) { ++ GenInstrALUFP_rr(0b1111000, 0b000, rd, rs1, zero_reg); ++} ++ ++#ifdef JS_CODEGEN_RISCV64 ++// RV64F Standard Extension (in addition to RV32F) ++ ++void AssemblerRISCVF::fcvt_l_s(Register rd, FPURegister rs1, ++ FPURoundingMode frm) { ++ GenInstrALUFP_rr(0b1100000, frm, rd, rs1, ToRegister(2)); ++} ++ ++void AssemblerRISCVF::fcvt_lu_s(Register rd, FPURegister rs1, ++ FPURoundingMode frm) { ++ GenInstrALUFP_rr(0b1100000, frm, rd, rs1, ToRegister(3)); ++} ++ ++void AssemblerRISCVF::fcvt_s_l(FPURegister rd, Register rs1, ++ FPURoundingMode frm) { ++ GenInstrALUFP_rr(0b1101000, frm, rd, rs1, ToRegister(2)); ++} ++ ++void AssemblerRISCVF::fcvt_s_lu(FPURegister rd, Register rs1, ++ FPURoundingMode frm) { ++ GenInstrALUFP_rr(0b1101000, frm, rd, rs1, ToRegister(3)); ++} ++#endif ++ ++} // namespace jit ++} // namespace js +diff --git a/js/src/jit/riscv64/extension/extension-riscv-f.h b/js/src/jit/riscv64/extension/extension-riscv-f.h +new file mode 100644 +index 0000000000..3ab46ffcf6 +--- /dev/null ++++ b/js/src/jit/riscv64/extension/extension-riscv-f.h +@@ -0,0 +1,66 @@ ++// Copyright 2022 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++#ifndef jit_riscv64_extension_Extension_riscv_f_h_ ++#define jit_riscv64_extension_Extension_riscv_f_h_ ++#include "mozilla/Assertions.h" ++ ++#include ++ ++#include "jit/riscv64/extension/base-assembler-riscv.h" ++#include "jit/riscv64/Register-riscv64.h" ++namespace js { ++namespace jit { ++class AssemblerRISCVF : public AssemblerRiscvBase { ++ // RV32F Standard Extension ++ public: ++ void flw(FPURegister rd, Register rs1, int16_t imm12); ++ void fsw(FPURegister source, Register base, int16_t imm12); ++ void fmadd_s(FPURegister rd, FPURegister rs1, FPURegister rs2, ++ FPURegister rs3, FPURoundingMode frm = RNE); ++ void fmsub_s(FPURegister rd, FPURegister rs1, FPURegister rs2, ++ FPURegister rs3, FPURoundingMode frm = RNE); ++ void fnmsub_s(FPURegister rd, FPURegister rs1, FPURegister rs2, ++ FPURegister rs3, FPURoundingMode frm = RNE); ++ void fnmadd_s(FPURegister rd, FPURegister rs1, FPURegister rs2, ++ FPURegister rs3, FPURoundingMode frm = RNE); ++ void fadd_s(FPURegister rd, FPURegister rs1, FPURegister rs2, ++ FPURoundingMode frm = RNE); ++ void fsub_s(FPURegister rd, FPURegister rs1, FPURegister rs2, ++ FPURoundingMode frm = RNE); ++ void fmul_s(FPURegister rd, FPURegister rs1, FPURegister rs2, ++ FPURoundingMode frm = RNE); ++ void fdiv_s(FPURegister rd, FPURegister rs1, FPURegister rs2, ++ FPURoundingMode frm = RNE); ++ void fsqrt_s(FPURegister rd, FPURegister rs1, FPURoundingMode frm = RNE); ++ void fsgnj_s(FPURegister rd, FPURegister rs1, FPURegister rs2); ++ void fsgnjn_s(FPURegister rd, FPURegister rs1, FPURegister rs2); ++ void fsgnjx_s(FPURegister rd, FPURegister rs1, FPURegister rs2); ++ void fmin_s(FPURegister rd, FPURegister rs1, FPURegister rs2); ++ void fmax_s(FPURegister rd, FPURegister rs1, FPURegister rs2); ++ void fcvt_w_s(Register rd, FPURegister rs1, FPURoundingMode frm = RNE); ++ void fcvt_wu_s(Register rd, FPURegister rs1, FPURoundingMode frm = RNE); ++ void fmv_x_w(Register rd, FPURegister rs1); ++ void feq_s(Register rd, FPURegister rs1, FPURegister rs2); ++ void flt_s(Register rd, FPURegister rs1, FPURegister rs2); ++ void fle_s(Register rd, FPURegister rs1, FPURegister rs2); ++ void fclass_s(Register rd, FPURegister rs1); ++ void fcvt_s_w(FPURegister rd, Register rs1, FPURoundingMode frm = RNE); ++ void fcvt_s_wu(FPURegister rd, Register rs1, FPURoundingMode frm = RNE); ++ void fmv_w_x(FPURegister rd, Register rs1); ++ ++#ifdef JS_CODEGEN_RISCV64 ++ // RV64F Standard Extension (in addition to RV32F) ++ void fcvt_l_s(Register rd, FPURegister rs1, FPURoundingMode frm = RNE); ++ void fcvt_lu_s(Register rd, FPURegister rs1, FPURoundingMode frm = RNE); ++ void fcvt_s_l(FPURegister rd, Register rs1, FPURoundingMode frm = RNE); ++ void fcvt_s_lu(FPURegister rd, Register rs1, FPURoundingMode frm = RNE); ++#endif ++ ++ void fmv_s(FPURegister rd, FPURegister rs) { fsgnj_s(rd, rs, rs); } ++ void fabs_s(FPURegister rd, FPURegister rs) { fsgnjx_s(rd, rs, rs); } ++ void fneg_s(FPURegister rd, FPURegister rs) { fsgnjn_s(rd, rs, rs); } ++}; ++} // namespace jit ++} // namespace js ++#endif // jit_riscv64_extension_Extension_riscv_F_h_ +diff --git a/js/src/jit/riscv64/extension/extension-riscv-m.cc b/js/src/jit/riscv64/extension/extension-riscv-m.cc +new file mode 100644 +index 0000000000..b5fcd6c34c +--- /dev/null ++++ b/js/src/jit/riscv64/extension/extension-riscv-m.cc +@@ -0,0 +1,68 @@ ++// Copyright 2022 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++#include "jit/riscv64/extension/extension-riscv-m.h" ++#include "jit/riscv64/Assembler-riscv64.h" ++#include "jit/riscv64/constant/Constant-riscv64.h" ++#include "jit/riscv64/Architecture-riscv64.h" ++namespace js { ++namespace jit { ++// RV32M Standard Extension ++ ++void AssemblerRISCVM::mul(Register rd, Register rs1, Register rs2) { ++ GenInstrALU_rr(0b0000001, 0b000, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVM::mulh(Register rd, Register rs1, Register rs2) { ++ GenInstrALU_rr(0b0000001, 0b001, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVM::mulhsu(Register rd, Register rs1, Register rs2) { ++ GenInstrALU_rr(0b0000001, 0b010, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVM::mulhu(Register rd, Register rs1, Register rs2) { ++ GenInstrALU_rr(0b0000001, 0b011, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVM::div(Register rd, Register rs1, Register rs2) { ++ GenInstrALU_rr(0b0000001, 0b100, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVM::divu(Register rd, Register rs1, Register rs2) { ++ GenInstrALU_rr(0b0000001, 0b101, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVM::rem(Register rd, Register rs1, Register rs2) { ++ GenInstrALU_rr(0b0000001, 0b110, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVM::remu(Register rd, Register rs1, Register rs2) { ++ GenInstrALU_rr(0b0000001, 0b111, rd, rs1, rs2); ++} ++ ++#ifdef JS_CODEGEN_RISCV64 ++// RV64M Standard Extension (in addition to RV32M) ++ ++void AssemblerRISCVM::mulw(Register rd, Register rs1, Register rs2) { ++ GenInstrALUW_rr(0b0000001, 0b000, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVM::divw(Register rd, Register rs1, Register rs2) { ++ GenInstrALUW_rr(0b0000001, 0b100, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVM::divuw(Register rd, Register rs1, Register rs2) { ++ GenInstrALUW_rr(0b0000001, 0b101, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVM::remw(Register rd, Register rs1, Register rs2) { ++ GenInstrALUW_rr(0b0000001, 0b110, rd, rs1, rs2); ++} ++ ++void AssemblerRISCVM::remuw(Register rd, Register rs1, Register rs2) { ++ GenInstrALUW_rr(0b0000001, 0b111, rd, rs1, rs2); ++} ++#endif ++} // namespace jit ++} // namespace js +diff --git a/js/src/jit/riscv64/extension/extension-riscv-m.h b/js/src/jit/riscv64/extension/extension-riscv-m.h +new file mode 100644 +index 0000000000..7c2c932516 +--- /dev/null ++++ b/js/src/jit/riscv64/extension/extension-riscv-m.h +@@ -0,0 +1,37 @@ ++// Copyright 2022 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef jit_riscv64_extension_Extension_riscv_m_h_ ++#define jit_riscv64_extension_Extension_riscv_m_h_ ++#include "mozilla/Assertions.h" ++ ++#include ++ ++#include "jit/riscv64/extension/base-assembler-riscv.h" ++#include "jit/riscv64/Register-riscv64.h" ++namespace js { ++namespace jit { ++class AssemblerRISCVM : public AssemblerRiscvBase { ++ // RV32M Standard Extension ++ public: ++ void mul(Register rd, Register rs1, Register rs2); ++ void mulh(Register rd, Register rs1, Register rs2); ++ void mulhsu(Register rd, Register rs1, Register rs2); ++ void mulhu(Register rd, Register rs1, Register rs2); ++ void div(Register rd, Register rs1, Register rs2); ++ void divu(Register rd, Register rs1, Register rs2); ++ void rem(Register rd, Register rs1, Register rs2); ++ void remu(Register rd, Register rs1, Register rs2); ++#ifdef JS_CODEGEN_RISCV64 ++ // RV64M Standard Extension (in addition to RV32M) ++ void mulw(Register rd, Register rs1, Register rs2); ++ void divw(Register rd, Register rs1, Register rs2); ++ void divuw(Register rd, Register rs1, Register rs2); ++ void remw(Register rd, Register rs1, Register rs2); ++ void remuw(Register rd, Register rs1, Register rs2); ++#endif ++}; ++} // namespace jit ++} // namespace js ++#endif // jit_riscv64_extension_Extension_riscv_M_h_ +diff --git a/js/src/jit/riscv64/extension/extension-riscv-v.cc b/js/src/jit/riscv64/extension/extension-riscv-v.cc +new file mode 100644 +index 0000000000..c7241158e0 +--- /dev/null ++++ b/js/src/jit/riscv64/extension/extension-riscv-v.cc +@@ -0,0 +1,891 @@ ++ ++// Copyright 2022 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "jit/riscv64/extension/extension-riscv-v.h" ++ ++#ifdef CAN_USE_RVV ++# include "src/codegen/assembler.h" ++# include "jit/riscv64/constant/Constant-riscv64.h" ++# include "jit/riscv64/extension/register-riscv.h" ++ ++namespace js { ++namespace jit { ++ ++// RVV ++ ++void AssemblerRISCVV::vredmaxu_vs(VRegister vd, VRegister vs2, VRegister vs1, ++ MaskType mask) { ++ GenInstrV(VREDMAXU_FUNCT6, OP_MVV, vd, vs1, vs2, mask); ++} ++ ++void AssemblerRISCVV::vredmax_vs(VRegister vd, VRegister vs2, VRegister vs1, ++ MaskType mask) { ++ GenInstrV(VREDMAX_FUNCT6, OP_MVV, vd, vs1, vs2, mask); ++} ++ ++void AssemblerRISCVV::vredmin_vs(VRegister vd, VRegister vs2, VRegister vs1, ++ MaskType mask) { ++ GenInstrV(VREDMIN_FUNCT6, OP_MVV, vd, vs1, vs2, mask); ++} ++ ++void AssemblerRISCVV::vredminu_vs(VRegister vd, VRegister vs2, VRegister vs1, ++ MaskType mask) { ++ GenInstrV(VREDMINU_FUNCT6, OP_MVV, vd, vs1, vs2, mask); ++} ++ ++void AssemblerRISCVV::vmv_vv(VRegister vd, VRegister vs1) { ++ GenInstrV(VMV_FUNCT6, OP_IVV, vd, vs1, v0, NoMask); ++} ++ ++void AssemblerRISCVV::vmv_vx(VRegister vd, Register rs1) { ++ GenInstrV(VMV_FUNCT6, OP_IVX, vd, rs1, v0, NoMask); ++} ++ ++void AssemblerRISCVV::vmv_vi(VRegister vd, uint8_t simm5) { ++ GenInstrV(VMV_FUNCT6, vd, simm5, v0, NoMask); ++} ++ ++void AssemblerRISCVV::vmv_xs(Register rd, VRegister vs2) { ++ GenInstrV(VWXUNARY0_FUNCT6, OP_MVV, rd, 0b00000, vs2, NoMask); ++} ++ ++void AssemblerRISCVV::vmv_sx(VRegister vd, Register rs1) { ++ GenInstrV(VRXUNARY0_FUNCT6, OP_MVX, vd, rs1, v0, NoMask); ++} ++ ++void AssemblerRISCVV::vmerge_vv(VRegister vd, VRegister vs1, VRegister vs2) { ++ GenInstrV(VMV_FUNCT6, OP_IVV, vd, vs1, vs2, Mask); ++} ++ ++void AssemblerRISCVV::vmerge_vx(VRegister vd, Register rs1, VRegister vs2) { ++ GenInstrV(VMV_FUNCT6, OP_IVX, vd, rs1, vs2, Mask); ++} ++ ++void AssemblerRISCVV::vmerge_vi(VRegister vd, uint8_t imm5, VRegister vs2) { ++ GenInstrV(VMV_FUNCT6, vd, imm5, vs2, Mask); ++} ++ ++void AssemblerRISCVV::vadc_vv(VRegister vd, VRegister vs1, VRegister vs2) { ++ GenInstrV(VADC_FUNCT6, OP_IVV, vd, vs1, vs2, Mask); ++} ++ ++void AssemblerRISCVV::vadc_vx(VRegister vd, Register rs1, VRegister vs2) { ++ GenInstrV(VADC_FUNCT6, OP_IVX, vd, rs1, vs2, Mask); ++} ++ ++void AssemblerRISCVV::vadc_vi(VRegister vd, uint8_t imm5, VRegister vs2) { ++ GenInstrV(VADC_FUNCT6, vd, imm5, vs2, Mask); ++} ++ ++void AssemblerRISCVV::vmadc_vv(VRegister vd, VRegister vs1, VRegister vs2) { ++ GenInstrV(VMADC_FUNCT6, OP_IVV, vd, vs1, vs2, Mask); ++} ++ ++void AssemblerRISCVV::vmadc_vx(VRegister vd, Register rs1, VRegister vs2) { ++ GenInstrV(VMADC_FUNCT6, OP_IVX, vd, rs1, vs2, Mask); ++} ++ ++void AssemblerRISCVV::vmadc_vi(VRegister vd, uint8_t imm5, VRegister vs2) { ++ GenInstrV(VMADC_FUNCT6, vd, imm5, vs2, Mask); ++} ++ ++void AssemblerRISCVV::vrgather_vv(VRegister vd, VRegister vs2, VRegister vs1, ++ MaskType mask) { ++ DCHECK_NE(vd, vs1); ++ DCHECK_NE(vd, vs2); ++ GenInstrV(VRGATHER_FUNCT6, OP_IVV, vd, vs1, vs2, mask); ++} ++ ++void AssemblerRISCVV::vrgather_vi(VRegister vd, VRegister vs2, int8_t imm5, ++ MaskType mask) { ++ DCHECK_NE(vd, vs2); ++ GenInstrV(VRGATHER_FUNCT6, vd, imm5, vs2, mask); ++} ++ ++void AssemblerRISCVV::vrgather_vx(VRegister vd, VRegister vs2, Register rs1, ++ MaskType mask) { ++ DCHECK_NE(vd, vs2); ++ GenInstrV(VRGATHER_FUNCT6, OP_IVX, vd, rs1, vs2, mask); ++} ++ ++void AssemblerRISCVV::vwaddu_wx(VRegister vd, VRegister vs2, Register rs1, ++ MaskType mask) { ++ GenInstrV(VWADDUW_FUNCT6, OP_MVX, vd, rs1, vs2, mask); ++} ++ ++void AssemblerRISCVV::vid_v(VRegister vd, MaskType mask) { ++ GenInstrV(VMUNARY0_FUNCT6, OP_MVV, vd, VID_V, v0, mask); ++} ++ ++# define DEFINE_OPIVV(name, funct6) \ ++ void AssemblerRISCVV::name##_vv(VRegister vd, VRegister vs2, \ ++ VRegister vs1, MaskType mask) { \ ++ GenInstrV(funct6, OP_IVV, vd, vs1, vs2, mask); \ ++ } ++ ++# define DEFINE_OPFVV(name, funct6) \ ++ void AssemblerRISCVV::name##_vv(VRegister vd, VRegister vs2, \ ++ VRegister vs1, MaskType mask) { \ ++ GenInstrV(funct6, OP_FVV, vd, vs1, vs2, mask); \ ++ } ++ ++# define DEFINE_OPFWV(name, funct6) \ ++ void AssemblerRISCVV::name##_wv(VRegister vd, VRegister vs2, \ ++ VRegister vs1, MaskType mask) { \ ++ GenInstrV(funct6, OP_FVV, vd, vs1, vs2, mask); \ ++ } ++ ++# define DEFINE_OPFRED(name, funct6) \ ++ void AssemblerRISCVV::name##_vs(VRegister vd, VRegister vs2, \ ++ VRegister vs1, MaskType mask) { \ ++ GenInstrV(funct6, OP_FVV, vd, vs1, vs2, mask); \ ++ } ++ ++# define DEFINE_OPIVX(name, funct6) \ ++ void AssemblerRISCVV::name##_vx(VRegister vd, VRegister vs2, Register rs1, \ ++ MaskType mask) { \ ++ GenInstrV(funct6, OP_IVX, vd, rs1, vs2, mask); \ ++ } ++ ++# define DEFINE_OPIVI(name, funct6) \ ++ void AssemblerRISCVV::name##_vi(VRegister vd, VRegister vs2, int8_t imm5, \ ++ MaskType mask) { \ ++ GenInstrV(funct6, vd, imm5, vs2, mask); \ ++ } ++ ++# define DEFINE_OPMVV(name, funct6) \ ++ void AssemblerRISCVV::name##_vv(VRegister vd, VRegister vs2, \ ++ VRegister vs1, MaskType mask) { \ ++ GenInstrV(funct6, OP_MVV, vd, vs1, vs2, mask); \ ++ } ++ ++// void GenInstrV(uint8_t funct6, OpcodeRISCVV opcode, VRegister vd, Register ++// rs1, ++// VRegister vs2, MaskType mask = NoMask); ++# define DEFINE_OPMVX(name, funct6) \ ++ void AssemblerRISCVV::name##_vx(VRegister vd, VRegister vs2, Register rs1, \ ++ MaskType mask) { \ ++ GenInstrV(funct6, OP_MVX, vd, rs1, vs2, mask); \ ++ } ++ ++# define DEFINE_OPFVF(name, funct6) \ ++ void AssemblerRISCVV::name##_vf(VRegister vd, VRegister vs2, \ ++ FPURegister fs1, MaskType mask) { \ ++ GenInstrV(funct6, OP_FVF, vd, fs1, vs2, mask); \ ++ } ++ ++# define DEFINE_OPFWF(name, funct6) \ ++ void AssemblerRISCVV::name##_wf(VRegister vd, VRegister vs2, \ ++ FPURegister fs1, MaskType mask) { \ ++ GenInstrV(funct6, OP_FVF, vd, fs1, vs2, mask); \ ++ } ++ ++# define DEFINE_OPFVV_FMA(name, funct6) \ ++ void AssemblerRISCVV::name##_vv(VRegister vd, VRegister vs1, \ ++ VRegister vs2, MaskType mask) { \ ++ GenInstrV(funct6, OP_FVV, vd, vs1, vs2, mask); \ ++ } ++ ++# define DEFINE_OPFVF_FMA(name, funct6) \ ++ void AssemblerRISCVV::name##_vf(VRegister vd, FPURegister fs1, \ ++ VRegister vs2, MaskType mask) { \ ++ GenInstrV(funct6, OP_FVF, vd, fs1, vs2, mask); \ ++ } ++ ++// vector integer extension ++# define DEFINE_OPMVV_VIE(name, vs1) \ ++ void AssemblerRISCVV::name(VRegister vd, VRegister vs2, MaskType mask) { \ ++ GenInstrV(VXUNARY0_FUNCT6, OP_MVV, vd, vs1, vs2, mask); \ ++ } ++ ++void AssemblerRISCVV::vfmv_vf(VRegister vd, FPURegister fs1, MaskType mask) { ++ GenInstrV(VMV_FUNCT6, OP_FVF, vd, fs1, v0, mask); ++} ++ ++void AssemblerRISCVV::vfmv_fs(FPURegister fd, VRegister vs2) { ++ GenInstrV(VWFUNARY0_FUNCT6, OP_FVV, fd, v0, vs2, NoMask); ++} ++ ++void AssemblerRISCVV::vfmv_sf(VRegister vd, FPURegister fs) { ++ GenInstrV(VRFUNARY0_FUNCT6, OP_FVF, vd, fs, v0, NoMask); ++} ++ ++DEFINE_OPIVV(vadd, VADD_FUNCT6) ++DEFINE_OPIVX(vadd, VADD_FUNCT6) ++DEFINE_OPIVI(vadd, VADD_FUNCT6) ++DEFINE_OPIVV(vsub, VSUB_FUNCT6) ++DEFINE_OPIVX(vsub, VSUB_FUNCT6) ++DEFINE_OPMVX(vdiv, VDIV_FUNCT6) ++DEFINE_OPMVX(vdivu, VDIVU_FUNCT6) ++DEFINE_OPMVX(vmul, VMUL_FUNCT6) ++DEFINE_OPMVX(vmulhu, VMULHU_FUNCT6) ++DEFINE_OPMVX(vmulhsu, VMULHSU_FUNCT6) ++DEFINE_OPMVX(vmulh, VMULH_FUNCT6) ++DEFINE_OPMVV(vdiv, VDIV_FUNCT6) ++DEFINE_OPMVV(vdivu, VDIVU_FUNCT6) ++DEFINE_OPMVV(vmul, VMUL_FUNCT6) ++DEFINE_OPMVV(vmulhu, VMULHU_FUNCT6) ++DEFINE_OPMVV(vmulhsu, VMULHSU_FUNCT6) ++DEFINE_OPMVV(vwmul, VWMUL_FUNCT6) ++DEFINE_OPMVV(vwmulu, VWMULU_FUNCT6) ++DEFINE_OPMVV(vmulh, VMULH_FUNCT6) ++DEFINE_OPMVV(vwadd, VWADD_FUNCT6) ++DEFINE_OPMVV(vwaddu, VWADDU_FUNCT6) ++DEFINE_OPMVV(vcompress, VCOMPRESS_FUNCT6) ++DEFINE_OPIVX(vsadd, VSADD_FUNCT6) ++DEFINE_OPIVV(vsadd, VSADD_FUNCT6) ++DEFINE_OPIVI(vsadd, VSADD_FUNCT6) ++DEFINE_OPIVX(vsaddu, VSADDU_FUNCT6) ++DEFINE_OPIVV(vsaddu, VSADDU_FUNCT6) ++DEFINE_OPIVI(vsaddu, VSADDU_FUNCT6) ++DEFINE_OPIVX(vssub, VSSUB_FUNCT6) ++DEFINE_OPIVV(vssub, VSSUB_FUNCT6) ++DEFINE_OPIVX(vssubu, VSSUBU_FUNCT6) ++DEFINE_OPIVV(vssubu, VSSUBU_FUNCT6) ++DEFINE_OPIVX(vrsub, VRSUB_FUNCT6) ++DEFINE_OPIVI(vrsub, VRSUB_FUNCT6) ++DEFINE_OPIVV(vminu, VMINU_FUNCT6) ++DEFINE_OPIVX(vminu, VMINU_FUNCT6) ++DEFINE_OPIVV(vmin, VMIN_FUNCT6) ++DEFINE_OPIVX(vmin, VMIN_FUNCT6) ++DEFINE_OPIVV(vmaxu, VMAXU_FUNCT6) ++DEFINE_OPIVX(vmaxu, VMAXU_FUNCT6) ++DEFINE_OPIVV(vmax, VMAX_FUNCT6) ++DEFINE_OPIVX(vmax, VMAX_FUNCT6) ++DEFINE_OPIVV(vand, VAND_FUNCT6) ++DEFINE_OPIVX(vand, VAND_FUNCT6) ++DEFINE_OPIVI(vand, VAND_FUNCT6) ++DEFINE_OPIVV(vor, VOR_FUNCT6) ++DEFINE_OPIVX(vor, VOR_FUNCT6) ++DEFINE_OPIVI(vor, VOR_FUNCT6) ++DEFINE_OPIVV(vxor, VXOR_FUNCT6) ++DEFINE_OPIVX(vxor, VXOR_FUNCT6) ++DEFINE_OPIVI(vxor, VXOR_FUNCT6) ++ ++DEFINE_OPIVX(vslidedown, VSLIDEDOWN_FUNCT6) ++DEFINE_OPIVI(vslidedown, VSLIDEDOWN_FUNCT6) ++DEFINE_OPIVX(vslideup, VSLIDEUP_FUNCT6) ++DEFINE_OPIVI(vslideup, VSLIDEUP_FUNCT6) ++ ++DEFINE_OPIVV(vmseq, VMSEQ_FUNCT6) ++DEFINE_OPIVX(vmseq, VMSEQ_FUNCT6) ++DEFINE_OPIVI(vmseq, VMSEQ_FUNCT6) ++ ++DEFINE_OPIVV(vmsne, VMSNE_FUNCT6) ++DEFINE_OPIVX(vmsne, VMSNE_FUNCT6) ++DEFINE_OPIVI(vmsne, VMSNE_FUNCT6) ++ ++DEFINE_OPIVV(vmsltu, VMSLTU_FUNCT6) ++DEFINE_OPIVX(vmsltu, VMSLTU_FUNCT6) ++ ++DEFINE_OPIVV(vmslt, VMSLT_FUNCT6) ++DEFINE_OPIVX(vmslt, VMSLT_FUNCT6) ++ ++DEFINE_OPIVV(vmsle, VMSLE_FUNCT6) ++DEFINE_OPIVX(vmsle, VMSLE_FUNCT6) ++DEFINE_OPIVI(vmsle, VMSLE_FUNCT6) ++ ++DEFINE_OPIVV(vmsleu, VMSLEU_FUNCT6) ++DEFINE_OPIVX(vmsleu, VMSLEU_FUNCT6) ++DEFINE_OPIVI(vmsleu, VMSLEU_FUNCT6) ++ ++DEFINE_OPIVI(vmsgt, VMSGT_FUNCT6) ++DEFINE_OPIVX(vmsgt, VMSGT_FUNCT6) ++ ++DEFINE_OPIVI(vmsgtu, VMSGTU_FUNCT6) ++DEFINE_OPIVX(vmsgtu, VMSGTU_FUNCT6) ++ ++DEFINE_OPIVV(vsrl, VSRL_FUNCT6) ++DEFINE_OPIVX(vsrl, VSRL_FUNCT6) ++DEFINE_OPIVI(vsrl, VSRL_FUNCT6) ++ ++DEFINE_OPIVV(vsra, VSRA_FUNCT6) ++DEFINE_OPIVX(vsra, VSRA_FUNCT6) ++DEFINE_OPIVI(vsra, VSRA_FUNCT6) ++ ++DEFINE_OPIVV(vsll, VSLL_FUNCT6) ++DEFINE_OPIVX(vsll, VSLL_FUNCT6) ++DEFINE_OPIVI(vsll, VSLL_FUNCT6) ++ ++DEFINE_OPIVV(vsmul, VSMUL_FUNCT6) ++DEFINE_OPIVX(vsmul, VSMUL_FUNCT6) ++ ++DEFINE_OPFVV(vfadd, VFADD_FUNCT6) ++DEFINE_OPFVF(vfadd, VFADD_FUNCT6) ++DEFINE_OPFVV(vfsub, VFSUB_FUNCT6) ++DEFINE_OPFVF(vfsub, VFSUB_FUNCT6) ++DEFINE_OPFVV(vfdiv, VFDIV_FUNCT6) ++DEFINE_OPFVF(vfdiv, VFDIV_FUNCT6) ++DEFINE_OPFVV(vfmul, VFMUL_FUNCT6) ++DEFINE_OPFVF(vfmul, VFMUL_FUNCT6) ++DEFINE_OPFVV(vmfeq, VMFEQ_FUNCT6) ++DEFINE_OPFVV(vmfne, VMFNE_FUNCT6) ++DEFINE_OPFVV(vmflt, VMFLT_FUNCT6) ++DEFINE_OPFVV(vmfle, VMFLE_FUNCT6) ++DEFINE_OPFVV(vfmax, VFMAX_FUNCT6) ++DEFINE_OPFVV(vfmin, VFMIN_FUNCT6) ++ ++// Vector Widening Floating-Point Add/Subtract Instructions ++DEFINE_OPFVV(vfwadd, VFWADD_FUNCT6) ++DEFINE_OPFVF(vfwadd, VFWADD_FUNCT6) ++DEFINE_OPFVV(vfwsub, VFWSUB_FUNCT6) ++DEFINE_OPFVF(vfwsub, VFWSUB_FUNCT6) ++DEFINE_OPFWV(vfwadd, VFWADD_W_FUNCT6) ++DEFINE_OPFWF(vfwadd, VFWADD_W_FUNCT6) ++DEFINE_OPFWV(vfwsub, VFWSUB_W_FUNCT6) ++DEFINE_OPFWF(vfwsub, VFWSUB_W_FUNCT6) ++ ++// Vector Widening Floating-Point Reduction Instructions ++DEFINE_OPFVV(vfwredusum, VFWREDUSUM_FUNCT6) ++DEFINE_OPFVV(vfwredosum, VFWREDOSUM_FUNCT6) ++ ++// Vector Widening Floating-Point Multiply ++DEFINE_OPFVV(vfwmul, VFWMUL_FUNCT6) ++DEFINE_OPFVF(vfwmul, VFWMUL_FUNCT6) ++ ++DEFINE_OPFRED(vfredmax, VFREDMAX_FUNCT6) ++ ++DEFINE_OPFVV(vfsngj, VFSGNJ_FUNCT6) ++DEFINE_OPFVF(vfsngj, VFSGNJ_FUNCT6) ++DEFINE_OPFVV(vfsngjn, VFSGNJN_FUNCT6) ++DEFINE_OPFVF(vfsngjn, VFSGNJN_FUNCT6) ++DEFINE_OPFVV(vfsngjx, VFSGNJX_FUNCT6) ++DEFINE_OPFVF(vfsngjx, VFSGNJX_FUNCT6) ++ ++// Vector Single-Width Floating-Point Fused Multiply-Add Instructions ++DEFINE_OPFVV_FMA(vfmadd, VFMADD_FUNCT6) ++DEFINE_OPFVF_FMA(vfmadd, VFMADD_FUNCT6) ++DEFINE_OPFVV_FMA(vfmsub, VFMSUB_FUNCT6) ++DEFINE_OPFVF_FMA(vfmsub, VFMSUB_FUNCT6) ++DEFINE_OPFVV_FMA(vfmacc, VFMACC_FUNCT6) ++DEFINE_OPFVF_FMA(vfmacc, VFMACC_FUNCT6) ++DEFINE_OPFVV_FMA(vfmsac, VFMSAC_FUNCT6) ++DEFINE_OPFVF_FMA(vfmsac, VFMSAC_FUNCT6) ++DEFINE_OPFVV_FMA(vfnmadd, VFNMADD_FUNCT6) ++DEFINE_OPFVF_FMA(vfnmadd, VFNMADD_FUNCT6) ++DEFINE_OPFVV_FMA(vfnmsub, VFNMSUB_FUNCT6) ++DEFINE_OPFVF_FMA(vfnmsub, VFNMSUB_FUNCT6) ++DEFINE_OPFVV_FMA(vfnmacc, VFNMACC_FUNCT6) ++DEFINE_OPFVF_FMA(vfnmacc, VFNMACC_FUNCT6) ++DEFINE_OPFVV_FMA(vfnmsac, VFNMSAC_FUNCT6) ++DEFINE_OPFVF_FMA(vfnmsac, VFNMSAC_FUNCT6) ++ ++// Vector Widening Floating-Point Fused Multiply-Add Instructions ++DEFINE_OPFVV_FMA(vfwmacc, VFWMACC_FUNCT6) ++DEFINE_OPFVF_FMA(vfwmacc, VFWMACC_FUNCT6) ++DEFINE_OPFVV_FMA(vfwnmacc, VFWNMACC_FUNCT6) ++DEFINE_OPFVF_FMA(vfwnmacc, VFWNMACC_FUNCT6) ++DEFINE_OPFVV_FMA(vfwmsac, VFWMSAC_FUNCT6) ++DEFINE_OPFVF_FMA(vfwmsac, VFWMSAC_FUNCT6) ++DEFINE_OPFVV_FMA(vfwnmsac, VFWNMSAC_FUNCT6) ++DEFINE_OPFVF_FMA(vfwnmsac, VFWNMSAC_FUNCT6) ++ ++// Vector Narrowing Fixed-Point Clip Instructions ++DEFINE_OPIVV(vnclip, VNCLIP_FUNCT6) ++DEFINE_OPIVX(vnclip, VNCLIP_FUNCT6) ++DEFINE_OPIVI(vnclip, VNCLIP_FUNCT6) ++DEFINE_OPIVV(vnclipu, VNCLIPU_FUNCT6) ++DEFINE_OPIVX(vnclipu, VNCLIPU_FUNCT6) ++DEFINE_OPIVI(vnclipu, VNCLIPU_FUNCT6) ++ ++// Vector Integer Extension ++DEFINE_OPMVV_VIE(vzext_vf8, 0b00010) ++DEFINE_OPMVV_VIE(vsext_vf8, 0b00011) ++DEFINE_OPMVV_VIE(vzext_vf4, 0b00100) ++DEFINE_OPMVV_VIE(vsext_vf4, 0b00101) ++DEFINE_OPMVV_VIE(vzext_vf2, 0b00110) ++DEFINE_OPMVV_VIE(vsext_vf2, 0b00111) ++ ++# undef DEFINE_OPIVI ++# undef DEFINE_OPIVV ++# undef DEFINE_OPIVX ++# undef DEFINE_OPFVV ++# undef DEFINE_OPFWV ++# undef DEFINE_OPFVF ++# undef DEFINE_OPFWF ++# undef DEFINE_OPFVV_FMA ++# undef DEFINE_OPFVF_FMA ++# undef DEFINE_OPMVV_VIE ++ ++void AssemblerRISCVV::vsetvli(Register rd, Register rs1, VSew vsew, Vlmul vlmul, ++ TailAgnosticType tail, MaskAgnosticType mask) { ++ int32_t zimm = GenZimm(vsew, vlmul, tail, mask); ++ Instr instr = OP_V | ((rd.code() & 0x1F) << kRvvRdShift) | (0x7 << 12) | ++ ((rs1.code() & 0x1F) << kRvvRs1Shift) | ++ (((uint32_t)zimm << kRvvZimmShift) & kRvvZimmMask) | 0x0 << 31; ++ emit(instr); ++} ++ ++void AssemblerRISCVV::vsetivli(Register rd, uint8_t uimm, VSew vsew, ++ Vlmul vlmul, TailAgnosticType tail, ++ MaskAgnosticType mask) { ++ MOZ_ASSERT(is_uint5(uimm)); ++ int32_t zimm = GenZimm(vsew, vlmul, tail, mask) & 0x3FF; ++ Instr instr = OP_V | ((rd.code() & 0x1F) << kRvvRdShift) | (0x7 << 12) | ++ ((uimm & 0x1F) << kRvvUimmShift) | ++ (((uint32_t)zimm << kRvvZimmShift) & kRvvZimmMask) | 0x3 << 30; ++ emit(instr); ++} ++ ++void AssemblerRISCVV::vsetvl(Register rd, Register rs1, Register rs2) { ++ Instr instr = OP_V | ((rd.code() & 0x1F) << kRvvRdShift) | (0x7 << 12) | ++ ((rs1.code() & 0x1F) << kRvvRs1Shift) | ++ ((rs2.code() & 0x1F) << kRvvRs2Shift) | 0x40 << 25; ++ emit(instr); ++} ++ ++uint8_t vsew_switch(VSew vsew) { ++ uint8_t width; ++ switch (vsew) { ++ case E8: ++ width = 0b000; ++ break; ++ case E16: ++ width = 0b101; ++ break; ++ case E32: ++ width = 0b110; ++ break; ++ default: ++ width = 0b111; ++ break; ++ } ++ return width; ++} ++ ++// OPIVV OPFVV OPMVV ++void AssemblerRISCVV::GenInstrV(uint8_t funct6, OpcodeRISCVV opcode, ++ VRegister vd, VRegister vs1, VRegister vs2, ++ MaskType mask) { ++ MOZ_ASSERT(opcode == OP_MVV || opcode == OP_FVV || opcode == OP_IVV); ++ Instr instr = (funct6 << kRvvFunct6Shift) | opcode | (mask << kRvvVmShift) | ++ ((vd.code() & 0x1F) << kRvvVdShift) | ++ ((vs1.code() & 0x1F) << kRvvVs1Shift) | ++ ((vs2.code() & 0x1F) << kRvvVs2Shift); ++ emit(instr); ++} ++ ++void AssemblerRISCVV::GenInstrV(uint8_t funct6, OpcodeRISCVV opcode, ++ VRegister vd, int8_t vs1, VRegister vs2, ++ MaskType mask) { ++ MOZ_ASSERT(opcode == OP_MVV || opcode == OP_FVV || opcode == OP_IVV); ++ Instr instr = (funct6 << kRvvFunct6Shift) | opcode | (mask << kRvvVmShift) | ++ ((vd.code() & 0x1F) << kRvvVdShift) | ++ ((vs1 & 0x1F) << kRvvVs1Shift) | ++ ((vs2.code() & 0x1F) << kRvvVs2Shift); ++ emit(instr); ++} ++// OPMVV OPFVV ++void AssemblerRISCVV::GenInstrV(uint8_t funct6, OpcodeRISCVV opcode, ++ Register rd, VRegister vs1, VRegister vs2, ++ MaskType mask) { ++ MOZ_ASSERT(opcode == OP_MVV || opcode == OP_FVV); ++ Instr instr = (funct6 << kRvvFunct6Shift) | opcode | (mask << kRvvVmShift) | ++ ((rd.code() & 0x1F) << kRvvVdShift) | ++ ((vs1.code() & 0x1F) << kRvvVs1Shift) | ++ ((vs2.code() & 0x1F) << kRvvVs2Shift); ++ emit(instr); ++} ++ ++// OPFVV ++void AssemblerRISCVV::GenInstrV(uint8_t funct6, OpcodeRISCVV opcode, ++ FPURegister fd, VRegister vs1, VRegister vs2, ++ MaskType mask) { ++ MOZ_ASSERT(opcode == OP_FVV); ++ Instr instr = (funct6 << kRvvFunct6Shift) | opcode | (mask << kRvvVmShift) | ++ ((fd.code() & 0x1F) << kRvvVdShift) | ++ ((vs1.code() & 0x1F) << kRvvVs1Shift) | ++ ((vs2.code() & 0x1F) << kRvvVs2Shift); ++ emit(instr); ++} ++ ++// OPIVX OPMVX ++void AssemblerRISCVV::GenInstrV(uint8_t funct6, OpcodeRISCVV opcode, ++ VRegister vd, Register rs1, VRegister vs2, ++ MaskType mask) { ++ MOZ_ASSERT(opcode == OP_IVX || opcode == OP_MVX); ++ Instr instr = (funct6 << kRvvFunct6Shift) | opcode | (mask << kRvvVmShift) | ++ ((vd.code() & 0x1F) << kRvvVdShift) | ++ ((rs1.code() & 0x1F) << kRvvRs1Shift) | ++ ((vs2.code() & 0x1F) << kRvvVs2Shift); ++ emit(instr); ++} ++ ++// OPFVF ++void AssemblerRISCVV::GenInstrV(uint8_t funct6, OpcodeRISCVV opcode, ++ VRegister vd, FPURegister fs1, VRegister vs2, ++ MaskType mask) { ++ MOZ_ASSERT(opcode == OP_FVF); ++ Instr instr = (funct6 << kRvvFunct6Shift) | opcode | (mask << kRvvVmShift) | ++ ((vd.code() & 0x1F) << kRvvVdShift) | ++ ((fs1.code() & 0x1F) << kRvvRs1Shift) | ++ ((vs2.code() & 0x1F) << kRvvVs2Shift); ++ emit(instr); ++} ++ ++// OPMVX ++void AssemblerRISCVV::GenInstrV(uint8_t funct6, Register rd, Register rs1, ++ VRegister vs2, MaskType mask) { ++ Instr instr = (funct6 << kRvvFunct6Shift) | OP_MVX | (mask << kRvvVmShift) | ++ ((rd.code() & 0x1F) << kRvvVdShift) | ++ ((rs1.code() & 0x1F) << kRvvRs1Shift) | ++ ((vs2.code() & 0x1F) << kRvvVs2Shift); ++ emit(instr); ++} ++// OPIVI ++void AssemblerRISCVV::GenInstrV(uint8_t funct6, VRegister vd, int8_t imm5, ++ VRegister vs2, MaskType mask) { ++ MOZ_ASSERT(is_uint5(imm5) || is_int5(imm5)); ++ Instr instr = (funct6 << kRvvFunct6Shift) | OP_IVI | (mask << kRvvVmShift) | ++ ((vd.code() & 0x1F) << kRvvVdShift) | ++ (((uint32_t)imm5 << kRvvImm5Shift) & kRvvImm5Mask) | ++ ((vs2.code() & 0x1F) << kRvvVs2Shift); ++ emit(instr); ++} ++ ++// VL VS ++void AssemblerRISCVV::GenInstrV(BaseOpcode opcode, uint8_t width, VRegister vd, ++ Register rs1, uint8_t umop, MaskType mask, ++ uint8_t IsMop, bool IsMew, uint8_t Nf) { ++ MOZ_ASSERT(opcode == LOAD_FP || opcode == STORE_FP); ++ Instr instr = opcode | ((vd.code() << kRvvVdShift) & kRvvVdMask) | ++ ((width << kRvvWidthShift) & kRvvWidthMask) | ++ ((rs1.code() << kRvvRs1Shift) & kRvvRs1Mask) | ++ ((umop << kRvvRs2Shift) & kRvvRs2Mask) | ++ ((mask << kRvvVmShift) & kRvvVmMask) | ++ ((IsMop << kRvvMopShift) & kRvvMopMask) | ++ ((IsMew << kRvvMewShift) & kRvvMewMask) | ++ ((Nf << kRvvNfShift) & kRvvNfMask); ++ emit(instr); ++} ++void AssemblerRISCVV::GenInstrV(BaseOpcode opcode, uint8_t width, VRegister vd, ++ Register rs1, Register rs2, MaskType mask, ++ uint8_t IsMop, bool IsMew, uint8_t Nf) { ++ MOZ_ASSERT(opcode == LOAD_FP || opcode == STORE_FP); ++ Instr instr = opcode | ((vd.code() << kRvvVdShift) & kRvvVdMask) | ++ ((width << kRvvWidthShift) & kRvvWidthMask) | ++ ((rs1.code() << kRvvRs1Shift) & kRvvRs1Mask) | ++ ((rs2.code() << kRvvRs2Shift) & kRvvRs2Mask) | ++ ((mask << kRvvVmShift) & kRvvVmMask) | ++ ((IsMop << kRvvMopShift) & kRvvMopMask) | ++ ((IsMew << kRvvMewShift) & kRvvMewMask) | ++ ((Nf << kRvvNfShift) & kRvvNfMask); ++ emit(instr); ++} ++// VL VS AMO ++void AssemblerRISCVV::GenInstrV(BaseOpcode opcode, uint8_t width, VRegister vd, ++ Register rs1, VRegister vs2, MaskType mask, ++ uint8_t IsMop, bool IsMew, uint8_t Nf) { ++ MOZ_ASSERT(opcode == LOAD_FP || opcode == STORE_FP || opcode == AMO); ++ Instr instr = opcode | ((vd.code() << kRvvVdShift) & kRvvVdMask) | ++ ((width << kRvvWidthShift) & kRvvWidthMask) | ++ ((rs1.code() << kRvvRs1Shift) & kRvvRs1Mask) | ++ ((vs2.code() << kRvvRs2Shift) & kRvvRs2Mask) | ++ ((mask << kRvvVmShift) & kRvvVmMask) | ++ ((IsMop << kRvvMopShift) & kRvvMopMask) | ++ ((IsMew << kRvvMewShift) & kRvvMewMask) | ++ ((Nf << kRvvNfShift) & kRvvNfMask); ++ emit(instr); ++} ++// vmv_xs vcpop_m vfirst_m ++void AssemblerRISCVV::GenInstrV(uint8_t funct6, OpcodeRISCVV opcode, ++ Register rd, uint8_t vs1, VRegister vs2, ++ MaskType mask) { ++ MOZ_ASSERT(opcode == OP_MVV); ++ Instr instr = (funct6 << kRvvFunct6Shift) | opcode | (mask << kRvvVmShift) | ++ ((rd.code() & 0x1F) << kRvvVdShift) | ++ ((vs1 & 0x1F) << kRvvVs1Shift) | ++ ((vs2.code() & 0x1F) << kRvvVs2Shift); ++ emit(instr); ++} ++ ++void AssemblerRISCVV::vl(VRegister vd, Register rs1, uint8_t lumop, VSew vsew, ++ MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(LOAD_FP, width, vd, rs1, lumop, mask, 0b00, 0, 0b000); ++} ++void AssemblerRISCVV::vls(VRegister vd, Register rs1, Register rs2, VSew vsew, ++ MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(LOAD_FP, width, vd, rs1, rs2, mask, 0b10, 0, 0b000); ++} ++void AssemblerRISCVV::vlx(VRegister vd, Register rs1, VRegister vs2, VSew vsew, ++ MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(LOAD_FP, width, vd, rs1, vs2, mask, 0b11, 0, 0); ++} ++ ++void AssemblerRISCVV::vs(VRegister vd, Register rs1, uint8_t sumop, VSew vsew, ++ MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(STORE_FP, width, vd, rs1, sumop, mask, 0b00, 0, 0b000); ++} ++void AssemblerRISCVV::vss(VRegister vs3, Register rs1, Register rs2, VSew vsew, ++ MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(STORE_FP, width, vs3, rs1, rs2, mask, 0b10, 0, 0b000); ++} ++ ++void AssemblerRISCVV::vsx(VRegister vd, Register rs1, VRegister vs2, VSew vsew, ++ MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(STORE_FP, width, vd, rs1, vs2, mask, 0b11, 0, 0b000); ++} ++void AssemblerRISCVV::vsu(VRegister vd, Register rs1, VRegister vs2, VSew vsew, ++ MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(STORE_FP, width, vd, rs1, vs2, mask, 0b01, 0, 0b000); ++} ++ ++void AssemblerRISCVV::vlseg2(VRegister vd, Register rs1, uint8_t lumop, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(LOAD_FP, width, vd, rs1, lumop, mask, 0b00, 0, 0b001); ++} ++ ++void AssemblerRISCVV::vlseg3(VRegister vd, Register rs1, uint8_t lumop, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(LOAD_FP, width, vd, rs1, lumop, mask, 0b00, 0, 0b010); ++} ++ ++void AssemblerRISCVV::vlseg4(VRegister vd, Register rs1, uint8_t lumop, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(LOAD_FP, width, vd, rs1, lumop, mask, 0b00, 0, 0b011); ++} ++ ++void AssemblerRISCVV::vlseg5(VRegister vd, Register rs1, uint8_t lumop, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(LOAD_FP, width, vd, rs1, lumop, mask, 0b00, 0, 0b100); ++} ++ ++void AssemblerRISCVV::vlseg6(VRegister vd, Register rs1, uint8_t lumop, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(LOAD_FP, width, vd, rs1, lumop, mask, 0b00, 0, 0b101); ++} ++ ++void AssemblerRISCVV::vlseg7(VRegister vd, Register rs1, uint8_t lumop, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(LOAD_FP, width, vd, rs1, lumop, mask, 0b00, 0, 0b110); ++} ++ ++void AssemblerRISCVV::vlseg8(VRegister vd, Register rs1, uint8_t lumop, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(LOAD_FP, width, vd, rs1, lumop, mask, 0b00, 0, 0b111); ++} ++void AssemblerRISCVV::vsseg2(VRegister vd, Register rs1, uint8_t sumop, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(STORE_FP, width, vd, rs1, sumop, mask, 0b00, 0, 0b001); ++} ++void AssemblerRISCVV::vsseg3(VRegister vd, Register rs1, uint8_t sumop, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(STORE_FP, width, vd, rs1, sumop, mask, 0b00, 0, 0b010); ++} ++void AssemblerRISCVV::vsseg4(VRegister vd, Register rs1, uint8_t sumop, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(STORE_FP, width, vd, rs1, sumop, mask, 0b00, 0, 0b011); ++} ++void AssemblerRISCVV::vsseg5(VRegister vd, Register rs1, uint8_t sumop, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(STORE_FP, width, vd, rs1, sumop, mask, 0b00, 0, 0b100); ++} ++void AssemblerRISCVV::vsseg6(VRegister vd, Register rs1, uint8_t sumop, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(STORE_FP, width, vd, rs1, sumop, mask, 0b00, 0, 0b101); ++} ++void AssemblerRISCVV::vsseg7(VRegister vd, Register rs1, uint8_t sumop, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(STORE_FP, width, vd, rs1, sumop, mask, 0b00, 0, 0b110); ++} ++void AssemblerRISCVV::vsseg8(VRegister vd, Register rs1, uint8_t sumop, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(STORE_FP, width, vd, rs1, sumop, mask, 0b00, 0, 0b111); ++} ++ ++void AssemblerRISCVV::vlsseg2(VRegister vd, Register rs1, Register rs2, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(LOAD_FP, width, vd, rs1, rs2, mask, 0b10, 0, 0b001); ++} ++void AssemblerRISCVV::vlsseg3(VRegister vd, Register rs1, Register rs2, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(LOAD_FP, width, vd, rs1, rs2, mask, 0b10, 0, 0b010); ++} ++void AssemblerRISCVV::vlsseg4(VRegister vd, Register rs1, Register rs2, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(LOAD_FP, width, vd, rs1, rs2, mask, 0b10, 0, 0b011); ++} ++void AssemblerRISCVV::vlsseg5(VRegister vd, Register rs1, Register rs2, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(LOAD_FP, width, vd, rs1, rs2, mask, 0b10, 0, 0b100); ++} ++void AssemblerRISCVV::vlsseg6(VRegister vd, Register rs1, Register rs2, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(LOAD_FP, width, vd, rs1, rs2, mask, 0b10, 0, 0b101); ++} ++void AssemblerRISCVV::vlsseg7(VRegister vd, Register rs1, Register rs2, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(LOAD_FP, width, vd, rs1, rs2, mask, 0b10, 0, 0b110); ++} ++void AssemblerRISCVV::vlsseg8(VRegister vd, Register rs1, Register rs2, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(LOAD_FP, width, vd, rs1, rs2, mask, 0b10, 0, 0b111); ++} ++void AssemblerRISCVV::vssseg2(VRegister vd, Register rs1, Register rs2, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(STORE_FP, width, vd, rs1, rs2, mask, 0b10, 0, 0b001); ++} ++void AssemblerRISCVV::vssseg3(VRegister vd, Register rs1, Register rs2, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(STORE_FP, width, vd, rs1, rs2, mask, 0b10, 0, 0b010); ++} ++void AssemblerRISCVV::vssseg4(VRegister vd, Register rs1, Register rs2, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(STORE_FP, width, vd, rs1, rs2, mask, 0b10, 0, 0b011); ++} ++void AssemblerRISCVV::vssseg5(VRegister vd, Register rs1, Register rs2, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(STORE_FP, width, vd, rs1, rs2, mask, 0b10, 0, 0b100); ++} ++void AssemblerRISCVV::vssseg6(VRegister vd, Register rs1, Register rs2, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(STORE_FP, width, vd, rs1, rs2, mask, 0b10, 0, 0b101); ++} ++void AssemblerRISCVV::vssseg7(VRegister vd, Register rs1, Register rs2, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(STORE_FP, width, vd, rs1, rs2, mask, 0b10, 0, 0b110); ++} ++void AssemblerRISCVV::vssseg8(VRegister vd, Register rs1, Register rs2, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(STORE_FP, width, vd, rs1, rs2, mask, 0b10, 0, 0b111); ++} ++ ++void AssemblerRISCVV::vlxseg2(VRegister vd, Register rs1, VRegister rs2, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(LOAD_FP, width, vd, rs1, rs2, mask, 0b11, 0, 0b001); ++} ++void AssemblerRISCVV::vlxseg3(VRegister vd, Register rs1, VRegister rs2, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(LOAD_FP, width, vd, rs1, rs2, mask, 0b11, 0, 0b010); ++} ++void AssemblerRISCVV::vlxseg4(VRegister vd, Register rs1, VRegister rs2, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(LOAD_FP, width, vd, rs1, rs2, mask, 0b11, 0, 0b011); ++} ++void AssemblerRISCVV::vlxseg5(VRegister vd, Register rs1, VRegister rs2, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(LOAD_FP, width, vd, rs1, rs2, mask, 0b11, 0, 0b100); ++} ++void AssemblerRISCVV::vlxseg6(VRegister vd, Register rs1, VRegister rs2, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(LOAD_FP, width, vd, rs1, rs2, mask, 0b11, 0, 0b101); ++} ++void AssemblerRISCVV::vlxseg7(VRegister vd, Register rs1, VRegister rs2, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(LOAD_FP, width, vd, rs1, rs2, mask, 0b11, 0, 0b110); ++} ++void AssemblerRISCVV::vlxseg8(VRegister vd, Register rs1, VRegister rs2, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(LOAD_FP, width, vd, rs1, rs2, mask, 0b11, 0, 0b111); ++} ++void AssemblerRISCVV::vsxseg2(VRegister vd, Register rs1, VRegister rs2, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(STORE_FP, width, vd, rs1, rs2, mask, 0b11, 0, 0b001); ++} ++void AssemblerRISCVV::vsxseg3(VRegister vd, Register rs1, VRegister rs2, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(STORE_FP, width, vd, rs1, rs2, mask, 0b11, 0, 0b010); ++} ++void AssemblerRISCVV::vsxseg4(VRegister vd, Register rs1, VRegister rs2, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(STORE_FP, width, vd, rs1, rs2, mask, 0b11, 0, 0b011); ++} ++void AssemblerRISCVV::vsxseg5(VRegister vd, Register rs1, VRegister rs2, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(STORE_FP, width, vd, rs1, rs2, mask, 0b11, 0, 0b100); ++} ++void AssemblerRISCVV::vsxseg6(VRegister vd, Register rs1, VRegister rs2, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(STORE_FP, width, vd, rs1, rs2, mask, 0b11, 0, 0b101); ++} ++void AssemblerRISCVV::vsxseg7(VRegister vd, Register rs1, VRegister rs2, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(STORE_FP, width, vd, rs1, rs2, mask, 0b11, 0, 0b110); ++} ++void AssemblerRISCVV::vsxseg8(VRegister vd, Register rs1, VRegister rs2, ++ VSew vsew, MaskType mask) { ++ uint8_t width = vsew_switch(vsew); ++ GenInstrV(STORE_FP, width, vd, rs1, rs2, mask, 0b11, 0, 0b111); ++} ++ ++void AssemblerRISCVV::vfirst_m(Register rd, VRegister vs2, MaskType mask) { ++ GenInstrV(VWXUNARY0_FUNCT6, OP_MVV, rd, 0b10001, vs2, mask); ++} ++ ++void AssemblerRISCVV::vcpop_m(Register rd, VRegister vs2, MaskType mask) { ++ GenInstrV(VWXUNARY0_FUNCT6, OP_MVV, rd, 0b10000, vs2, mask); ++} ++ ++LoadStoreLaneParams::LoadStoreLaneParams(MachineRepresentation rep, ++ uint8_t laneidx) { ++ switch (rep) { ++ case MachineRepresentation::kWord8: ++ *this = LoadStoreLaneParams(laneidx, 8, kRvvVLEN / 16); ++ break; ++ case MachineRepresentation::kWord16: ++ *this = LoadStoreLaneParams(laneidx, 16, kRvvVLEN / 8); ++ break; ++ case MachineRepresentation::kWord32: ++ *this = LoadStoreLaneParams(laneidx, 32, kRvvVLEN / 4); ++ break; ++ case MachineRepresentation::kWord64: ++ *this = LoadStoreLaneParams(laneidx, 64, kRvvVLEN / 2); ++ break; ++ default: ++ UNREACHABLE(); ++ } ++} ++ ++} // namespace jit ++} // namespace js ++#endif +diff --git a/js/src/jit/riscv64/extension/extension-riscv-v.h b/js/src/jit/riscv64/extension/extension-riscv-v.h +new file mode 100644 +index 0000000000..8f04f24c56 +--- /dev/null ++++ b/js/src/jit/riscv64/extension/extension-riscv-v.h +@@ -0,0 +1,484 @@ ++// Copyright 2022 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef jit_riscv64_extension_Extension_riscv_v_h_ ++#define jit_riscv64_extension_Extension_riscv_v_h_ ++#ifdef CAN_USE_RVV ++# include "jit/riscv64/Architecture-riscv64.h" ++# include "jit/riscv64/constant/Constant-riscv64.h" ++# include "jit/riscv64/extension/base-assembler-riscv.h" ++ ++namespace js { ++namespace jit { ++ ++class AssemblerRISCVV : public AssemblerRiscvBase { ++ public: ++ // RVV ++ static int32_t GenZimm(VSew vsew, Vlmul vlmul, TailAgnosticType tail = tu, ++ MaskAgnosticType mask = mu) { ++ return (mask << 7) | (tail << 6) | ((vsew & 0x7) << 3) | (vlmul & 0x7); ++ } ++ ++ void vl(VRegister vd, Register rs1, uint8_t lumop, VSew vsew, ++ MaskType mask = NoMask); ++ void vls(VRegister vd, Register rs1, Register rs2, VSew vsew, ++ MaskType mask = NoMask); ++ void vlx(VRegister vd, Register rs1, VRegister vs3, VSew vsew, ++ MaskType mask = NoMask); ++ ++ void vs(VRegister vd, Register rs1, uint8_t sumop, VSew vsew, ++ MaskType mask = NoMask); ++ void vss(VRegister vd, Register rs1, Register rs2, VSew vsew, ++ MaskType mask = NoMask); ++ void vsx(VRegister vd, Register rs1, VRegister vs3, VSew vsew, ++ MaskType mask = NoMask); ++ ++ void vsu(VRegister vd, Register rs1, VRegister vs3, VSew vsew, ++ MaskType mask = NoMask); ++ ++# define SegInstr(OP) \ ++ void OP##seg2(ARG); \ ++ void OP##seg3(ARG); \ ++ void OP##seg4(ARG); \ ++ void OP##seg5(ARG); \ ++ void OP##seg6(ARG); \ ++ void OP##seg7(ARG); \ ++ void OP##seg8(ARG); ++ ++# define ARG \ ++ VRegister vd, Register rs1, uint8_t lumop, VSew vsew, MaskType mask = NoMask ++ ++ SegInstr(vl) SegInstr(vs) ++# undef ARG ++ ++# define ARG \ ++ VRegister vd, Register rs1, Register rs2, VSew vsew, MaskType mask = NoMask ++ ++ SegInstr(vls) SegInstr(vss) ++# undef ARG ++ ++# define ARG \ ++ VRegister vd, Register rs1, VRegister rs2, VSew vsew, MaskType mask = NoMask ++ ++ SegInstr(vsx) SegInstr(vlx) ++# undef ARG ++# undef SegInstr ++ ++ // RVV Vector Arithmetic Instruction ++ ++ void vmv_vv(VRegister vd, VRegister vs1); ++ void vmv_vx(VRegister vd, Register rs1); ++ void vmv_vi(VRegister vd, uint8_t simm5); ++ void vmv_xs(Register rd, VRegister vs2); ++ void vmv_sx(VRegister vd, Register rs1); ++ void vmerge_vv(VRegister vd, VRegister vs1, VRegister vs2); ++ void vmerge_vx(VRegister vd, Register rs1, VRegister vs2); ++ void vmerge_vi(VRegister vd, uint8_t imm5, VRegister vs2); ++ ++ void vredmaxu_vs(VRegister vd, VRegister vs2, VRegister vs1, ++ MaskType mask = NoMask); ++ void vredmax_vs(VRegister vd, VRegister vs2, VRegister vs1, ++ MaskType mask = NoMask); ++ void vredmin_vs(VRegister vd, VRegister vs2, VRegister vs1, ++ MaskType mask = NoMask); ++ void vredminu_vs(VRegister vd, VRegister vs2, VRegister vs1, ++ MaskType mask = NoMask); ++ ++ void vadc_vv(VRegister vd, VRegister vs1, VRegister vs2); ++ void vadc_vx(VRegister vd, Register rs1, VRegister vs2); ++ void vadc_vi(VRegister vd, uint8_t imm5, VRegister vs2); ++ ++ void vmadc_vv(VRegister vd, VRegister vs1, VRegister vs2); ++ void vmadc_vx(VRegister vd, Register rs1, VRegister vs2); ++ void vmadc_vi(VRegister vd, uint8_t imm5, VRegister vs2); ++ ++ void vfmv_vf(VRegister vd, FPURegister fs1, MaskType mask = NoMask); ++ void vfmv_fs(FPURegister fd, VRegister vs2); ++ void vfmv_sf(VRegister vd, FPURegister fs); ++ ++ void vwaddu_wx(VRegister vd, VRegister vs2, Register rs1, ++ MaskType mask = NoMask); ++ void vid_v(VRegister vd, MaskType mask = Mask); ++ ++# define DEFINE_OPIVV(name, funct6) \ ++ void name##_vv(VRegister vd, VRegister vs2, VRegister vs1, \ ++ MaskType mask = NoMask); ++ ++# define DEFINE_OPIVX(name, funct6) \ ++ void name##_vx(VRegister vd, VRegister vs2, Register rs1, \ ++ MaskType mask = NoMask); ++ ++# define DEFINE_OPIVI(name, funct6) \ ++ void name##_vi(VRegister vd, VRegister vs2, int8_t imm5, \ ++ MaskType mask = NoMask); ++ ++# define DEFINE_OPMVV(name, funct6) \ ++ void name##_vv(VRegister vd, VRegister vs2, VRegister vs1, \ ++ MaskType mask = NoMask); ++ ++# define DEFINE_OPMVX(name, funct6) \ ++ void name##_vx(VRegister vd, VRegister vs2, Register rs1, \ ++ MaskType mask = NoMask); ++ ++# define DEFINE_OPFVV(name, funct6) \ ++ void name##_vv(VRegister vd, VRegister vs2, VRegister vs1, \ ++ MaskType mask = NoMask); ++ ++# define DEFINE_OPFWV(name, funct6) \ ++ void name##_wv(VRegister vd, VRegister vs2, VRegister vs1, \ ++ MaskType mask = NoMask); ++ ++# define DEFINE_OPFRED(name, funct6) \ ++ void name##_vs(VRegister vd, VRegister vs2, VRegister vs1, \ ++ MaskType mask = NoMask); ++ ++# define DEFINE_OPFVF(name, funct6) \ ++ void name##_vf(VRegister vd, VRegister vs2, FPURegister fs1, \ ++ MaskType mask = NoMask); ++ ++# define DEFINE_OPFWF(name, funct6) \ ++ void name##_wf(VRegister vd, VRegister vs2, FPURegister fs1, \ ++ MaskType mask = NoMask); ++ ++# define DEFINE_OPFVV_FMA(name, funct6) \ ++ void name##_vv(VRegister vd, VRegister vs1, VRegister vs2, \ ++ MaskType mask = NoMask); ++ ++# define DEFINE_OPFVF_FMA(name, funct6) \ ++ void name##_vf(VRegister vd, FPURegister fs1, VRegister vs2, \ ++ MaskType mask = NoMask); ++ ++# define DEFINE_OPMVV_VIE(name) \ ++ void name(VRegister vd, VRegister vs2, MaskType mask = NoMask); ++ ++ DEFINE_OPIVV(vadd, VADD_FUNCT6) ++ DEFINE_OPIVX(vadd, VADD_FUNCT6) ++ DEFINE_OPIVI(vadd, VADD_FUNCT6) ++ DEFINE_OPIVV(vsub, VSUB_FUNCT6) ++ DEFINE_OPIVX(vsub, VSUB_FUNCT6) ++ DEFINE_OPMVX(vdiv, VDIV_FUNCT6) ++ DEFINE_OPMVX(vdivu, VDIVU_FUNCT6) ++ DEFINE_OPMVX(vmul, VMUL_FUNCT6) ++ DEFINE_OPMVX(vmulhu, VMULHU_FUNCT6) ++ DEFINE_OPMVX(vmulhsu, VMULHSU_FUNCT6) ++ DEFINE_OPMVX(vmulh, VMULH_FUNCT6) ++ DEFINE_OPMVV(vdiv, VDIV_FUNCT6) ++ DEFINE_OPMVV(vdivu, VDIVU_FUNCT6) ++ DEFINE_OPMVV(vmul, VMUL_FUNCT6) ++ DEFINE_OPMVV(vmulhu, VMULHU_FUNCT6) ++ DEFINE_OPMVV(vmulhsu, VMULHSU_FUNCT6) ++ DEFINE_OPMVV(vmulh, VMULH_FUNCT6) ++ DEFINE_OPMVV(vwmul, VWMUL_FUNCT6) ++ DEFINE_OPMVV(vwmulu, VWMULU_FUNCT6) ++ DEFINE_OPMVV(vwaddu, VWADDU_FUNCT6) ++ DEFINE_OPMVV(vwadd, VWADD_FUNCT6) ++ DEFINE_OPMVV(vcompress, VCOMPRESS_FUNCT6) ++ DEFINE_OPIVX(vsadd, VSADD_FUNCT6) ++ DEFINE_OPIVV(vsadd, VSADD_FUNCT6) ++ DEFINE_OPIVI(vsadd, VSADD_FUNCT6) ++ DEFINE_OPIVX(vsaddu, VSADD_FUNCT6) ++ DEFINE_OPIVV(vsaddu, VSADDU_FUNCT6) ++ DEFINE_OPIVI(vsaddu, VSADDU_FUNCT6) ++ DEFINE_OPIVX(vssub, VSSUB_FUNCT6) ++ DEFINE_OPIVV(vssub, VSSUB_FUNCT6) ++ DEFINE_OPIVX(vssubu, VSSUBU_FUNCT6) ++ DEFINE_OPIVV(vssubu, VSSUBU_FUNCT6) ++ DEFINE_OPIVX(vrsub, VRSUB_FUNCT6) ++ DEFINE_OPIVI(vrsub, VRSUB_FUNCT6) ++ DEFINE_OPIVV(vminu, VMINU_FUNCT6) ++ DEFINE_OPIVX(vminu, VMINU_FUNCT6) ++ DEFINE_OPIVV(vmin, VMIN_FUNCT6) ++ DEFINE_OPIVX(vmin, VMIN_FUNCT6) ++ DEFINE_OPIVV(vmaxu, VMAXU_FUNCT6) ++ DEFINE_OPIVX(vmaxu, VMAXU_FUNCT6) ++ DEFINE_OPIVV(vmax, VMAX_FUNCT6) ++ DEFINE_OPIVX(vmax, VMAX_FUNCT6) ++ DEFINE_OPIVV(vand, VAND_FUNCT6) ++ DEFINE_OPIVX(vand, VAND_FUNCT6) ++ DEFINE_OPIVI(vand, VAND_FUNCT6) ++ DEFINE_OPIVV(vor, VOR_FUNCT6) ++ DEFINE_OPIVX(vor, VOR_FUNCT6) ++ DEFINE_OPIVI(vor, VOR_FUNCT6) ++ DEFINE_OPIVV(vxor, VXOR_FUNCT6) ++ DEFINE_OPIVX(vxor, VXOR_FUNCT6) ++ DEFINE_OPIVI(vxor, VXOR_FUNCT6) ++ DEFINE_OPIVV(vrgather, VRGATHER_FUNCT6) ++ DEFINE_OPIVX(vrgather, VRGATHER_FUNCT6) ++ DEFINE_OPIVI(vrgather, VRGATHER_FUNCT6) ++ ++ DEFINE_OPIVX(vslidedown, VSLIDEDOWN_FUNCT6) ++ DEFINE_OPIVI(vslidedown, VSLIDEDOWN_FUNCT6) ++ DEFINE_OPIVX(vslideup, VSLIDEUP_FUNCT6) ++ DEFINE_OPIVI(vslideup, VSLIDEUP_FUNCT6) ++ ++ DEFINE_OPIVV(vmseq, VMSEQ_FUNCT6) ++ DEFINE_OPIVX(vmseq, VMSEQ_FUNCT6) ++ DEFINE_OPIVI(vmseq, VMSEQ_FUNCT6) ++ ++ DEFINE_OPIVV(vmsne, VMSNE_FUNCT6) ++ DEFINE_OPIVX(vmsne, VMSNE_FUNCT6) ++ DEFINE_OPIVI(vmsne, VMSNE_FUNCT6) ++ ++ DEFINE_OPIVV(vmsltu, VMSLTU_FUNCT6) ++ DEFINE_OPIVX(vmsltu, VMSLTU_FUNCT6) ++ ++ DEFINE_OPIVV(vmslt, VMSLT_FUNCT6) ++ DEFINE_OPIVX(vmslt, VMSLT_FUNCT6) ++ ++ DEFINE_OPIVV(vmsle, VMSLE_FUNCT6) ++ DEFINE_OPIVX(vmsle, VMSLE_FUNCT6) ++ DEFINE_OPIVI(vmsle, VMSLE_FUNCT6) ++ ++ DEFINE_OPIVV(vmsleu, VMSLEU_FUNCT6) ++ DEFINE_OPIVX(vmsleu, VMSLEU_FUNCT6) ++ DEFINE_OPIVI(vmsleu, VMSLEU_FUNCT6) ++ ++ DEFINE_OPIVI(vmsgt, VMSGT_FUNCT6) ++ DEFINE_OPIVX(vmsgt, VMSGT_FUNCT6) ++ ++ DEFINE_OPIVI(vmsgtu, VMSGTU_FUNCT6) ++ DEFINE_OPIVX(vmsgtu, VMSGTU_FUNCT6) ++ ++ DEFINE_OPIVV(vsrl, VSRL_FUNCT6) ++ DEFINE_OPIVX(vsrl, VSRL_FUNCT6) ++ DEFINE_OPIVI(vsrl, VSRL_FUNCT6) ++ ++ DEFINE_OPIVV(vsra, VSRA_FUNCT6) ++ DEFINE_OPIVX(vsra, VSRA_FUNCT6) ++ DEFINE_OPIVI(vsra, VSRA_FUNCT6) ++ ++ DEFINE_OPIVV(vsll, VSLL_FUNCT6) ++ DEFINE_OPIVX(vsll, VSLL_FUNCT6) ++ DEFINE_OPIVI(vsll, VSLL_FUNCT6) ++ ++ DEFINE_OPIVV(vsmul, VSMUL_FUNCT6) ++ DEFINE_OPIVX(vsmul, VSMUL_FUNCT6) ++ ++ DEFINE_OPFVV(vfadd, VFADD_FUNCT6) ++ DEFINE_OPFVF(vfadd, VFADD_FUNCT6) ++ DEFINE_OPFVV(vfsub, VFSUB_FUNCT6) ++ DEFINE_OPFVF(vfsub, VFSUB_FUNCT6) ++ DEFINE_OPFVV(vfdiv, VFDIV_FUNCT6) ++ DEFINE_OPFVF(vfdiv, VFDIV_FUNCT6) ++ DEFINE_OPFVV(vfmul, VFMUL_FUNCT6) ++ DEFINE_OPFVF(vfmul, VFMUL_FUNCT6) ++ ++ // Vector Widening Floating-Point Add/Subtract Instructions ++ DEFINE_OPFVV(vfwadd, VFWADD_FUNCT6) ++ DEFINE_OPFVF(vfwadd, VFWADD_FUNCT6) ++ DEFINE_OPFVV(vfwsub, VFWSUB_FUNCT6) ++ DEFINE_OPFVF(vfwsub, VFWSUB_FUNCT6) ++ DEFINE_OPFWV(vfwadd, VFWADD_W_FUNCT6) ++ DEFINE_OPFWF(vfwadd, VFWADD_W_FUNCT6) ++ DEFINE_OPFWV(vfwsub, VFWSUB_W_FUNCT6) ++ DEFINE_OPFWF(vfwsub, VFWSUB_W_FUNCT6) ++ ++ // Vector Widening Floating-Point Reduction Instructions ++ DEFINE_OPFVV(vfwredusum, VFWREDUSUM_FUNCT6) ++ DEFINE_OPFVV(vfwredosum, VFWREDOSUM_FUNCT6) ++ ++ // Vector Widening Floating-Point Multiply ++ DEFINE_OPFVV(vfwmul, VFWMUL_FUNCT6) ++ DEFINE_OPFVF(vfwmul, VFWMUL_FUNCT6) ++ ++ DEFINE_OPFVV(vmfeq, VMFEQ_FUNCT6) ++ DEFINE_OPFVV(vmfne, VMFNE_FUNCT6) ++ DEFINE_OPFVV(vmflt, VMFLT_FUNCT6) ++ DEFINE_OPFVV(vmfle, VMFLE_FUNCT6) ++ DEFINE_OPFVV(vfmax, VMFMAX_FUNCT6) ++ DEFINE_OPFVV(vfmin, VMFMIN_FUNCT6) ++ DEFINE_OPFRED(vfredmax, VFREDMAX_FUNCT6) ++ ++ DEFINE_OPFVV(vfsngj, VFSGNJ_FUNCT6) ++ DEFINE_OPFVF(vfsngj, VFSGNJ_FUNCT6) ++ DEFINE_OPFVV(vfsngjn, VFSGNJN_FUNCT6) ++ DEFINE_OPFVF(vfsngjn, VFSGNJN_FUNCT6) ++ DEFINE_OPFVV(vfsngjx, VFSGNJX_FUNCT6) ++ DEFINE_OPFVF(vfsngjx, VFSGNJX_FUNCT6) ++ ++ // Vector Single-Width Floating-Point Fused Multiply-Add Instructions ++ DEFINE_OPFVV_FMA(vfmadd, VFMADD_FUNCT6) ++ DEFINE_OPFVF_FMA(vfmadd, VFMADD_FUNCT6) ++ DEFINE_OPFVV_FMA(vfmsub, VFMSUB_FUNCT6) ++ DEFINE_OPFVF_FMA(vfmsub, VFMSUB_FUNCT6) ++ DEFINE_OPFVV_FMA(vfmacc, VFMACC_FUNCT6) ++ DEFINE_OPFVF_FMA(vfmacc, VFMACC_FUNCT6) ++ DEFINE_OPFVV_FMA(vfmsac, VFMSAC_FUNCT6) ++ DEFINE_OPFVF_FMA(vfmsac, VFMSAC_FUNCT6) ++ DEFINE_OPFVV_FMA(vfnmadd, VFNMADD_FUNCT6) ++ DEFINE_OPFVF_FMA(vfnmadd, VFNMADD_FUNCT6) ++ DEFINE_OPFVV_FMA(vfnmsub, VFNMSUB_FUNCT6) ++ DEFINE_OPFVF_FMA(vfnmsub, VFNMSUB_FUNCT6) ++ DEFINE_OPFVV_FMA(vfnmacc, VFNMACC_FUNCT6) ++ DEFINE_OPFVF_FMA(vfnmacc, VFNMACC_FUNCT6) ++ DEFINE_OPFVV_FMA(vfnmsac, VFNMSAC_FUNCT6) ++ DEFINE_OPFVF_FMA(vfnmsac, VFNMSAC_FUNCT6) ++ ++ // Vector Widening Floating-Point Fused Multiply-Add Instructions ++ DEFINE_OPFVV_FMA(vfwmacc, VFWMACC_FUNCT6) ++ DEFINE_OPFVF_FMA(vfwmacc, VFWMACC_FUNCT6) ++ DEFINE_OPFVV_FMA(vfwnmacc, VFWNMACC_FUNCT6) ++ DEFINE_OPFVF_FMA(vfwnmacc, VFWNMACC_FUNCT6) ++ DEFINE_OPFVV_FMA(vfwmsac, VFWMSAC_FUNCT6) ++ DEFINE_OPFVF_FMA(vfwmsac, VFWMSAC_FUNCT6) ++ DEFINE_OPFVV_FMA(vfwnmsac, VFWNMSAC_FUNCT6) ++ DEFINE_OPFVF_FMA(vfwnmsac, VFWNMSAC_FUNCT6) ++ ++ // Vector Narrowing Fixed-Point Clip Instructions ++ DEFINE_OPIVV(vnclip, VNCLIP_FUNCT6) ++ DEFINE_OPIVX(vnclip, VNCLIP_FUNCT6) ++ DEFINE_OPIVI(vnclip, VNCLIP_FUNCT6) ++ DEFINE_OPIVV(vnclipu, VNCLIPU_FUNCT6) ++ DEFINE_OPIVX(vnclipu, VNCLIPU_FUNCT6) ++ DEFINE_OPIVI(vnclipu, VNCLIPU_FUNCT6) ++ ++ // Vector Integer Extension ++ DEFINE_OPMVV_VIE(vzext_vf8) ++ DEFINE_OPMVV_VIE(vsext_vf8) ++ DEFINE_OPMVV_VIE(vzext_vf4) ++ DEFINE_OPMVV_VIE(vsext_vf4) ++ DEFINE_OPMVV_VIE(vzext_vf2) ++ DEFINE_OPMVV_VIE(vsext_vf2) ++ ++# undef DEFINE_OPIVI ++# undef DEFINE_OPIVV ++# undef DEFINE_OPIVX ++# undef DEFINE_OPMVV ++# undef DEFINE_OPMVX ++# undef DEFINE_OPFVV ++# undef DEFINE_OPFWV ++# undef DEFINE_OPFVF ++# undef DEFINE_OPFWF ++# undef DEFINE_OPFVV_FMA ++# undef DEFINE_OPFVF_FMA ++# undef DEFINE_OPMVV_VIE ++# undef DEFINE_OPFRED ++ ++# define DEFINE_VFUNARY(name, funct6, vs1) \ ++ void name(VRegister vd, VRegister vs2, MaskType mask = NoMask) { \ ++ GenInstrV(funct6, OP_FVV, vd, vs1, vs2, mask); \ ++ } ++ ++ DEFINE_VFUNARY(vfcvt_xu_f_v, VFUNARY0_FUNCT6, VFCVT_XU_F_V) ++ DEFINE_VFUNARY(vfcvt_x_f_v, VFUNARY0_FUNCT6, VFCVT_X_F_V) ++ DEFINE_VFUNARY(vfcvt_f_x_v, VFUNARY0_FUNCT6, VFCVT_F_X_V) ++ DEFINE_VFUNARY(vfcvt_f_xu_v, VFUNARY0_FUNCT6, VFCVT_F_XU_V) ++ DEFINE_VFUNARY(vfwcvt_xu_f_v, VFUNARY0_FUNCT6, VFWCVT_XU_F_V) ++ DEFINE_VFUNARY(vfwcvt_x_f_v, VFUNARY0_FUNCT6, VFWCVT_X_F_V) ++ DEFINE_VFUNARY(vfwcvt_f_x_v, VFUNARY0_FUNCT6, VFWCVT_F_X_V) ++ DEFINE_VFUNARY(vfwcvt_f_xu_v, VFUNARY0_FUNCT6, VFWCVT_F_XU_V) ++ DEFINE_VFUNARY(vfwcvt_f_f_v, VFUNARY0_FUNCT6, VFWCVT_F_F_V) ++ ++ DEFINE_VFUNARY(vfncvt_f_f_w, VFUNARY0_FUNCT6, VFNCVT_F_F_W) ++ DEFINE_VFUNARY(vfncvt_x_f_w, VFUNARY0_FUNCT6, VFNCVT_X_F_W) ++ DEFINE_VFUNARY(vfncvt_xu_f_w, VFUNARY0_FUNCT6, VFNCVT_XU_F_W) ++ ++ DEFINE_VFUNARY(vfclass_v, VFUNARY1_FUNCT6, VFCLASS_V) ++ DEFINE_VFUNARY(vfsqrt_v, VFUNARY1_FUNCT6, VFSQRT_V) ++ DEFINE_VFUNARY(vfrsqrt7_v, VFUNARY1_FUNCT6, VFRSQRT7_V) ++ DEFINE_VFUNARY(vfrec7_v, VFUNARY1_FUNCT6, VFREC7_V) ++# undef DEFINE_VFUNARY ++ ++ void vnot_vv(VRegister dst, VRegister src, MaskType mask = NoMask) { ++ vxor_vi(dst, src, -1, mask); ++ } ++ ++ void vneg_vv(VRegister dst, VRegister src, MaskType mask = NoMask) { ++ vrsub_vx(dst, src, zero_reg, mask); ++ } ++ ++ void vfneg_vv(VRegister dst, VRegister src, MaskType mask = NoMask) { ++ vfsngjn_vv(dst, src, src, mask); ++ } ++ void vfabs_vv(VRegister dst, VRegister src, MaskType mask = NoMask) { ++ vfsngjx_vv(dst, src, src, mask); ++ } ++ void vfirst_m(Register rd, VRegister vs2, MaskType mask = NoMask); ++ ++ void vcpop_m(Register rd, VRegister vs2, MaskType mask = NoMask); ++ ++ protected: ++ void vsetvli(Register rd, Register rs1, VSew vsew, Vlmul vlmul, ++ TailAgnosticType tail = tu, MaskAgnosticType mask = mu); ++ ++ void vsetivli(Register rd, uint8_t uimm, VSew vsew, Vlmul vlmul, ++ TailAgnosticType tail = tu, MaskAgnosticType mask = mu); ++ ++ inline void vsetvlmax(Register rd, VSew vsew, Vlmul vlmul, ++ TailAgnosticType tail = tu, ++ MaskAgnosticType mask = mu) { ++ vsetvli(rd, zero_reg, vsew, vlmul, tu, mu); ++ } ++ ++ inline void vsetvl(VSew vsew, Vlmul vlmul, TailAgnosticType tail = tu, ++ MaskAgnosticType mask = mu) { ++ vsetvli(zero_reg, zero_reg, vsew, vlmul, tu, mu); ++ } ++ ++ void vsetvl(Register rd, Register rs1, Register rs2); ++ ++ // ----------------------------RVV------------------------------------------ ++ // vsetvl ++ void GenInstrV(Register rd, Register rs1, Register rs2); ++ // vsetvli ++ void GenInstrV(Register rd, Register rs1, uint32_t zimm); ++ // OPIVV OPFVV OPMVV ++ void GenInstrV(uint8_t funct6, OpcodeRISCVV opcode, VRegister vd, ++ VRegister vs1, VRegister vs2, MaskType mask = NoMask); ++ void GenInstrV(uint8_t funct6, OpcodeRISCVV opcode, VRegister vd, int8_t vs1, ++ VRegister vs2, MaskType mask = NoMask); ++ void GenInstrV(uint8_t funct6, OpcodeRISCVV opcode, VRegister vd, ++ VRegister vs2, MaskType mask = NoMask); ++ // OPMVV OPFVV ++ void GenInstrV(uint8_t funct6, OpcodeRISCVV opcode, Register rd, ++ VRegister vs1, VRegister vs2, MaskType mask = NoMask); ++ // OPFVV ++ void GenInstrV(uint8_t funct6, OpcodeRISCVV opcode, FPURegister fd, ++ VRegister vs1, VRegister vs2, MaskType mask = NoMask); ++ ++ // OPIVX OPMVX ++ void GenInstrV(uint8_t funct6, OpcodeRISCVV opcode, VRegister vd, ++ Register rs1, VRegister vs2, MaskType mask = NoMask); ++ // OPFVF ++ void GenInstrV(uint8_t funct6, OpcodeRISCVV opcode, VRegister vd, ++ FPURegister fs1, VRegister vs2, MaskType mask = NoMask); ++ // OPMVX ++ void GenInstrV(uint8_t funct6, Register rd, Register rs1, VRegister vs2, ++ MaskType mask = NoMask); ++ // OPIVI ++ void GenInstrV(uint8_t funct6, VRegister vd, int8_t simm5, VRegister vs2, ++ MaskType mask = NoMask); ++ ++ // VL VS ++ void GenInstrV(BaseOpcode opcode, uint8_t width, VRegister vd, Register rs1, ++ uint8_t umop, MaskType mask, uint8_t IsMop, bool IsMew, ++ uint8_t Nf); ++ ++ void GenInstrV(BaseOpcode opcode, uint8_t width, VRegister vd, Register rs1, ++ Register rs2, MaskType mask, uint8_t IsMop, bool IsMew, ++ uint8_t Nf); ++ // VL VS AMO ++ void GenInstrV(BaseOpcode opcode, uint8_t width, VRegister vd, Register rs1, ++ VRegister vs2, MaskType mask, uint8_t IsMop, bool IsMew, ++ uint8_t Nf); ++ // vmv_xs vcpop_m vfirst_m ++ void GenInstrV(uint8_t funct6, OpcodeRISCVV opcode, Register rd, uint8_t vs1, ++ VRegister vs2, MaskType mask); ++}; ++ ++class LoadStoreLaneParams { ++ public: ++ int sz; ++ uint8_t laneidx; ++ ++ LoadStoreLaneParams(MachineRepresentation rep, uint8_t laneidx); ++ ++ private: ++ LoadStoreLaneParams(uint8_t laneidx, int sz, int lanes) ++ : sz(sz), laneidx(laneidx % lanes) {} ++}; ++} // namespace jit ++} // namespace js ++#endif ++#endif // jit_riscv64_extension_Extension_riscv_V_h_ +diff --git a/js/src/jit/riscv64/extension/extension-riscv-zicsr.cc b/js/src/jit/riscv64/extension/extension-riscv-zicsr.cc +new file mode 100644 +index 0000000000..7fa87393a3 +--- /dev/null ++++ b/js/src/jit/riscv64/extension/extension-riscv-zicsr.cc +@@ -0,0 +1,44 @@ ++// Copyright 2022 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++#include "jit/riscv64/extension/extension-riscv-zicsr.h" ++ ++#include "jit/riscv64/constant/Constant-riscv64.h" ++#include "jit/riscv64/Register-riscv64.h" ++#include "jit/riscv64/Assembler-riscv64.h" ++#include "jit/riscv64/Architecture-riscv64.h" ++namespace js { ++namespace jit { ++ ++void AssemblerRISCVZicsr::csrrw(Register rd, ControlStatusReg csr, ++ Register rs1) { ++ GenInstrCSR_ir(0b001, rd, csr, rs1); ++} ++ ++void AssemblerRISCVZicsr::csrrs(Register rd, ControlStatusReg csr, ++ Register rs1) { ++ GenInstrCSR_ir(0b010, rd, csr, rs1); ++} ++ ++void AssemblerRISCVZicsr::csrrc(Register rd, ControlStatusReg csr, ++ Register rs1) { ++ GenInstrCSR_ir(0b011, rd, csr, rs1); ++} ++ ++void AssemblerRISCVZicsr::csrrwi(Register rd, ControlStatusReg csr, ++ uint8_t imm5) { ++ GenInstrCSR_ii(0b101, rd, csr, imm5); ++} ++ ++void AssemblerRISCVZicsr::csrrsi(Register rd, ControlStatusReg csr, ++ uint8_t imm5) { ++ GenInstrCSR_ii(0b110, rd, csr, imm5); ++} ++ ++void AssemblerRISCVZicsr::csrrci(Register rd, ControlStatusReg csr, ++ uint8_t imm5) { ++ GenInstrCSR_ii(0b111, rd, csr, imm5); ++} ++ ++} // namespace jit ++} // namespace js +diff --git a/js/src/jit/riscv64/extension/extension-riscv-zicsr.h b/js/src/jit/riscv64/extension/extension-riscv-zicsr.h +new file mode 100644 +index 0000000000..e1fba4fa57 +--- /dev/null ++++ b/js/src/jit/riscv64/extension/extension-riscv-zicsr.h +@@ -0,0 +1,57 @@ ++// Copyright 2022 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef jit_riscv64_extension_Extension_riscv_zicsr_h_ ++#define jit_riscv64_extension_Extension_riscv_zicsr_h_ ++#include "mozilla/Assertions.h" ++ ++#include ++ ++#include "jit/riscv64/extension/base-assembler-riscv.h" ++#include "jit/riscv64/Register-riscv64.h" ++namespace js { ++namespace jit { ++ ++class AssemblerRISCVZicsr : public AssemblerRiscvBase { ++ public: ++ // CSR ++ void csrrw(Register rd, ControlStatusReg csr, Register rs1); ++ void csrrs(Register rd, ControlStatusReg csr, Register rs1); ++ void csrrc(Register rd, ControlStatusReg csr, Register rs1); ++ void csrrwi(Register rd, ControlStatusReg csr, uint8_t imm5); ++ void csrrsi(Register rd, ControlStatusReg csr, uint8_t imm5); ++ void csrrci(Register rd, ControlStatusReg csr, uint8_t imm5); ++ ++ // Read instructions-retired counter ++ void rdinstret(Register rd) { csrrs(rd, csr_instret, zero_reg); } ++ void rdinstreth(Register rd) { csrrs(rd, csr_instreth, zero_reg); } ++ void rdcycle(Register rd) { csrrs(rd, csr_cycle, zero_reg); } ++ void rdcycleh(Register rd) { csrrs(rd, csr_cycleh, zero_reg); } ++ void rdtime(Register rd) { csrrs(rd, csr_time, zero_reg); } ++ void rdtimeh(Register rd) { csrrs(rd, csr_timeh, zero_reg); } ++ ++ void csrr(Register rd, ControlStatusReg csr) { csrrs(rd, csr, zero_reg); } ++ void csrw(ControlStatusReg csr, Register rs) { csrrw(zero_reg, csr, rs); } ++ void csrs(ControlStatusReg csr, Register rs) { csrrs(zero_reg, csr, rs); } ++ void csrc(ControlStatusReg csr, Register rs) { csrrc(zero_reg, csr, rs); } ++ ++ void csrwi(ControlStatusReg csr, uint8_t imm) { csrrwi(zero_reg, csr, imm); } ++ void csrsi(ControlStatusReg csr, uint8_t imm) { csrrsi(zero_reg, csr, imm); } ++ void csrci(ControlStatusReg csr, uint8_t imm) { csrrci(zero_reg, csr, imm); } ++ ++ void frcsr(Register rd) { csrrs(rd, csr_fcsr, zero_reg); } ++ void fscsr(Register rd, Register rs) { csrrw(rd, csr_fcsr, rs); } ++ void fscsr(Register rs) { csrrw(zero_reg, csr_fcsr, rs); } ++ ++ void frrm(Register rd) { csrrs(rd, csr_frm, zero_reg); } ++ void fsrm(Register rd, Register rs) { csrrw(rd, csr_frm, rs); } ++ void fsrm(Register rs) { csrrw(zero_reg, csr_frm, rs); } ++ ++ void frflags(Register rd) { csrrs(rd, csr_fflags, zero_reg); } ++ void fsflags(Register rd, Register rs) { csrrw(rd, csr_fflags, rs); } ++ void fsflags(Register rs) { csrrw(zero_reg, csr_fflags, rs); } ++}; ++} // namespace jit ++} // namespace js ++#endif // jit_riscv64_extension_Extension_riscv_zicsr_h_ +diff --git a/js/src/jit/riscv64/extension/extension-riscv-zifencei.cc b/js/src/jit/riscv64/extension/extension-riscv-zifencei.cc +new file mode 100644 +index 0000000000..ec8080b0cb +--- /dev/null ++++ b/js/src/jit/riscv64/extension/extension-riscv-zifencei.cc +@@ -0,0 +1,17 @@ ++// Copyright 2022 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++#include "jit/riscv64/extension/extension-riscv-zifencei.h" ++ ++#include "jit/riscv64/extension/base-assembler-riscv.h" ++#include "jit/riscv64/constant/Constant-riscv64.h" ++#include "jit/riscv64/Assembler-riscv64.h" ++#include "jit/riscv64/Architecture-riscv64.h" ++namespace js { ++namespace jit { ++ ++void AssemblerRISCVZifencei::fence_i() { ++ GenInstrI(0b001, MISC_MEM, ToRegister(0), ToRegister(0), 0); ++} ++} // namespace jit ++} // namespace js +diff --git a/js/src/jit/riscv64/extension/extension-riscv-zifencei.h b/js/src/jit/riscv64/extension/extension-riscv-zifencei.h +new file mode 100644 +index 0000000000..a245320ec4 +--- /dev/null ++++ b/js/src/jit/riscv64/extension/extension-riscv-zifencei.h +@@ -0,0 +1,20 @@ ++// Copyright 2022 the V8 project authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef jit_riscv64_extension_Extension_riscv_zifencei_h_ ++#define jit_riscv64_extension_Extension_riscv_zifencei_h_ ++#include "mozilla/Assertions.h" ++ ++#include ++ ++#include "jit/riscv64/extension/base-assembler-riscv.h" ++namespace js { ++namespace jit { ++class AssemblerRISCVZifencei : public AssemblerRiscvBase { ++ public: ++ void fence_i(); ++}; ++} // namespace jit ++} // namespace js ++#endif // jit_riscv64_extension_Extension_riscv_zifencei_h_ +diff --git a/js/src/jit/shared/Assembler-shared.h b/js/src/jit/shared/Assembler-shared.h +index 1393ac5152..ac8ae48a11 100644 +--- a/js/src/jit/shared/Assembler-shared.h ++++ b/js/src/jit/shared/Assembler-shared.h +@@ -26,13 +26,14 @@ + + #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \ + defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) || \ +- defined(JS_CODEGEN_LOONG64) ++ defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) + // Push return addresses callee-side. + # define JS_USE_LINK_REGISTER + #endif + + #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) || \ +- defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_LOONG64) ++ defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_LOONG64) || \ ++ defined(JS_CODEGEN_RISCV64) + // JS_CODELABEL_LINKMODE gives labels additional metadata + // describing how Bind() should patch them. + # define JS_CODELABEL_LINKMODE +diff --git a/js/src/jit/shared/CodeGenerator-shared.cpp b/js/src/jit/shared/CodeGenerator-shared.cpp +index 70c75ddb59..141c3c50e9 100644 +--- a/js/src/jit/shared/CodeGenerator-shared.cpp ++++ b/js/src/jit/shared/CodeGenerator-shared.cpp +@@ -997,7 +997,7 @@ Label* CodeGeneratorShared::getJumpLabelForBranch(MBasicBlock* block) { + // This function is not used for MIPS/MIPS64/LOONG64. They have + // branchToBlock. + #if !defined(JS_CODEGEN_MIPS32) && !defined(JS_CODEGEN_MIPS64) && \ +- !defined(JS_CODEGEN_LOONG64) ++ !defined(JS_CODEGEN_LOONG64) && !defined(JS_CODEGEN_RISCV64) + void CodeGeneratorShared::jumpToBlock(MBasicBlock* mir, + Assembler::Condition cond) { + // Skip past trivial blocks. +diff --git a/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h b/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h +index 476e1b2e75..6f8b2fcd74 100644 +--- a/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h ++++ b/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h +@@ -809,8 +809,8 @@ struct AssemblerBufferWithConstantPools + public: + // Get the next buffer offset where an instruction would be inserted. + // This may flush the current constant pool before returning nextOffset(). +- BufferOffset nextInstrOffset() { +- if (!hasSpaceForInsts(/* numInsts= */ 1, /* numPoolEntries= */ 0)) { ++ BufferOffset nextInstrOffset(int numInsts = 1) { ++ if (!hasSpaceForInsts(numInsts, /* numPoolEntries= */ 0)) { + JitSpew(JitSpew_Pools, + "[%d] nextInstrOffset @ %d caused a constant pool spill", id, + this->nextOffset().getOffset()); +@@ -897,7 +897,7 @@ struct AssemblerBufferWithConstantPools + + #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \ + defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) || \ +- defined(JS_CODEGEN_LOONG64) ++ defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) + return this->putU32Aligned(value); + #else + return this->AssemblerBuffer::putInt(value); +diff --git a/js/src/jit/shared/Lowering-shared-inl.h b/js/src/jit/shared/Lowering-shared-inl.h +index 9b496e88ba..501cae0f2c 100644 +--- a/js/src/jit/shared/Lowering-shared-inl.h ++++ b/js/src/jit/shared/Lowering-shared-inl.h +@@ -519,7 +519,7 @@ LAllocation LIRGeneratorShared::useRegisterOrNonDoubleConstant( + } + + #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \ +- defined(JS_CODEGEN_LOONG64) ++ defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) + LAllocation LIRGeneratorShared::useAnyOrConstant(MDefinition* mir) { + return useRegisterOrConstant(mir); + } +diff --git a/js/src/jsapi-tests/testJitABIcalls.cpp b/js/src/jsapi-tests/testJitABIcalls.cpp +index 4b72c54b36..c14e166790 100644 +--- a/js/src/jsapi-tests/testJitABIcalls.cpp ++++ b/js/src/jsapi-tests/testJitABIcalls.cpp +@@ -662,6 +662,9 @@ class JitABICall final : public JSAPITest, public DefineCheckArgs { + #elif defined(JS_CODEGEN_LOONG64) + Register base = t0; + regs.take(base); ++#elif defined(JS_CODEGEN_RISCV64) ++ Register base = t0; ++ regs.take(base); + #else + # error "Unknown architecture!" + #endif +diff --git a/js/src/jsapi-tests/testsJit.cpp b/js/src/jsapi-tests/testsJit.cpp +index 29de274004..2ad96c8e5d 100644 +--- a/js/src/jsapi-tests/testsJit.cpp ++++ b/js/src/jsapi-tests/testsJit.cpp +@@ -24,7 +24,7 @@ void PrepareJit(js::jit::MacroAssembler& masm) { + save.add(js::jit::d15); + #endif + #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) || \ +- defined(JS_CODEGEN_LOONG64) ++ defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) + save.add(js::jit::ra); + #elif defined(JS_USE_LINK_REGISTER) + save.add(js::jit::lr); +@@ -42,7 +42,7 @@ bool ExecuteJit(JSContext* cx, js::jit::MacroAssembler& masm) { + save.add(js::jit::d15); + #endif + #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) || \ +- defined(JS_CODEGEN_LOONG64) ++ defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) + save.add(js::jit::ra); + #elif defined(JS_USE_LINK_REGISTER) + save.add(js::jit::lr); +diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp +index 05a00528fd..5e24f1ba0c 100644 +--- a/js/src/shell/js.cpp ++++ b/js/src/shell/js.cpp +@@ -102,6 +102,9 @@ + #ifdef JS_SIMULATOR_LOONG64 + # include "jit/loong64/Simulator-loong64.h" + #endif ++#ifdef JS_SIMULATOR_RISCV64 ++# include "jit/riscv64/Simulator-riscv64.h" ++#endif + #include "jit/CacheIRHealth.h" + #include "jit/InlinableNatives.h" + #include "jit/Ion.h" +@@ -11435,6 +11438,28 @@ static bool SetContextOptions(JSContext* cx, const OptionParser& op) { + } + #endif + ++#ifdef DEBUG ++# ifdef JS_CODEGEN_RISCV64 ++ if (op.getBoolOption("riscv-debug")) { ++ jit::Assembler::FLAG_riscv_debug = true; ++ } ++# endif ++# ifdef JS_SIMULATOR_RISCV64 ++ if (op.getBoolOption("trace-sim")) { ++ jit::Simulator::FLAG_trace_sim = true; ++ } ++ if (op.getBoolOption("debug-sim")) { ++ jit::Simulator::FLAG_debug_sim = true; ++ } ++ if (op.getBoolOption("riscv-trap-to-simulator-debugger")) { ++ jit::Simulator::FLAG_riscv_trap_to_simulator_debugger = true; ++ } ++ int32_t stopAt = op.getIntOption("riscv-sim-stop-at"); ++ if (stopAt >= 0) { ++ jit::Simulator::StopSimAt = stopAt; ++ } ++# endif ++#endif + reportWarnings = op.getBoolOption('w'); + compileOnly = op.getBoolOption('c'); + printTiming = op.getBoolOption('b'); +@@ -12321,6 +12346,19 @@ int main(int argc, char** argv) { + "Stop the LoongArch64 simulator after the given " + "NUMBER of instructions.", + -1) || ++#ifdef JS_CODEGEN_RISCV64 ++ !op.addBoolOption('\0', "riscv-debug", "debug print riscv info.") || ++#endif ++#ifdef JS_SIMULATOR_RISCV64 ++ !op.addBoolOption('\0', "trace-sim", "print simulator info.") || ++ !op.addBoolOption('\0', "debug-sim", "debug simulator.") || ++ !op.addBoolOption('\0', "riscv-trap-to-simulator-debugger", ++ "trap into simulator debuggger.") || ++ !op.addIntOption('\0', "riscv-sim-stop-at", "NUMBER", ++ "Stop the riscv simulator after the given " ++ "NUMBER of instructions.", ++ -1) || ++#endif + !op.addIntOption('\0', "nursery-size", "SIZE-MB", + "Set the maximum nursery size in MB", + JS::DefaultNurseryMaxBytes / 1024 / 1024) || +diff --git a/js/src/util/Poison.h b/js/src/util/Poison.h +index cb8e1abc64..11e181612c 100644 +--- a/js/src/util/Poison.h ++++ b/js/src/util/Poison.h +@@ -95,6 +95,9 @@ const uint8_t JS_SCOPE_DATA_TRAILING_NAMES_PATTERN = 0xCC; + # define JS_SWEPT_CODE_PATTERN 0x01 // undefined instruction + #elif defined(JS_CODEGEN_LOONG64) + # define JS_SWEPT_CODE_PATTERN 0x01 // undefined instruction ++#elif defined(JS_CODEGEN_RISCV64) ++# define JS_SWEPT_CODE_PATTERN \ ++ 0x29 // illegal sb instruction, crashes in user mode. + #else + # error "JS_SWEPT_CODE_PATTERN not defined for this platform" + #endif +diff --git a/js/src/wasm/WasmBCDefs.h b/js/src/wasm/WasmBCDefs.h +index 0ddea318ce..4e2d35b488 100644 +--- a/js/src/wasm/WasmBCDefs.h ++++ b/js/src/wasm/WasmBCDefs.h +@@ -50,6 +50,9 @@ + #if defined(JS_CODEGEN_LOONG64) + # include "jit/loong64/Assembler-loong64.h" + #endif ++#if defined(JS_CODEGEN_RISCV64) ++# include "jit/riscv64/Assembler-riscv64.h" ++#endif + #include "js/ScalarType.h" + #include "util/Memory.h" + #include "wasm/TypedObject.h" +@@ -178,6 +181,10 @@ enum class RhsDestOp { True = true }; + # define RABALDR_PIN_INSTANCE + #endif + ++#ifdef JS_CODEGEN_RISCV64 ++# define RABALDR_PIN_INSTANCE ++#endif ++ + // Max number of pushes onto the value stack for any opcode or emitter that + // does not push a variable, unbounded amount (anything with multiple + // results). This includes also intermediate pushes such as values pushed as +diff --git a/js/src/wasm/WasmBCMemory.cpp b/js/src/wasm/WasmBCMemory.cpp +index 94e739090b..c24c5f477f 100644 +--- a/js/src/wasm/WasmBCMemory.cpp ++++ b/js/src/wasm/WasmBCMemory.cpp +@@ -332,7 +332,8 @@ void BaseCompiler::boundsCheckBelow4GBAccess(RegPtr instance, RegI64 ptr, + + // Make sure the ptr could be used as an index register. + static inline void ToValidIndex(MacroAssembler& masm, RegI32 ptr) { +-#if defined(JS_CODEGEN_MIPS64) || defined(JS_CODEGEN_LOONG64) ++#if defined(JS_CODEGEN_MIPS64) || defined(JS_CODEGEN_LOONG64) || \ ++ defined(JS_CODEGEN_RISCV64) + // When ptr is used as an index, it will be added to a 64-bit register. + // So we should explicitly promote ptr to 64-bit. Since now ptr holds a + // unsigned 32-bit value, we zero-extend it to 64-bit here. +@@ -548,6 +549,8 @@ void BaseCompiler::executeLoad(MemoryAccessDesc* access, AccessCheck* check, + } else { + masm.wasmLoad(*access, HeapReg, ptr, ptr, dest.any()); + } ++#elif defined(JS_CODEGEN_RISCV64) ++ MOZ_CRASH("UNIMPLEMENTED ON RISCV64"); + #else + MOZ_CRASH("BaseCompiler platform hook: load"); + #endif +@@ -1214,6 +1217,24 @@ static void Deallocate(BaseCompiler* bc, RegI32 rv, const Temps& temps) { + bc->maybeFree(temps.t2); + } + ++#elif defined(JS_CODEGEN_RISCV64) ++ ++using Temps = Nothing; ++ ++static void PopAndAllocate(BaseCompiler*, ValType, Scalar::Type, AtomicOp, ++ RegI32*, RegI32*, Temps*) { ++ MOZ_CRASH("UNIMPLEMENTED ON RISCV64"); ++} ++ ++static void Perform(BaseCompiler*, const MemoryAccessDesc&, BaseIndex, AtomicOp, ++ RegI32, RegI32, const Temps&) { ++ MOZ_CRASH("UNIMPLEMENTED ON RISCV64"); ++} ++ ++static void Deallocate(BaseCompiler*, RegI32, const Temps&) { ++ MOZ_CRASH("UNIMPLEMENTED ON RISCV64"); ++} ++ + #elif defined(JS_CODEGEN_NONE) + + using Temps = Nothing; +@@ -1374,6 +1395,20 @@ static void Deallocate(BaseCompiler* bc, AtomicOp op, RegI64 rv, RegI64 temp) { + bc->freeI64(rv); + bc->freeI64(temp); + } ++#elif defined(JS_CODEGEN_RISCV64) ++ ++static void PopAndAllocate(BaseCompiler*, AtomicOp, RegI64*, RegI64*, RegI64*) { ++ MOZ_CRASH("UNIMPLEMENTED ON RISCV64"); ++} ++ ++static void Perform(BaseCompiler*, const MemoryAccessDesc&, BaseIndex, ++ AtomicOp op, RegI64, RegI64, RegI64) { ++ MOZ_CRASH("UNIMPLEMENTED ON RISCV64"); ++} ++ ++static void Deallocate(BaseCompiler*, AtomicOp, RegI64, RegI64) { ++ MOZ_CRASH("UNIMPLEMENTED ON RISCV64"); ++} + + #elif defined(JS_CODEGEN_NONE) + +@@ -1549,6 +1584,21 @@ static void Deallocate(BaseCompiler* bc, RegI32 rv, const Temps& temps) { + bc->maybeFree(temps.t1); + bc->maybeFree(temps.t2); + } ++#elif defined(JS_CODEGEN_RISCV64) ++ ++using Temps = Nothing; ++ ++static void PopAndAllocate(BaseCompiler*, ValType, Scalar::Type, RegI32*, ++ RegI32*, Temps*) { ++ MOZ_CRASH("UNIMPLEMENTED ON RISCV64"); ++} ++static void Perform(BaseCompiler*, const MemoryAccessDesc&, BaseIndex, RegI32, ++ RegI32, const Temps&) { ++ MOZ_CRASH("UNIMPLEMENTED ON RISCV64"); ++} ++static void Deallocate(BaseCompiler*, RegI32, const Temps&) { ++ MOZ_CRASH("UNIMPLEMENTED ON RISCV64"); ++} + + #elif defined(JS_CODEGEN_NONE) + +@@ -1675,6 +1725,15 @@ static void Deallocate(BaseCompiler* bc, RegI64 rd, RegI64 rv) { + bc->maybeFree(rd); + } + ++#elif defined(JS_CODEGEN_RISCV64) ++ ++static void PopAndAllocate(BaseCompiler*, RegI64*, RegI64*) { ++ MOZ_CRASH("UNIMPLEMENTED ON RISCV64"); ++} ++static void Deallocate(BaseCompiler*, RegI64, RegI64) { ++ MOZ_CRASH("UNIMPLEMENTED ON RISCV64"); ++} ++ + #elif defined(JS_CODEGEN_NONE) + + static void PopAndAllocate(BaseCompiler*, RegI64*, RegI64*) {} +@@ -1859,6 +1918,23 @@ static void Deallocate(BaseCompiler* bc, RegI32 rexpect, RegI32 rnew, + bc->maybeFree(temps.t1); + bc->maybeFree(temps.t2); + } ++#elif defined(JS_CODEGEN_RISCV64) ++ ++using Temps = Nothing; ++ ++static void PopAndAllocate(BaseCompiler*, ValType, Scalar::Type, RegI32*, ++ RegI32*, RegI32*, Temps*) { ++ MOZ_CRASH("UNIMPLEMENTED ON RISCV64"); ++} ++ ++static void Perform(BaseCompiler*, const MemoryAccessDesc&, BaseIndex, RegI32, ++ RegI32, RegI32, const Temps& temps) { ++ MOZ_CRASH("UNIMPLEMENTED ON RISCV64"); ++} ++ ++static void Deallocate(BaseCompiler*, RegI32, RegI32, const Temps&) { ++ MOZ_CRASH("UNIMPLEMENTED ON RISCV64"); ++} + + #elif defined(JS_CODEGEN_NONE) + +@@ -2071,6 +2147,22 @@ static void Deallocate(BaseCompiler* bc, RegI64 rexpect, RegI64 rnew) { + bc->freeI64(rnew); + } + ++#elif defined(JS_CODEGEN_RISCV64) ++ ++template ++static void PopAndAllocate(BaseCompiler* bc, RegI64* rexpect, RegI64* rnew, ++ RegI64* rd) { ++ MOZ_CRASH("UNIMPLEMENTED ON RISCV64"); ++} ++static void Perform(BaseCompiler* bc, const MemoryAccessDesc& access, ++ BaseIndex srcAddr, RegI64 rexpect, RegI64 rnew, RegI64 rd) { ++ MOZ_CRASH("UNIMPLEMENTED ON RISCV64"); ++} ++template ++static void Deallocate(BaseCompiler* bc, RegI64 rexpect, RegI64 rnew) { ++ MOZ_CRASH("UNIMPLEMENTED ON RISCV64"); ++} ++ + #elif defined(JS_CODEGEN_NONE) + + template +diff --git a/js/src/wasm/WasmBaselineCompile.cpp b/js/src/wasm/WasmBaselineCompile.cpp +index bd5dd61885..e7fe8ffbe4 100644 +--- a/js/src/wasm/WasmBaselineCompile.cpp ++++ b/js/src/wasm/WasmBaselineCompile.cpp +@@ -273,7 +273,8 @@ void BaseCompiler::tableSwitch(Label* theTable, RegI32 switchValue, + // Jump indirect via table element. + masm.ma_ldr(DTRAddr(scratch, DtrRegImmShift(switchValue, LSL, 2)), pc, Offset, + Assembler::Always); +-#elif defined(JS_CODEGEN_MIPS64) || defined(JS_CODEGEN_LOONG64) ++#elif defined(JS_CODEGEN_MIPS64) || defined(JS_CODEGEN_LOONG64) || \ ++ defined(JS_CODEGEN_RISCV64) + ScratchI32 scratch(*this); + CodeLabel tableCl; + +@@ -689,7 +690,8 @@ void BaseCompiler::insertBreakablePoint(CallSiteDesc::Kind kind) { + masm.ma_bl(&debugTrapStub_, Assembler::NonZero); + masm.append(CallSiteDesc(iter_.lastOpcodeOffset(), kind), + CodeOffset(masm.currentOffset())); +-#elif defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_MIPS64) ++#elif defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_MIPS64) || \ ++ defined(JS_CODEGEN_RISCV64) + ScratchPtr scratch(*this); + Label L; + masm.loadPtr(Address(InstanceReg, Instance::offsetOfDebugTrapHandler()), +@@ -764,7 +766,8 @@ void BaseCompiler::insertBreakpointStub() { + masm.ma_tst(tmp2, Imm32(1 << func_.index % 32), tmp1, Assembler::Always); + masm.ma_bx(lr, Assembler::Zero); + } +-#elif defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_MIPS64) ++#elif defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_MIPS64) || \ ++ defined(JS_CODEGEN_RISCV64) + { + ScratchPtr scratch(*this); + +@@ -1047,7 +1050,8 @@ void BaseCompiler::popStackResults(ABIResultIter& iter, StackHeight stackBase) { + Stk& v = stk_.back(); + switch (v.kind()) { + case Stk::ConstI32: +-#if defined(JS_CODEGEN_MIPS64) || defined(JS_CODEGEN_LOONG64) ++#if defined(JS_CODEGEN_MIPS64) || defined(JS_CODEGEN_LOONG64) || \ ++ defined(JS_CODEGEN_RISCV64) + fr.storeImmediatePtrToStack(v.i32val_, resultHeight, temp); + #else + fr.storeImmediatePtrToStack(uint32_t(v.i32val_), resultHeight, temp); +@@ -1697,7 +1701,7 @@ RegI32 BaseCompiler::needRotate64Temp() { + return needI32(); + #elif defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM) || \ + defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_MIPS64) || \ +- defined(JS_CODEGEN_LOONG64) ++ defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) + return RegI32::Invalid(); + #else + MOZ_CRASH("BaseCompiler platform hook: needRotate64Temp"); +diff --git a/js/src/wasm/WasmCodegenTypes.cpp b/js/src/wasm/WasmCodegenTypes.cpp +index ee216a249c..98c23032ad 100644 +--- a/js/src/wasm/WasmCodegenTypes.cpp ++++ b/js/src/wasm/WasmCodegenTypes.cpp +@@ -168,8 +168,8 @@ CodeRange::CodeRange(uint32_t funcIndex, uint32_t funcLineOrBytecode, + kind_(Function) { + MOZ_ASSERT(begin_ < ret_); + MOZ_ASSERT(ret_ < end_); +- MOZ_ASSERT(offsets.uncheckedCallEntry - begin_ <= UINT8_MAX); +- MOZ_ASSERT(offsets.tierEntry - begin_ <= UINT8_MAX); ++ MOZ_ASSERT(offsets.uncheckedCallEntry - begin_ <= UINT16_MAX); ++ MOZ_ASSERT(offsets.tierEntry - begin_ <= UINT16_MAX); + u.funcIndex_ = funcIndex; + u.func.lineOrBytecode_ = funcLineOrBytecode; + u.func.beginToUncheckedCallEntry_ = offsets.uncheckedCallEntry - begin_; +diff --git a/js/src/wasm/WasmCodegenTypes.h b/js/src/wasm/WasmCodegenTypes.h +index d0b30fea01..1466aefd40 100644 +--- a/js/src/wasm/WasmCodegenTypes.h ++++ b/js/src/wasm/WasmCodegenTypes.h +@@ -286,8 +286,13 @@ class CodeRange { + union { + struct { + uint32_t lineOrBytecode_; ++#if defined(JS_CODEGEN_RISCV64) ++ uint16_t beginToUncheckedCallEntry_; ++ uint16_t beginToTierEntry_; ++#else + uint8_t beginToUncheckedCallEntry_; + uint8_t beginToTierEntry_; ++#endif + } func; + struct { + uint16_t beginToUntrustedFPStart_; +diff --git a/js/src/wasm/WasmCompile.cpp b/js/src/wasm/WasmCompile.cpp +index 842f75d07d..24fa5613d3 100644 +--- a/js/src/wasm/WasmCompile.cpp ++++ b/js/src/wasm/WasmCompile.cpp +@@ -52,6 +52,7 @@ uint32_t wasm::ObservedCPUFeatures() { + MIPS64 = 0x5, + ARM64 = 0x6, + LOONG64 = 0x7, ++ RISCV64 = 0x8, + ARCH_BITS = 3 + }; + +@@ -75,6 +76,9 @@ uint32_t wasm::ObservedCPUFeatures() { + #elif defined(JS_CODEGEN_LOONG64) + MOZ_ASSERT(jit::GetLOONG64Flags() <= (UINT32_MAX >> ARCH_BITS)); + return LOONG64 | (jit::GetLOONG64Flags() << ARCH_BITS); ++#elif defined(JS_CODEGEN_RISCV64) ++ MOZ_ASSERT(jit::GetRISCV64Flags() <= (UINT32_MAX >> ARCH_BITS)); ++ return RISCV64 | (jit::GetRISCV64Flags() << ARCH_BITS); + #elif defined(JS_CODEGEN_NONE) + return 0; + #else +diff --git a/js/src/wasm/WasmFrameIter.cpp b/js/src/wasm/WasmFrameIter.cpp +index 5446cccd4c..6084e9fa3f 100644 +--- a/js/src/wasm/WasmFrameIter.cpp ++++ b/js/src/wasm/WasmFrameIter.cpp +@@ -379,6 +379,12 @@ static const unsigned PushedRetAddr = 8; + static const unsigned PushedFP = 12; + static const unsigned SetFP = 16; + static const unsigned PoppedFP = 4; ++#elif defined(JS_CODEGEN_RISCV64) ++static const unsigned PushedRetAddr = 8; ++static const unsigned PushedFP = 16; ++static const unsigned SetFP = 20; ++static const unsigned PoppedFP = 4; ++static const unsigned PoppedFPJitEntry = 0; + #elif defined(JS_CODEGEN_NONE) + // Synthetic values to satisfy asserts and avoid compiler warnings. + static const unsigned PushedRetAddr = 0; +@@ -423,7 +429,6 @@ void wasm::ClearExitFP(MacroAssembler& masm, Register scratch) { + + static void GenerateCallablePrologue(MacroAssembler& masm, uint32_t* entry) { + AutoCreatedBy acb(masm, "GenerateCallablePrologue"); +- + masm.setFramePushed(0); + + // ProfilingFrameIterator needs to know the offsets of several key +@@ -461,6 +466,17 @@ static void GenerateCallablePrologue(MacroAssembler& masm, uint32_t* entry) { + masm.moveStackPtrTo(FramePointer); + MOZ_ASSERT_IF(!masm.oom(), SetFP == masm.currentOffset() - *entry); + } ++#elif defined(JS_CODEGEN_RISCV64) ++ { ++ *entry = masm.currentOffset(); ++ BlockTrampolinePoolScope block_trampoline_pool(&masm, 5); ++ masm.ma_push(ra); ++ MOZ_ASSERT_IF(!masm.oom(), PushedRetAddr == masm.currentOffset() - *entry); ++ masm.ma_push(FramePointer); ++ MOZ_ASSERT_IF(!masm.oom(), PushedFP == masm.currentOffset() - *entry); ++ masm.moveStackPtrTo(FramePointer); ++ MOZ_ASSERT_IF(!masm.oom(), SetFP == masm.currentOffset() - *entry); ++ } + #elif defined(JS_CODEGEN_ARM64) + { + // We do not use the PseudoStackPointer. However, we may be called in a +@@ -546,6 +562,18 @@ static void GenerateCallableEpilogue(MacroAssembler& masm, unsigned framePushed, + masm.addToStackPtr(Imm32(sizeof(Frame))); + masm.as_jirl(zero, ra, BOffImm16(0)); + ++#elif defined(JS_CODEGEN_RISCV64) ++ { ++ BlockTrampolinePoolScope block_trampoline_pool(&masm, 20); ++ masm.loadPtr(Address(StackPointer, Frame::callerFPOffset()), FramePointer); ++ poppedFP = masm.currentOffset(); ++ masm.loadPtr(Address(StackPointer, Frame::returnAddressOffset()), ra); ++ ++ *ret = masm.currentOffset(); ++ masm.addToStackPtr(Imm32(sizeof(Frame))); ++ masm.jalr(zero, ra, 0); ++ masm.nop(); ++ } + #elif defined(JS_CODEGEN_ARM64) + + // See comment at equivalent place in |GenerateCallablePrologue| above. +@@ -816,6 +844,10 @@ void wasm::GenerateJitEntryPrologue(MacroAssembler& masm, Offsets* offsets) { + #elif defined(JS_CODEGEN_LOONG64) + offsets->begin = masm.currentOffset(); + masm.push(ra); ++#elif defined(JS_CODEGEN_RISCV64) ++ BlockTrampolinePoolScope block_trampoline_pool(&masm, 10); ++ offsets->begin = masm.currentOffset(); ++ masm.push(ra); + #elif defined(JS_CODEGEN_ARM64) + AutoForbidPoolsAndNops afp(&masm, + /* number of instructions in scope = */ 3); +@@ -1132,6 +1164,25 @@ bool js::wasm::StartUnwinding(const RegisterState& registers, + fixedFP = fp; + AssertMatchesCallSite(fixedPC, fixedFP); + } else ++#elif defined(JS_CODEGEN_RISCV64) ++ if (codeRange->isThunk()) { ++ // The FarJumpIsland sequence temporary scrambles ra. ++ // Don't unwind to caller. ++ fixedPC = pc; ++ fixedFP = fp; ++ *unwoundCaller = false; ++ AssertMatchesCallSite( ++ Frame::fromUntaggedWasmExitFP(fp)->returnAddress(), ++ Frame::fromUntaggedWasmExitFP(fp)->rawCaller()); ++ } else if (offsetFromEntry < PushedFP) { ++ // On Riscv64 we rely on register state instead of state saved on ++ // stack until the wasm::Frame is completely built. ++ // On entry the return address is in ra (registers.lr) and ++ // fp holds the caller's fp. ++ fixedPC = (uint8_t*)registers.lr; ++ fixedFP = fp; ++ AssertMatchesCallSite(fixedPC, fixedFP); ++ } else + #elif defined(JS_CODEGEN_ARM64) + if (offsetFromEntry < PushedFP || codeRange->isThunk()) { + // Constraints above ensure that this covers BeforePushRetAddr and +@@ -1189,6 +1240,16 @@ bool js::wasm::StartUnwinding(const RegisterState& registers, + fixedPC = Frame::fromUntaggedWasmExitFP(sp)->returnAddress(); + fixedFP = fp; + AssertMatchesCallSite(fixedPC, fixedFP); ++#elif defined(JS_CODEGEN_RISCV64) ++ } else if (offsetInCode >= codeRange->ret() - PoppedFP && ++ offsetInCode <= codeRange->ret()) { ++ // The fixedFP field of the Frame has been loaded into fp. ++ // The ra might also be loaded, but the Frame structure is still on ++ // stack, so we can acess the ra from there. ++ MOZ_ASSERT(*sp == fp); ++ fixedPC = Frame::fromUntaggedWasmExitFP(sp)->returnAddress(); ++ fixedFP = fp; ++ AssertMatchesCallSite(fixedPC, fixedFP); + #elif defined(JS_CODEGEN_ARM64) + // The stack pointer does not move until all values have + // been restored so several cases can be coalesced here. +diff --git a/js/src/wasm/WasmGC.cpp b/js/src/wasm/WasmGC.cpp +index 5b8c112620..74b08f6237 100644 +--- a/js/src/wasm/WasmGC.cpp ++++ b/js/src/wasm/WasmGC.cpp +@@ -281,6 +281,11 @@ bool wasm::IsValidStackMapKey(bool debugEnabled, const uint8_t* nextPC) { + # elif defined(JS_CODEGEN_LOONG64) + // TODO(loong64): Implement IsValidStackMapKey. + return true; ++# elif defined(JS_CODEGEN_RISCV64) ++ const uint32_t* insn = (const uint32_t*)nextPC; ++ return (((uintptr_t(insn) & 3) == 0) && ++ (insn[-1] == 0x00006037 && insn[-2] == 0x00100073) || // break; ++ ((insn[-1] & kBaseOpcodeMask) == JALR)); + # else + MOZ_CRASH("IsValidStackMapKey: requires implementation on this platform"); + # endif +diff --git a/js/src/wasm/WasmIonCompile.cpp b/js/src/wasm/WasmIonCompile.cpp +index bf4eccdbd0..419902e3fe 100644 +--- a/js/src/wasm/WasmIonCompile.cpp ++++ b/js/src/wasm/WasmIonCompile.cpp +@@ -6973,9 +6973,10 @@ bool wasm::IonCompileFunctions(const ModuleEnvironment& moduleEnv, + } + + bool js::wasm::IonPlatformSupport() { +-#if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86) || \ +- defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS64) || \ +- defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_LOONG64) ++#if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86) || \ ++ defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS64) || \ ++ defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_LOONG64) || \ ++ defined(JS_CODEGEN_RISCV64) + return true; + #else + return false; +diff --git a/js/src/wasm/WasmStubs.cpp b/js/src/wasm/WasmStubs.cpp +index 5fe088b61b..c5ee3df170 100644 +--- a/js/src/wasm/WasmStubs.cpp ++++ b/js/src/wasm/WasmStubs.cpp +@@ -731,7 +731,7 @@ static bool GenerateInterpEntry(MacroAssembler& masm, const FuncExport& fe, + // Save the return address if it wasn't already saved by the call insn. + #ifdef JS_USE_LINK_REGISTER + # if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS64) || \ +- defined(JS_CODEGEN_LOONG64) ++ defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) + masm.pushReturnAddress(); + # elif defined(JS_CODEGEN_ARM64) + // WasmPush updates framePushed() unlike pushReturnAddress(), but that's +@@ -2227,7 +2227,7 @@ static bool GenerateImportInterpExit(MacroAssembler& masm, const FuncImport& fi, + MOZ_ASSERT(NonVolatileRegs.has(InstanceReg)); + #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM) || \ + defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_MIPS64) || \ +- defined(JS_CODEGEN_LOONG64) ++ defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) + MOZ_ASSERT(NonVolatileRegs.has(HeapReg)); + #endif + +@@ -2706,6 +2706,17 @@ static const LiveRegisterSet RegsToPreserve( + # ifdef ENABLE_WASM_SIMD + # error "high lanes of SIMD registers need to be saved too." + # endif ++#elif defined(JS_CODEGEN_RISCV64) ++static const LiveRegisterSet RegsToPreserve( ++ GeneralRegisterSet(Registers::AllMask & ++ ~((uint32_t(1) << Registers::tp) | ++ (uint32_t(1) << Registers::fp) | ++ (uint32_t(1) << Registers::sp) | ++ (uint32_t(1) << Registers::zero))), ++ FloatRegisterSet(FloatRegisters::AllDoubleMask)); ++# ifdef ENABLE_WASM_SIMD ++# error "high lanes of SIMD registers need to be saved too." ++# endif + #elif defined(JS_CODEGEN_ARM64) + // We assume that traps do not happen while lr is live. This both ensures that + // the size of RegsToPreserve is a multiple of 2 (preserving WasmStackAlignment) +diff --git a/js/xpconnect/src/XPCJSContext.cpp b/js/xpconnect/src/XPCJSContext.cpp +index bb0e2936e1..36929c6b28 100644 +--- a/js/xpconnect/src/XPCJSContext.cpp ++++ b/js/xpconnect/src/XPCJSContext.cpp +@@ -945,7 +945,8 @@ static void LoadStartupJSPrefs(XPCJSContext* xpccx) { + StaticPrefs::javascript_options_jit_full_debug_checks_DoNotUseDirectly()); + #endif + +-#if !defined(JS_CODEGEN_MIPS32) && !defined(JS_CODEGEN_MIPS64) ++#if !defined(JS_CODEGEN_MIPS32) && !defined(JS_CODEGEN_MIPS64) && \ ++ !defined(JS_CODEGEN_RISCV64) + JS_SetGlobalJitCompilerOption( + cx, JSJITCOMPILER_SPECTRE_INDEX_MASKING, + StaticPrefs::javascript_options_spectre_index_masking_DoNotUseDirectly()); +diff --git a/toolkit/content/license.html b/toolkit/content/license.html +index b213750f69..951f0888c5 100644 +--- a/toolkit/content/license.html ++++ b/toolkit/content/license.html +@@ -6544,8 +6544,9 @@ THE SOFTWARE. +

This license applies to certain files in the directories + js/src/irregexp, + js/src/builtin, +- js/src/jit/arm and +- js/src/jit/mips. ++ js/src/jit/arm, ++ js/src/jit/mips and ++ js/src/jit/riscv64. +

+
+ Copyright 2006-2012 the V8 project authors. All rights reserved.